From 5c6e18e6a087823e4a3719177c92238b206e3aeb Mon Sep 17 00:00:00 2001 From: nick_m Date: Mon, 28 Dec 2015 05:33:04 +1100 Subject: [PATCH] Tempo ramps - remove the concept of bars from tempo sections. - this helps where tempo and meter have a somewhat circular dependency. MetricSection now has a musical position expressed in beats (a double). MeterSection still has a bbt, but it really isn't needed as we have enough information to discover the number of bars at a given beat without it. TempoSection now has a hack to enable loading of legacy sessions, which will ultimately be a lot cleaner than the current code. Removing bars from tempo sections also allows us to place them at arbitrary frames (implemented here). --- gtk2_ardour/editor_drag.cc | 24 +- gtk2_ardour/editor_ops.cc | 4 +- gtk2_ardour/editor_tempodisplay.cc | 10 +- libs/ardour/ardour/tempo.h | 77 +- libs/ardour/tempo.cc | 664 +++++++++--------- libs/ardour/test/bbt_test.cc | 2 +- libs/ardour/test/framepos_minus_beats_test.cc | 18 +- libs/ardour/test/framepos_plus_beats_test.cc | 18 +- libs/ardour/test/framewalk_to_beats_test.cc | 18 +- libs/ardour/test/midi_clock_slave_test.h | 4 +- libs/ardour/test/tempo_test.cc | 8 +- 11 files changed, 413 insertions(+), 434 deletions(-) diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index dccb6cac51..dfd6407a6e 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3210,7 +3210,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred) if (_copy == true) { _editor->begin_reversible_command (_("copy meter mark")); XMLNode &before = map.get_state(); - map.add_meter (_marker->meter(), when); + map.add_meter (_marker->meter(), map.beat_at_frame (_marker->position()), when); XMLNode &after = map.get_state(); _editor->session()->add_command(new MementoCommand(map, &before, &after)); _editor->commit_reversible_command (); @@ -3220,7 +3220,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred) /* we removed it before, so add it back now */ - map.add_meter (_marker->meter(), when); + map.add_meter (_marker->meter(), map.beat_at_frame (_marker->position()), when); XMLNode &after = map.get_state(); _editor->session()->add_command(new MementoCommand(map, before_state, &after)); _editor->commit_reversible_command (); @@ -3239,7 +3239,7 @@ MeterMarkerDrag::aborted (bool moved) if (moved) { TempoMap& map (_editor->session()->tempo_map()); /* we removed it before, so add it back now */ - map.add_meter (_marker->meter(), _marker->meter().frame()); + map.add_meter (_marker->meter(), map.beat_at_frame (_marker->meter().frame()), _marker->meter().bbt()); // delete the dummy marker we used for visual representation while moving. // a new visual marker will show up automatically. delete _marker; @@ -3309,15 +3309,15 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) TempoMap& map (_editor->session()->tempo_map()); /* get current state */ before_state = &map.get_state(); - /* remove the section while we drag it */ - //map.remove_tempo (section, true); } + _marker->hide(); } framepos_t const pf = adjusted_current_frame (event, false); - TempoMap& map (_editor->session()->tempo_map()); - _marker->set_position (pf); - map.gui_set_tempo_frame (*_real_section, pf); + double const baf = _editor->session()->tempo_map().beat_at_frame (pf); + + _marker->set_position (adjusted_current_frame (event, false)); + _editor->session()->tempo_map().gui_set_tempo_frame (*_real_section, pf, baf); show_verbose_cursor_time (pf); } @@ -3339,22 +3339,18 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) motion (event, false); TempoMap& map (_editor->session()->tempo_map()); - framepos_t beat_time = map.round_to_beat (_marker->position(), RoundNearest); - Timecode::BBT_Time when; - - map.bbt_time (beat_time, when); if (_copy == true) { _editor->begin_reversible_command (_("copy tempo mark")); XMLNode &before = map.get_state(); - map.add_tempo (_marker->tempo(), when, _marker->tempo().type()); + map.add_tempo (_marker->tempo(), map.beat_at_frame (_marker->position()), _marker->tempo().type()); XMLNode &after = map.get_state(); _editor->session()->add_command (new MementoCommand(map, &before, &after)); _editor->commit_reversible_command (); } else { /* we removed it before, so add it back now */ - map.replace_tempo (*_real_section, _marker->tempo().beats_per_minute() , when, _marker->tempo().type()); + map.replace_tempo (*_real_section, _marker->tempo().beats_per_minute() , map.beat_at_frame (_marker->position()), _marker->tempo().type()); XMLNode &after = map.get_state(); _editor->session()->add_command (new MementoCommand(map, before_state, &after)); _editor->commit_reversible_command (); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index f094c793ac..59a5e8d6db 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -6552,9 +6552,7 @@ Editor::define_one_bar (framepos_t start, framepos_t end) } else if (t.frame() == start) { _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type()); } else { - Timecode::BBT_Time bbt; - _session->tempo_map().bbt_time (start, bbt); - _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt, TempoSection::Type::Constant); + _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), _session->tempo_map().beat_at_frame (start), TempoSection::Type::Constant); } XMLNode& after (_session->tempo_map().get_state()); diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 3dad29ef51..0cb53ee5b4 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -143,13 +143,11 @@ Editor::marker_position_changed () for (Marks::iterator x = metric_marks.begin(); x != metric_marks.end(); ++x) { if ((tempo_marker = dynamic_cast (*x)) != 0) { if ((ts = &tempo_marker->tempo()) != 0) { - cerr << "tempo section found for tempo marker " << endl; tempo_marker->set_position (ts->frame ()); } } if ((meter_marker = dynamic_cast (*x)) != 0) { if ((ms = &meter_marker->meter()) != 0) { - cerr << "meter section found for meter marker " << endl; meter_marker->set_position (ms->frame ()); } } @@ -249,7 +247,7 @@ Editor::mouse_add_new_tempo_event (framepos_t frame) begin_reversible_command (_("add tempo mark")); XMLNode &before = map.get_state(); - map.add_tempo (Tempo (bpm,nt), requested, tempo_dialog.get_tempo_type()); + map.add_tempo (Tempo (bpm,nt), map.bbt_to_beats (requested), tempo_dialog.get_tempo_type()); XMLNode &after = map.get_state(); _session->add_command(new MementoCommand(map, &before, &after)); commit_reversible_command (); @@ -288,7 +286,7 @@ Editor::mouse_add_new_meter_event (framepos_t frame) begin_reversible_command (_("add meter mark")); XMLNode &before = map.get_state(); - map.add_meter (Meter (bpb, note_type), requested); + map.add_meter (Meter (bpb, note_type), map.bbt_to_beats (requested), requested); _session->add_command(new MementoCommand(map, &before, &map.get_state())); commit_reversible_command (); @@ -338,7 +336,7 @@ Editor::edit_meter_section (MeterSection* section) begin_reversible_command (_("replace tempo mark")); XMLNode &before = _session->tempo_map().get_state(); - _session->tempo_map().replace_meter (*section, Meter (bpb, note_type), when); + _session->tempo_map().replace_meter (*section, Meter (bpb, note_type), _session->tempo_map().bbt_to_beats (when), when); XMLNode &after = _session->tempo_map().get_state(); _session->add_command(new MementoCommand(_session->tempo_map(), &before, &after)); commit_reversible_command (); @@ -364,7 +362,7 @@ Editor::edit_tempo_section (TempoSection* section) begin_reversible_command (_("replace tempo mark")); XMLNode &before = _session->tempo_map().get_state(); - _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), when, tempo_dialog.get_tempo_type()); + _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), _session->tempo_map().bbt_to_beats (when), tempo_dialog.get_tempo_type()); XMLNode &after = _session->tempo_map().get_state(); _session->add_command (new MementoCommand(_session->tempo_map(), &before, &after)); commit_reversible_command (); diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index e8f40e4236..f27622c91f 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -99,26 +99,24 @@ class LIBARDOUR_API Meter { /** A section of timeline with a certain Tempo or Meter. */ class LIBARDOUR_API MetricSection { public: - MetricSection (const Timecode::BBT_Time& start) - : _start (start), _frame (0), _movable (true), _position_lock_style (MusicTime) {} - MetricSection (framepos_t start) - : _frame (start), _movable (true), _position_lock_style (MusicTime) {} + MetricSection (double start) + : _beat (start), _frame (0), _movable (true), _position_lock_style (MusicTime) {} + MetricSection (framepos_t frame) + : _beat (0), _frame (frame), _movable (true), _position_lock_style (MusicTime) {} virtual ~MetricSection() {} + const double start () const { return _beat; } - const Timecode::BBT_Time& start() const { return _start; } - framepos_t frame() const { return _frame; } - - void set_movable (bool yn) { _movable = yn; } - bool movable() const { return _movable; } + const double& beat() const { return _beat; } + void set_beat (double beat) { _beat = beat;} + framepos_t frame() const { return _frame; } virtual void set_frame (framepos_t f) { _frame = f; } - virtual void set_start (const Timecode::BBT_Time& w) { - _start = w; - } + void set_movable (bool yn) { _movable = yn; } + bool movable() const { return _movable; } /* MeterSections are not stateful in the full sense, but we do want them to control their own @@ -129,8 +127,7 @@ class LIBARDOUR_API MetricSection { void set_position_lock_style (PositionLockStyle ps) { _position_lock_style = ps; } private: - - Timecode::BBT_Time _start; + double _beat; framepos_t _frame; bool _movable; PositionLockStyle _position_lock_style; @@ -139,15 +136,23 @@ private: /** A section of timeline with a certain Meter. */ class LIBARDOUR_API MeterSection : public MetricSection, public Meter { public: - MeterSection (const Timecode::BBT_Time& start, double bpb, double note_type) - : MetricSection (start), Meter (bpb, note_type) {} - MeterSection (framepos_t start, double bpb, double note_type) - : MetricSection (start), Meter (bpb, note_type) {} + MeterSection (double start, const Timecode::BBT_Time& bbt, double bpb, double note_type) + : MetricSection (start), Meter (bpb, note_type), _bbt (bbt) {} + MeterSection (framepos_t frame, double bpb, double note_type) + : MetricSection (frame), Meter (bpb, note_type) {} MeterSection (const XMLNode&); static const std::string xml_state_node_name; XMLNode& get_state() const; + + void set_start (std::pair& w) { + set_beat (w.first); + _bbt = w.second; + } + const Timecode::BBT_Time& bbt() const { return _bbt; } +private: + Timecode::BBT_Time _bbt; }; /** A section of timeline with a certain Tempo. */ @@ -158,16 +163,20 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { Constant, }; - TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type, Type tempo_type) + TempoSection (const double& start, double qpm, double note_type, Type tempo_type) : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {} - TempoSection (framepos_t start, double qpm, double note_type, Type tempo_type) - : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {} + TempoSection (framepos_t frame, double qpm, double note_type, Type tempo_type) + : MetricSection (frame), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {} TempoSection (const XMLNode&); static const std::string xml_state_node_name; XMLNode& get_state() const; + void set_start (const double& w) { + set_beat (w); + } + void update_bar_offset_from_bbt (const Meter&); void update_bbt_time_from_bar_offset (const Meter&); double bar_offset() const { return _bar_offset; } @@ -184,6 +193,8 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { double beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; framepos_t frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + Timecode::BBT_Time legacy_bbt () { return _legacy_bbt; } + private: framecnt_t minute_to_frame (double time, framecnt_t frame_rate) const; @@ -217,6 +228,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { */ double _bar_offset; Type _type; + Timecode::BBT_Time _legacy_bbt; }; typedef std::list Metrics; @@ -232,7 +244,7 @@ class LIBARDOUR_API TempoMetric { void set_tempo (const Tempo& t) { _tempo = &t; } void set_meter (const Meter& m) { _meter = &m; } void set_frame (framepos_t f) { _frame = f; } - void set_start (const Timecode::BBT_Time& t) { _start = t; } + void set_start (const double& t) { _start = t; } void set_metric (const MetricSection* section) { const MeterSection* meter; @@ -250,13 +262,13 @@ class LIBARDOUR_API TempoMetric { const Meter& meter() const { return *_meter; } const Tempo& tempo() const { return *_tempo; } framepos_t frame() const { return _frame; } - const Timecode::BBT_Time& start() const { return _start; } + const double& start() const { return _start; } private: const Meter* _meter; const Tempo* _tempo; framepos_t _frame; - Timecode::BBT_Time _start; + double _start; }; /** Tempo Map - mapping of timecode to musical time. @@ -346,18 +358,17 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible const Meter& meter_at (framepos_t) const; const TempoSection& tempo_section_at (framepos_t) const; - TempoSection* tempo_section_after (framepos_t) const; const MeterSection& meter_section_at (framepos_t) const; - void add_tempo (const Tempo&, Timecode::BBT_Time where, TempoSection::Type type); - void add_meter (const Meter&, Timecode::BBT_Time where); + void add_tempo (const Tempo&, double where, TempoSection::Type type); + void add_meter (const Meter&, double start, Timecode::BBT_Time where); void remove_tempo (const TempoSection&, bool send_signal); void remove_meter (const MeterSection&, bool send_signal); - void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where, TempoSection::Type type); - void gui_set_tempo_frame (TempoSection&, framepos_t where); - void replace_meter (const MeterSection&, const Meter&, const Timecode::BBT_Time& where); + void replace_tempo (const TempoSection&, const Tempo&, const double& where, TempoSection::Type type); + void gui_set_tempo_frame (TempoSection&, framepos_t where, double beat); + void replace_meter (const MeterSection&, const Meter&, const double& start, const Timecode::BBT_Time& where); framepos_t round_to_bar (framepos_t frame, RoundMode dir); framepos_t round_to_beat (framepos_t frame, RoundMode dir); @@ -393,6 +404,8 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible PBD::Signal0 MetricPositionChanged; + double bbt_to_beats (Timecode::BBT_Time bbt); + private: friend class ::BBTTest; @@ -424,8 +437,8 @@ private: void do_insert (MetricSection* section); - void add_tempo_locked (const Tempo&, Timecode::BBT_Time where, bool recompute, TempoSection::Type type); - void add_meter_locked (const Meter&, Timecode::BBT_Time where, bool recompute); + void add_tempo_locked (const Tempo&, double where, bool recompute, TempoSection::Type type); + void add_meter_locked (const Meter&, double start, Timecode::BBT_Time where, bool recompute); bool remove_tempo_locked (const TempoSection&); bool remove_meter_locked (const MeterSection&); diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 8dd2e28073..ba4b40a17e 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -71,23 +71,26 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const const string TempoSection::xml_state_node_name = "Tempo"; TempoSection::TempoSection (const XMLNode& node) - : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo()) + : MetricSection (0.0), Tempo (TempoMap::default_tempo()) { const XMLProperty *prop; BBT_Time start; LocaleGuard lg; if ((prop = node.property ("start")) == 0) { - error << _("TempoSection XML node has no \"start\" property") << endmsg; + error << _("MeterSection XML node has no \"start\" property") << endmsg; throw failed_constructor(); } if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - &start.bars, - &start.beats, - &start.ticks) < 3) { + &bbt.bars, + &bbt.beats, + &bbt.ticks) == 3) { + /* legacy session - start used to be in bbt*/ + _legacy_bbt = bbt; + start = -1.0; + } else if (sscanf (prop->value().c_str(), "%lf", &start) != 1 || start < 0.0) { error << _("TempoSection XML node has an illegal \"start\" value") << endmsg; - throw failed_constructor(); } set_start (start); @@ -146,10 +149,7 @@ TempoSection::get_state() const char buf[256]; LocaleGuard lg; - snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - start().bars, - start().beats, - start().ticks); + snprintf (buf, sizeof (buf), "%f", beat()); root->add_property ("start", buf); snprintf (buf, sizeof (buf), "%f", _beats_per_minute); root->add_property ("beats-per-minute", buf); @@ -170,7 +170,7 @@ void TempoSection::update_bar_offset_from_bbt (const Meter& m) { - _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) / + _bar_offset = (start() * BBT_Time::ticks_per_beat) / (m.divisions_per_bar() * BBT_Time::ticks_per_beat); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar())); @@ -316,24 +316,20 @@ TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const void TempoSection::update_bbt_time_from_bar_offset (const Meter& meter) { - BBT_Time new_start; + double new_start; if (_bar_offset < 0.0) { /* not set yet */ return; } - new_start.bars = start().bars; + new_start = start(); double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset; - new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat); - //new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */ - new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); + new_start = ticks / BBT_Time::ticks_per_beat; - /* remember the 1-based counting properties of beats */ - new_start.beats += 1; DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n", - _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats)); + _bar_offset, meter.divisions_per_bar(), ticks, new_start, new_start)); set_start (new_start); } @@ -343,24 +339,42 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter) const string MeterSection::xml_state_node_name = "Meter"; MeterSection::MeterSection (const XMLNode& node) - : MetricSection (BBT_Time()), Meter (TempoMap::default_meter()) + : MetricSection (0.0), Meter (TempoMap::default_meter()) { XMLProperty const * prop; BBT_Time start; LocaleGuard lg; + const XMLProperty *prop; + BBT_Time bbt; + double beat = 0.0; + pair start; if ((prop = node.property ("start")) == 0) { error << _("MeterSection XML node has no \"start\" property") << endmsg; throw failed_constructor(); } - if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - &start.bars, - &start.beats, - &start.ticks) < 3) { + &bbt.bars, + &bbt.beats, + &bbt.ticks) == 3) { + /* legacy session - start used to be in bbt*/ + beat = -1.0; + } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) { error << _("MeterSection XML node has an illegal \"start\" value") << endmsg; throw failed_constructor(); } + start.first = beat; + + if ((prop = node.property ("bbt")) == 0) { + error << _("MeterSection XML node has no \"bbt\" property") << endmsg; + } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, + &bbt.bars, + &bbt.beats, + &bbt.ticks) < 3) { + error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg; + throw failed_constructor(); + } + start.second = bbt; set_start (start); @@ -404,9 +418,11 @@ MeterSection::get_state() const LocaleGuard lg; snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - start().bars, - start().beats, - start().ticks); + bbt().bars, + bbt().beats, + bbt().ticks); + root->add_property ("bbt", buf); + snprintf (buf, sizeof (buf), "%f", start()); root->add_property ("start", buf); snprintf (buf, sizeof (buf), "%f", _note_type); root->add_property ("note-type", buf); @@ -441,8 +457,8 @@ TempoMap::TempoMap (framecnt_t fr) start.beats = 1; start.ticks = 0; - TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp); - MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor()); + TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp); + MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor()); t->set_movable (false); m->set_movable (false); @@ -542,24 +558,24 @@ TempoMap::do_insert (MetricSection* section) /* we only allow new meters to be inserted on beat 1 of an existing * measure. */ - - if (dynamic_cast(section)) { - assert (section->start().ticks == 0); + MeterSection* m = 0; + if ((m = dynamic_cast(section)) != 0) { + assert (m->bbt().ticks == 0); /* we need to (potentially) update the BBT times of tempo sections based on this new meter. */ - if ((section->start().beats != 1) || (section->start().ticks != 0)) { + if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) { - BBT_Time corrected = section->start(); - corrected.beats = 1; - corrected.ticks = 0; + pair corrected = make_pair (m->start(), m->bbt()); + corrected.second.beats = 1; + corrected.second.ticks = 0; warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"), - section->start(), corrected) << endmsg; + m->bbt(), corrected.second) << endmsg; - section->set_start (corrected); + m->set_start (corrected); } } @@ -574,24 +590,23 @@ TempoMap::do_insert (MetricSection* section) for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { - bool const iter_is_tempo = dynamic_cast (*i) != 0; - bool const insert_is_tempo = dynamic_cast (section) != 0; + TempoSection* const tempo = dynamic_cast (*i); + TempoSection* const insert_tempo = dynamic_cast (section); - if (iter_is_tempo && insert_is_tempo) { + if (tempo && insert_tempo) { /* Tempo sections */ - if ((*i)->start().bars == section->start().bars && - (*i)->start().beats == section->start().beats) { + if (tempo->start() == insert_tempo->start()) { - if (!(*i)->movable()) { + if (!tempo->movable()) { /* can't (re)move this section, so overwrite * its data content (but not its properties as * a section). */ - *(dynamic_cast(*i)) = *(dynamic_cast(section)); + *(dynamic_cast(*i)) = *(dynamic_cast(insert_tempo)); need_add = false; } else { metrics.erase (i); @@ -599,11 +614,12 @@ TempoMap::do_insert (MetricSection* section) break; } - } else if (!iter_is_tempo && !insert_is_tempo) { + } else if (!tempo && !insert_tempo) { /* Meter Sections */ - - if ((*i)->start().bars == section->start().bars) { + MeterSection* const meter = dynamic_cast (*i); + MeterSection* const insert_meter = dynamic_cast (section); + if (meter->start() == insert_meter->start()) { if (!(*i)->movable()) { @@ -612,7 +628,7 @@ TempoMap::do_insert (MetricSection* section) * a section */ - *(dynamic_cast(*i)) = *(dynamic_cast(section)); + *(dynamic_cast(*i)) = *(dynamic_cast(insert_meter)); need_add = false; } else { metrics.erase (i); @@ -631,12 +647,27 @@ TempoMap::do_insert (MetricSection* section) */ if (need_add) { + MeterSection* const insert_meter = dynamic_cast (section); + TempoSection* const insert_tempo = dynamic_cast (section); Metrics::iterator i; + if (insert_meter) { + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* const meter = dynamic_cast (*i); - for (i = metrics.begin(); i != metrics.end(); ++i) { - if ((*i)->start() > section->start()) { - break; + if (meter && meter->start() > insert_meter->start()) { + break; + } + } + } else if (insert_tempo) { + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* const tempo = dynamic_cast (*i); + + if (tempo) { + if (tempo->start() > insert_tempo->start()) { + break; + } + } } } @@ -645,7 +676,7 @@ TempoMap::do_insert (MetricSection* section) } void -TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::Type type) +TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type) { { Glib::Threads::RWLock::WriterLock lm (lock); @@ -668,15 +699,21 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T } void -TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame) -{ - { - TempoSection& first (first_tempo()); - if (ts.frame() != first.frame()) { - BBT_Time bbt; - { - Glib::Threads::RWLock::WriterLock lm (lock); - +TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where) +{ + TempoSection& first (first_tempo()); + if (ts.frame() != first.frame()) { + { + Glib::Threads::RWLock::WriterLock lm (lock); + /* currently this is always done in audio time */ + if (0) { + //if (ts.position_lock_style() == AudioTime) { + /* MusicTime */ + ts.set_start (beat_where); + MetricSectionSorter cmp; + metrics.sort (cmp); + } else { + /*AudioTime*/ ts.set_frame (frame); MetricSectionFrameSorter fcmp; @@ -684,64 +721,60 @@ TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame) Metrics::const_iterator i; TempoSection* prev_ts = 0; - MeterSection* meter = 0; + TempoSection* next_ts = 0; for (i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; - MeterSection* m; - if ((t = dynamic_cast (*i)) != 0) { - if ((*i)->frame() > frame - 1) { + if (t->frame() >= frame) { break; } prev_ts = t; } + } - if ((m = dynamic_cast (*i)) != 0) { + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; + if ((t = dynamic_cast (*i)) != 0) { - if ((*i)->frame() > frame) { + if (t->frame() > frame) { + next_ts = t; break; } - - meter = m; } } - if (prev_ts && meter) { - - /* set the start bbt */ - double const ticks_to_ts = prev_ts->tick_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate); - double beats = ticks_to_ts / BBT_Time::ticks_per_beat; - Timecode::BBT_Time bbt; - - bbt.bars = (uint32_t) floor (beats / meter->divisions_per_bar()); - beats -= bbt.bars * meter->divisions_per_bar(); - bbt.beats = (uint32_t) floor (beats); - beats -= bbt.beats; - bbt.ticks = (uint32_t) floor (beats * BBT_Time::ticks_per_beat); - - /* now add the prev ts bbt */ - bbt.bars += prev_ts->start().bars; - bbt.beats += prev_ts->start().beats; - - if (bbt.beats > meter->divisions_per_bar()) { - ++bbt.bars; - bbt.beats -= meter->divisions_per_bar(); - } - bbt.ticks += prev_ts->start().ticks; - if (bbt.ticks > BBT_Time::ticks_per_beat) { - ++bbt.beats; - bbt.ticks -= BBT_Time::ticks_per_beat; + if (prev_ts) { + /* set the start beat */ + double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate); + double beats = beats_to_ts + prev_ts->start(); + + if (next_ts) { + if (next_ts->start() < beats) { + /* with frame-based editing, it is possible to get in a + situation where if the tempo was placed at the mouse pointer frame, + the following music-based tempo would jump to an earlier frame, + changing the start beat of the moved tempo. + in this case, we have to do some beat-based comparison TODO + */ + + ts.set_start (next_ts->start()); + } else if (prev_ts->start() > beats) { + ts.set_start (prev_ts->start()); + } else { + ts.set_start (beats); + } + } else { + ts.set_start (beats); } - ts.set_start (bbt); + MetricSectionSorter cmp; + metrics.sort (cmp); } - - MetricSectionSorter cmp; - metrics.sort (cmp); - recompute_map (false); } + + recompute_map (false); } } @@ -749,7 +782,7 @@ TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame) } void -TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::Type type) +TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type) { { Glib::Threads::RWLock::WriterLock lm (lock); @@ -761,40 +794,10 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::T } void -TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::Type type) +TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type) { TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type); - /* find the meter to use to set the bar offset of this - * tempo section. - */ - - const Meter* meter = &first_meter(); - - /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing - at something, because we insert the default tempo and meter during - TempoMap construction. - - now see if we can find better candidates. - */ - - for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - - const MeterSection* m; - - if (where < (*i)->start()) { - break; - } - - if ((m = dynamic_cast(*i)) != 0) { - meter = m; - } - } - - //ts->update_bar_offset_from_bbt (*meter); - - /* and insert it */ - do_insert (ts); if (recompute) { @@ -803,15 +806,14 @@ TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, } void -TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where) +TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where) { { Glib::Threads::RWLock::WriterLock lm (lock); MeterSection& first (first_meter()); - if (ms.start() != first.start()) { remove_meter_locked (ms); - add_meter_locked (meter, where, true); + add_meter_locked (meter, start, where, true); } else { /* cannot move the first meter section */ *static_cast(&first) = meter; @@ -823,11 +825,11 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T } void -TempoMap::add_meter (const Meter& meter, BBT_Time where) +TempoMap::add_meter (const Meter& meter, double start, BBT_Time where) { { Glib::Threads::RWLock::WriterLock lm (lock); - add_meter_locked (meter, where, true); + add_meter_locked (meter, start, where, true); } @@ -841,7 +843,7 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) } void -TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute) +TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bool recompute) { /* a new meter always starts a new bar on the first beat. so round the start time appropriately. remember that @@ -858,7 +860,7 @@ TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute) /* new meters *always* start on a beat. */ where.ticks = 0; - do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor())); + do_insert (new MeterSection (start, where, meter.divisions_per_bar(), meter.note_divisor())); if (recompute) { recompute_map (true); @@ -1055,33 +1057,6 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) current.bars = 1; current.beats = 1; current.ticks = 0; - if (reassign_tempo_bbt) { - - MeterSection* rmeter = meter; - - DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n"); - - for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { - - TempoSection* ts; - MeterSection* ms; - - if ((ts = dynamic_cast(*i)) != 0) { - - /* reassign the BBT time of this tempo section - * based on its bar offset position. - */ - - //ts->update_bbt_time_from_bar_offset (*rmeter); - - } else if ((ms = dynamic_cast(*i)) != 0) { - rmeter = ms; - } else { - fatal << _("programming error: unhandled MetricSection type") << endmsg; - abort(); /*NOTREACHED*/ - } - } - } DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo))); @@ -1107,7 +1082,6 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, { /* CALLER MUST HOLD WRITE LOCK */ - uint32_t first_tick_in_new_meter = 0; Metrics::const_iterator i; Metrics::const_iterator mi; @@ -1119,78 +1093,43 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, if ((m = dynamic_cast (*mi)) != 0) { if (m->start() >= prev_ts->start()) { - first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter for (i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; if ((t = dynamic_cast (*i)) != 0) { - if (t->position_lock_style() == AudioTime) { - /* set the start bbt */ - double const ticks_at_ts = prev_ts->tick_at_frame (t->frame(), t->beats_per_minute(), t->frame(), _frame_rate); - double const ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1)) * BBT_Time::ticks_per_beat) + prev_ts->start().ticks; - - double const ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts; - double beats = ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat; - - Timecode::BBT_Time bbt; - bbt.bars = (uint32_t) floor (beats / meter->divisions_per_bar()); - beats -= bbt.bars * meter->divisions_per_bar(); - bbt.beats = (uint32_t) floor (beats); - beats -= bbt.beats; - bbt.ticks = (uint32_t) floor (beats * BBT_Time::ticks_per_beat); - /* now add the prev ts bbt */ - bbt.bars += prev_ts->start().bars; - bbt.beats += prev_ts->start().beats; - - if (bbt.beats > meter->divisions_per_bar()) { - ++bbt.bars; - bbt.beats -= meter->divisions_per_bar(); - } - bbt.ticks += prev_ts->start().ticks; - if (bbt.ticks > BBT_Time::ticks_per_beat) { - ++bbt.beats; - bbt.ticks -= BBT_Time::ticks_per_beat; - } - t->set_start (bbt); - - if (m->start() < t->start() && m->start() == prev_ts->start()) { - m->set_frame (prev_ts->frame()); - } else if (m->start() < t->start() && m->start() > prev_ts->start()) { - m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), t->frame(), _frame_rate) + prev_ts->frame()); - } - - } else if (t->start() >= m->start() && t->start() > prev_ts->start()) { - //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl; - //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl; + if (t->start() >= m->start() && t->start() > prev_ts->start()) { /*tempo section (t) lies in the previous meter */ - double const ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) ) * BBT_Time::ticks_per_beat) + t->start().ticks; - double const ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1)) * BBT_Time::ticks_per_beat) + prev_ts->start().ticks; - double const ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts; + double const beats_relative_to_prev_ts = t->start() - prev_ts->start(); + double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat; + /* assume (falsely) that the target tempo is constant */ - double length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate); + double const t_fpb = t->frames_per_beat (_frame_rate); + double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0; + + double length_estimate = beats_relative_to_prev_ts * av_fpb; + if (prev_ts->type() == TempoSection::Type::Constant) { - length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate); + length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate); } - cerr<< "initial length extimate = " << length_estimate << " ticks_relative_to_prev_ts " << ticks_relative_to_prev_ts << endl; - double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()); - cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl; + + double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5; double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf - while (fabs (tick_error) >= system_precision_at_target_tempo) { + while (fabs (tick_error) > system_precision_at_target_tempo) { double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate); tick_error = ticks_relative_to_prev_ts - actual_ticks; - length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate); + length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate); } + t->set_frame (length_estimate + prev_ts->frame()); - if (m->start() < t->start() && m->start() == prev_ts->start()) { + double const meter_start_beats = m->start(); + if (meter_start_beats < t->start() && meter_start_beats == prev_ts->start()) { m->set_frame (prev_ts->frame()); - } else if (m->start() < t->start() && m->start() > prev_ts->start()) { - cerr << "recompute map - music lock style setting meter frame to " << prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) << " ticks = " << first_tick_in_new_meter - ticks_at_prev_ts << endl; - - m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) + prev_ts->frame()); + } else if (meter_start_beats < t->start() && meter_start_beats > prev_ts->start()) { + m->set_frame (prev_ts->frame_at_tick (((m->start() * BBT_Time::ticks_per_beat) - (prev_ts->start() * BBT_Time::ticks_per_beat)), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) + prev_ts->frame()); } } prev_ts = t; @@ -1231,7 +1170,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const return m; } - +/* XX meters only */ TempoMetric TempoMap::metric_at (BBT_Time bbt) const { @@ -1246,14 +1185,16 @@ TempoMap::metric_at (BBT_Time bbt) const */ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* mw; + if ((mw = dynamic_cast (*i)) != 0) { + BBT_Time section_start (mw->bbt()); - BBT_Time section_start ((*i)->start()); + if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) { + break; + } - if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) { - break; + m.set_metric (*i); } - - m.set_metric (*i); } return m; @@ -1303,15 +1244,46 @@ TempoMap::bars_in_meter_section (MeterSection* ms) const return -1; } +double +TempoMap::bbt_to_beats (Timecode::BBT_Time bbt) +{ + double accumulated_ticks = 0.0; + MeterSection* prev_ms = 0; + Metrics::const_iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* m; + if ((m = dynamic_cast (*i)) != 0) { + + if (m->bbt() > bbt) { + break; + } + if (prev_ms) { + accumulated_ticks += (m->bbt().bars - prev_ms->bbt().bars) * prev_ms->divisions_per_bar() * BBT_Time::ticks_per_beat; + } + prev_ms = m; + + } + + } + double const remaining_ticks_in_bbt = ((bbt.bars - prev_ms->bbt().bars) * prev_ms->divisions_per_bar() * BBT_Time::ticks_per_beat) + + ((bbt.beats - prev_ms->bbt().beats) * BBT_Time::ticks_per_beat) + + (bbt.ticks - prev_ms->bbt().ticks); + double const total_ticks = remaining_ticks_in_bbt + accumulated_ticks; + + return total_ticks / BBT_Time::ticks_per_beat; +} + Timecode::BBT_Time TempoMap::beats_to_bbt (double beats) { /* CALLER HOLDS READ LOCK */ - BBT_Time ret; MeterSection* prev_ms = &first_meter(); - framecnt_t frame = frame_at_beat (beats); + //framecnt_t frame = frame_at_beat (beats); uint32_t cnt = 0; + BBT_Time ret; + /* XX most of this is utter crap */ if (n_meters() == 1) { uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor()); @@ -1339,87 +1311,58 @@ TempoMap::beats_to_bbt (double beats) return ret; } - uint32_t first_beat_in_meter = 0; uint32_t accumulated_bars = 0; + double accumulated_beats = 0; + Metrics::const_iterator i; for (i = metrics.begin(); i != metrics.end(); ++i) { MeterSection* m = 0; if ((m = dynamic_cast (*i)) != 0) { - first_beat_in_meter = beat_at_frame (m->frame()); - if (beats < first_beat_in_meter) { + if (beats < ((m->bbt().bars - prev_ms->bbt().bars) * prev_ms->divisions_per_bar()) + accumulated_beats) { /* this is the meter after the one our beat is on*/ break; } - int32_t const bars_in_ms = bars_in_meter_section (m); - if (bars_in_ms > 0) { - accumulated_bars += bars_in_ms; + if (prev_ms) { + accumulated_bars += m->bbt().bars - prev_ms->bbt().bars; + accumulated_beats += (m->bbt().bars - prev_ms->bbt().bars) * prev_ms->divisions_per_bar(); } prev_ms = m; ++cnt; } } - //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter = " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars << endl; - - if (beats > first_beat_in_meter) { - /* prev_ms is the relevant one here */ - - /* now get the ticks at frame */ - double ticks_at_frame = tick_at_frame (frame); - - /* find the number of ticks at the beginning of the meter section (bar 1)*/ - double ticks_at_ms = tick_at_frame (prev_ms->frame()); - - double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat; - - uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor()); - double remaining_beats = beats_used_by_ms - (bars * prev_ms->note_divisor()); - double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat; - ret.bars = bars + accumulated_bars; - ret.beats = (uint32_t) floor (remaining_beats); - ret.ticks = (uint32_t) floor (remaining_ticks + 0.5); + /* now find all tempo sections between prev_ms and beat */ + TempoSection* prev_ts = &first_tempo(); - /* now ensure we srtart at 1 1 0 */ - ++ret.bars; - ++ret.beats; - //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl; - if (ret.ticks >= BBT_Time::ticks_per_beat) { - ++ret.beats; - ret.ticks -= BBT_Time::ticks_per_beat; - } + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; - if (ret.beats > prev_ms->note_divisor()) { - ++ret.bars; - ret.beats = 1; + if ((t = dynamic_cast (*i)) != 0) { + if (beats < t->start()) { + break; + } + prev_ts = t; } - - return ret; } - /* find the number of ticks at the beginning of the meter section (bar 1)*/ - double ticks_at_ms = tick_at_frame (prev_ms->frame()); - - /* now get the ticks at frame */ - double ticks_at_frame = tick_at_frame (frame); + double beats_in_ts = beats - prev_ts->start(); + uint32_t bars_in_prev_ts = (uint32_t) floor (beats_in_ts / prev_ms->note_divisor()); + uint32_t beats_after_prev_ts_bar = (uint32_t) floor (beats_in_ts - (bars_in_prev_ts * prev_ms->note_divisor())); + double remaining_ticks = (beats_in_ts - (bars_in_prev_ts * prev_ms->note_divisor()) + beats_after_prev_ts_bar) * BBT_Time::ticks_per_beat; - double ticks_within_ms = ticks_at_frame - ticks_at_ms; - - ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars; - uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat); - ret.beats = (uint32_t) floor (remaining_ticks); - remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat; - - /* only round ticks */ ret.ticks = (uint32_t) floor (remaining_ticks + 0.5); + ret.beats = beats_after_prev_ts_bar; + ret.bars = bars_in_prev_ts + accumulated_bars; - /* now ensure we srtart at 1 1 0 */ + /* 0 0 0 to 1 1 0 - based mapping*/ ++ret.bars; ++ret.beats; + if (ret.ticks >= BBT_Time::ticks_per_beat) { ++ret.beats; ret.ticks -= BBT_Time::ticks_per_beat; @@ -1841,30 +1784,6 @@ TempoMap::get_grid (vector& points, } } -TempoSection* -TempoMap::tempo_section_after (framepos_t frame) const -{ - Glib::Threads::RWLock::ReaderLock lm (lock); - - Metrics::const_iterator i; - TempoSection* next = 0; - - for (i = metrics.begin(); i != metrics.end(); ++i) { - TempoSection* t; - - if ((t = dynamic_cast (*i)) != 0) { - - if ((*i)->frame() > frame) { - next = t; - break; - } - } - } - - return next; -} - - const TempoSection& TempoMap::tempo_section_at (framepos_t frame) const { @@ -1903,7 +1822,20 @@ TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const Glib::Threads::RWLock::ReaderLock lm (lock); const TempoSection* ts_at = &tempo_section_at (frame); - const TempoSection* ts_after = tempo_section_after (frame); + const TempoSection* ts_after = 0; + Metrics::const_iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; + + if ((t = dynamic_cast (*i)) != 0) { + + if ((*i)->frame() > frame) { + ts_after = t; + break; + } + } + } if (ts_after) { return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate)); @@ -2049,7 +1981,24 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) MetricSectionSorter cmp; metrics.sort (cmp); } + /* check for legacy sessions where bbt was the base musical unit for tempo */ + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* prev_ms; + TempoSection* prev_ts; + if ((prev_ms = dynamic_cast(*i)) != 0) { + if (prev_ms->start() < 0.0) { + /*XX we cannot possibly make this work??. */ + pair start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt()); + prev_ms->set_start (start); + } + } else if ((prev_ts = dynamic_cast(*i)) != 0) { + if (prev_ts->start() < 0.0) { + double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat); + prev_ts->set_start (start); + } + } + } /* check for multiple tempo/meters at the same location, which ardour2 somehow allowed. */ @@ -2057,16 +2006,20 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) Metrics::iterator prev = metrics.end(); for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { if (prev != metrics.end()) { - if (dynamic_cast(*prev) && dynamic_cast(*i)) { - if ((*prev)->start() == (*i)->start()) { - cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg; - error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg; + MeterSection* ms; + MeterSection* prev_ms; + TempoSection* ts; + TempoSection* prev_ts; + if ((prev_ms = dynamic_cast(*prev)) != 0 && (ms = dynamic_cast(*i)) != 0) { + if (prev_ms->start() == ms->start()) { + cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg; + error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg; return -1; } - } else if (dynamic_cast(*prev) && dynamic_cast(*i)) { - if ((*prev)->start() == (*i)->start()) { - cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg; - error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg; + } else if ((prev_ts = dynamic_cast(*prev)) != 0 && (ts = dynamic_cast(*i)) != 0) { + if (prev_ts->start() == ts->start()) { + cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg; + error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg; return -1; } } @@ -2095,7 +2048,7 @@ TempoMap::dump (std::ostream& o) const o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? " << t->movable() << ')' << endl; } else if ((m = dynamic_cast(*i)) != 0) { - o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() + o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame() << " (movable? " << m->movable() << ')' << endl; } } @@ -2167,46 +2120,67 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) for (i = metrics.begin(); i != metrics.end(); ++i) { BBT_Time bbt; - TempoMetric metric (*meter, *tempo); - + //TempoMetric metric (*meter, *tempo); + MeterSection* ms = const_cast(meter); + TempoSection* ts = const_cast(tempo); if (prev) { - metric.set_start (prev->start()); - metric.set_frame (prev->frame()); - } else { - // metric will be at frames=0 bbt=1|1|0 by default - // which is correct for our purpose - } - - bbt_time ((*i)->frame(), bbt); - - // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; - - if (first) { - first = false; - } else { + if (ts){ + if ((t = dynamic_cast(prev)) != 0) { + ts->set_start (t->start()); + } + if ((m = dynamic_cast(prev)) != 0) { + ts->set_start (m->start()); + } + ts->set_frame (prev->frame()); - if (bbt.ticks > BBT_Time::ticks_per_beat/2) { - /* round up to next beat */ - bbt.beats += 1; } - - bbt.ticks = 0; - - if (bbt.beats != 1) { - /* round up to next bar */ - bbt.bars += 1; - bbt.beats = 1; + if (ms) { + if ((m = dynamic_cast(prev)) != 0) { + pair start = make_pair (m->start(), m->bbt()); + ms->set_start (start); + } + if ((t = dynamic_cast(prev)) != 0) { + pair start = make_pair (t->start(), beats_to_bbt (t->start())); + ms->set_start (start); + } + ms->set_frame (prev->frame()); } + + } else { + // metric will be at frames=0 bbt=1|1|0 by default + // which is correct for our purpose } // cerr << bbt << endl; - (*i)->set_start (bbt); - if ((t = dynamic_cast(*i)) != 0) { + t->set_start (beat_at_frame (m->frame())); tempo = t; // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <(*i)) != 0) { + bbt_time (m->frame(), bbt); + + // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; + + if (first) { + first = false; + } else { + + if (bbt.ticks > BBT_Time::ticks_per_beat/2) { + /* round up to next beat */ + bbt.beats += 1; + } + + bbt.ticks = 0; + + if (bbt.beats != 1) { + /* round up to next bar */ + bbt.bars += 1; + bbt.beats = 1; + } + } + pair start = make_pair (beat_at_frame (m->frame()), bbt); + m->set_start (start); meter = m; // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() < (§ion)) != 0) { o << *((const Tempo*) ts); } else if ((ms = dynamic_cast (§ion)) != 0) { - o << *((const Meter*) ms); + //o << *((const Meter*) ms); } return o; diff --git a/libs/ardour/test/bbt_test.cc b/libs/ardour/test/bbt_test.cc index 416d02ba14..507af574d2 100644 --- a/libs/ardour/test/bbt_test.cc +++ b/libs/ardour/test/bbt_test.cc @@ -16,7 +16,7 @@ BBTTest::addTest () Tempo tempo(120); Meter meter(4.0, 4.0); - map.add_meter (meter, BBT_Time(1, 1, 0)); + map.add_meter (meter, 0.0, BBT_Time(1, 1, 0)); /* add some good stuff here */ } diff --git a/libs/ardour/test/framepos_minus_beats_test.cc b/libs/ardour/test/framepos_minus_beats_test.cc index 6f1bba53b2..588a38c097 100644 --- a/libs/ardour/test/framepos_minus_beats_test.cc +++ b/libs/ardour/test/framepos_minus_beats_test.cc @@ -22,8 +22,8 @@ FrameposMinusBeatsTest::singleTempoTest () Tempo tempo (bpm); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); - map.add_tempo (tempo, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); + map.add_tempo (tempo, 0.0, TempoSection::Type::Constant); /* Subtract 1 beat from beat 3 of the first bar */ framepos_t r = map.framepos_minus_beats (frames_per_beat * 2, Beats(1)); @@ -42,7 +42,7 @@ FrameposMinusBeatsTest::doubleTempoTest () TempoMap map (sampling_rate); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 4 @@ -63,9 +63,9 @@ FrameposMinusBeatsTest::doubleTempoTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (4, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 12.0, TempoSection::Type::Constant); /* Now some tests */ @@ -94,7 +94,7 @@ FrameposMinusBeatsTest::doubleTempoWithMeterTest () TempoMap map (sampling_rate); Meter meterA (4, 4); - map.add_meter (meterA, BBT_Time (1, 1, 0)); + map.add_meter (meterA, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 4 @@ -115,11 +115,11 @@ FrameposMinusBeatsTest::doubleTempoWithMeterTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (4, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 12.0, TempoSection::Type::Constant); Meter meterB (3, 4); - map.add_meter (meterB, BBT_Time (4, 1, 0)); + map.add_meter (meterB, 12.0, BBT_Time (4, 1, 0)); /* Now some tests */ diff --git a/libs/ardour/test/framepos_plus_beats_test.cc b/libs/ardour/test/framepos_plus_beats_test.cc index 425ae84343..ab52861f50 100644 --- a/libs/ardour/test/framepos_plus_beats_test.cc +++ b/libs/ardour/test/framepos_plus_beats_test.cc @@ -21,8 +21,8 @@ FrameposPlusBeatsTest::singleTempoTest () Tempo tempo (bpm); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); - map.add_tempo (tempo, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); + map.add_tempo (tempo, 0.0, TempoSection::Type::Constant); /* Add 1 beat to beat 3 of the first bar */ framepos_t r = map.framepos_plus_beats (frames_per_beat * 2, Evoral::Beats(1)); @@ -41,7 +41,7 @@ FrameposPlusBeatsTest::doubleTempoTest () TempoMap map (sampling_rate); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 4 @@ -62,9 +62,9 @@ FrameposPlusBeatsTest::doubleTempoTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (4, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 12.0, TempoSection::Type::Constant); /* Now some tests */ @@ -93,7 +93,7 @@ FrameposPlusBeatsTest::doubleTempoWithMeterTest () TempoMap map (sampling_rate); Meter meterA (4, 4); - map.add_meter (meterA, BBT_Time (1, 1, 0)); + map.add_meter (meterA, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 4 @@ -114,11 +114,11 @@ FrameposPlusBeatsTest::doubleTempoWithMeterTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (4, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 12.0, TempoSection::Type::Constant); Meter meterB (3, 4); - map.add_meter (meterB, BBT_Time (4, 1, 0)); + map.add_meter (meterB, 12.0, BBT_Time (4, 1, 0)); /* Now some tests */ diff --git a/libs/ardour/test/framewalk_to_beats_test.cc b/libs/ardour/test/framewalk_to_beats_test.cc index 84ddf6fc30..f6e84ddfad 100644 --- a/libs/ardour/test/framewalk_to_beats_test.cc +++ b/libs/ardour/test/framewalk_to_beats_test.cc @@ -20,8 +20,8 @@ FramewalkToBeatsTest::singleTempoTest () Tempo tempo (bpm); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); - map.add_tempo (tempo, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); + map.add_tempo (tempo, 0.0, TempoSection::Type::Constant); /* Walk 1 beats-worth of frames from beat 3 */ double r = map.framewalk_to_beats (frames_per_beat * 2, frames_per_beat * 1).to_double(); @@ -47,7 +47,7 @@ FramewalkToBeatsTest::doubleTempoTest () TempoMap map (sampling_rate); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 4 @@ -70,9 +70,9 @@ FramewalkToBeatsTest::doubleTempoTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (4, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 12.0, TempoSection::Type::Constant); /* Now some tests */ @@ -103,7 +103,7 @@ FramewalkToBeatsTest::tripleTempoTest () TempoMap map (sampling_rate); Meter meter (4, 4); - map.add_meter (meter, BBT_Time (1, 1, 0)); + map.add_meter (meter, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 2, 160bpm at bar 3 @@ -125,11 +125,11 @@ FramewalkToBeatsTest::tripleTempoTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (2, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 4.0, TempoSection::Type::Constant); Tempo tempoC (160); - map.add_tempo (tempoC, BBT_Time (3, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoC, 8.0, TempoSection::Type::Constant); /* Walk from 1|3 to 4|1 */ double r = map.framewalk_to_beats (2 * 24e3, (2 * 24e3) + (4 * 12e3) + (4 * 18e3)).to_double(); diff --git a/libs/ardour/test/midi_clock_slave_test.h b/libs/ardour/test/midi_clock_slave_test.h index 392e2876e9..ba87f8892a 100644 --- a/libs/ardour/test/midi_clock_slave_test.h +++ b/libs/ardour/test/midi_clock_slave_test.h @@ -48,8 +48,8 @@ class TestSlaveSessionProxy : public ISlaveSessionProxy { meter (4.0, 4.0) { _tempo_map = new TempoMap (FRAME_RATE); - _tempo_map->add_tempo (tempo, Timecode::BBT_Time(1, 1, 0), TempoSection::Type::Constant); - _tempo_map->add_meter (meter, Timecode::BBT_Time(1, 1, 0)); + _tempo_map->add_tempo (tempo, 0.0, TempoSection::Type::Constant); + _tempo_map->add_meter (meter, 0.0, Timecode::BBT_Time(1, 1, 0)); } // Controlling the mock object diff --git a/libs/ardour/test/tempo_test.cc b/libs/ardour/test/tempo_test.cc index 04b24f6d73..a7f9c1d405 100644 --- a/libs/ardour/test/tempo_test.cc +++ b/libs/ardour/test/tempo_test.cc @@ -14,7 +14,7 @@ TempoTest::recomputeMapTest () TempoMap map (sampling_rate); Meter meterA (4, 4); - map.add_meter (meterA, BBT_Time (1, 1, 0)); + map.add_meter (meterA, 0.0, BBT_Time (1, 1, 0)); /* 120bpm at bar 1, 240bpm at bar 4 @@ -35,11 +35,11 @@ TempoTest::recomputeMapTest () */ Tempo tempoA (120); - map.add_tempo (tempoA, BBT_Time (1, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoA, 0.0, TempoSection::Type::Constant); Tempo tempoB (240); - map.add_tempo (tempoB, BBT_Time (4, 1, 0), TempoSection::Type::Constant); + map.add_tempo (tempoB, 12.0, TempoSection::Type::Constant); Meter meterB (3, 4); - map.add_meter (meterB, BBT_Time (4, 1, 0)); + map.add_meter (meterB, 12.0, BBT_Time (4, 1, 0)); list::iterator i = map.metrics.begin(); CPPUNIT_ASSERT_EQUAL (framepos_t (0), (*i)->frame ()); -- 2.30.2