diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index 3f00dbcb2f..bb59fd85af 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -567,6 +567,7 @@ + diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 854eb57a92..06510b13e7 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -1101,12 +1101,6 @@ AutomationLine::change_model (AutomationList::iterator i, double x, double y) { } -void -AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta) -{ - alist->move_range (start, end, xdelta, ydelta); -} - void AutomationLine::set_list(boost::shared_ptr list) { diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 1123a7b9a4..7fd9dd3447 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -162,7 +162,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin void sync_model_with_view_line (uint32_t, uint32_t); virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y); - virtual void change_model_range (ARDOUR::AutomationList::iterator,ARDOUR::AutomationList::iterator, double delta, float ydelta); void reset_callback (const Evoral::ControlList&); void list_changed (); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 2b421163e4..4ce8b1bcd2 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -356,6 +356,7 @@ class Editor : public PublicEditor void update_layering_model (); void toggle_link_region_and_track_selection (); + void toggle_automation_follows_regions (); /* redirect shared ops menu. caller must free returned menu */ diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 75d4b76668..24b411dd11 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -100,6 +100,7 @@ Editor::register_actions () /* add named actions for the editor */ ActionManager::register_toggle_action (editor_actions, "link-region-and-track-selection", _("Link Region/Track Selection"), mem_fun (*this, &Editor::toggle_link_region_and_track_selection)); + ActionManager::register_toggle_action (editor_actions, "automation-follows-regions", _("Automation follows regions"), mem_fun (*this, &Editor::toggle_automation_follows_regions)); ActionManager::register_action (editor_actions, "break-drag", _("Break drag"), mem_fun (*this, &Editor::break_drag)); act = ActionManager::register_toggle_action (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), mem_fun (*this, &Editor::editor_mixer_button_toggled)); @@ -1731,6 +1732,12 @@ Editor::toggle_link_region_and_track_selection () ActionManager::toggle_config_state ("Editor", "link-region-and-track-selection", &Configuration::set_link_region_and_track_selection, &Configuration::get_link_region_and_track_selection); } +void +Editor::toggle_automation_follows_regions () +{ + ActionManager::toggle_config_state ("Editor", "automation-follows-regions", &Configuration::set_automation_follows_regions, &Configuration::get_automation_follows_regions); +} + /** A Configuration parameter has changed. * @param parameter_name Name of the changed parameter. */ @@ -1777,6 +1784,8 @@ Editor::parameter_changed (const char* parameter_name) toggle_meter_updating(); } else if (PARAM_IS ("link-region-and-track-selection")) { ActionManager::map_some_state ("Editor", "link-region-and-track-selection", &Configuration::get_link_region_and_track_selection); + } else if (PARAM_IS ("automation-follows-regions")) { + ActionManager::map_some_state ("Editor", "automation-follows-regions", &Configuration::get_automation_follows_regions); } #undef PARAM_IS diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 990581e63c..68b53da86c 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -1297,12 +1297,17 @@ MixerStrip::map_frozen () void MixerStrip::hide_redirect_editors () { - _route->foreach_processor (this, &MixerStrip::hide_processor_editor); + _route->foreach_processor (mem_fun (*this, &MixerStrip::hide_processor_editor)); } void -MixerStrip::hide_processor_editor (boost::shared_ptr processor) +MixerStrip::hide_processor_editor (boost::weak_ptr p) { + boost::shared_ptr processor (p.lock ()); + if (!processor) { + return; + } + void* gui = processor->get_gui (); if (gui) { diff --git a/gtk2_ardour/mixer_strip.h b/gtk2_ardour/mixer_strip.h index 12d84c48fc..393a38569e 100644 --- a/gtk2_ardour/mixer_strip.h +++ b/gtk2_ardour/mixer_strip.h @@ -250,7 +250,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox void name_changed (); void update_speed_display (); void map_frozen (); - void hide_processor_editor (boost::shared_ptr processor); + void hide_processor_editor (boost::weak_ptr processor); void hide_redirect_editors (); bool ignore_speed_adjustment; diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index fe648352d0..75e54e4b66 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -562,8 +562,7 @@ ProcessorBox::redisplay_processors () processor_active_connections.clear (); processor_name_connections.clear (); - void (ProcessorBox::*method)(boost::shared_ptr) = &ProcessorBox::add_processor_to_display; - _route->foreach_processor (this, method); + _route->foreach_processor (mem_fun (*this, &ProcessorBox::add_processor_to_display)); switch (_placement) { case PreFader: @@ -576,8 +575,13 @@ ProcessorBox::redisplay_processors () } void -ProcessorBox::add_processor_to_display (boost::shared_ptr processor) +ProcessorBox::add_processor_to_display (boost::weak_ptr p) { + boost::shared_ptr processor (p.lock ()); + if (!processor) { + return; + } + if (processor->placement() != _placement) { return; } diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 92a301633a..6d57230cf8 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -154,7 +154,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject bool processor_button_press_event (GdkEventButton *); bool processor_button_release_event (GdkEventButton *); void redisplay_processors (); - void add_processor_to_display (boost::shared_ptr); + void add_processor_to_display (boost::weak_ptr); void row_deleted (const Gtk::TreeModel::Path& path); void show_processor_active (boost::weak_ptr); void show_processor_name (boost::weak_ptr); diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 8e462c984e..431017aff9 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -286,8 +286,8 @@ RouteTimeAxisView::post_construct () update_diskstream_display (); subplugin_menu.items().clear (); - _route->foreach_processor (this, &RouteTimeAxisView::add_processor_to_subplugin_menu); - _route->foreach_processor (this, &RouteTimeAxisView::add_existing_processor_automation_curves); + _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu)); + _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves)); reset_processor_automation_curves (); } @@ -1920,8 +1920,13 @@ RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::Process } void -RouteTimeAxisView::add_existing_processor_automation_curves (boost::shared_ptr processor) +RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr p) { + boost::shared_ptr processor (p.lock ()); + if (!processor) { + return; + } + set s; boost::shared_ptr al; @@ -1977,8 +1982,13 @@ RouteTimeAxisView::add_automation_child(Evoral::Parameter param, boost::shared_p void -RouteTimeAxisView::add_processor_to_subplugin_menu (boost::shared_ptr processor) +RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p) { + boost::shared_ptr processor (p.lock ()); + if (!processor) { + return; + } + using namespace Menu_Helpers; ProcessorAutomationInfo *rai; list::iterator x; @@ -2108,8 +2118,8 @@ RouteTimeAxisView::processors_changed () subplugin_menu.items().clear (); - _route->foreach_processor (this, &RouteTimeAxisView::add_processor_to_subplugin_menu); - _route->foreach_processor (this, &RouteTimeAxisView::add_existing_processor_automation_curves); + _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu)); + _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves)); for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ) { diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index 405f4937ab..c41e60afce 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -184,7 +184,7 @@ protected: void processors_changed (); - void add_processor_to_subplugin_menu (boost::shared_ptr); + void add_processor_to_subplugin_menu (boost::weak_ptr); void remove_processor_automation_node (ProcessorAutomationNode* pan); void processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo*, @@ -205,7 +205,7 @@ protected: find_processor_automation_curve (boost::shared_ptr i, Evoral::Parameter); void add_processor_automation_curve (boost::shared_ptr r, Evoral::Parameter); - void add_existing_processor_automation_curves (boost::shared_ptr); + void add_existing_processor_automation_curves (boost::weak_ptr); void add_automation_child(Evoral::Parameter param, boost::shared_ptr track, bool show=true); diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index bbf766d877..208fcbb0f0 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -82,6 +82,7 @@ CONFIG_VARIABLE (EditMode, edit_mode, "edit-mode", Slide) CONFIG_VARIABLE (LayerModel, layer_model, "layer-model", MoveAddHigher) CONFIG_VARIABLE (bool, link_region_and_track_selection, "link-region-and-track-selection", false) CONFIG_VARIABLE (std::string, keyboard_layout_name, "keyboard-layout-name", "ansi") +CONFIG_VARIABLE (bool, automation_follows_regions, "automation-follows-regions", false) /* monitoring, mute, solo etc */ diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index 824d864663..955f2ed8e7 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -140,6 +140,8 @@ class Diskstream : public SessionObject void remove_region_from_last_capture (boost::weak_ptr wregion); + void move_processor_automation (boost::weak_ptr, Evoral::RangeMoveList const &); + sigc::signal RecordEnableChanged; sigc::signal SpeedChanged; sigc::signal ReverseChanged; @@ -204,6 +206,7 @@ class Diskstream : public SessionObject virtual void playlist_changed (Change); virtual void playlist_deleted (boost::weak_ptr); + virtual void playlist_ranges_moved (Evoral::RangeMoveList const &); virtual void transport_stopped (struct tm&, time_t, bool abort) = 0; virtual void transport_looped (nframes_t transport_frame) = 0; @@ -299,6 +302,7 @@ class Diskstream : public SessionObject sigc::connection ports_created_c; sigc::connection plmod_connection; sigc::connection plgone_connection; + sigc::connection plregion_connection; Flag _flags; }; diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index b937c412ab..cdde9855b2 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -35,7 +35,9 @@ #include #include -#include +#include + +#include #include #include @@ -126,6 +128,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this Modified; sigc::signal NameChanged; sigc::signal LengthChanged; + sigc::signal RangesMoved; static string bump_name (string old_name, Session&); @@ -177,6 +180,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this > all_regions; /* all regions ever added to this playlist */ + std::list region_state_changed_connections; DataType _type; mutable gint block_notifications; mutable gint ignore_state_changes; @@ -186,6 +190,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this); void mark_session_dirty(); diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 28d15b7090..1d3fc25164 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -142,10 +142,10 @@ class Route : public IO void flush_processors (); - template void foreach_processor (T *obj, void (T::*func)(boost::shared_ptr)) { + void foreach_processor (sigc::slot > method) { Glib::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (obj->*func) (*i); + method (boost::weak_ptr (*i)); } } diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index 9665176a67..334e4336af 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include "i18n.h" #include @@ -312,6 +314,7 @@ Diskstream::use_playlist (boost::shared_ptr playlist) plmod_connection.disconnect (); plgone_connection.disconnect (); + plregion_connection.disconnect (); if (_playlist) { _playlist->release(); @@ -326,6 +329,7 @@ Diskstream::use_playlist (boost::shared_ptr playlist) plmod_connection = _playlist->Modified.connect (mem_fun (*this, &Diskstream::playlist_modified)); plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), boost::weak_ptr(_playlist))); + plregion_connection = _playlist->RangesMoved.connect (mem_fun (*this, &Diskstream::playlist_ranges_moved)); } /* don't do this if we've already asked for it *or* if we are setting up @@ -409,3 +413,64 @@ Diskstream::remove_region_from_last_capture (boost::weak_ptr wregion) _last_capture_regions.remove (region); } +void +Diskstream::playlist_ranges_moved (Evoral::RangeMoveList const & movements) +{ + if (Config->get_automation_follows_regions () == false) { + return; + } + + /* move gain automation */ + boost::shared_ptr gain_alist = _io->gain_control()->alist(); + XMLNode & before = gain_alist->get_state (); + gain_alist->move_ranges (movements); + _session.add_command ( + new MementoCommand ( + *gain_alist.get(), &before, &gain_alist->get_state () + ) + ); + + /* move panner automation */ + Panner & p = _io->panner (); + for (uint32_t i = 0; i < p.npanners (); ++i) { + + boost::shared_ptr pan_alist = p.streampanner(i).pan_control()->alist(); + XMLNode & before = pan_alist->get_state (); + pan_alist->move_ranges (movements); + _session.add_command ( + new MementoCommand ( + *pan_alist.get(), &before, &pan_alist->get_state () + ) + ); + } + + /* move processor automation */ + /* XXX: ewww */ + Route * route = dynamic_cast (_io); + if (route) { + route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &Diskstream::move_processor_automation), movements)); + } +} + +void +Diskstream::move_processor_automation (boost::weak_ptr p, Evoral::RangeMoveList const & movements) +{ + boost::shared_ptr processor (p.lock ()); + if (!processor) { + return; + } + + set const a = processor->what_can_be_automated (); + + for (set::iterator i = a.begin (); i != a.end (); ++i) { + boost::shared_ptr al = processor->automation_control(*i)->alist(); + XMLNode & before = al->get_state (); + al->move_ranges (movements); + _session.add_command ( + new MementoCommand ( + *al.get(), &before, &al->get_state () + ) + ); + } +} + diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index fbe9990933..433e567efa 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -360,6 +360,25 @@ Playlist::notify_region_removed (boost::shared_ptr r) } } +void +Playlist::notify_region_moved (boost::shared_ptr r) +{ + Evoral::RangeMove const move (r->last_position (), r->length (), r->position ()); + + if (holding_state ()) { + + pending_range_moves.push_back (move); + + } else { + + Evoral::RangeMoveList m; + m.push_back (move); + RangesMoved (m); + + } + +} + void Playlist::notify_region_added (boost::shared_ptr r) { @@ -452,9 +471,14 @@ Playlist::flush_notifications () check_dependents (*s, false); } + if (!pending_range_moves.empty ()) { + RangesMoved (pending_range_moves); + } + pending_adds.clear (); pending_removes.clear (); pending_bounds.clear (); + pending_range_moves.clear (); in_flush = false; } @@ -559,8 +583,10 @@ Playlist::add_region_internal (boost::shared_ptr region, nframes_t posit } } - region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), - boost::weak_ptr (region))); + region_state_changed_connections.push_back ( + region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), + boost::weak_ptr (region))) + ); return true; } @@ -1288,20 +1314,22 @@ Playlist::region_changed (Change what_changed, boost::shared_ptr region) return false; } - { - if (what_changed & BoundsChanged) { - region_bounds_changed (what_changed, region); - save = !(_splicing || _nudging); - } + if (what_changed & BoundsChanged) { + region_bounds_changed (what_changed, region); + save = !(_splicing || _nudging); + } - if ((what_changed & our_interests) && - !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) { - check_dependents (region, false); - } + if ((what_changed & our_interests) && + !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) { + check_dependents (region, false); + } + + if (what_changed & Change (ARDOUR::PositionChanged)) { + notify_region_moved (region); + } - if (what_changed & our_interests) { - save = true; - } + if (what_changed & our_interests) { + save = true; } return save; @@ -1320,6 +1348,17 @@ Playlist::clear (bool with_signals) { { RegionLock rl (this); + + for ( + std::list::iterator i = region_state_changed_connections.begin (); + i != region_state_changed_connections.end (); + ++i + ) { + + i->disconnect (); + + } + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { pending_removes.insert (*i); } diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 36799300fe..48050f9e93 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -120,7 +120,7 @@ public: void erase_range (double start, double end); void erase (iterator); void erase (iterator, iterator); - void move_range (iterator start, iterator end, double, double); + void move_ranges (RangeMoveList const &); void modify (iterator, double, double); boost::shared_ptr cut (double, double); @@ -241,6 +241,7 @@ protected: bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const; boost::shared_ptr cut_copy_clear (double, double, int op); + bool erase_range_internal (double start, double end, EventList &); virtual void maybe_signal_changed (); diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp index fc1001c7d8..4bca6f1288 100644 --- a/libs/evoral/evoral/types.hpp +++ b/libs/evoral/evoral/types.hpp @@ -20,6 +20,7 @@ #define EVORAL_TYPES_HPP #include +#include namespace Evoral { @@ -41,6 +42,16 @@ typedef double EventLength; /** Type of an event (opaque, mapped by application) */ typedef uint32_t EventType; +/** Type to describe the movement of a time range */ +struct RangeMove { + RangeMove (EventTime f, FrameTime l, EventTime t) : from (f), length (l), to (t) {} + EventTime from; ///< start of the range + FrameTime length; ///< length of the range + EventTime to; ///< new start of the range +}; + +typedef std::list RangeMoveList; + } // namespace Evoral #endif // EVORAL_TYPES_HPP diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index e1cea8e191..033e375a8a 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -390,16 +390,10 @@ ControlList::erase_range (double start, double endt) { Glib::Mutex::Lock lm (_lock); - ControlEvent cp (start, 0.0f); - iterator s; - iterator e; + erased = erase_range_internal (start, endt, _events); - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) { - cp.when = endt; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - _events.erase (s, e); + if (erased) { reposition_for_rt_add (0); - erased = true; mark_dirty (); } @@ -410,36 +404,22 @@ ControlList::erase_range (double start, double endt) } } -void -ControlList::move_range (iterator start, iterator end, double xdelta, double ydelta) +bool +ControlList::erase_range_internal (double start, double endt, EventList & events) { - /* note: we assume higher level logic is in place to avoid this - reordering the time-order of control events in the list. ie. all - points after end are later than (end)->when. - */ + bool erased = false; + ControlEvent cp (start, 0.0f); + iterator s; + iterator e; - { - Glib::Mutex::Lock lm (_lock); - - while (start != end) { - (*start)->when += xdelta; - (*start)->value += ydelta; - if (isnan ((*start)->value)) { - abort (); - } - ++start; - } - - if (!_frozen) { - _events.sort (event_time_less_than); - } else { - _sort_pending = true; - } - - mark_dirty (); + if ((s = lower_bound (events.begin(), events.end(), &cp, time_comparator)) != events.end()) { + cp.when = endt; + e = upper_bound (events.begin(), events.end(), &cp, time_comparator); + events.erase (s, e); + erased = true; } - maybe_signal_changed (); + return erased; } void @@ -1315,5 +1295,51 @@ ControlList::paste (ControlList& alist, double pos, float times) return true; } +/** Move automation around according to a list of region movements */ +void +ControlList::move_ranges (RangeMoveList const & movements) +{ + { + Glib::Mutex::Lock lm (_lock); + + /* a copy of the events list before we started moving stuff around */ + EventList old_events = _events; + + /* clear the source and destination ranges in the new list */ + for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) { + + erase_range_internal (i->from, i->from + i->length, _events); + erase_range_internal (i->to, i->to + i->length, _events); + + } + + /* copy the events into the new list */ + for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) { + iterator j = old_events.begin (); + EventTime const limit = i->from + i->length; + EventTime const dx = i->to - i->from; + while (j != old_events.end () && (*j)->when <= limit) { + if ((*j)->when >= i->from) { + ControlEvent* ev = new ControlEvent (**j); + ev->when += dx; + _events.push_back (ev); + } + ++j; + } + } + + if (!_frozen) { + _events.sort (event_time_less_than); + } else { + _sort_pending = true; + } + + reposition_for_rt_add (0); + mark_dirty (); + } + + maybe_signal_changed (); +} + } // namespace Evoral