From 38c613cd9a59f6ece041afa972b201a3d843d3e5 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 23 Jan 2023 03:57:53 +0100 Subject: [PATCH] Towards arranging sections This allows to move or copy whole sections of the timline (everything you hear) to a differnt position on the timeline. NB. Markers and tempo-map are not yet moved, and interpolated MIDI events are lost. --- libs/ardour/ardour/route.h | 1 + libs/ardour/ardour/session.h | 2 + libs/ardour/luabindings.cc | 1 + libs/ardour/route.cc | 30 ++++++++++++++ libs/ardour/session.cc | 58 ++++++++++++++++++++++++++++ share/scripts/s_cut_copy_section.lua | 8 ++++ 6 files changed, 100 insertions(+) create mode 100644 share/scripts/s_cut_copy_section.lua diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 376f73b331..20d565ff1f 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -167,6 +167,7 @@ public: /* end of vfunc-based API */ void shift (timepos_t const &, timecnt_t const &); + void cut_copy_section (timepos_t const& start, timepos_t const& end, timepos_t const& to, bool const copy); /* controls use set_solo() to modify this route's solo state */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 907aed987a..a88572777d 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1169,6 +1169,8 @@ public: void clear_range_selection (); void clear_object_selection (); + void cut_copy_section (Temporal::timepos_t const& start, Temporal::timepos_t const& end, Temporal::timepos_t const& to, bool const copy = false); + /* buffers for gain and pan */ gain_t* gain_automation_buffer () const; diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 60c40fb1e8..8ebb09070e 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -2902,6 +2902,7 @@ LuaBindings::common (lua_State* L) .addFunction ("session_range_is_free", &Session::session_range_is_free) .addFunction ("set_session_range_is_free", &Session::set_session_range_is_free) .addFunction ("remove_route_group", (void (Session::*)(RouteGroup*))&Session::remove_route_group) + .addFunction ("cut_copy_section", &Session::cut_copy_section) .addFunction ("vca_manager", &Session::vca_manager_ptr) .addExtCFunction ("timecode_to_sample_lua", ARDOUR::LuaAPI::timecode_to_sample_lua) .addExtCFunction ("sample_to_timecode_lua", ARDOUR::LuaAPI::sample_to_timecode_lua) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 980db8e7e9..ae4b096a92 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -4540,6 +4540,36 @@ Route::shift (timepos_t const & pos, timecnt_t const & distance) } } +void +Route::cut_copy_section (timepos_t const& start, timepos_t const& end, timepos_t const& to, bool const copy) +{ + ControllableSet acs; + automatables (acs); + for (auto& ec : acs) { + boost::shared_ptr ac = boost::dynamic_pointer_cast (ec); + if (!ac) { + continue; + } + boost::shared_ptr al = ac->alist(); + if (!al || al->empty ()) { + continue; + } + + XMLNode &before = al->get_state (); + boost::shared_ptr cl = copy ? al->copy (start, end) : al->cut (start, end); + if (!copy) { + /* remove time (negative distance), ripple */ + al->shift (start, end.distance (start)); + } + /* make space at the inserion point */ + al->shift (to, start.distance (end)); + al->paste (*cl, to); + + XMLNode &after = al->get_state (); + _session.add_command (new MementoCommand (*al.get(), &before, &after)); + } +} + void Route::set_plugin_state_dir (boost::weak_ptr p, const std::string& d) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 89c049662d..5116ef5112 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -7186,6 +7186,64 @@ Session::clear_object_selection () _object_selection = Temporal::Range (timepos_t::max (Temporal::AudioTime), timepos_t::max (Temporal::AudioTime)); } +void +Session::cut_copy_section (timepos_t const& start, timepos_t const& end, timepos_t const& to, bool const copy) +{ + std::list ltr; + TimelineRange tlr (start, end, 0); + ltr.push_back (tlr); + + if (copy) { + begin_reversible_command (_("Copy Section")); + } else { + begin_reversible_command (_("Move Section")); + } + + { + /* disable DiskReader::playlist_ranges_moved moving automation */ + bool automation_follows = Config->get_automation_follows_regions (); + Config->set_automation_follows_regions (false); + + for (auto& pl : _playlists->playlists) { + pl->freeze (); + pl->clear_changes (); + pl->clear_owned_changes (); + + boost::shared_ptr p = copy ? pl->copy (ltr) : pl->cut (ltr); + // TODO copy interpolated MIDI events + if (!copy) { + pl->ripple (start, end.distance(start), NULL); + } + + /* now make space at the insertion-point */ + pl->ripple (to, start.distance(end), NULL); + + pl->paste (p, to, 1); + + vector cmds; + pl->rdiff (cmds); + add_commands (cmds); + add_command (new StatefulDiffCommand (pl)); + } + + for (auto& pl : _playlists->playlists) { + pl->thaw (); + } + + Config->set_automation_follows_regions (automation_follows); + } + + for (auto& r : *(routes.reader())) { + r->cut_copy_section (start, end, to, copy); + } + + // TODO: update ranges and Tempo-Map + + if (!abort_empty_reversible_command ()) { + commit_reversible_command (); + } +} + void Session::auto_connect_route (boost::shared_ptr route, bool connect_inputs, diff --git a/share/scripts/s_cut_copy_section.lua b/share/scripts/s_cut_copy_section.lua new file mode 100644 index 0000000000..58bf3d93ad --- /dev/null +++ b/share/scripts/s_cut_copy_section.lua @@ -0,0 +1,8 @@ +ardour { ["type"] = "Snippet", name = "Move Section" } + +function factory (params) return function () + local start = Temporal.timepos_t(96000) + local _end = Temporal.timepos_t(144000) + local to = Temporal.timepos_t(480000) + Session:cut_copy_section (start, _end, to , true) +end end