From ecb0cd5d119d28092a8f48e4521ac5eba197bb54 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 25 Jun 2010 20:47:09 +0000 Subject: [PATCH] Make MIDI region `automation' respect the automation mode so that it is only played back if the automation mode is set to "Play". Munge AutoState for AutomationRegionViews so that they reflect their AutomationTimeAxisView's setting. Fixes #3135. git-svn-id: svn://localhost/ardour2/branches/3.0@7304 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/automation_region_view.h | 1 - gtk2_ardour/automation_streamview.cc | 34 ++++++++++------ gtk2_ardour/automation_streamview.h | 2 + gtk2_ardour/automation_time_axis.cc | 1 + libs/ardour/ardour/automatable.h | 8 ++++ libs/ardour/ardour/midi_model.h | 2 +- libs/ardour/ardour/midi_region.h | 6 +++ libs/ardour/ardour/midi_source.h | 10 ++++- libs/ardour/ardour/midi_track.h | 2 +- libs/ardour/automatable.cc | 15 +++++++ libs/ardour/midi_region.cc | 59 ++++++++++++++++++++++++++-- libs/ardour/midi_source.cc | 14 ++++++- libs/evoral/evoral/Sequence.hpp | 6 ++- libs/evoral/src/Sequence.cpp | 12 ++++-- 14 files changed, 146 insertions(+), 26 deletions(-) diff --git a/gtk2_ardour/automation_region_view.h b/gtk2_ardour/automation_region_view.h index 6d3a00d4b6..691e2cd050 100644 --- a/gtk2_ardour/automation_region_view.h +++ b/gtk2_ardour/automation_region_view.h @@ -54,7 +54,6 @@ public: inline AutomationTimeAxisView* automation_view() const { return dynamic_cast(&trackview); } - void set_line(boost::shared_ptr line) { _line = line; } boost::shared_ptr line() { return _line; } // We are a ghost. Meta ghosts? Crazy talk. diff --git a/gtk2_ardour/automation_streamview.cc b/gtk2_ardour/automation_streamview.cc index fad14006b2..006834c59d 100644 --- a/gtk2_ardour/automation_streamview.cc +++ b/gtk2_ardour/automation_streamview.cc @@ -58,6 +58,7 @@ AutomationStreamView::AutomationStreamView (AutomationTimeAxisView& tv) new ArdourCanvas::Group(*tv.canvas_display())) , _controller(tv.controller()) , _automation_view(tv) + , _pending_automation_state (Off) { //canvas_rect->property_fill_color_rgba() = stream_base_color; canvas_rect->property_outline_color_rgba() = RGBA_BLACK; @@ -82,9 +83,9 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr region mr->midi_source()->load_model(); } - const boost::shared_ptr control - = boost::dynamic_pointer_cast( - region->control(_controller->controllable()->parameter())); + const boost::shared_ptr control = boost::dynamic_pointer_cast ( + region->control (_controller->controllable()->parameter(), true) + ); boost::shared_ptr list; if (control) { @@ -130,6 +131,12 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr region /* catch regionview going away */ region->DropReferences.connect (*this, invalidator (*this), boost::bind (&AutomationStreamView::remove_region_view, this, boost::weak_ptr(region)), gui_context()); + /* setup automation state for this region */ + boost::shared_ptr line = region_view->line (); + if (line && line->the_list()) { + line->the_list()->set_automation_state (automation_state ()); + } + RegionViewAdded (region_view); return region_view; @@ -144,11 +151,18 @@ AutomationStreamView::display_region(AutomationRegionView* region_view) void AutomationStreamView::set_automation_state (AutoState state) { - std::list::iterator i; - for (i = region_views.begin(); i != region_views.end(); ++i) { - boost::shared_ptr line = ((AutomationRegionView*)(*i))->line(); - if (line && line->the_list()) { - line->the_list()->set_automation_state (state); + /* XXX: not sure if this is right, but for now the automation state is basically held by + the regions' AutomationLists. Each region is always set to have the same AutoState. + */ + + if (region_views.empty()) { + _pending_automation_state = state; + } else { + for (std::list::iterator i = region_views.begin(); i != region_views.end(); ++i) { + boost::shared_ptr line = dynamic_cast(*i)->line(); + if (line && line->the_list()) { + line->the_list()->set_automation_state (state); + } } } } @@ -196,10 +210,8 @@ AutomationStreamView::color_handler () AutoState AutomationStreamView::automation_state () const { - /* XXX: bit of a hack: just return the state of our first RegionView */ - if (region_views.empty()) { - return Off; + return _pending_automation_state; } boost::shared_ptr line = ((AutomationRegionView*) region_views.front())->line (); diff --git a/gtk2_ardour/automation_streamview.h b/gtk2_ardour/automation_streamview.h index a3235f7b0e..01d3435714 100644 --- a/gtk2_ardour/automation_streamview.h +++ b/gtk2_ardour/automation_streamview.h @@ -69,6 +69,8 @@ class AutomationStreamView : public StreamView boost::shared_ptr _controller; AutomationTimeAxisView& _automation_view; + /** automation state that should be applied when this view gets its first RegionView */ + ARDOUR::AutoState _pending_automation_state; }; #endif /* __ardour_automation_streamview_h__ */ diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 9163b10dc1..841ec65fb9 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -270,6 +270,7 @@ AutomationTimeAxisView::set_automation_state (AutoState state) #endif } + cout << "_view = " << _view << "\n"; if (_view) { _view->set_automation_state (state); diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index 9b83705b0a..79bbec5199 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -24,6 +24,7 @@ #include #include #include +#include "pbd/signals.h" #include "evoral/ControlSet.hpp" #include "ardour/types.h" @@ -94,6 +95,9 @@ public: int set_automation_state (const XMLNode&, Evoral::Parameter default_param); XMLNode& get_automation_state(); + /** Emitted when the automation state of one of our controls changes */ + PBD::Signal1 AutomationStateChanged; + protected: Session& _a_session; @@ -109,6 +113,10 @@ public: nframes_t _last_automation_snapshot; static nframes_t _automation_interval; + +private: + void automation_state_changed (Evoral::Parameter const &); + PBD::ScopedConnectionList _control_connections; ///< connections to our controls' signals }; diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 9b2c66d5f0..f879c201ee 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -146,7 +146,7 @@ public: InsertMergePolicy insert_merge_policy () const; void set_insert_merge_policy (InsertMergePolicy); - + protected: int resolve_overlaps_unlocked (const NotePtr, void* arg = 0); diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 568638ed21..ac65b86fc3 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -115,6 +115,12 @@ class MidiRegion : public Region void set_position_internal (framepos_t pos, bool allow_bbt_recompute); void switch_source(boost::shared_ptr source); + void model_changed (); + void model_automation_state_changed (Evoral::Parameter const &); + + std::set _filtered_parameters; ///< parameters that we ask our source not to return when reading + PBD::ScopedConnection _model_connection; + PBD::ScopedConnection _source_connection; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 36c8548e20..0d0b744a95 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -63,7 +63,10 @@ class MidiSource : virtual public Source virtual nframes_t midi_read (Evoral::EventSink& dst, sframes_t source_start, sframes_t start, nframes_t cnt, - sframes_t stamp_offset, sframes_t negative_stamp_offset, MidiStateTracker*) const; + sframes_t stamp_offset, + sframes_t negative_stamp_offset, + MidiStateTracker*, + std::set const &) const; virtual nframes_t midi_write (MidiRingBuffer& src, sframes_t source_start, @@ -111,9 +114,12 @@ class MidiSource : virtual public Source void set_note_mode(NoteMode mode); boost::shared_ptr model() { return _model; } - void set_model(boost::shared_ptr m) { _model = m; } + void set_model (boost::shared_ptr); void drop_model(); + /** Emitted when a different MidiModel is set */ + PBD::Signal0 ModelChanged; + protected: virtual void flush_midi() = 0; diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index d181bea596..42446da70d 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -66,7 +66,7 @@ public: /** A control that will send "immediate" events to a MIDI track when twiddled */ struct MidiControl : public AutomationControl { MidiControl(MidiTrack* route, const Evoral::Parameter& param, - boost::shared_ptr al = boost::shared_ptr()) + boost::shared_ptr al = boost::shared_ptr()) : AutomationControl (route->session(), param, al) , _route (route) {} diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 3d705b3ea7..1e5b497bf7 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -150,6 +150,16 @@ Automatable::add_control(boost::shared_ptr ac) ControlSet::add_control(ac); _can_automate_list.insert(param); auto_state_changed(param); // sync everything up + + /* connect to automation_state_changed so that we can emit a signal when one of our controls' + automation state changes + */ + boost::shared_ptr c = boost::dynamic_pointer_cast (ac); + if (c) { + c->alist()->automation_state_changed.connect_same_thread ( + _control_connections, boost::bind (&Automatable::automation_state_changed, this, c->parameter()) + ); + } } void @@ -469,3 +479,8 @@ Automatable::automation_control (const Evoral::Parameter& id) const return boost::dynamic_pointer_cast(Evoral::ControlSet::control(id)); } +void +Automatable::automation_state_changed (Evoral::Parameter const & p) +{ + AutomationStateChanged (p); /* EMIT SIGNAL */ +} diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index abb3191463..fe49547bf3 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -24,7 +24,6 @@ #include - #include #include "pbd/basename.h" @@ -53,6 +52,8 @@ MidiRegion::MidiRegion (const SourceList& srcs) : Region (srcs) { midi_source(0)->Switched.connect_same_thread (*this, boost::bind (&MidiRegion::switch_source, this, _1)); + midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this)); + model_changed (); assert(_name.val().find("/") == string::npos); assert(_type == DataType::MIDI); } @@ -63,6 +64,8 @@ MidiRegion::MidiRegion (boost::shared_ptr other, frameoffset_t { assert(_name.val().find("/") == string::npos); midi_source(0)->Switched.connect_same_thread (*this, boost::bind (&MidiRegion::switch_source, this, _1)); + midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this)); + model_changed (); } MidiRegion::~MidiRegion () @@ -180,7 +183,8 @@ MidiRegion::_read_at (const SourceList& /*srcs*/, Evoral::EventSink& to_read, // read duration in frames output_buffer_position, // the offset in the output buffer negative_output_buffer_position, // amount to substract from note times - tracker + tracker, + _filtered_parameters ) != to_read) { return 0; /* "read nothing" */ } @@ -246,14 +250,63 @@ MidiRegion::midi_source (uint32_t n) const void MidiRegion::switch_source(boost::shared_ptr src) { + _source_connection.disconnect (); + boost::shared_ptr msrc = boost::dynamic_pointer_cast(src); - if (!msrc) + if (!msrc) { return; + } // MIDI regions have only one source _sources.clear(); _sources.push_back(msrc); set_name(msrc->name()); + + msrc->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this)); } +void +MidiRegion::model_changed () +{ + /* build list of filtered Parameters, being those whose automation state is not `Play' */ + + _filtered_parameters.clear (); + + Automatable::Controls const & c = model()->controls(); + + for (Automatable::Controls::const_iterator i = c.begin(); i != c.end(); ++i) { + boost::shared_ptr ac = boost::dynamic_pointer_cast (i->second); + assert (ac); + if (ac->alist()->automation_state() != Play) { + _filtered_parameters.insert (ac->parameter ()); + } + } + + /* watch for changes to controls' AutoState */ + model()->AutomationStateChanged.connect_same_thread ( + _model_connection, boost::bind (&MidiRegion::model_automation_state_changed, this, _1) + ); +} + +void +MidiRegion::model_automation_state_changed (Evoral::Parameter const & p) +{ + /* Update our filtered parameters list after a change to a parameter's AutoState */ + + boost::shared_ptr ac = model()->automation_control (p); + assert (ac); + + if (ac->alist()->automation_state() == Play) { + _filtered_parameters.erase (p); + } else { + _filtered_parameters.insert (p); + } + + /* the source will have an iterator into the model, and that iterator will have been set up + for a given set of filtered_paramters, so now that we've changed that list we must invalidate + the iterator. + */ + Glib::Mutex::Lock lm (midi_source(0)->mutex()); + midi_source(0)->invalidate (); +} diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 94c1a9b811..2e549b6fc6 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -142,11 +142,13 @@ MidiSource::invalidate () _model_iter.invalidate(); } +/** @param filtered A set of parameters whose MIDI messages will not be returned */ nframes_t MidiSource::midi_read (Evoral::EventSink& dst, sframes_t source_start, sframes_t start, nframes_t cnt, sframes_t stamp_offset, sframes_t negative_stamp_offset, - MidiStateTracker* tracker) const + MidiStateTracker* tracker, + std::set const & filtered) const { Glib::Mutex::Lock lm (_lock); @@ -158,7 +160,7 @@ MidiSource::midi_read (Evoral::EventSink& dst, sframes_t source_start // If the cached iterator is invalid, search for the first event past start if (_last_read_end == 0 || start != _last_read_end || !_model_iter_valid) { DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("*** %1 search for relevant iterator for %1 / %2\n", _name, source_start, start)); - for (i = _model->begin(); i != _model->end(); ++i) { + for (i = _model->begin(0, filtered); i != _model->end(); ++i) { if (converter.to(i->time()) >= start) { break; } @@ -311,4 +313,12 @@ MidiSource::drop_model () { cerr << name() << " drop model\n"; _model.reset(); + ModelChanged (); /* EMIT SIGNAL */ +} + +void +MidiSource::set_model (boost::shared_ptr m) +{ + _model = m; + ModelChanged (); /* EMIT SIGNAL */ } diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index 79a4181f67..1ad456b302 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -184,7 +184,7 @@ public: class const_iterator { public: const_iterator(); - const_iterator(const Sequence