summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/automation_region_view.h1
-rw-r--r--gtk2_ardour/automation_streamview.cc34
-rw-r--r--gtk2_ardour/automation_streamview.h2
-rw-r--r--gtk2_ardour/automation_time_axis.cc1
-rw-r--r--libs/ardour/ardour/automatable.h8
-rw-r--r--libs/ardour/ardour/midi_model.h2
-rw-r--r--libs/ardour/ardour/midi_region.h6
-rw-r--r--libs/ardour/ardour/midi_source.h10
-rw-r--r--libs/ardour/ardour/midi_track.h2
-rw-r--r--libs/ardour/automatable.cc15
-rw-r--r--libs/ardour/midi_region.cc59
-rw-r--r--libs/ardour/midi_source.cc14
-rw-r--r--libs/evoral/evoral/Sequence.hpp6
-rw-r--r--libs/evoral/src/Sequence.cpp12
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<AutomationTimeAxisView*>(&trackview); }
- void set_line(boost::shared_ptr<AutomationLine> line) { _line = line; }
boost::shared_ptr<AutomationLine> 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> region
mr->midi_source()->load_model();
}
- const boost::shared_ptr<AutomationControl> control
- = boost::dynamic_pointer_cast<AutomationControl>(
- region->control(_controller->controllable()->parameter()));
+ const boost::shared_ptr<AutomationControl> control = boost::dynamic_pointer_cast<AutomationControl> (
+ region->control (_controller->controllable()->parameter(), true)
+ );
boost::shared_ptr<AutomationList> list;
if (control) {
@@ -130,6 +131,12 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region
/* catch regionview going away */
region->DropReferences.connect (*this, invalidator (*this), boost::bind (&AutomationStreamView::remove_region_view, this, boost::weak_ptr<Region>(region)), gui_context());
+ /* setup automation state for this region */
+ boost::shared_ptr<AutomationLine> 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<RegionView *>::iterator i;
- for (i = region_views.begin(); i != region_views.end(); ++i) {
- boost::shared_ptr<AutomationLine> 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<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+ boost::shared_ptr<AutomationLine> line = dynamic_cast<AutomationRegionView*>(*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<AutomationLine> 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<AutomationController> _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 <set>
#include <string>
#include <boost/shared_ptr.hpp>
+#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<void, Evoral::Parameter> 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> source);
+ void model_changed ();
+ void model_automation_state_changed (Evoral::Parameter const &);
+
+ std::set<Evoral::Parameter> _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<nframes_t>& 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<Evoral::Parameter> const &) const;
virtual nframes_t midi_write (MidiRingBuffer<nframes_t>& src,
sframes_t source_start,
@@ -111,9 +114,12 @@ class MidiSource : virtual public Source
void set_note_mode(NoteMode mode);
boost::shared_ptr<MidiModel> model() { return _model; }
- void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
+ void set_model (boost::shared_ptr<MidiModel>);
void drop_model();
+ /** Emitted when a different MidiModel is set */
+ PBD::Signal0<void> 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<AutomationList> al = boost::shared_ptr<AutomationList>())
+ boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>())
: 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<Evoral::Control> 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<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl> (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<const AutomationControl>(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 <set>
-
#include <glibmm/thread.h>
#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<const MidiRegion> 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<nframes_t>&
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<Source> src)
{
+ _source_connection.disconnect ();
+
boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(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<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (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<AutomationControl> 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<nframes_t>& 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<Evoral::Parameter> const & filtered) const
{
Glib::Mutex::Lock lm (_lock);
@@ -158,7 +160,7 @@ MidiSource::midi_read (Evoral::EventSink<nframes_t>& 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<MidiModel> 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<Time>& seq, Time t);
+ const_iterator(const Sequence<Time>& seq, Time t, std::set<Evoral::Parameter> const &);
~const_iterator();
inline bool valid() const { return !_is_end && _event; }
@@ -221,7 +221,9 @@ public:
ControlIterators::iterator _control_iter;
};
- const_iterator begin(Time t=0) const { return const_iterator(*this, t); }
+ const_iterator begin (Time t=0, std::set<Evoral::Parameter> const & f = std::set<Evoral::Parameter> ()) const {
+ return const_iterator (*this, t, f);
+ }
const const_iterator& end() const { return _end_iter; }
typename Notes::const_iterator note_lower_bound (Time t) const;
diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp
index ddd978fefa..add32fb9bd 100644
--- a/libs/evoral/src/Sequence.cpp
+++ b/libs/evoral/src/Sequence.cpp
@@ -53,7 +53,7 @@ Sequence<Time>::const_iterator::const_iterator()
}
template<typename Time>
-Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t)
+Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t, std::set<Evoral::Parameter> const & filtered)
: _seq(&seq)
, _type(NIL)
, _is_end((t == DBL_MAX) || seq.empty())
@@ -90,6 +90,12 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
bool found = false;
size_t earliest_control_index = 0;
for (Controls::const_iterator i = seq._controls.begin(); i != seq._controls.end(); ++i) {
+
+ if (filtered.find (i->first) != filtered.end()) {
+ /* this parameter is filtered, so don't bother setting up an iterator for it */
+ continue;
+ }
+
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: control: %1\n", seq._type_map.to_symbol(i->first)));
double x, y;
bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y, true);
@@ -385,7 +391,7 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
, _overlap_pitch_resolution (FirstOnFirstOff)
, _writing(false)
, _type_map(type_map)
- , _end_iter(*this, DBL_MAX)
+ , _end_iter(*this, DBL_MAX, std::set<Evoral::Parameter> ())
, _percussive(false)
, _lowest_note(127)
, _highest_note(0)
@@ -403,7 +409,7 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
, _overlap_pitch_resolution (other._overlap_pitch_resolution)
, _writing(false)
, _type_map(other._type_map)
- , _end_iter(*this, DBL_MAX)
+ , _end_iter(*this, DBL_MAX, std::set<Evoral::Parameter> ())
, _percussive(other._percussive)
, _lowest_note(other._lowest_note)
, _highest_note(other._highest_note)