diff options
-rw-r--r-- | gtk2_ardour/editor.h | 1 | ||||
-rw-r--r-- | gtk2_ardour/editor_drag.cc | 126 | ||||
-rw-r--r-- | gtk2_ardour/editor_drag.h | 8 | ||||
-rw-r--r-- | gtk2_ardour/editor_markers.cc | 46 | ||||
-rw-r--r-- | gtk2_ardour/editor_tempodisplay.cc | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/tempo.h | 12 | ||||
-rw-r--r-- | libs/ardour/tempo.cc | 193 |
7 files changed, 277 insertions, 113 deletions
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index ec781e2df6..917dacb53e 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1688,6 +1688,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void marker_menu_rename (); void rename_marker (ArdourMarker *marker); void toggle_marker_lock_style (); + void toggle_tempo_clamped (); void toggle_tempo_type (); void continue_previous_tempo (); void ramp_to_next_tempo (); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index cd60f721bf..1538457f7e 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3364,7 +3364,7 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) , _copy (c) , _grab_bpm (120.0, 4.0) , _grab_qn (0.0) - , before_state (0) + , _before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n"); @@ -3423,7 +3423,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) _marker->hide(); /* get current state */ - before_state = &map.get_state(); + _before_state = &map.get_state(); if (!_copy) { _editor->begin_reversible_command (_("move tempo mark")); @@ -3451,7 +3451,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) { double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0)); stringstream strs; - _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), true); + _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm)); strs << "end:" << fixed << setprecision(3) << new_bpm; show_verbose_cursor_text (strs.str()); @@ -3459,7 +3459,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) /* use vertical movement to alter tempo .. should be log */ double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0)); stringstream strs; - _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), false); + _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute())); strs << "start:" << fixed << setprecision(3) << new_bpm; show_verbose_cursor_text (strs.str()); @@ -3501,7 +3501,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) TempoMap& map (_editor->session()->tempo_map()); XMLNode &after = map.get_state(); - _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after)); + _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after)); _editor->commit_reversible_command (); // delete the dummy marker we used for visual representation while moving. @@ -3515,7 +3515,7 @@ TempoMarkerDrag::aborted (bool moved) _marker->set_position (_marker->tempo().frame()); if (moved) { TempoMap& map (_editor->session()->tempo_map()); - map.set_state (*before_state, Stateful::current_state_version); + map.set_state (*_before_state, Stateful::current_state_version); // delete the dummy (hidden) marker we used for events while moving. delete _marker; } @@ -3525,7 +3525,7 @@ BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) , _grab_qn (0.0) , _tempo (0) - , before_state (0) + , _before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n"); @@ -3540,6 +3540,14 @@ BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) _editor->tempo_curve_selected (_tempo, true); ostringstream sstr; + if (_tempo->clamped()) { + TempoSection* prev = map.previous_tempo_section (_tempo); + if (prev) { + _editor->tempo_curve_selected (prev, true); + sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n"; + } + } + sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute(); show_verbose_cursor_text (sstr.str()); } @@ -3548,6 +3556,9 @@ void BBTRulerDrag::setup_pointer_frame_offset () { TempoMap& map (_editor->session()->tempo_map()); + /* get current state */ + _before_state = &map.get_state(); + const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame())); const uint32_t divisions = _editor->get_grid_beat_divisions (0); double beat = 0.0; @@ -3574,8 +3585,6 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move) TempoMap& map (_editor->session()->tempo_map()); if (first_move) { - /* get current state */ - before_state = &map.get_state(); _editor->begin_reversible_command (_("stretch tempo")); } @@ -3591,7 +3600,15 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move) /* adjust previous tempo to match pointer frame */ _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf); } + ostringstream sstr; + if (_tempo->clamped()) { + TempoSection* prev = map.previous_tempo_section (_tempo); + if (prev) { + _editor->tempo_curve_selected (prev, true); + sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n"; + } + } sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute(); show_verbose_cursor_text (sstr.str()); } @@ -3606,16 +3623,23 @@ BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred) TempoMap& map (_editor->session()->tempo_map()); XMLNode &after = map.get_state(); - _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after)); + _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after)); _editor->commit_reversible_command (); _editor->tempo_curve_selected (_tempo, false); + + if (_tempo->clamped()) { + TempoSection* prev_tempo = map.previous_tempo_section (_tempo); + if (prev_tempo) { + _editor->tempo_curve_selected (prev_tempo, false); + } + } } void BBTRulerDrag::aborted (bool moved) { if (moved) { - _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version); + _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version); } } @@ -3626,7 +3650,7 @@ TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i) , _tempo (0) , _next_tempo (0) , _drag_valid (true) - , before_state (0) + , _before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n"); @@ -3637,6 +3661,8 @@ TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); TempoMap& map (_editor->session()->tempo_map()); + /* get current state */ + _before_state = &map.get_state(); _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame())); _next_tempo = map.next_tempo_section (_tempo); @@ -3697,8 +3723,6 @@ TempoTwistDrag::motion (GdkEvent* event, bool first_move) TempoMap& map (_editor->session()->tempo_map()); if (first_move) { - /* get current state */ - before_state = &map.get_state(); _editor->begin_reversible_command (_("twist tempo")); } @@ -3734,7 +3758,7 @@ TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred) _editor->tempo_curve_selected (_next_tempo, false); XMLNode &after = map.get_state(); - _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after)); + _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after)); _editor->commit_reversible_command (); } @@ -3742,7 +3766,7 @@ void TempoTwistDrag::aborted (bool moved) { if (moved) { - _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version); + _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version); } } @@ -3750,22 +3774,37 @@ TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) , _grab_qn (0.0) , _tempo (0) - , before_state (0) + , _before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n"); - + TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker")); + _tempo = &marker->tempo(); + _grab_qn = _tempo->pulse() * 4.0; } void TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); - TempoMap& map (_editor->session()->tempo_map()); - _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame())); - _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), true); + TempoMap& tmap (_editor->session()->tempo_map()); + + /* get current state */ + _before_state = &tmap.get_state(); + ostringstream sstr; - sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n"; + + TempoSection* prev = 0; + if ((prev = tmap.previous_tempo_section (_tempo)) != 0) { + _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true); + sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n"; + } + + if (_tempo->clamped()) { + _editor->tempo_curve_selected (_tempo, true); + sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute(); + } + show_verbose_cursor_text (sstr.str()); } @@ -3773,21 +3812,6 @@ void TempoEndDrag::setup_pointer_frame_offset () { TempoMap& map (_editor->session()->tempo_map()); - const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame())); - const uint32_t divisions = _editor->get_grid_beat_divisions (0); - double beat = 0.0; - - if (divisions > 0) { - beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions); - } else { - /* while it makes some sense for the user to determine the division to 'grab', - grabbing a bar often leads to confusing results wrt the actual tempo section being altered - and the result over steep tempo curves. Use sixteenths. - */ - beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4); - } - - _grab_qn = map.quarter_note_at_beat (beat); _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn); @@ -3799,8 +3823,6 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move) TempoMap& map (_editor->session()->tempo_map()); if (first_move) { - /* get current state */ - before_state = &map.get_state(); _editor->begin_reversible_command (_("stretch end tempo")); } @@ -3810,7 +3832,12 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move) map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf); ostringstream sstr; - sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute(); + sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n"; + + if (_tempo->clamped()) { + sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute(); + } + show_verbose_cursor_text (sstr.str()); } @@ -3821,19 +3848,28 @@ TempoEndDrag::finished (GdkEvent* event, bool movement_occurred) return; } - TempoMap& map (_editor->session()->tempo_map()); + TempoMap& tmap (_editor->session()->tempo_map()); - XMLNode &after = map.get_state(); - _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after)); + XMLNode &after = tmap.get_state(); + _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after)); _editor->commit_reversible_command (); - _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), false); + + TempoSection* prev = 0; + if ((prev = tmap.previous_tempo_section (_tempo)) != 0) { + _editor->tempo_curve_selected (prev, false); + } + + if (_tempo->clamped()) { + _editor->tempo_curve_selected (_tempo, false); + + } } void TempoEndDrag::aborted (bool moved) { if (moved) { - _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version); + _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version); } } diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index d41ca2708f..84356f4636 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -794,7 +794,7 @@ private: bool _movable; ARDOUR::Tempo _grab_bpm; double _grab_qn; - XMLNode* before_state; + XMLNode* _before_state; }; /** BBT Ruler drag */ @@ -821,7 +821,7 @@ public: private: double _grab_qn; ARDOUR::TempoSection* _tempo; - XMLNode* before_state; + XMLNode* _before_state; }; /** tempo curve twist drag */ @@ -851,7 +851,7 @@ private: ARDOUR::TempoSection* _tempo; ARDOUR::TempoSection* _next_tempo; bool _drag_valid; - XMLNode* before_state; + XMLNode* _before_state; }; @@ -879,7 +879,7 @@ public: private: double _grab_qn; ARDOUR::TempoSection* _tempo; - XMLNode* before_state; + XMLNode* _before_state; }; /** Drag of the playhead cursor */ diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index 66911fc34c..40942e1569 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -991,8 +991,17 @@ Editor::build_tempo_marker_menu (TempoMarker* loc, bool can_remove) MenuList& items = tempo_marker_menu->items(); tempo_marker_menu->set_name ("ArdourContextMenu"); - if (loc->tempo().type() == TempoSection::Ramp) { - items.push_back (MenuElem (_("Set Constant"), sigc::mem_fun(*this, &Editor::toggle_tempo_type))); + if (!loc->tempo().initial()) { + if (loc->tempo().clamped()) { + items.push_back (MenuElem (_("Unlock Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped))); + } else { + items.push_back (MenuElem (_("Lock Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped))); + } + + TempoSection* prev_ts = _session->tempo_map().previous_tempo_section (&loc->tempo()); + if (prev_ts && prev_ts->end_note_types_per_minute() != loc->tempo().note_types_per_minute()) { + items.push_back (MenuElem (_("Continue"), sigc::mem_fun(*this, &Editor::continue_previous_tempo))); + } } TempoSection* next_ts = _session->tempo_map().next_tempo_section (&loc->tempo()); @@ -1000,9 +1009,8 @@ Editor::build_tempo_marker_menu (TempoMarker* loc, bool can_remove) items.push_back (MenuElem (_("Ramp to Next"), sigc::mem_fun(*this, &Editor::ramp_to_next_tempo))); } - TempoSection* prev_ts = _session->tempo_map().previous_tempo_section (&loc->tempo()); - if (prev_ts && prev_ts->end_note_types_per_minute() != loc->tempo().note_types_per_minute()) { - items.push_back (MenuElem (_("Continue"), sigc::mem_fun(*this, &Editor::continue_previous_tempo))); + if (loc->tempo().type() == TempoSection::Ramp) { + items.push_back (MenuElem (_("Set Constant"), sigc::mem_fun(*this, &Editor::toggle_tempo_type))); } if (loc->tempo().position_lock_style() == AudioTime && can_remove) { @@ -1464,6 +1472,34 @@ Editor::toggle_tempo_type () commit_reversible_command (); } } +/* clamped locks the previous section end tempo to the start tempo */ +void +Editor::toggle_tempo_clamped () +{ + TempoMarker* tm; + MeterMarker* mm; + dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm); + + if (tm) { + begin_reversible_command (_("Clamp Tempo")); + XMLNode &before = _session->tempo_map().get_state(); + + TempoSection* tsp = &tm->tempo(); + TempoSection* prev = _session->tempo_map().previous_tempo_section (tsp); + + if (prev) { + /* set to the end tempo of the previous section */ + Tempo new_tempo (prev->end_note_types_per_minute(), prev->note_type(), tsp->end_note_types_per_minute()); + _session->tempo_map().gui_change_tempo (tsp, new_tempo); + } + + tsp->set_clamped (!tsp->clamped()); + + XMLNode &after = _session->tempo_map().get_state(); + _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after)); + commit_reversible_command (); + } +} void Editor::continue_previous_tempo () diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 752373db6e..7f34e19398 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -316,6 +316,10 @@ Editor::redisplay_tempo (bool immediate_redraw) void Editor::tempo_curve_selected (TempoSection* ts, bool yn) { + if (ts == 0) { + return; + } + for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ++x) { if (&(*x)->tempo() == ts) { if (yn) { diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index b1c2dc6428..dcf68d7f16 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -201,7 +201,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { }; TempoSection (const double& pulse, const double& minute, Tempo tempo, PositionLockStyle pls, framecnt_t sr) - : MetricSection (pulse, minute, pls, true, sr), Tempo (tempo), _c (0.0), _active (true), _locked_to_meter (false) {} + : MetricSection (pulse, minute, pls, true, sr), Tempo (tempo), _c (0.0), _active (true), _locked_to_meter (false), _clamped (false) {} TempoSection (const XMLNode&, const framecnt_t sample_rate); @@ -220,6 +220,9 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { bool locked_to_meter () const { return _locked_to_meter; } void set_locked_to_meter (bool yn) { _locked_to_meter = yn; } + bool clamped () const { return _clamped; } + void set_clamped (bool yn) { _clamped = yn; } + Tempo tempo_at_minute (const double& minute) const; double minute_at_ntpm (const double& ntpm, const double& pulse) const; @@ -264,9 +267,11 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { this enables us to keep the tempo change at the same relative position within the bar if/when the meter changes. */ + double _c; bool _active; bool _locked_to_meter; + bool _clamped; Timecode::BBT_Time _legacy_bbt; bool _legacy_end; }; @@ -501,7 +506,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible void gui_set_tempo_position (TempoSection*, const framepos_t& frame, const int& sub_num); void gui_set_meter_position (MeterSection*, const framepos_t& frame); - bool gui_change_tempo (TempoSection*, const Tempo& bpm, bool change_end); + bool gui_change_tempo (TempoSection*, const Tempo& bpm); void gui_stretch_tempo (TempoSection* tempo, const framepos_t frame, const framepos_t end_frame); void gui_stretch_tempo_end (TempoSection* tempo, const framepos_t frame, const framepos_t end_frame); bool gui_twist_tempi (TempoSection* first, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame); @@ -515,6 +520,9 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible private: + TempoSection* previous_tempo_section_locked (const Metrics& metrics, TempoSection*) const; + TempoSection* next_tempo_section_locked (const Metrics& metrics, TempoSection*) const; + double beat_at_minute_locked (const Metrics& metrics, const double& minute) const; double minute_at_beat_locked (const Metrics& metrics, const double& beat) const; diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index e11d28bd98..bb61fb7012 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -90,6 +90,7 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) , _c (0.0) , _active (true) , _locked_to_meter (false) + , _clamped (false) , _legacy_end (false) { XMLProperty const * prop; @@ -147,6 +148,13 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) } } + if ((prop = node.property ("clamped")) == 0) { + warning << _("TempoSection XML node has no \"clamped\" property") << endmsg; + set_clamped (false); + } else { + set_clamped (string_is_affirmative (prop->value())); + } + /* XX replace old end-beats-per-minute name with note-types-per-minute */ if ((prop = node.property ("end-beats-per-minute")) != 0) { if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) { @@ -223,6 +231,8 @@ TempoSection::get_state() const root->add_property ("beats-per-minute", buf); snprintf (buf, sizeof (buf), "%lf", _note_type); root->add_property ("note-type", buf); + snprintf (buf, sizeof (buf), "%s", _clamped?"yes":"no"); + root->add_property ("clamped", buf); snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute); root->add_property ("end-beats-per-minute", buf); snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no"); @@ -1119,7 +1129,8 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul return; } - const bool locked_to_meter = ts.locked_to_meter(); + bool const locked_to_meter = ts.locked_to_meter(); + bool const ts_clamped = ts.clamped(); TempoSection* new_ts = 0; { @@ -1135,6 +1146,7 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul } else { remove_tempo_locked (ts); new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter); + new_ts->set_clamped (ts_clamped); if (new_ts && new_ts->type() == TempoSection::Constant) { new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute()); @@ -1162,6 +1174,7 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul first.set_minute (minute_at_frame (frame)); first.set_position_lock_style (AudioTime); first.set_locked_to_meter (true); + first.set_clamped (ts_clamped); { /* cannot move the first tempo section */ *static_cast<Tempo*>(&first) = tempo; @@ -3381,7 +3394,7 @@ TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame) } bool -TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end) +TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm) { Metrics future_map; bool can_solve = false; @@ -3389,27 +3402,39 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end) Glib::Threads::RWLock::WriterLock lm (lock); TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts); - if (change_end && tempo_copy->type() == TempoSection::Constant) { + if (tempo_copy->type() == TempoSection::Constant) { tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute()); tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute()); - } else if (change_end) { - tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute()); } else { tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute()); + tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute()); + } + + if (ts->clamped()) { + TempoSection* prev = 0; + if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) { + prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute()); + } } recompute_tempi (future_map); if (check_solved (future_map)) { - if (change_end && ts->type() == TempoSection::Constant) { + if (ts->type() == TempoSection::Constant) { ts->set_end_note_types_per_minute (bpm.note_types_per_minute()); ts->set_note_types_per_minute (bpm.note_types_per_minute()); - } else if (change_end) { - ts->set_end_note_types_per_minute (bpm.note_types_per_minute()); } else { + ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute()); ts->set_note_types_per_minute (bpm.note_types_per_minute()); } + if (ts->clamped()) { + TempoSection* prev = 0; + if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) { + prev->set_end_note_types_per_minute (ts->note_types_per_minute()); + } + } + recompute_map (_metrics); can_solve = true; } @@ -3457,18 +3482,36 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const fra framepos_t const min_dframe = 2; double new_bpm; + if (prev_t->clamped()) { + TempoSection* next_t = next_tempo_section_locked (future_map, prev_t); + TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t); + /* the change in frames is the result of changing the slope of at most 2 previous tempo sections. + constant to constant is straightforward, as the tempo prev to prev_t has constant slope. + */ + double contribution = 0.0; + if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) { + contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame()); + } + framepos_t const fr_off = (end_frame - frame); + const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off); - if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) { - - new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) - / (double) (end_frame - prev_t->frame())); + if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) { + new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame()) + / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame())); + } else { + new_bpm = prev_t->note_types_per_minute(); + } } else { - new_bpm = prev_t->note_types_per_minute(); - } + if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) { - std::cout << "new bpm : " << new_bpm << std::endl; - new_bpm = min (new_bpm, (double) 1000.0); + new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) + / (double) (end_frame - prev_t->frame())); + } else { + new_bpm = prev_t->note_types_per_minute(); + } + new_bpm = min (new_bpm, (double) 1000.0); + } /* don't clamp and proceed here. testing has revealed that this can go negative, which is an entirely different thing to just being too low. @@ -3485,6 +3528,13 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const fra prev_t->set_note_types_per_minute (new_bpm); } + if (prev_t->clamped()) { + TempoSection* prev = 0; + if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) { + prev->set_end_note_types_per_minute (prev_t->note_types_per_minute()); + } + } + recompute_tempi (future_map); recompute_meters (future_map); @@ -3495,12 +3545,17 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const fra ts->set_end_note_types_per_minute (new_bpm); ts->set_note_types_per_minute (new_bpm); } + if (ts->clamped()) { + TempoSection* prev = 0; + if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) { + prev->set_end_note_types_per_minute (ts->note_types_per_minute()); + } + } recompute_tempi (_metrics); recompute_meters (_metrics); } } - MetricPositionChanged (PropertyChange ()); // Emit Signal out: Metrics::const_iterator d = future_map.begin(); @@ -3508,6 +3563,8 @@ out: delete (*d); ++d; } + MetricPositionChanged (PropertyChange ()); // Emit Signal + } void @@ -3532,31 +3589,17 @@ TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts); -/* - TempoSection* next_t = 0; - for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) { - if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) { - next_t = static_cast<TempoSection*> (*i); - break; - } - } - - if (!next_t) { - return; - } -*/ if (!prev_t) { return; } - /* minimum allowed measurement distance in frames */ framepos_t const min_dframe = 2; double new_bpm; if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) { - new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame()) - / (double) (end_frame - prev_t->frame())); + new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame) + / (double) (prev_t->frame() - end_frame)); } else { new_bpm = prev_t->end_note_types_per_minute(); } @@ -3569,18 +3612,31 @@ TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const prev_t->set_end_note_types_per_minute (new_bpm); + TempoSection* next = 0; + if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) { + if (next->clamped()) { + next->set_note_types_per_minute (prev_t->end_note_types_per_minute()); + } + } + recompute_tempi (future_map); recompute_meters (future_map); if (check_solved (future_map)) { ts->set_end_note_types_per_minute (new_bpm); + TempoSection* true_next = 0; + if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) { + if (true_next->clamped()) { + true_next->set_note_types_per_minute (ts->end_note_types_per_minute()); + } + } + recompute_tempi (_metrics); recompute_meters (_metrics); } } - MetricPositionChanged (PropertyChange ()); // Emit Signal out: Metrics::const_iterator d = future_map.begin(); @@ -3589,7 +3645,9 @@ out: ++d; } + MetricPositionChanged (PropertyChange ()); // Emit Signal } + bool TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame) { @@ -3638,7 +3696,6 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t } if (!next_to_next_t) { - std::cout << "no next to next t" << std::endl; return false; } @@ -3651,10 +3708,8 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off); - framepos_t old_tc_pos = tempo_copy->frame(); - framepos_t old_next_pos = next_t->frame(); + framepos_t old_tc_minute = tempo_copy->minute(); double old_next_minute = next_t->minute(); - framepos_t old_next_to_next_pos = next_to_next_t->frame(); double old_next_to_next_minute = next_to_next_t->minute(); double new_bpm; @@ -3682,6 +3737,7 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t if (tempo_copy->type() == TempoSection::Constant) { tempo_copy->set_end_note_types_per_minute (new_bpm); } + recompute_tempi (future_map); if (check_solved (future_map)) { @@ -3689,11 +3745,14 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t if (!next_t) { return false; } + ts->set_note_types_per_minute (new_bpm); if (ts->type() == TempoSection::Constant) { ts->set_end_note_types_per_minute (new_bpm); } + recompute_map (_metrics); + can_solve = true; } @@ -3701,7 +3760,7 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) { new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute) - / (double) ((old_next_to_next_minute) - old_next_minute)); + / (double) ((old_next_to_next_minute) - old_next_minute)); } else { new_next_bpm = next_t->note_types_per_minute(); @@ -3730,20 +3789,22 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t double copy_frame_ratio = 1.0; if (next_to_next_t) { - next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos); + next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute); - copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos)); - - } else { - //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame())); - //next_pulse_ratio = (start_pulse / end_pulse); + copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute)); } new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio; - new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio; + new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio; - next_t->set_note_types_per_minute (new_next_bpm); tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm); + + if (next_t->clamped()) { + next_t->set_note_types_per_minute (new_copy_end_bpm); + } else { + next_t->set_note_types_per_minute (new_next_bpm); + } + recompute_tempi (future_map); if (check_solved (future_map)) { @@ -3757,7 +3818,13 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t if (!next_t) { return false; } - next_t->set_note_types_per_minute (new_next_bpm); + + if (next_t->clamped()) { + next_t->set_note_types_per_minute (new_copy_end_bpm); + } else { + next_t->set_note_types_per_minute (new_next_bpm); + } + ts->set_end_note_types_per_minute (new_copy_end_bpm); recompute_map (_metrics); can_solve = true; @@ -3770,9 +3837,8 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t delete (*d); ++d; } - if (can_solve) { - MetricPositionChanged (PropertyChange ()); // Emit Signal - } + + MetricPositionChanged (PropertyChange ()); // Emit Signal return can_solve; } @@ -4288,15 +4354,22 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be TempoSection* TempoMap::previous_tempo_section (TempoSection* ts) const { + Glib::Threads::RWLock::ReaderLock lm (lock); + + return previous_tempo_section_locked (_metrics, ts); + +} + +TempoSection* +TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const +{ if (!ts) { return 0; } - Glib::Threads::RWLock::ReaderLock lm (lock); - TempoSection* prev = 0; - for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { if ((*i)->is_tempo()) { TempoSection* t = static_cast<TempoSection*> (*i); @@ -4325,15 +4398,21 @@ TempoMap::previous_tempo_section (TempoSection* ts) const TempoSection* TempoMap::next_tempo_section (TempoSection* ts) const { + Glib::Threads::RWLock::ReaderLock lm (lock); + + return next_tempo_section_locked (_metrics, ts); +} + +TempoSection* +TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const +{ if (!ts) { return 0; } - Glib::Threads::RWLock::ReaderLock lm (lock); - TempoSection* prev = 0; - for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { if ((*i)->is_tempo()) { TempoSection* t = static_cast<TempoSection*> (*i); |