View Issue Details

IDProjectCategoryView StatusLast Update
0002425ardourfeaturespublic2020-04-19 20:13
Reporterhectro Assigned Toseablade  
PrioritynormalSeveritymajorReproducibilityN/A
Status closedResolutionfixed 
Product VersionSVN/2.0-ongoing 
Summary0002425: request for being able to lock automation to the region
DescriptionI just worked on a project for several days that ended having over 30 tracks, almost all of them with plugin automation tracks. It was a real nightmare to move whole sections of the project in order to make room for new sections in the middle. I had to redo the automation and/or to cut and paste it section by section (and having difficulties to paste it at the exact same place in relationship to the region). Like in other multitrack applications (Reaper, Cubase,etc) it would be great to have a button or settings option where to choose if automation follows the region or not. I didn't pay a cent for Ardour and it has helped me to accomplish several projects (commercial and artistic) and $50 is probably way below the cost of the work needed to accomplish this... but maybe others can join.
TagsNo tags attached.

  Users sponsoring this issue
Sponsors List Total Sponsorship = US$ 100

2008-10-22 04:47: hectro (US$ 50)
2008-11-20 03:00: seablade (US$ 50)
  Users sponsoring this issue (Total Sponsorship = US$ 100)

Activities

seablade

2008-11-20 03:01

manager   ~0005247

Seconded. I have a strong feeling this is going to be difficult to do in Ardour sadly, but at least I can offer to find $50 to get it done if it will help at all.

  Seablade

hectro

2008-11-20 03:36

reporter   ~0005248

Great Seablade! Well, if we get more people on board maybe the sponsorship can help with all the work needed for implementing this.

2008-12-01 14:36

 

afr.patch (28,450 bytes)   
--- a/gtk2_ardour/ardour.menus
+++ b/gtk2_ardour/ardour.menus
@@ -555,6 +555,7 @@
                	     <menuitem action='RubberbandingSnapsToGrid'/>
                	     <menuitem action='AutoAnalyseAudio'/>
                	     <menuitem action='toggle-region-fades'/>
+		     <menuitem action='automation-follows-regions'/>
 		</menu>
          </menu>
 	 <menu name='Help' action='Help'>
diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc
index 854eb57..06510b1 100644
--- a/gtk2_ardour/automation_line.cc
+++ b/gtk2_ardour/automation_line.cc
@@ -1102,12 +1102,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<ARDOUR::AutomationList> list)
 {
 	alist = list;
diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h
index 1123a7b..7fd9dd3 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 279de99..f56b163 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -355,6 +355,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 0f78b60..2c46409 100644
--- a/gtk2_ardour/editor_actions.cc
+++ b/gtk2_ardour/editor_actions.cc
@@ -98,6 +98,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));
@@ -1703,6 +1704,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.
  */
@@ -1749,6 +1756,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 373babf..fa0197c 100644
--- a/gtk2_ardour/mixer_strip.cc
+++ b/gtk2_ardour/mixer_strip.cc
@@ -1219,12 +1219,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> processor)
+MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> 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 63a4797..51b891d 100644
--- a/gtk2_ardour/mixer_strip.h
+++ b/gtk2_ardour/mixer_strip.h
@@ -239,7 +239,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<ARDOUR::Processor> processor);
+	void hide_processor_editor (boost::weak_ptr<ARDOUR::Processor> processor);
 	void hide_redirect_editors ();
 
 	bool ignore_speed_adjustment;
diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc
index 370bb75..974cf5e 100644
--- a/gtk2_ardour/processor_box.cc
+++ b/gtk2_ardour/processor_box.cc
@@ -559,8 +559,7 @@ ProcessorBox::redisplay_processors ()
 	processor_active_connections.clear ();
 	processor_name_connections.clear ();
 
-	void (ProcessorBox::*method)(boost::shared_ptr<Processor>) = &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:
@@ -573,8 +572,13 @@ ProcessorBox::redisplay_processors ()
 }
 
 void
-ProcessorBox::add_processor_to_display (boost::shared_ptr<Processor> processor)
+ProcessorBox::add_processor_to_display (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> 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 5d3eb73..e148f14 100644
--- a/gtk2_ardour/processor_box.h
+++ b/gtk2_ardour/processor_box.h
@@ -156,7 +156,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<ARDOUR::Processor>);
+	void add_processor_to_display (boost::weak_ptr<ARDOUR::Processor>);
 	void row_deleted (const Gtk::TreeModel::Path& path);
 	void show_processor_active (boost::weak_ptr<ARDOUR::Processor>);
 	void show_processor_name (boost::weak_ptr<ARDOUR::Processor>);
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc
index 71ad6e7..2a50b10 100644
--- a/gtk2_ardour/route_time_axis.cc
+++ b/gtk2_ardour/route_time_axis.cc
@@ -279,8 +279,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 ();
 }
 
@@ -1900,8 +1900,13 @@ RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::Process
 }
 
 void
-RouteTimeAxisView::add_existing_processor_automation_curves (boost::shared_ptr<Processor> processor)
+RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
 	set<Evoral::Parameter> s;
 	boost::shared_ptr<AutomationLine> al;
 
@@ -1957,8 +1962,13 @@ RouteTimeAxisView::add_automation_child(Evoral::Parameter param, boost::shared_p
 
 
 void
-RouteTimeAxisView::add_processor_to_subplugin_menu (boost::shared_ptr<Processor> processor)
+RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
 	using namespace Menu_Helpers;
 	ProcessorAutomationInfo *rai;
 	list<ProcessorAutomationInfo*>::iterator x;
@@ -2087,8 +2097,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<ProcessorAutomationInfo*>::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 405f493..c41e60a 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<ARDOUR::Processor>);
+	void add_processor_to_subplugin_menu (boost::weak_ptr<ARDOUR::Processor>);
 	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<ARDOUR::Processor> i, Evoral::Parameter);
 
 	void add_processor_automation_curve (boost::shared_ptr<ARDOUR::Processor> r, Evoral::Parameter);
-	void add_existing_processor_automation_curves (boost::shared_ptr<ARDOUR::Processor>);
+	void add_existing_processor_automation_curves (boost::weak_ptr<ARDOUR::Processor>);
 
 	void add_automation_child(Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show=true);
 	
diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h
index 5565e20..ce1dc89 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 824d864..955f2ed 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<Region> wregion);
 
+	void move_processor_automation (boost::weak_ptr<Processor>, Evoral::RangeMoveList const &);
+
 	sigc::signal<void>            RecordEnableChanged;
 	sigc::signal<void>            SpeedChanged;
 	sigc::signal<void>            ReverseChanged;
@@ -204,6 +206,7 @@ class Diskstream : public SessionObject
 
 	virtual void playlist_changed (Change);
 	virtual void playlist_deleted (boost::weak_ptr<Playlist>);
+	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 b937c41..cdde985 100644
--- a/libs/ardour/ardour/playlist.h
+++ b/libs/ardour/ardour/playlist.h
@@ -35,7 +35,9 @@
 
 #include <pbd/undo.h>
 #include <pbd/stateful.h> 
-#include <pbd/statefuldestructible.h> 
+#include <pbd/statefuldestructible.h>
+
+#include <evoral/types.hpp>
 
 #include <ardour/ardour.h>
 #include <ardour/session_object.h>
@@ -126,6 +128,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this<Pla
 	sigc::signal<void>      Modified;
 	sigc::signal<void>      NameChanged;
 	sigc::signal<void>      LengthChanged;
+	sigc::signal<void, Evoral::RangeMoveList const &> RangesMoved;
 
 	static string bump_name (string old_name, Session&);
 
@@ -177,6 +180,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this<Pla
 
 	RegionList       regions;  /* the current list of regions in the playlist */
 	std::set<boost::shared_ptr<Region> > all_regions; /* all regions ever added to this playlist */
+	std::list<sigc::connection> 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<Pla
 	RegionList       pending_bounds;
 	bool             pending_modified;
 	bool             pending_length;
+	Evoral::RangeMoveList pending_range_moves;
 	bool             save_on_thaw;
 	string           last_save_reason;
 	uint32_t         in_set_state;
@@ -227,6 +232,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this<Pla
 	void notify_layering_changed ();
 	void notify_modified ();
 	void notify_state_changed (Change);
+	void notify_region_moved (boost::shared_ptr<Region>);
 
 	void mark_session_dirty();
 
diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h
index fb98cb5..59d4637 100644
--- a/libs/ardour/ardour/route.h
+++ b/libs/ardour/ardour/route.h
@@ -140,10 +140,10 @@ class Route : public IO
 
 	void flush_processors ();
 
-	template<class T> void foreach_processor (T *obj, void (T::*func)(boost::shared_ptr<Processor>)) {
+	void foreach_processor (sigc::slot<void, boost::weak_ptr<Processor> > method) {
 		Glib::RWLock::ReaderLock lm (_processor_lock);
 		for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-			(obj->*func) (*i);
+			method (boost::weak_ptr<Processor> (*i));
 		}
 	}
 
diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc
index 9665176..334e433 100644
--- a/libs/ardour/diskstream.cc
+++ b/libs/ardour/diskstream.cc
@@ -37,6 +37,7 @@
 #include <pbd/basename.h>
 #include <glibmm/thread.h>
 #include <pbd/xml++.h>
+#include <pbd/memento_command.h>
 
 #include <ardour/ardour.h>
 #include <ardour/audioengine.h>
@@ -48,6 +49,7 @@
 #include <ardour/playlist.h>
 #include <ardour/cycle_timer.h>
 #include <ardour/region.h>
+#include <ardour/panner.h>
 
 #include "i18n.h"
 #include <locale.h>
@@ -312,6 +314,7 @@ Diskstream::use_playlist (boost::shared_ptr<Playlist> 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> 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>(_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<Region> 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<AutomationList> gain_alist = _io->gain_control()->alist();
+	XMLNode & before = gain_alist->get_state ();
+	gain_alist->move_ranges (movements);
+	_session.add_command (
+		new MementoCommand<AutomationList> (
+			*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<AutomationList> pan_alist = p.streampanner(i).pan_control()->alist();
+		XMLNode & before = pan_alist->get_state ();
+		pan_alist->move_ranges (movements);
+		_session.add_command (
+			new MementoCommand<AutomationList> (
+				*pan_alist.get(), &before, &pan_alist->get_state ()
+				)
+			);
+	}
+
+	/* move processor automation */
+	/* XXX: ewww */
+	Route * route = dynamic_cast<Route*> (_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<Processor> p, Evoral::RangeMoveList const & movements)
+{
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
+	set<Evoral::Parameter> const a = processor->what_can_be_automated ();
+
+	for (set<Evoral::Parameter>::iterator i = a.begin (); i != a.end (); ++i) {
+		boost::shared_ptr<AutomationList> al = processor->automation_control(*i)->alist();
+		XMLNode & before = al->get_state ();
+		al->move_ranges (movements);
+		_session.add_command (
+			new MementoCommand<AutomationList> (
+				*al.get(), &before, &al->get_state ()
+				)
+			);
+	}
+}
+
diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc
index fbe9990..433e567 100644
--- a/libs/ardour/playlist.cc
+++ b/libs/ardour/playlist.cc
@@ -361,6 +361,25 @@ Playlist::notify_region_removed (boost::shared_ptr<Region> r)
 }
 
 void
+Playlist::notify_region_moved (boost::shared_ptr<Region> 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<Region> r)
 {
 	/* the length change might not be true, but we have to act
@@ -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> region, nframes_t posit
 		}
 	}
 
-	region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), 
-						  boost::weak_ptr<Region> (region)));
+	region_state_changed_connections.push_back (
+		region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), 
+							  boost::weak_ptr<Region> (region)))
+		);
 
 	return true;
 }
@@ -1288,20 +1314,22 @@ Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> 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<sigc::connection>::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 3679930..48050f9 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<ControlList> 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<ControlList> 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 fc1001c..4bca6f1 100644
--- a/libs/evoral/evoral/types.hpp
+++ b/libs/evoral/evoral/types.hpp
@@ -20,6 +20,7 @@
 #define EVORAL_TYPES_HPP
 
 #include <stdint.h>
+#include <list>
 
 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<RangeMove> RangeMoveList;
+
 } // namespace Evoral
 
 #endif // EVORAL_TYPES_HPP
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
index e1cea8e..033e375 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.
-	*/
-
-	{
-		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 ();
+	bool erased = false;
+	ControlEvent cp (start, 0.0f);
+	iterator s;
+	iterator e;
+
+	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
 
diff --git a/libs/evoral/test/SConstruct b/libs/evoral/test/SConstruct
new file mode 100644
index 0000000..538c68e
--- /dev/null
+++ b/libs/evoral/test/SConstruct
@@ -0,0 +1,18 @@
+
+import os
+
+env = Environment ()
+
+tests = 'control_list.hpp'
+
+env.Append (CPPPATH = ['../../glibmm2/glib', '../../sigc++2', '..'])
+env.Append (LIBPATH = ['..', '../../glibmm2', '../../sigc++2', '../../pbd'])
+env.Append (LIBS = ['evoral', 'glibmm2', 'sigc++2', 'pbd'])
+env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
+env.Command ('runner.cpp', Split (tests), 'cxxtestgen.py --error-printer -o $TARGET $SOURCES')
+runner = env.Program ('runner', 'runner.cpp')
+e = os.environ
+e['LD_LIBRARY_PATH'] = '..:../../glibmm2:../../sigc++2:../../pbd'
+env.Append (ENV = e)
+env.Command ('test', runner, './runner')
+
diff --git a/libs/evoral/test/control_list.hpp b/libs/evoral/test/control_list.hpp
new file mode 100644
index 0000000..d582cdc
--- /dev/null
+++ b/libs/evoral/test/control_list.hpp
@@ -0,0 +1,71 @@
+#include <glibmm.h>
+#include <cxxtest/TestSuite.h>
+#include "evoral/ControlList.hpp"
+
+class ControlListTests : public CxxTest::TestSuite {
+
+public:
+	void test_move_ranges_one_region ()
+	{
+		Glib::thread_init ();
+		
+		Evoral::ControlList cl (Evoral::Parameter (0));
+
+		cl.add (0, 0);
+		cl.add (1, 1);
+		cl.add (2, 2);
+		cl.add (3, 3);
+
+		Evoral::RangeMoveList ml;
+
+		ml.push_back (Evoral::RangeMove (0, 2, 4));
+
+		cl.move_ranges (ml);
+
+		Evoral::ControlList::iterator i = cl.begin ();
+		TS_ASSERT_EQUALS ((*i)->when, 3);
+		TS_ASSERT_EQUALS ((*i)->value, 3);
+		++i;
+		TS_ASSERT_EQUALS ((*i)->when, 4);
+		TS_ASSERT_EQUALS ((*i)->value, 0);
+		++i;
+		TS_ASSERT_EQUALS ((*i)->when, 5);
+		TS_ASSERT_EQUALS ((*i)->value, 1);
+		++i;
+		TS_ASSERT_EQUALS ((*i)->when, 6);
+		TS_ASSERT_EQUALS ((*i)->value, 2);
+	}
+
+	void test_move_ranges_two_regions ()
+	{
+		Evoral::ControlList cl (Evoral::Parameter (0));
+
+		cl.add (0, 0);
+		cl.add (1, 1);
+		cl.add (2, 2);
+		cl.add (3, 3);
+
+		Evoral::RangeMoveList ml;
+
+		ml.push_back (Evoral::RangeMove (0, 1, 2));
+		ml.push_back (Evoral::RangeMove (2, 3, 4));
+
+		cl.move_ranges (ml);
+
+		Evoral::ControlList::iterator i = cl.begin ();
+		TS_ASSERT_EQUALS ((*i)->when, 2);
+		TS_ASSERT_EQUALS ((*i)->value, 0);
+		++i;
+		TS_ASSERT_EQUALS ((*i)->when, 3);
+		TS_ASSERT_EQUALS ((*i)->value, 1);
+		++i;
+		TS_ASSERT_EQUALS ((*i)->when, 4);
+		TS_ASSERT_EQUALS ((*i)->value, 2);
+		++i;
+		TS_ASSERT_EQUALS ((*i)->when, 5);
+		TS_ASSERT_EQUALS ((*i)->value, 3);
+		
+
+	}
+
+};
afr.patch (28,450 bytes)   

cth103

2008-12-01 14:37

administrator   ~0005409

Attached is the first cut of a patch (against SVN 3.0) which implements this. Any comments welcome.

paul

2008-12-02 17:09

administrator   ~0005423

carl - hold out for more money! :))

hectro

2008-12-03 06:44

reporter   ~0005467

Thanks a lot for this! I'll build Ardour 3.0 and test this next week.

2008-12-16 01:53

 

afr-2.patch (23,604 bytes)   
diff -r 392898016f34 gtk2_ardour/automation_line.cc
--- a/gtk2_ardour/automation_line.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/automation_line.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -1102,12 +1102,6 @@
 }
 
 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<ARDOUR::AutomationList> list)
 {
 	alist = list;
diff -r 392898016f34 gtk2_ardour/automation_line.h
--- a/gtk2_ardour/automation_line.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/automation_line.h	Tue Dec 16 01:51:10 2008 +0000
@@ -162,7 +162,6 @@
 	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 -r 392898016f34 gtk2_ardour/editor.h
--- a/gtk2_ardour/editor.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/editor.h	Tue Dec 16 01:51:10 2008 +0000
@@ -356,6 +356,7 @@
 	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 -r 392898016f34 gtk2_ardour/editor_actions.cc
--- a/gtk2_ardour/editor_actions.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/editor_actions.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -100,6 +100,7 @@
 	/* 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 @@
 	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 @@
 		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 -r 392898016f34 gtk2_ardour/mixer_strip.cc
--- a/gtk2_ardour/mixer_strip.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/mixer_strip.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -1297,12 +1297,17 @@
 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> processor)
+MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
 	void* gui = processor->get_gui ();
 	
 	if (gui) {
diff -r 392898016f34 gtk2_ardour/mixer_strip.h
--- a/gtk2_ardour/mixer_strip.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/mixer_strip.h	Tue Dec 16 01:51:10 2008 +0000
@@ -250,7 +250,7 @@
 	void name_changed ();
 	void update_speed_display ();
 	void map_frozen ();
-	void hide_processor_editor (boost::shared_ptr<ARDOUR::Processor> processor);
+	void hide_processor_editor (boost::weak_ptr<ARDOUR::Processor> processor);
 	void hide_redirect_editors ();
 
 	bool ignore_speed_adjustment;
diff -r 392898016f34 gtk2_ardour/processor_box.cc
--- a/gtk2_ardour/processor_box.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/processor_box.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -562,8 +562,7 @@
 	processor_active_connections.clear ();
 	processor_name_connections.clear ();
 
-	void (ProcessorBox::*method)(boost::shared_ptr<Processor>) = &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 @@
 }
 
 void
-ProcessorBox::add_processor_to_display (boost::shared_ptr<Processor> processor)
+ProcessorBox::add_processor_to_display (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
 	if (processor->placement() != _placement) {
 		return;
 	}
diff -r 392898016f34 gtk2_ardour/processor_box.h
--- a/gtk2_ardour/processor_box.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/processor_box.h	Tue Dec 16 01:51:10 2008 +0000
@@ -154,7 +154,7 @@
 	bool processor_button_press_event (GdkEventButton *);
 	bool processor_button_release_event (GdkEventButton *);
 	void redisplay_processors ();
-	void add_processor_to_display (boost::shared_ptr<ARDOUR::Processor>);
+	void add_processor_to_display (boost::weak_ptr<ARDOUR::Processor>);
 	void row_deleted (const Gtk::TreeModel::Path& path);
 	void show_processor_active (boost::weak_ptr<ARDOUR::Processor>);
 	void show_processor_name (boost::weak_ptr<ARDOUR::Processor>);
diff -r 392898016f34 gtk2_ardour/route_time_axis.cc
--- a/gtk2_ardour/route_time_axis.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/route_time_axis.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -286,8 +286,8 @@
 	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 @@
 }
 
 void
-RouteTimeAxisView::add_existing_processor_automation_curves (boost::shared_ptr<Processor> processor)
+RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
 	set<Evoral::Parameter> s;
 	boost::shared_ptr<AutomationLine> al;
 
@@ -1977,8 +1982,13 @@
 
 
 void
-RouteTimeAxisView::add_processor_to_subplugin_menu (boost::shared_ptr<Processor> processor)
+RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
 {
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
 	using namespace Menu_Helpers;
 	ProcessorAutomationInfo *rai;
 	list<ProcessorAutomationInfo*>::iterator x;
@@ -2108,8 +2118,8 @@
 
 	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<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
 
diff -r 392898016f34 gtk2_ardour/route_time_axis.h
--- a/gtk2_ardour/route_time_axis.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/gtk2_ardour/route_time_axis.h	Tue Dec 16 01:51:10 2008 +0000
@@ -184,7 +184,7 @@
 
 	void processors_changed ();
 	
-	void add_processor_to_subplugin_menu (boost::shared_ptr<ARDOUR::Processor>);
+	void add_processor_to_subplugin_menu (boost::weak_ptr<ARDOUR::Processor>);
 	void remove_processor_automation_node (ProcessorAutomationNode* pan);
 
 	void processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo*,
@@ -205,7 +205,7 @@
 	find_processor_automation_curve (boost::shared_ptr<ARDOUR::Processor> i, Evoral::Parameter);
 
 	void add_processor_automation_curve (boost::shared_ptr<ARDOUR::Processor> r, Evoral::Parameter);
-	void add_existing_processor_automation_curves (boost::shared_ptr<ARDOUR::Processor>);
+	void add_existing_processor_automation_curves (boost::weak_ptr<ARDOUR::Processor>);
 
 	void add_automation_child(Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show=true);
 	
diff -r 392898016f34 libs/ardour/ardour/configuration_vars.h
--- a/libs/ardour/ardour/configuration_vars.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/ardour/ardour/configuration_vars.h	Tue Dec 16 01:51:10 2008 +0000
@@ -82,6 +82,7 @@
 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 -r 392898016f34 libs/ardour/ardour/diskstream.h
--- a/libs/ardour/ardour/diskstream.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/ardour/ardour/diskstream.h	Tue Dec 16 01:51:10 2008 +0000
@@ -140,6 +140,8 @@
 
 	void remove_region_from_last_capture (boost::weak_ptr<Region> wregion);
 
+	void move_processor_automation (boost::weak_ptr<Processor>, Evoral::RangeMoveList const &);
+
 	sigc::signal<void>            RecordEnableChanged;
 	sigc::signal<void>            SpeedChanged;
 	sigc::signal<void>            ReverseChanged;
@@ -204,6 +206,7 @@
 
 	virtual void playlist_changed (Change);
 	virtual void playlist_deleted (boost::weak_ptr<Playlist>);
+	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 @@
 	sigc::connection ports_created_c;
 	sigc::connection plmod_connection;
 	sigc::connection plgone_connection;
+	sigc::connection plregion_connection;
 	
 	Flag _flags;
 };
diff -r 392898016f34 libs/ardour/ardour/playlist.h
--- a/libs/ardour/ardour/playlist.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/ardour/ardour/playlist.h	Tue Dec 16 01:51:10 2008 +0000
@@ -35,7 +35,9 @@
 
 #include <pbd/undo.h>
 #include <pbd/stateful.h> 
-#include <pbd/statefuldestructible.h> 
+#include <pbd/statefuldestructible.h>
+
+#include <evoral/types.hpp>
 
 #include <ardour/ardour.h>
 #include <ardour/session_object.h>
@@ -126,6 +128,7 @@
 	sigc::signal<void>      Modified;
 	sigc::signal<void>      NameChanged;
 	sigc::signal<void>      LengthChanged;
+	sigc::signal<void, Evoral::RangeMoveList const &> RangesMoved;
 
 	static string bump_name (string old_name, Session&);
 
@@ -177,6 +180,7 @@
 
 	RegionList       regions;  /* the current list of regions in the playlist */
 	std::set<boost::shared_ptr<Region> > all_regions; /* all regions ever added to this playlist */
+	std::list<sigc::connection> region_state_changed_connections;
 	DataType        _type;
 	mutable gint    block_notifications;
 	mutable gint    ignore_state_changes;
@@ -186,6 +190,7 @@
 	RegionList       pending_bounds;
 	bool             pending_modified;
 	bool             pending_length;
+	Evoral::RangeMoveList pending_range_moves;
 	bool             save_on_thaw;
 	string           last_save_reason;
 	uint32_t         in_set_state;
@@ -227,6 +232,7 @@
 	void notify_layering_changed ();
 	void notify_modified ();
 	void notify_state_changed (Change);
+	void notify_region_moved (boost::shared_ptr<Region>);
 
 	void mark_session_dirty();
 
diff -r 392898016f34 libs/ardour/ardour/route.h
--- a/libs/ardour/ardour/route.h	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/ardour/ardour/route.h	Tue Dec 16 01:51:10 2008 +0000
@@ -142,10 +142,10 @@
 
 	void flush_processors ();
 
-	template<class T> void foreach_processor (T *obj, void (T::*func)(boost::shared_ptr<Processor>)) {
+	void foreach_processor (sigc::slot<void, boost::weak_ptr<Processor> > method) {
 		Glib::RWLock::ReaderLock lm (_processor_lock);
 		for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-			(obj->*func) (*i);
+			method (boost::weak_ptr<Processor> (*i));
 		}
 	}
 
diff -r 392898016f34 libs/ardour/diskstream.cc
--- a/libs/ardour/diskstream.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/ardour/diskstream.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -37,6 +37,7 @@
 #include <pbd/basename.h>
 #include <glibmm/thread.h>
 #include <pbd/xml++.h>
+#include <pbd/memento_command.h>
 
 #include <ardour/ardour.h>
 #include <ardour/audioengine.h>
@@ -48,6 +49,7 @@
 #include <ardour/playlist.h>
 #include <ardour/cycle_timer.h>
 #include <ardour/region.h>
+#include <ardour/panner.h>
 
 #include "i18n.h"
 #include <locale.h>
@@ -312,6 +314,7 @@
 
 		plmod_connection.disconnect ();
 		plgone_connection.disconnect ();
+		plregion_connection.disconnect ();
 
 		if (_playlist) {
 			_playlist->release();
@@ -326,6 +329,7 @@
 		
 		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>(_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 @@
 	_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<AutomationList> gain_alist = _io->gain_control()->alist();
+	XMLNode & before = gain_alist->get_state ();
+	gain_alist->move_ranges (movements);
+	_session.add_command (
+		new MementoCommand<AutomationList> (
+			*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<AutomationList> pan_alist = p.streampanner(i).pan_control()->alist();
+		XMLNode & before = pan_alist->get_state ();
+		pan_alist->move_ranges (movements);
+		_session.add_command (
+			new MementoCommand<AutomationList> (
+				*pan_alist.get(), &before, &pan_alist->get_state ()
+				)
+			);
+	}
+
+	/* move processor automation */
+	/* XXX: ewww */
+	Route * route = dynamic_cast<Route*> (_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<Processor> p, Evoral::RangeMoveList const & movements)
+{
+	boost::shared_ptr<Processor> processor (p.lock ());
+	if (!processor) {
+		return;
+	}
+	
+	set<Evoral::Parameter> const a = processor->what_can_be_automated ();
+
+	for (set<Evoral::Parameter>::iterator i = a.begin (); i != a.end (); ++i) {
+		boost::shared_ptr<AutomationList> al = processor->automation_control(*i)->alist();
+		XMLNode & before = al->get_state ();
+		al->move_ranges (movements);
+		_session.add_command (
+			new MementoCommand<AutomationList> (
+				*al.get(), &before, &al->get_state ()
+				)
+			);
+	}
+}
+
diff -r 392898016f34 libs/ardour/playlist.cc
--- a/libs/ardour/playlist.cc	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/ardour/playlist.cc	Tue Dec 16 01:51:10 2008 +0000
@@ -361,6 +361,25 @@
 }
 
 void
+Playlist::notify_region_moved (boost::shared_ptr<Region> 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<Region> r)
 {
 	/* the length change might not be true, but we have to act
@@ -452,9 +471,14 @@
 		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 @@
 		}
 	}
 
-	region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), 
-						  boost::weak_ptr<Region> (region)));
+	region_state_changed_connections.push_back (
+		region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), 
+							  boost::weak_ptr<Region> (region)))
+		);
 
 	return true;
 }
@@ -1288,20 +1314,22 @@
 		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 @@
 {
 	{ 
 		RegionLock rl (this);
+
+		for (
+			std::list<sigc::connection>::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 -r 392898016f34 libs/evoral/evoral/ControlList.hpp
--- a/libs/evoral/evoral/ControlList.hpp	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/evoral/evoral/ControlList.hpp	Tue Dec 16 01:51:10 2008 +0000
@@ -120,7 +120,7 @@
 	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<ControlList> cut (double, double);
@@ -241,6 +241,7 @@
 	bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
 
 	boost::shared_ptr<ControlList> cut_copy_clear (double, double, int op);
+	bool erase_range_internal (double start, double end, EventList &);
 
 	virtual void maybe_signal_changed ();
 	
diff -r 392898016f34 libs/evoral/evoral/types.hpp
--- a/libs/evoral/evoral/types.hpp	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/evoral/evoral/types.hpp	Tue Dec 16 01:51:10 2008 +0000
@@ -20,6 +20,7 @@
 #define EVORAL_TYPES_HPP
 
 #include <stdint.h>
+#include <list>
 
 namespace Evoral {
 
@@ -41,6 +42,16 @@
 /** 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<RangeMove> RangeMoveList;
+
 } // namespace Evoral
 
 #endif // EVORAL_TYPES_HPP
diff -r 392898016f34 libs/evoral/src/ControlList.cpp
--- a/libs/evoral/src/ControlList.cpp	Mon Dec 15 18:56:30 2008 +0000
+++ b/libs/evoral/src/ControlList.cpp	Tue Dec 16 01:51:10 2008 +0000
@@ -390,16 +390,10 @@
 
 	{
 		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 @@
 	}
 }
 
-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 @@
 	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
 
afr-2.patch (23,604 bytes)   

cth103

2008-12-16 01:55

administrator   ~0005526

Uploaded afr-2.patch which is the same patch but updated to apply cleanly to 3.0 SVN after the 2.X merge.

cth103

2008-12-16 23:22

administrator   ~0005528

Now applied to the 3.0 branch. Testing and comments welcome.

seablade

2009-01-15 01:08

manager   ~0005607

Resolving the issue due to the long period of time with no response. Carl the donation will be going via Ardour donate at the top of next month, so you and Paul can get together and sort that one for the moment. I need to look a bit closer at the donation system in Mantis sometime I think.

    Seablade

system

2020-04-19 20:13

developer   ~0021803

Issue has been closed automatically, by Trigger Close Plugin.
Feel free to re-open with additional information if you think the issue is not resolved.

Issue History

Date Modified Username Field Change
2008-10-22 04:47 hectro New Issue
2008-10-22 04:47 hectro Sponsorship Added hectro: US$ 50
2008-10-22 04:47 hectro Sponsorship Total 0 => 50
2008-11-20 03:00 seablade Sponsorship Added seablade: US$ 50
2008-11-20 03:00 seablade Sponsorship Total 50 => 100
2008-11-20 03:01 seablade Note Added: 0005247
2008-11-20 03:36 hectro Note Added: 0005248
2008-12-01 14:36 cth103 File Added: afr.patch
2008-12-01 14:37 cth103 Note Added: 0005409
2008-12-01 14:37 cth103 Status new => feedback
2008-12-02 17:09 paul Note Added: 0005423
2008-12-03 06:44 hectro Note Added: 0005467
2008-12-16 01:53 cth103 File Added: afr-2.patch
2008-12-16 01:55 cth103 Note Added: 0005526
2008-12-16 23:22 cth103 Note Added: 0005528
2009-01-15 01:08 seablade cost => 0.00
2009-01-15 01:08 seablade Status feedback => resolved
2009-01-15 01:08 seablade Resolution open => fixed
2009-01-15 01:08 seablade Assigned To => seablade
2009-01-15 01:08 seablade Note Added: 0005607
2020-04-19 20:13 system Note Added: 0021803
2020-04-19 20:13 system Status resolved => closed