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);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
76 : MetricSection (0.0, 0, MusicTime, true)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 XMLProperty const * prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 pulse p is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& p, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 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:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 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.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%f", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 The Shaggs - Things I Wonder
590 https://www.youtube.com/watch?v=9wQK6zMJOoQ
592 Tempo is the rate of the musical pulse.
593 Meters divide the pulses into measures and beats.
595 TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
596 and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
597 Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
598 a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
600 MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
602 Both tempos and meters have a pulse position and a frame position.
603 Meters also have a beat position, which is always 0.0 for the first meter.
604 TempoSections and MeterSections may be locked to either audio or music (position lock style).
605 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
612 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
613 sb->beat() - meter->beat() / meter->note_divisor().
614 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
615 two meters is of course
616 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
618 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
619 Beat to frame conversion of course requires the use of meter and tempo.
621 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
622 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
624 Recomputing the map is the process where the 'missing' position
625 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
626 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
627 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).
629 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
630 and querying its appropriate meter/tempo.
632 It is important to keep the _metrics in an order that makes sense.
633 Because ramped MusicTime and AudioTime tempos can interact with each other,
634 reordering is frequent. Care must be taken to keep _metrics in a solved state.
635 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
639 Music and audio-locked objects may seem interchangeable on the surface, but when translating
640 between audio samples and beats, keep in mind that a sample is only a quantised approximation
641 of the actual time (in minutes) of a beat.
642 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
643 mean that this frame is the actual location in time of 1|3|0.
645 You cannot use a frame measurement to determine beat distance except under special circumstances
646 (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).
648 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
649 sample space the user is operating at to be translated correctly to the object.
651 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
652 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
653 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
655 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
657 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
658 The result is rounded to audio frames.
659 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
662 frame_at_beat (beat_at_frame (frame)) == frame
664 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
666 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
667 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
668 For audio-locked objects, use frame position to calculate beat position.
670 The above pointless example would then do:
671 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
674 struct MetricSectionSorter {
675 bool operator() (const MetricSection* a, const MetricSection* b) {
676 return a->pulse() < b->pulse();
680 struct MetricSectionFrameSorter {
681 bool operator() (const MetricSection* a, const MetricSection* b) {
682 return a->frame() < b->frame();
686 TempoMap::TempoMap (framecnt_t fr)
689 BBT_Time start (1, 1, 0);
691 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
692 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
694 t->set_movable (false);
695 m->set_movable (false);
697 /* note: frame time is correct (zero) for both of these */
699 _metrics.push_back (t);
700 _metrics.push_back (m);
704 TempoMap::~TempoMap ()
706 Metrics::const_iterator d = _metrics.begin();
707 while (d != _metrics.end()) {
715 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_tempo_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_tempo_locked (const TempoSection& tempo)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
739 if (dynamic_cast<TempoSection*> (*i) != 0) {
740 if (tempo.frame() == (*i)->frame()) {
741 if ((*i)->movable()) {
754 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
756 bool removed = false;
759 Glib::Threads::RWLock::WriterLock lm (lock);
760 if ((removed = remove_meter_locked (tempo))) {
761 if (complete_operation) {
762 recompute_map (_metrics);
767 if (removed && complete_operation) {
768 PropertyChanged (PropertyChange ());
773 TempoMap::remove_meter_locked (const MeterSection& meter)
776 if (meter.position_lock_style() == AudioTime) {
777 /* remove meter-locked tempo */
778 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
780 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
781 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
790 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
791 if (dynamic_cast<MeterSection*> (*i) != 0) {
792 if (meter.frame() == (*i)->frame()) {
793 if ((*i)->movable()) {
806 TempoMap::do_insert (MetricSection* section)
808 bool need_add = true;
809 /* we only allow new meters to be inserted on beat 1 of an existing
813 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
815 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
817 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
818 corrected.second.beats = 1;
819 corrected.second.ticks = 0;
820 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
821 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
822 m->bbt(), corrected.second) << endmsg;
823 //m->set_pulse (corrected);
827 /* Look for any existing MetricSection that is of the same type and
828 in the same bar as the new one, and remove it before adding
829 the new one. Note that this means that if we find a matching,
830 existing section, we can break out of the loop since we're
831 guaranteed that there is only one such match.
834 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
836 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
837 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
838 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
839 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
841 if (tempo && insert_tempo) {
844 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
845 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
847 if (!tempo->movable()) {
849 /* can't (re)move this section, so overwrite
850 * its data content (but not its properties as
854 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
855 (*i)->set_position_lock_style (AudioTime);
857 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
858 t->set_type (insert_tempo->type());
868 } else if (meter && insert_meter) {
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
874 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
876 if (!meter->movable()) {
878 /* can't (re)move this section, so overwrite
879 * its data content (but not its properties as
883 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
884 (*i)->set_position_lock_style (AudioTime);
894 /* non-matching types, so we don't care */
898 /* Add the given MetricSection, if we didn't just reset an existing
903 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
904 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
907 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
908 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
911 bool const ipm = insert_meter->position_lock_style() == MusicTime;
912 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
917 } else if (insert_tempo) {
918 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
919 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
922 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
923 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
930 _metrics.insert (i, section);
931 //dump (_metrics, std::cout);
936 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
938 TempoSection* ts = 0;
940 Glib::Threads::RWLock::WriterLock lm (lock);
941 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
945 PropertyChanged (PropertyChange ());
951 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
953 const bool locked_to_meter = ts.locked_to_meter();
956 Glib::Threads::RWLock::WriterLock lm (lock);
957 TempoSection& first (first_tempo());
958 if (ts.frame() != first.frame()) {
959 remove_tempo_locked (ts);
960 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
962 first.set_type (type);
963 first.set_pulse (0.0);
964 first.set_frame (frame);
965 first.set_position_lock_style (AudioTime);
967 /* cannot move the first tempo section */
968 *static_cast<Tempo*>(&first) = tempo;
969 recompute_map (_metrics);
974 PropertyChanged (PropertyChange ());
978 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
979 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
981 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
982 t->set_locked_to_meter (locked_to_meter);
988 if (pls == AudioTime) {
989 solved = solve_map_frame (_metrics, t, t->frame());
991 solved = solve_map_pulse (_metrics, t, t->pulse());
993 recompute_meters (_metrics);
996 if (!solved && recompute) {
997 warning << "Adding tempo may have left the tempo map unsolved." << endmsg;
998 recompute_map (_metrics);
1005 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1007 MeterSection* m = 0;
1009 Glib::Threads::RWLock::WriterLock lm (lock);
1010 m = add_meter_locked (meter, beat, where, frame, pls, true);
1015 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1016 dump (_metrics, std::cerr);
1020 PropertyChanged (PropertyChange ());
1025 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1028 Glib::Threads::RWLock::WriterLock lm (lock);
1029 const double beat = beat_at_bbt_locked (_metrics, where);
1032 remove_meter_locked (ms);
1033 add_meter_locked (meter, beat, where, frame, pls, true);
1035 MeterSection& first (first_meter());
1036 TempoSection& first_t (first_tempo());
1037 /* cannot move the first meter section */
1038 *static_cast<Meter*>(&first) = meter;
1039 first.set_position_lock_style (AudioTime);
1040 first.set_pulse (0.0);
1041 first.set_frame (frame);
1042 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1043 first.set_beat (beat);
1044 first_t.set_frame (first.frame());
1045 first_t.set_pulse (0.0);
1046 first_t.set_position_lock_style (AudioTime);
1047 recompute_map (_metrics);
1051 PropertyChanged (PropertyChange ());
1055 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1057 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1058 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1059 TempoSection* mlt = 0;
1061 if (pls == AudioTime) {
1062 /* add meter-locked tempo */
1063 mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1071 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1072 bool solved = false;
1074 do_insert (new_meter);
1078 if (pls == AudioTime) {
1079 solved = solve_map_frame (_metrics, new_meter, frame);
1081 solved = solve_map_bbt (_metrics, new_meter, where);
1082 /* required due to resetting the pulse of meter-locked tempi above.
1083 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1084 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1086 recompute_map (_metrics);
1090 if (!solved && recompute) {
1091 /* if this has failed to solve, there is little we can do other than to ensure that
1092 the new map is recalculated.
1094 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1095 recompute_map (_metrics);
1102 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1104 Tempo newtempo (beats_per_minute, note_type);
1107 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1108 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1113 Glib::Threads::RWLock::WriterLock lm (lock);
1114 *((Tempo*) t) = newtempo;
1115 recompute_map (_metrics);
1117 PropertyChanged (PropertyChange ());
1124 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1126 Tempo newtempo (beats_per_minute, note_type);
1129 TempoSection* first;
1130 Metrics::iterator i;
1132 /* find the TempoSection immediately preceding "where"
1135 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1137 if ((*i)->frame() > where) {
1143 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1156 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1166 Glib::Threads::RWLock::WriterLock lm (lock);
1167 /* cannot move the first tempo section */
1168 *((Tempo*)prev) = newtempo;
1169 recompute_map (_metrics);
1172 PropertyChanged (PropertyChange ());
1176 TempoMap::first_meter () const
1178 const MeterSection *m = 0;
1180 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1181 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1186 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1187 abort(); /*NOTREACHED*/
1192 TempoMap::first_meter ()
1194 MeterSection *m = 0;
1196 /* CALLER MUST HOLD LOCK */
1198 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1199 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1204 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1205 abort(); /*NOTREACHED*/
1210 TempoMap::first_tempo () const
1212 const TempoSection *t = 0;
1214 /* CALLER MUST HOLD LOCK */
1216 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1217 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1221 if (!t->movable()) {
1227 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1228 abort(); /*NOTREACHED*/
1233 TempoMap::first_tempo ()
1235 TempoSection *t = 0;
1237 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1238 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1242 if (!t->movable()) {
1248 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1249 abort(); /*NOTREACHED*/
1253 TempoMap::recompute_tempi (Metrics& metrics)
1255 TempoSection* prev_t = 0;
1257 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1260 if ((*i)->is_tempo()) {
1261 t = static_cast<TempoSection*> (*i);
1265 if (!t->movable()) {
1273 if (t->position_lock_style() == AudioTime) {
1274 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1275 if (!t->locked_to_meter()) {
1276 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1280 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1281 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1288 prev_t->set_c_func (0.0);
1291 /* tempos must be positioned correctly.
1292 the current approach is to use a meter's bbt time as its base position unit.
1293 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1294 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1297 TempoMap::recompute_meters (Metrics& metrics)
1299 MeterSection* meter = 0;
1300 MeterSection* prev_m = 0;
1302 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1303 if (!(*mi)->is_tempo()) {
1304 meter = static_cast<MeterSection*> (*mi);
1305 if (meter->position_lock_style() == AudioTime) {
1307 pair<double, BBT_Time> b_bbt;
1308 TempoSection* meter_locked_tempo = 0;
1309 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1311 if ((*ii)->is_tempo()) {
1312 t = static_cast<TempoSection*> (*ii);
1313 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1314 meter_locked_tempo = t;
1321 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1322 if (beats + prev_m->beat() != meter->beat()) {
1323 /* reordering caused a bbt change */
1324 b_bbt = make_pair (beats + prev_m->beat()
1325 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1326 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1328 } else if (meter->movable()) {
1329 b_bbt = make_pair (meter->beat(), meter->bbt());
1330 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1333 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1335 if (meter_locked_tempo) {
1336 meter_locked_tempo->set_pulse (pulse);
1338 meter->set_beat (b_bbt);
1339 meter->set_pulse (pulse);
1344 pair<double, BBT_Time> b_bbt;
1346 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1347 if (beats + prev_m->beat() != meter->beat()) {
1348 /* reordering caused a bbt change */
1349 b_bbt = make_pair (beats + prev_m->beat()
1350 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1352 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1354 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1356 /* shouldn't happen - the first is audio-locked */
1357 pulse = pulse_at_beat_locked (metrics, meter->beat());
1358 b_bbt = make_pair (meter->beat(), meter->bbt());
1361 meter->set_beat (b_bbt);
1362 meter->set_pulse (pulse);
1363 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1372 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1374 /* CALLER MUST HOLD WRITE LOCK */
1378 /* we will actually stop once we hit
1385 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1388 /* silly call from Session::process() during startup
1393 recompute_tempi (metrics);
1394 recompute_meters (metrics);
1398 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1400 Glib::Threads::RWLock::ReaderLock lm (lock);
1401 TempoMetric m (first_meter(), first_tempo());
1403 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1404 at something, because we insert the default tempo and meter during
1405 TempoMap construction.
1407 now see if we can find better candidates.
1410 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1412 if ((*i)->frame() > frame) {
1426 /* XX meters only */
1428 TempoMap::metric_at (BBT_Time bbt) const
1430 Glib::Threads::RWLock::ReaderLock lm (lock);
1431 TempoMetric m (first_meter(), first_tempo());
1433 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1434 at something, because we insert the default tempo and meter during
1435 TempoMap construction.
1437 now see if we can find better candidates.
1440 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1442 if (!(*i)->is_tempo()) {
1443 mw = static_cast<MeterSection*> (*i);
1444 BBT_Time section_start (mw->bbt());
1446 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1458 TempoMap::beat_at_frame (const framecnt_t& frame) const
1460 Glib::Threads::RWLock::ReaderLock lm (lock);
1461 return beat_at_frame_locked (_metrics, frame);
1464 /* meter / tempo section based */
1466 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1468 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1469 MeterSection* prev_m = 0;
1470 MeterSection* next_m = 0;
1472 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1473 if (!(*i)->is_tempo()) {
1474 if (prev_m && (*i)->frame() > frame) {
1475 next_m = static_cast<MeterSection*> (*i);
1478 prev_m = static_cast<MeterSection*> (*i);
1481 if (frame < prev_m->frame()) {
1484 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1486 /* audio locked meters fake their beat */
1487 if (next_m && next_m->beat() < beat) {
1488 return next_m->beat();
1495 TempoMap::frame_at_beat (const double& beat) const
1497 Glib::Threads::RWLock::ReaderLock lm (lock);
1498 return frame_at_beat_locked (_metrics, beat);
1501 /* meter & tempo section based */
1503 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1505 MeterSection* prev_m = 0;
1506 TempoSection* prev_t = 0;
1510 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1511 if (!(*i)->is_tempo()) {
1512 m = static_cast<MeterSection*> (*i);
1513 if (prev_m && m->beat() > beat) {
1522 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1523 if ((*i)->is_tempo()) {
1524 t = static_cast<TempoSection*> (*i);
1525 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1533 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1537 TempoMap::tempo_at_frame (const framepos_t& frame) const
1539 Glib::Threads::RWLock::ReaderLock lm (lock);
1540 return tempo_at_frame_locked (_metrics, frame);
1544 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1546 TempoSection* prev_t = 0;
1550 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1551 if ((*i)->is_tempo()) {
1552 t = static_cast<TempoSection*> (*i);
1556 if ((prev_t) && t->frame() > frame) {
1557 /* t is the section past frame */
1558 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1559 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1566 const double ret = prev_t->beats_per_minute();
1567 const Tempo ret_tempo (ret, prev_t->note_type ());
1572 /** returns the frame at which the supplied tempo occurs, or
1573 * the frame of the last tempo section (search exhausted)
1574 * only the position of the first occurence will be returned
1578 TempoMap::frame_at_tempo (const Tempo& tempo) const
1580 Glib::Threads::RWLock::ReaderLock lm (lock);
1581 return frame_at_tempo_locked (_metrics, tempo);
1586 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1588 TempoSection* prev_t = 0;
1589 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1591 Metrics::const_iterator i;
1593 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1595 if ((*i)->is_tempo()) {
1596 t = static_cast<TempoSection*> (*i);
1602 const double t_ppm = t->beats_per_minute() / t->note_type();
1604 if (t_ppm == tempo_ppm) {
1609 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1611 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1612 return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1619 return prev_t->frame();
1622 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1623 * as there is no intermediate frame rounding.
1626 TempoMap::tempo_at_beat (const double& beat) const
1628 Glib::Threads::RWLock::ReaderLock lm (lock);
1629 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1630 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1631 const double note_type = prev_t->note_type();
1633 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1637 TempoMap::pulse_at_beat (const double& beat) const
1639 Glib::Threads::RWLock::ReaderLock lm (lock);
1640 return pulse_at_beat_locked (_metrics, beat);
1644 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1646 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1648 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1652 TempoMap::beat_at_pulse (const double& pulse) const
1654 Glib::Threads::RWLock::ReaderLock lm (lock);
1655 return beat_at_pulse_locked (_metrics, pulse);
1659 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1661 MeterSection* prev_m = 0;
1663 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1665 if (!(*i)->is_tempo()) {
1666 m = static_cast<MeterSection*> (*i);
1667 if (prev_m && m->pulse() > pulse) {
1668 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1676 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1681 TempoMap::pulse_at_frame (const framepos_t& frame) const
1683 Glib::Threads::RWLock::ReaderLock lm (lock);
1684 return pulse_at_frame_locked (_metrics, frame);
1687 /* tempo section based */
1689 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1691 /* HOLD (at least) THE READER LOCK */
1692 TempoSection* prev_t = 0;
1694 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1696 if ((*i)->is_tempo()) {
1697 t = static_cast<TempoSection*> (*i);
1701 if (prev_t && t->frame() > frame) {
1702 /*the previous ts is the one containing the frame */
1703 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1710 /* treated as constant for this ts */
1711 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1713 return pulses_in_section + prev_t->pulse();
1717 TempoMap::frame_at_pulse (const double& pulse) const
1719 Glib::Threads::RWLock::ReaderLock lm (lock);
1720 return frame_at_pulse_locked (_metrics, pulse);
1723 /* tempo section based */
1725 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1727 /* HOLD THE READER LOCK */
1729 const TempoSection* prev_t = 0;
1731 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1734 if ((*i)->is_tempo()) {
1735 t = static_cast<TempoSection*> (*i);
1739 if (prev_t && t->pulse() > pulse) {
1740 return prev_t->frame_at_pulse (pulse, _frame_rate);
1746 /* must be treated as constant, irrespective of _type */
1747 double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
1749 return (framecnt_t) floor (dtime) + prev_t->frame();
1753 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1755 Glib::Threads::RWLock::ReaderLock lm (lock);
1756 return beat_at_bbt_locked (_metrics, bbt);
1761 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1763 /* CALLER HOLDS READ LOCK */
1765 MeterSection* prev_m = 0;
1767 /* because audio-locked meters have 'fake' integral beats,
1768 there is no pulse offset here.
1772 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1773 if (!(*i)->is_tempo()) {
1774 m = static_cast<MeterSection*> (*i);
1776 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1777 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1785 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1786 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1787 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1793 TempoMap::bbt_at_beat (const double& beats)
1795 Glib::Threads::RWLock::ReaderLock lm (lock);
1796 return bbt_at_beat_locked (_metrics, beats);
1800 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1802 /* CALLER HOLDS READ LOCK */
1803 MeterSection* prev_m = 0;
1804 const double beats = max (0.0, b);
1806 MeterSection* m = 0;
1808 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1809 if (!(*i)->is_tempo()) {
1810 m = static_cast<MeterSection*> (*i);
1812 if (m->beat() > beats) {
1813 /* this is the meter after the one our beat is on*/
1822 const double beats_in_ms = beats - prev_m->beat();
1823 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1824 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1825 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1826 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1830 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1831 ret.beats = (uint32_t) floor (remaining_beats);
1832 ret.bars = total_bars;
1834 /* 0 0 0 to 1 1 0 - based mapping*/
1838 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1840 ret.ticks -= BBT_Time::ticks_per_beat;
1843 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1852 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1854 Glib::Threads::RWLock::ReaderLock lm (lock);
1856 return pulse_at_bbt_locked (_metrics, bbt);
1860 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1862 /* CALLER HOLDS READ LOCK */
1864 MeterSection* prev_m = 0;
1866 /* because audio-locked meters have 'fake' integral beats,
1867 there is no pulse offset here.
1871 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1872 if (!(*i)->is_tempo()) {
1873 m = static_cast<MeterSection*> (*i);
1875 if (m->bbt().bars > bbt.bars) {
1883 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1884 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1885 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1891 TempoMap::bbt_at_pulse (const double& pulse)
1893 Glib::Threads::RWLock::ReaderLock lm (lock);
1895 return bbt_at_pulse_locked (_metrics, pulse);
1899 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1901 MeterSection* prev_m = 0;
1903 MeterSection* m = 0;
1905 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1907 if (!(*i)->is_tempo()) {
1908 m = static_cast<MeterSection*> (*i);
1911 double const pulses_to_m = m->pulse() - prev_m->pulse();
1912 if (prev_m->pulse() + pulses_to_m > pulse) {
1913 /* this is the meter after the one our beat is on*/
1922 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1923 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1924 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1925 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1926 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1930 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1931 ret.beats = (uint32_t) floor (remaining_beats);
1932 ret.bars = total_bars;
1934 /* 0 0 0 to 1 1 0 mapping*/
1938 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1940 ret.ticks -= BBT_Time::ticks_per_beat;
1943 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1952 TempoMap::bbt_at_frame (framepos_t frame)
1959 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1962 Glib::Threads::RWLock::ReaderLock lm (lock);
1964 return bbt_at_frame_locked (_metrics, frame);
1968 TempoMap::bbt_at_frame_rt (framepos_t frame)
1970 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1973 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1976 return bbt_at_frame_locked (_metrics, frame);
1980 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1987 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1991 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1992 MeterSection* prev_m = 0;
1993 MeterSection* next_m = 0;
1997 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1998 if (!(*i)->is_tempo()) {
1999 m = static_cast<MeterSection*> (*i);
2000 if (prev_m && m->frame() > frame) {
2008 double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
2010 /* handle frame before first meter */
2011 if (frame < prev_m->frame()) {
2014 /* audio locked meters fake their beat */
2015 if (next_m && next_m->beat() < beat) {
2016 beat = next_m->beat();
2019 beat = max (0.0, beat);
2021 const double beats_in_ms = beat - prev_m->beat();
2022 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2023 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2024 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2025 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2029 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2030 ret.beats = (uint32_t) floor (remaining_beats);
2031 ret.bars = total_bars;
2033 /* 0 0 0 to 1 1 0 - based mapping*/
2037 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2039 ret.ticks -= BBT_Time::ticks_per_beat;
2042 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2051 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2054 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2058 if (bbt.beats < 1) {
2059 throw std::logic_error ("beats are counted from one");
2061 Glib::Threads::RWLock::ReaderLock lm (lock);
2063 return frame_at_bbt_locked (_metrics, bbt);
2066 /* meter & tempo section based */
2068 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2070 /* HOLD THE READER LOCK */
2072 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2077 TempoMap::check_solved (const Metrics& metrics) const
2079 TempoSection* prev_t = 0;
2080 MeterSection* prev_m = 0;
2082 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2085 if ((*i)->is_tempo()) {
2086 t = static_cast<TempoSection*> (*i);
2091 /* check ordering */
2092 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2096 /* precision check ensures tempo and frames align.*/
2097 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
2098 if (!t->locked_to_meter()) {
2103 /* gradient limit - who knows what it should be?
2104 things are also ok (if a little chaotic) without this
2106 if (fabs (prev_t->c_func()) > 1000.0) {
2107 //std::cout << "c : " << prev_t->c_func() << std::endl;
2114 if (!(*i)->is_tempo()) {
2115 m = static_cast<MeterSection*> (*i);
2116 if (prev_m && m->position_lock_style() == AudioTime) {
2117 const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
2118 const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate);
2119 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2120 It is complicated by the fact that audio locked meters represent a discontinuity in the pulse
2121 (they place an exact pulse at a particular time expressed only in frames).
2122 This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music)
2123 away from its actual frame (which is now the frame location of the exact pulse).
2124 This can result in the calculated frame (from the previous musical section)
2125 differing from the exact frame by one sample.
2128 if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) {
2142 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2144 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2146 if ((*i)->is_tempo()) {
2147 t = static_cast<TempoSection*> (*i);
2148 if (!t->movable()) {
2149 t->set_active (true);
2152 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2153 t->set_active (false);
2155 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2156 t->set_active (true);
2157 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2166 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2168 TempoSection* prev_t = 0;
2169 TempoSection* section_prev = 0;
2170 framepos_t first_m_frame = 0;
2172 /* can't move a tempo before the first meter */
2173 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2175 if (!(*i)->is_tempo()) {
2176 m = static_cast<MeterSection*> (*i);
2177 if (!m->movable()) {
2178 first_m_frame = m->frame();
2183 if (section->movable() && frame <= first_m_frame) {
2187 section->set_active (true);
2188 section->set_frame (frame);
2190 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2192 if ((*i)->is_tempo()) {
2193 t = static_cast<TempoSection*> (*i);
2200 section_prev = prev_t;
2201 if (t->locked_to_meter()) {
2206 if (t->position_lock_style() == MusicTime) {
2207 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2208 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2210 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2211 if (!t->locked_to_meter()) {
2212 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2221 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2222 if (!section->locked_to_meter()) {
2223 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2228 recompute_tempi (imaginary);
2230 if (check_solved (imaginary)) {
2233 dunp (imaginary, std::cout);
2237 MetricSectionFrameSorter fcmp;
2238 imaginary.sort (fcmp);
2240 recompute_tempi (imaginary);
2242 if (check_solved (imaginary)) {
2250 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2252 TempoSection* prev_t = 0;
2253 TempoSection* section_prev = 0;
2255 section->set_pulse (pulse);
2257 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2259 if ((*i)->is_tempo()) {
2260 t = static_cast<TempoSection*> (*i);
2264 if (!t->movable()) {
2271 section_prev = prev_t;
2274 if (t->position_lock_style() == MusicTime) {
2275 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2276 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2278 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2279 if (!t->locked_to_meter()) {
2280 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2289 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2290 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2294 recompute_tempi (imaginary);
2296 if (check_solved (imaginary)) {
2299 dunp (imaginary, std::cout);
2303 MetricSectionSorter cmp;
2304 imaginary.sort (cmp);
2306 recompute_tempi (imaginary);
2308 * XX need a restriction here, but only for this case,
2309 * as audio locked tempos don't interact in the same way.
2311 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2313 * |50 bpm |250 bpm |60 bpm
2314 * drag 250 to the pulse after 60->
2315 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2317 if (check_solved (imaginary)) {
2325 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2327 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2328 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2329 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2333 if (!section->movable()) {
2334 /* lock the first tempo to our first meter */
2335 if (!set_active_tempos (imaginary, frame)) {
2340 TempoSection* meter_locked_tempo = 0;
2342 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2344 if ((*ii)->is_tempo()) {
2345 t = static_cast<TempoSection*> (*ii);
2346 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2347 meter_locked_tempo = t;
2353 if (!meter_locked_tempo) {
2357 MeterSection* prev_m = 0;
2359 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2360 bool solved = false;
2362 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2364 if (!(*i)->is_tempo()) {
2365 m = static_cast<MeterSection*> (*i);
2367 if (prev_m && section->movable()) {
2368 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2369 if (beats + prev_m->beat() < section->beat()) {
2370 /* set the frame/pulse corresponding to its musical position,
2371 * as an earlier time than this has been requested.
2373 const double new_pulse = ((section->beat() - prev_m->beat())
2374 / prev_m->note_divisor()) + prev_m->pulse();
2376 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2378 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2379 meter_locked_tempo->set_pulse (new_pulse);
2380 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2381 section->set_frame (smallest_frame);
2382 section->set_pulse (new_pulse);
2387 Metrics::const_iterator d = future_map.begin();
2388 while (d != future_map.end()) {
2397 /* all is ok. set section's locked tempo if allowed.
2398 possibly disallowed if there is an adjacent audio-locked tempo.
2399 XX this check could possibly go. its never actually happened here.
2401 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2402 meter_copy->set_frame (frame);
2404 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2405 section->set_frame (frame);
2406 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2407 / prev_m->note_divisor()) + prev_m->pulse());
2408 solve_map_frame (imaginary, meter_locked_tempo, frame);
2413 Metrics::const_iterator d = future_map.begin();
2414 while (d != future_map.end()) {
2424 /* not movable (first meter atm) */
2426 tempo_copy->set_frame (frame);
2427 tempo_copy->set_pulse (0.0);
2429 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2430 section->set_frame (frame);
2431 meter_locked_tempo->set_frame (frame);
2432 meter_locked_tempo->set_pulse (0.0);
2433 solve_map_frame (imaginary, meter_locked_tempo, frame);
2438 Metrics::const_iterator d = future_map.begin();
2439 while (d != future_map.end()) {
2448 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2449 section->set_beat (b_bbt);
2450 section->set_pulse (0.0);
2460 MetricSectionFrameSorter fcmp;
2461 imaginary.sort (fcmp);
2463 recompute_meters (imaginary);
2469 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2471 /* disallow setting section to an existing meter's bbt */
2472 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2474 if (!(*i)->is_tempo()) {
2475 m = static_cast<MeterSection*> (*i);
2476 if (m != section && m->bbt().bars == when.bars) {
2482 MeterSection* prev_m = 0;
2483 MeterSection* section_prev = 0;
2485 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2487 if (!(*i)->is_tempo()) {
2488 m = static_cast<MeterSection*> (*i);
2489 pair<double, BBT_Time> b_bbt;
2490 double new_pulse = 0.0;
2492 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2493 section_prev = prev_m;
2494 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2495 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2496 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2498 section->set_beat (b_bbt);
2499 section->set_pulse (pulse);
2500 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2505 if (m->position_lock_style() == AudioTime) {
2506 TempoSection* meter_locked_tempo = 0;
2508 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2510 if ((*ii)->is_tempo()) {
2511 t = static_cast<TempoSection*> (*ii);
2512 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2513 meter_locked_tempo = t;
2519 if (!meter_locked_tempo) {
2524 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2526 if (beats + prev_m->beat() != m->beat()) {
2527 /* tempo/ meter change caused a change in beat (bar). */
2528 b_bbt = make_pair (beats + prev_m->beat()
2529 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2530 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2531 } else if (m->movable()) {
2532 b_bbt = make_pair (m->beat(), m->bbt());
2533 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2536 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2539 meter_locked_tempo->set_pulse (new_pulse);
2540 m->set_beat (b_bbt);
2541 m->set_pulse (new_pulse);
2545 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2546 if (beats + prev_m->beat() != m->beat()) {
2547 /* tempo/ meter change caused a change in beat (bar). */
2548 b_bbt = make_pair (beats + prev_m->beat()
2549 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2551 b_bbt = make_pair (beats + prev_m->beat()
2554 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2555 m->set_beat (b_bbt);
2556 m->set_pulse (new_pulse);
2557 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2564 if (!section_prev) {
2566 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2567 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2568 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2570 section->set_beat (b_bbt);
2571 section->set_pulse (pulse);
2572 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2575 MetricSectionSorter cmp;
2576 imaginary.sort (cmp);
2578 recompute_meters (imaginary);
2583 /** places a copy of _metrics into copy and returns a pointer
2584 * to section's equivalent in copy.
2587 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2589 TempoSection* ret = 0;
2591 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2594 if ((*i)->is_tempo()) {
2595 t = static_cast<TempoSection*> (*i);
2597 ret = new TempoSection (*t);
2598 copy.push_back (ret);
2602 TempoSection* cp = new TempoSection (*t);
2603 copy.push_back (cp);
2605 if (!(*i)->is_tempo()) {
2606 m = static_cast<MeterSection *> (*i);
2607 MeterSection* cp = new MeterSection (*m);
2608 copy.push_back (cp);
2616 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2618 MeterSection* ret = 0;
2620 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2623 if ((*i)->is_tempo()) {
2624 t = static_cast<TempoSection*> (*i);
2625 TempoSection* cp = new TempoSection (*t);
2626 copy.push_back (cp);
2629 if (!(*i)->is_tempo()) {
2630 m = static_cast<MeterSection *> (*i);
2632 ret = new MeterSection (*m);
2633 copy.push_back (ret);
2636 MeterSection* cp = new MeterSection (*m);
2637 copy.push_back (cp);
2644 /** answers the question "is this a valid beat position for this tempo section?".
2645 * it returns true if the tempo section can be moved to the requested bbt position,
2646 * leaving the tempo map in a solved state.
2647 * @param section the tempo section to be moved
2648 * @param bbt the requested new position for the tempo section
2649 * @return true if the tempo section can be moved to the position, otherwise false.
2652 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2655 TempoSection* tempo_copy = 0;
2658 Glib::Threads::RWLock::ReaderLock lm (lock);
2659 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2665 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2667 Metrics::const_iterator d = copy.begin();
2668 while (d != copy.end()) {
2677 * 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,
2678 * taking any possible reordering as a consequence of this into account.
2679 * @param section - the section to be altered
2680 * @param bbt - the bbt where the altered tempo will fall
2681 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2683 pair<double, framepos_t>
2684 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2687 pair<double, framepos_t> ret = make_pair (0.0, 0);
2689 Glib::Threads::RWLock::ReaderLock lm (lock);
2691 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2693 const double beat = beat_at_bbt_locked (future_map, bbt);
2695 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2696 ret.first = tempo_copy->pulse();
2697 ret.second = tempo_copy->frame();
2699 ret.first = section->pulse();
2700 ret.second = section->frame();
2703 Metrics::const_iterator d = future_map.begin();
2704 while (d != future_map.end()) {
2712 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2715 bool was_musical = ts->position_lock_style() == MusicTime;
2717 if (sub_num == 0 && was_musical) {
2718 /* if we're not snapping to music,
2719 AudioTime and MusicTime may be treated identically.
2721 ts->set_position_lock_style (AudioTime);
2724 if (ts->position_lock_style() == MusicTime) {
2726 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2727 Glib::Threads::RWLock::WriterLock lm (lock);
2728 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2729 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2730 double pulse = pulse_at_beat_locked (future_map, beat);
2732 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2733 solve_map_pulse (_metrics, ts, pulse);
2734 recompute_meters (_metrics);
2741 Glib::Threads::RWLock::WriterLock lm (lock);
2742 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2743 if (solve_map_frame (future_map, tempo_copy, frame)) {
2744 solve_map_frame (_metrics, ts, frame);
2745 recompute_meters (_metrics);
2750 if (sub_num == 0 && was_musical) {
2751 ts->set_position_lock_style (MusicTime);
2754 Metrics::const_iterator d = future_map.begin();
2755 while (d != future_map.end()) {
2760 MetricPositionChanged (); // Emit Signal
2764 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2768 if (ms->position_lock_style() == AudioTime) {
2771 Glib::Threads::RWLock::WriterLock lm (lock);
2772 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2774 if (solve_map_frame (future_map, copy, frame)) {
2775 solve_map_frame (_metrics, ms, frame);
2776 recompute_tempi (_metrics);
2781 Glib::Threads::RWLock::WriterLock lm (lock);
2782 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2784 const double beat = beat_at_frame_locked (_metrics, frame);
2785 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2787 if (solve_map_bbt (future_map, copy, bbt)) {
2788 solve_map_bbt (_metrics, ms, bbt);
2789 recompute_tempi (_metrics);
2794 Metrics::const_iterator d = future_map.begin();
2795 while (d != future_map.end()) {
2800 MetricPositionChanged (); // Emit Signal
2804 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2807 bool can_solve = false;
2809 Glib::Threads::RWLock::WriterLock lm (lock);
2810 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2811 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2812 recompute_tempi (future_map);
2814 if (check_solved (future_map)) {
2815 ts->set_beats_per_minute (bpm.beats_per_minute());
2816 recompute_map (_metrics);
2821 Metrics::const_iterator d = future_map.begin();
2822 while (d != future_map.end()) {
2827 MetricPositionChanged (); // Emit Signal
2833 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2836 Ts (future prev_t) Tnext
2839 |----------|----------
2846 Glib::Threads::RWLock::WriterLock lm (lock);
2852 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2853 TempoSection* prev_to_prev_t = 0;
2854 const frameoffset_t fr_off = end_frame - frame;
2856 if (prev_t && prev_t->pulse() > 0.0) {
2857 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2860 TempoSection* next_t = 0;
2861 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2862 TempoSection* t = 0;
2863 if ((*i)->is_tempo()) {
2864 t = static_cast<TempoSection*> (*i);
2865 if (t->frame() > ts->frame()) {
2871 /* minimum allowed measurement distance in frames */
2872 const framepos_t min_dframe = 2;
2874 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2875 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2877 double contribution = 0.0;
2879 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2880 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2883 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2885 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2886 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2890 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2892 if (prev_t->position_lock_style() == MusicTime) {
2893 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2894 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
2896 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2897 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2899 new_bpm = prev_t->beats_per_minute();
2902 /* prev to prev is irrelevant */
2904 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
2905 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2907 new_bpm = prev_t->beats_per_minute();
2912 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2913 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
2915 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2916 / (double) ((end_frame) - prev_to_prev_t->frame()));
2918 new_bpm = prev_t->beats_per_minute();
2921 /* prev_to_prev_t is irrelevant */
2923 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
2924 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2926 new_bpm = prev_t->beats_per_minute();
2932 double frame_ratio = 1.0;
2933 double pulse_ratio = 1.0;
2934 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2936 if (prev_to_prev_t) {
2937 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
2938 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2940 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
2941 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2944 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
2945 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2947 pulse_ratio = (start_pulse / end_pulse);
2949 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2952 /* don't clamp and proceed here.
2953 testing has revealed that this can go negative,
2954 which is an entirely different thing to just being too low.
2956 if (new_bpm < 0.5) {
2959 new_bpm = min (new_bpm, (double) 1000.0);
2960 prev_t->set_beats_per_minute (new_bpm);
2961 recompute_tempi (future_map);
2962 recompute_meters (future_map);
2964 if (check_solved (future_map)) {
2965 ts->set_beats_per_minute (new_bpm);
2966 recompute_tempi (_metrics);
2967 recompute_meters (_metrics);
2971 Metrics::const_iterator d = future_map.begin();
2972 while (d != future_map.end()) {
2977 MetricPositionChanged (); // Emit Signal
2981 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
2983 Glib::Threads::RWLock::ReaderLock lm (lock);
2985 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
2989 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
2991 double beat = beat_at_frame_locked (metrics, frame);
2993 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
2994 } else if (sub_num == 1) {
2996 beat = floor (beat + 0.5);
2997 } else if (sub_num == -1) {
2999 Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
3002 beat = beat_at_bbt_locked (metrics, bbt);
3008 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3010 Glib::Threads::RWLock::ReaderLock lm (lock);
3012 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
3013 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
3014 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
3016 return frame_at_beat_locked (_metrics, total_beats);
3020 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3022 return round_to_type (fr, dir, Bar);
3026 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3028 return round_to_type (fr, dir, Beat);
3032 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3034 Glib::Threads::RWLock::ReaderLock lm (lock);
3035 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
3036 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3037 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3039 ticks -= beats * BBT_Time::ticks_per_beat;
3042 /* round to next (or same iff dir == RoundUpMaybe) */
3044 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3046 if (mod == 0 && dir == RoundUpMaybe) {
3047 /* right on the subdivision, which is fine, so do nothing */
3049 } else if (mod == 0) {
3050 /* right on the subdivision, so the difference is just the subdivision ticks */
3051 ticks += ticks_one_subdivisions_worth;
3054 /* not on subdivision, compute distance to next subdivision */
3056 ticks += ticks_one_subdivisions_worth - mod;
3059 if (ticks >= BBT_Time::ticks_per_beat) {
3060 ticks -= BBT_Time::ticks_per_beat;
3062 } else if (dir < 0) {
3064 /* round to previous (or same iff dir == RoundDownMaybe) */
3066 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3068 if (difference == 0 && dir == RoundDownAlways) {
3069 /* right on the subdivision, but force-rounding down,
3070 so the difference is just the subdivision ticks */
3071 difference = ticks_one_subdivisions_worth;
3074 if (ticks < difference) {
3075 ticks = BBT_Time::ticks_per_beat - ticks;
3077 ticks -= difference;
3081 /* round to nearest */
3084 /* compute the distance to the previous and next subdivision */
3086 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3088 /* closer to the next subdivision, so shift forward */
3090 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3092 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3094 if (ticks > BBT_Time::ticks_per_beat) {
3096 ticks -= BBT_Time::ticks_per_beat;
3097 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3100 } else if (rem > 0) {
3102 /* closer to previous subdivision, so shift backward */
3106 /* can't go backwards past zero, so ... */
3109 /* step back to previous beat */
3111 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3112 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3114 ticks = lrint (ticks - rem);
3115 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3118 /* on the subdivision, do nothing */
3122 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3128 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3130 Glib::Threads::RWLock::ReaderLock lm (lock);
3132 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3133 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3138 /* find bar previous to 'frame' */
3141 return frame_at_bbt_locked (_metrics, bbt);
3143 } else if (dir > 0) {
3144 /* find bar following 'frame' */
3148 return frame_at_bbt_locked (_metrics, bbt);
3150 /* true rounding: find nearest bar */
3151 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3154 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3156 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3158 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3169 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3170 } else if (dir > 0) {
3171 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3173 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3182 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3183 framepos_t lower, framepos_t upper)
3185 Glib::Threads::RWLock::ReaderLock lm (lock);
3186 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3188 /* although the map handles negative beats, bbt doesn't. */
3193 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3197 while (pos < upper) {
3198 pos = frame_at_beat_locked (_metrics, cnt);
3199 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3200 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3201 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3202 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3208 TempoMap::tempo_section_at_frame (framepos_t frame) const
3210 Glib::Threads::RWLock::ReaderLock lm (lock);
3211 return tempo_section_at_frame_locked (_metrics, frame);
3215 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3217 TempoSection* prev = 0;
3221 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3223 if ((*i)->is_tempo()) {
3224 t = static_cast<TempoSection*> (*i);
3228 if (prev && t->frame() > frame) {
3238 abort(); /*NOTREACHED*/
3245 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3247 TempoSection* prev_t = 0;
3248 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3252 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3253 if ((*i)->is_tempo()) {
3254 t = static_cast<TempoSection*> (*i);
3255 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3265 /* don't use this to calculate length (the tempo is only correct for this frame).
3266 do that stuff based on the beat_at_frame and frame_at_beat api
3269 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3271 Glib::Threads::RWLock::ReaderLock lm (lock);
3273 const TempoSection* ts_at = 0;
3274 const TempoSection* ts_after = 0;
3275 Metrics::const_iterator i;
3278 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3280 if ((*i)->is_tempo()) {
3281 t = static_cast<TempoSection*> (*i);
3285 if (ts_at && (*i)->frame() > frame) {
3294 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3296 /* must be treated as constant tempo */
3297 return ts_at->frames_per_beat (_frame_rate);
3301 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3303 Metrics::const_iterator i;
3304 MeterSection* prev = 0;
3308 for (i = metrics.begin(); i != metrics.end(); ++i) {
3310 if (!(*i)->is_tempo()) {
3311 m = static_cast<MeterSection*> (*i);
3313 if (prev && (*i)->frame() > frame) {
3323 abort(); /*NOTREACHED*/
3331 TempoMap::meter_section_at_frame (framepos_t frame) const
3333 Glib::Threads::RWLock::ReaderLock lm (lock);
3334 return meter_section_at_frame_locked (_metrics, frame);
3338 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3340 MeterSection* prev_m = 0;
3342 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3344 if (!(*i)->is_tempo()) {
3345 m = static_cast<MeterSection*> (*i);
3346 if (prev_m && m->beat() > beat) {
3357 TempoMap::meter_section_at_beat (double beat) const
3359 Glib::Threads::RWLock::ReaderLock lm (lock);
3360 return meter_section_at_beat_locked (_metrics, beat);
3364 TempoMap::meter_at_frame (framepos_t frame) const
3366 TempoMetric m (metric_at (frame));
3371 TempoMap::fix_legacy_session ()
3373 MeterSection* prev_m = 0;
3374 TempoSection* prev_t = 0;
3376 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3380 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3381 if (!m->movable()) {
3382 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3386 m->set_position_lock_style (AudioTime);
3391 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3392 + (m->bbt().beats - 1)
3393 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3395 m->set_beat (start);
3396 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3397 + (m->bbt().beats - 1)
3398 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3399 m->set_pulse (start_beat / prev_m->note_divisor());
3402 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3408 if (!t->movable()) {
3411 t->set_position_lock_style (AudioTime);
3417 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3418 + (t->legacy_bbt().beats - 1)
3419 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3421 t->set_pulse (beat / prev_m->note_divisor());
3423 /* really shouldn't happen but.. */
3424 t->set_pulse (beat / 4.0);
3433 TempoMap::get_state ()
3435 Metrics::const_iterator i;
3436 XMLNode *root = new XMLNode ("TempoMap");
3439 Glib::Threads::RWLock::ReaderLock lm (lock);
3440 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3441 root->add_child_nocopy ((*i)->get_state());
3449 TempoMap::set_state (const XMLNode& node, int /*version*/)
3452 Glib::Threads::RWLock::WriterLock lm (lock);
3455 XMLNodeConstIterator niter;
3456 Metrics old_metrics (_metrics);
3459 nlist = node.children();
3461 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3462 XMLNode* child = *niter;
3464 if (child->name() == TempoSection::xml_state_node_name) {
3467 TempoSection* ts = new TempoSection (*child);
3468 _metrics.push_back (ts);
3471 catch (failed_constructor& err){
3472 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3473 _metrics = old_metrics;
3477 } else if (child->name() == MeterSection::xml_state_node_name) {
3480 MeterSection* ms = new MeterSection (*child);
3481 _metrics.push_back (ms);
3484 catch (failed_constructor& err) {
3485 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3486 _metrics = old_metrics;
3492 if (niter == nlist.end()) {
3493 MetricSectionSorter cmp;
3494 _metrics.sort (cmp);
3497 /* check for legacy sessions where bbt was the base musical unit for tempo */
3498 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3500 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3501 if (t->legacy_bbt().bars != 0) {
3502 fix_legacy_session();
3509 /* check for multiple tempo/meters at the same location, which
3510 ardour2 somehow allowed.
3513 Metrics::iterator prev = _metrics.end();
3514 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3515 if (prev != _metrics.end()) {
3517 MeterSection* prev_m;
3519 TempoSection* prev_t;
3520 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3521 if (prev_m->pulse() == ms->pulse()) {
3522 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3523 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3526 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3527 if (prev_t->pulse() == ts->pulse()) {
3528 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3529 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3537 recompute_map (_metrics);
3540 PropertyChanged (PropertyChange ());
3546 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3548 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3549 const MeterSection* m;
3550 const TempoSection* t;
3551 const TempoSection* prev_t = 0;
3553 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3555 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3556 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3557 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3558 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3560 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3561 o << "calculated : " << prev_t->tempo_at_pulse (t->pulse()) * prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
3564 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3565 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3566 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3569 o << "------" << std::endl;
3573 TempoMap::n_tempos() const
3575 Glib::Threads::RWLock::ReaderLock lm (lock);
3578 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3579 if ((*i)->is_tempo()) {
3588 TempoMap::n_meters() const
3590 Glib::Threads::RWLock::ReaderLock lm (lock);
3593 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3594 if (!(*i)->is_tempo()) {
3603 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3606 Glib::Threads::RWLock::WriterLock lm (lock);
3607 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3608 if ((*i)->frame() >= where && (*i)->movable ()) {
3609 (*i)->set_frame ((*i)->frame() + amount);
3613 /* now reset the BBT time of all metrics, based on their new
3614 * audio time. This is the only place where we do this reverse
3618 Metrics::iterator i;
3619 const MeterSection* meter;
3620 const TempoSection* tempo;
3624 meter = &first_meter ();
3625 tempo = &first_tempo ();
3630 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3633 MetricSection* prev = 0;
3635 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3638 //TempoMetric metric (*meter, *tempo);
3639 MeterSection* ms = const_cast<MeterSection*>(meter);
3640 TempoSection* ts = const_cast<TempoSection*>(tempo);
3643 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3647 ts->set_pulse (t->pulse());
3649 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3650 ts->set_pulse (m->pulse());
3652 ts->set_frame (prev->frame());
3656 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3657 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3658 ms->set_beat (start);
3659 ms->set_pulse (m->pulse());
3661 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3665 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3666 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3667 ms->set_beat (start);
3668 ms->set_pulse (t->pulse());
3670 ms->set_frame (prev->frame());
3674 // metric will be at frames=0 bbt=1|1|0 by default
3675 // which is correct for our purpose
3678 // cerr << bbt << endl;
3680 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3684 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3686 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3687 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3688 bbt = bbt_at_frame_locked (_metrics, m->frame());
3690 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3696 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3697 /* round up to next beat */
3703 if (bbt.beats != 1) {
3704 /* round up to next bar */
3709 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3710 m->set_beat (start);
3711 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3713 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3715 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3716 abort(); /*NOTREACHED*/
3722 recompute_map (_metrics);
3726 PropertyChanged (PropertyChange ());
3729 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3733 std::list<MetricSection*> metric_kill_list;
3735 TempoSection* last_tempo = NULL;
3736 MeterSection* last_meter = NULL;
3737 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3738 bool meter_after = false; // is there a meter marker likewise?
3740 Glib::Threads::RWLock::WriterLock lm (lock);
3741 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3742 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3743 metric_kill_list.push_back(*i);
3744 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3747 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3751 else if ((*i)->frame() >= where) {
3752 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3753 (*i)->set_frame ((*i)->frame() - amount);
3754 if ((*i)->frame() == where) {
3755 // marker was immediately after end of range
3756 tempo_after = dynamic_cast<TempoSection*> (*i);
3757 meter_after = dynamic_cast<MeterSection*> (*i);
3763 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3764 if (last_tempo && !tempo_after) {
3765 metric_kill_list.remove(last_tempo);
3766 last_tempo->set_frame(where);
3769 if (last_meter && !meter_after) {
3770 metric_kill_list.remove(last_meter);
3771 last_meter->set_frame(where);
3775 //remove all the remaining metrics
3776 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3777 _metrics.remove(*i);
3782 recompute_map (_metrics);
3785 PropertyChanged (PropertyChange ());
3789 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3790 * pos can be -ve, if required.
3793 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3795 Glib::Threads::RWLock::ReaderLock lm (lock);
3797 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double());
3800 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3802 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3804 Glib::Threads::RWLock::ReaderLock lm (lock);
3806 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
3809 /** Add the BBT interval op to pos and return the result */
3811 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3813 Glib::Threads::RWLock::ReaderLock lm (lock);
3815 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3816 pos_bbt.ticks += op.ticks;
3817 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3819 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3821 pos_bbt.beats += op.beats;
3822 /* the meter in effect will start on the bar */
3823 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();
3824 while (pos_bbt.beats >= divisions_per_bar + 1) {
3826 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3827 pos_bbt.beats -= divisions_per_bar;
3829 pos_bbt.bars += op.bars;
3831 return frame_at_bbt_locked (_metrics, pos_bbt);
3834 /** Count the number of beats that are equivalent to distance when going forward,
3838 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3840 Glib::Threads::RWLock::ReaderLock lm (lock);
3842 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
3846 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3852 operator<< (std::ostream& o, const Meter& m) {
3853 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3857 operator<< (std::ostream& o, const Tempo& t) {
3858 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3862 operator<< (std::ostream& o, const MetricSection& section) {
3864 o << "MetricSection @ " << section.frame() << ' ';
3866 const TempoSection* ts;
3867 const MeterSection* ms;
3869 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3870 o << *((const Tempo*) ts);
3871 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3872 o << *((const Meter*) ms);