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