summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/automation_line.cc36
-rw-r--r--gtk2_ardour/automation_line.h10
-rw-r--r--gtk2_ardour/automation_region_view.cc1
-rw-r--r--gtk2_ardour/automation_streamview.cc22
-rw-r--r--gtk2_ardour/automation_streamview.h1
-rw-r--r--gtk2_ardour/automation_time_axis.cc50
-rw-r--r--gtk2_ardour/automation_time_axis.h7
-rw-r--r--libs/ardour/ardour/midi_model.h7
-rw-r--r--libs/ardour/ardour/midi_source.h13
-rw-r--r--libs/ardour/ardour/smf_source.h2
-rw-r--r--libs/ardour/automatable.cc2
-rw-r--r--libs/ardour/midi_model.cc62
-rw-r--r--libs/ardour/midi_source.cc77
-rw-r--r--libs/ardour/midi_stretch.cc7
-rw-r--r--libs/ardour/route.cc2
-rw-r--r--libs/ardour/smf_source.cc18
-rw-r--r--libs/evoral/evoral/ControlList.hpp6
-rw-r--r--libs/evoral/evoral/ControlSet.hpp5
-rw-r--r--libs/evoral/evoral/Sequence.hpp7
-rw-r--r--libs/evoral/src/ControlList.cpp16
-rw-r--r--libs/evoral/src/ControlSet.cpp7
-rw-r--r--libs/evoral/src/Sequence.cpp23
22 files changed, 285 insertions, 96 deletions
diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc
index 82372f39e9..ed94b03355 100644
--- a/gtk2_ardour/automation_line.cc
+++ b/gtk2_ardour/automation_line.cc
@@ -67,7 +67,6 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv
, _parent_group (parent)
, _time_converter (converter ? (*converter) : default_converter)
{
- _interpolation = al->interpolation();
points_visible = false;
update_pending = false;
_uses_gain_mapping = false;
@@ -86,7 +85,7 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv
line->signal_event().connect (sigc::mem_fun (*this, &AutomationLine::event_handler));
- alist->StateChanged.connect (_state_connection, invalidator (*this), boost::bind (&AutomationLine::list_changed, this), gui_context());
+ connect_to_list ();
trackview.session()->register_with_memento_command_factory(alist->id(), this);
@@ -95,7 +94,9 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv
set_uses_gain_mapping (true);
}
- set_interpolation(alist->interpolation());
+ interpolation_changed (alist->interpolation ());
+
+ connect_to_list ();
}
AutomationLine::~AutomationLine ()
@@ -122,7 +123,7 @@ AutomationLine::queue_reset ()
void
AutomationLine::show ()
{
- if (_interpolation != AutomationList::Discrete) {
+ if (alist->interpolation() != AutomationList::Discrete) {
line->show();
}
@@ -148,7 +149,7 @@ AutomationLine::hide ()
double
AutomationLine::control_point_box_size ()
{
- if (_interpolation == AutomationList::Discrete) {
+ if (alist->interpolation() == AutomationList::Discrete) {
return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
4.0);
}
@@ -470,7 +471,7 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
line->property_points() = line_points;
- if (_visible && _interpolation != AutomationList::Discrete) {
+ if (_visible && alist->interpolation() != AutomationList::Discrete) {
line->show();
}
@@ -1117,10 +1118,11 @@ AutomationLine::change_model (AutomationList::iterator /*i*/, double /*x*/, doub
}
void
-AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
+AutomationLine::set_list (boost::shared_ptr<ARDOUR::AutomationList> list)
{
alist = list;
- queue_reset();
+ queue_reset ();
+ connect_to_list ();
}
void
@@ -1222,13 +1224,10 @@ AutomationLine::model_to_view_coord (double& x, double& y) const
x = _time_converter.to(x);
}
-
+/** Called when our list has announced that its interpolation style has changed */
void
-AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
+AutomationLine::interpolation_changed (AutomationList::InterpolationStyle style)
{
- _interpolation = style;
- alist->set_interpolation (_interpolation);
-
if (style == AutomationList::Discrete) {
show_all_control_points();
line->hide();
@@ -1301,3 +1300,14 @@ AutomationLine::clear_always_in_view ()
alist->apply_to_points (*this, &AutomationLine::reset_callback);
}
+void
+AutomationLine::connect_to_list ()
+{
+ _list_connections.drop_connections ();
+
+ alist->StateChanged.connect (_list_connections, invalidator (*this), boost::bind (&AutomationLine::list_changed, this), gui_context());
+
+ alist->InterpolationChanged.connect (
+ _list_connections, invalidator (*this), boost::bind (&AutomationLine::interpolation_changed, this, _1), gui_context()
+ );
+}
diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h
index 3e0f0f4bb3..161d33a80b 100644
--- a/gtk2_ardour/automation_line.h
+++ b/gtk2_ardour/automation_line.h
@@ -52,6 +52,7 @@ namespace Gnome {
}
}
+/** A GUI representation of an ARDOUR::AutomationList */
class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
{
public:
@@ -91,8 +92,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void set_line_color (uint32_t);
uint32_t get_line_color() const { return _line_color; }
- void set_interpolation(ARDOUR::AutomationList::InterpolationStyle style);
-
void show ();
void hide ();
void set_height (guint32);
@@ -174,7 +173,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void reset_callback (const Evoral::ControlList&);
void list_changed ();
- PBD::ScopedConnection _state_connection;
virtual bool event_handler (GdkEvent*);
virtual void add_model_point (ALPoints& tmp_points, double frame, double yfract);
@@ -189,12 +187,12 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
std::list<double> _always_in_view;
const Evoral::TimeConverter<double, ARDOUR::sframes_t>& _time_converter;
- ARDOUR::AutomationList::InterpolationStyle _interpolation;
void reset_line_coords (ControlPoint&);
void add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t);
-
double control_point_box_size ();
+ void connect_to_list ();
+ void interpolation_changed (ARDOUR::AutomationList::InterpolationStyle);
struct ModelRepresentation {
ARDOUR::AutomationList::iterator start;
@@ -211,6 +209,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void model_representation (ControlPoint&, ModelRepresentation&);
+ PBD::ScopedConnectionList _list_connections;
+
friend class AudioRegionGainLine;
};
diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc
index 45bd5d770b..9f3d169dde 100644
--- a/gtk2_ardour/automation_region_view.cc
+++ b/gtk2_ardour/automation_region_view.cc
@@ -75,7 +75,6 @@ AutomationRegionView::create_line (boost::shared_ptr<ARDOUR::AutomationList> lis
ARDOUR::EventTypeMap::instance().to_symbol(list->parameter()),
trackview, *get_canvas_group(), list, &_time_converter));
_line->set_colors();
- _line->set_interpolation(list->interpolation());
_line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE));
_line->show();
_line->show_all_control_points();
diff --git a/gtk2_ardour/automation_streamview.cc b/gtk2_ardour/automation_streamview.cc
index 006834c59d..146cdc7b89 100644
--- a/gtk2_ardour/automation_streamview.cc
+++ b/gtk2_ardour/automation_streamview.cc
@@ -237,14 +237,28 @@ AutomationStreamView::has_automation () const
return false;
}
+/** Our parent AutomationTimeAxisView calls this when the user requests a particular
+ * InterpolationStyle; tell the AutomationLists in our regions.
+ */
void
AutomationStreamView::set_interpolation (AutomationList::InterpolationStyle s)
{
- for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+ for (list<RegionView*>::const_iterator i = region_views.begin(); i != region_views.end(); ++i) {
AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*i);
assert (arv);
- if (arv->line()) {
- arv->line()->set_interpolation (s);
- }
+ arv->line()->the_list()->set_interpolation (s);
+ }
+}
+
+AutomationList::InterpolationStyle
+AutomationStreamView::interpolation () const
+{
+ if (region_views.empty()) {
+ return AutomationList::Linear;
}
+
+ AutomationRegionView* v = dynamic_cast<AutomationRegionView*> (region_views.front());
+ assert (v);
+
+ return v->line()->the_list()->interpolation ();
}
diff --git a/gtk2_ardour/automation_streamview.h b/gtk2_ardour/automation_streamview.h
index 01d3435714..335d63ca7a 100644
--- a/gtk2_ardour/automation_streamview.h
+++ b/gtk2_ardour/automation_streamview.h
@@ -57,6 +57,7 @@ class AutomationStreamView : public StreamView
bool has_automation () const;
void set_interpolation (ARDOUR::AutomationList::InterpolationStyle);
+ ARDOUR::AutomationList::InterpolationStyle interpolation () const;
private:
void setup_rec_box ();
diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc
index 841ec65fb9..a7114f8ff0 100644
--- a/gtk2_ardour/automation_time_axis.cc
+++ b/gtk2_ardour/automation_time_axis.cc
@@ -270,7 +270,6 @@ AutomationTimeAxisView::set_automation_state (AutoState state)
#endif
}
- cout << "_view = " << _view << "\n";
if (_view) {
_view->set_automation_state (state);
@@ -347,13 +346,12 @@ AutomationTimeAxisView::automation_state_changed ()
}
}
+/** The interpolation style of our AutomationList has changed, so update */
void
-AutomationTimeAxisView::interpolation_changed ()
+AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
{
- AutomationList::InterpolationStyle style = _control->list()->interpolation();
-
if (mode_line_item && mode_discrete_item) {
- if (style == AutomationList::Discrete) {
+ if (s == AutomationList::Discrete) {
mode_discrete_item->set_active(true);
mode_line_item->set_active(false);
} else {
@@ -361,25 +359,20 @@ AutomationTimeAxisView::interpolation_changed ()
mode_discrete_item->set_active(false);
}
}
-
- if (_line) {
- _line->set_interpolation(style);
- }
-
- if (_view) {
- _view->set_interpolation (style);
- }
}
+/** A menu item has been selected to change our interpolation mode */
void
AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
{
- _control->list()->set_interpolation(style);
- if (_line) {
- _line->set_interpolation(style);
- }
+ /* Tell our view's list, if we have one, otherwise tell our own.
+ * Everything else will be signalled back from that.
+ */
+
if (_view) {
_view->set_interpolation (style);
+ } else {
+ _control->list()->set_interpolation (style);
}
}
@@ -546,6 +539,9 @@ AutomationTimeAxisView::build_display_menu ()
/* mode menu */
+ /* current interpolation state */
+ AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
+
if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
Menu* auto_mode_menu = manage (new Menu);
@@ -558,17 +554,13 @@ AutomationTimeAxisView::build_display_menu ()
sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
AutomationList::Discrete)));
mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
- mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
+ mode_discrete_item->set_active (s == AutomationList::Discrete);
am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
AutomationList::Linear)));
mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
-
- // Set default interpolation type to linear if this isn't a (usually) discrete controller
- if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) {
- mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
- }
+ mode_line_item->set_active (s == AutomationList::Linear);
items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
}
@@ -576,7 +568,7 @@ AutomationTimeAxisView::build_display_menu ()
/* make sure the automation menu state is correct */
automation_state_changed ();
- interpolation_changed ();
+ interpolation_changed (s);
}
void
@@ -834,7 +826,7 @@ void
AutomationTimeAxisView::clear_lines ()
{
_line.reset();
- automation_connection.disconnect ();
+ _list_connections.drop_connections ();
}
void
@@ -844,7 +836,13 @@ AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
assert(!_line);
assert(line->the_list() == _control->list());
- _control->alist()->automation_state_changed.connect (automation_connection, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context());
+ _control->alist()->automation_state_changed.connect (
+ _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
+ );
+
+ _control->alist()->InterpolationChanged.connect (
+ _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
+ );
_line = line;
//_controller = AutomationController::create(_session, line->the_list(), _control);
diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h
index 9d4802f6fc..4b21ec8533 100644
--- a/gtk2_ardour/automation_time_axis.h
+++ b/gtk2_ardour/automation_time_axis.h
@@ -116,7 +116,8 @@ class AutomationTimeAxisView : public TimeAxisView {
ArdourCanvas::SimpleRect* _base_rect;
boost::shared_ptr<AutomationLine> _line;
- AutomationStreamView* _view;
+ /** AutomationStreamView if we are editing region-based automation (for MIDI), otherwise 0 */
+ AutomationStreamView* _view;
std::string _name;
bool ignore_toggle;
@@ -156,9 +157,9 @@ class AutomationTimeAxisView : public TimeAxisView {
void automation_state_changed ();
void set_interpolation (ARDOUR::AutomationList::InterpolationStyle);
- void interpolation_changed ();
+ void interpolation_changed (ARDOUR::AutomationList::InterpolationStyle);
- PBD::ScopedConnection automation_connection;
+ PBD::ScopedConnectionList _list_connections;
void update_extra_xml_shown (bool editor_shown);
diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h
index 4e348af287..a8303539b5 100644
--- a/libs/ardour/ardour/midi_model.h
+++ b/libs/ardour/ardour/midi_model.h
@@ -148,6 +148,8 @@ public:
InsertMergePolicy insert_merge_policy () const;
void set_insert_merge_policy (InsertMergePolicy);
+ boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
+
protected:
int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
@@ -170,6 +172,11 @@ public:
private:
friend class DeltaCommand;
+ void source_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
+ void control_list_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
+
+ PBD::ScopedConnectionList _midi_source_connections;
+
// We cannot use a boost::shared_ptr here to avoid a retain cycle
MidiSource* _midi_source;
InsertMergePolicy _insert_merge_policy;
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
index 0d0b744a95..8d20f9c7b6 100644
--- a/libs/ardour/ardour/midi_source.h
+++ b/libs/ardour/ardour/midi_source.h
@@ -117,8 +117,15 @@ class MidiSource : virtual public Source
void set_model (boost::shared_ptr<MidiModel>);
void drop_model();
+ Evoral::ControlList::InterpolationStyle interpolation_of (Evoral::Parameter) const;
+ void set_interpolation_of (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
+ void copy_interpolation_from (boost::shared_ptr<MidiSource>);
+ void copy_interpolation_from (MidiSource *);
+
/** Emitted when a different MidiModel is set */
PBD::Signal0<void> ModelChanged;
+ /** Emitted when a parameter's interpolation style is changed */
+ PBD::Signal2<void, Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationChanged;
protected:
virtual void flush_midi() = 0;
@@ -146,6 +153,12 @@ class MidiSource : virtual public Source
mutable double _length_beats;
mutable sframes_t _last_read_end;
sframes_t _last_write_end;
+
+ /** Map of interpolation styles to use for Parameters; if they are not in this map,
+ * the correct interpolation style can be obtained from EventTypeMap::interpolation_of ()
+ */
+ typedef std::map<Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationStyleMap;
+ InterpolationStyleMap _interpolation_style;
};
}
diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h
index d271cb0dba..6dcea9dd60 100644
--- a/libs/ardour/ardour/smf_source.h
+++ b/libs/ardour/ardour/smf_source.h
@@ -82,8 +82,6 @@ private:
sframes_t position,
nframes_t cnt);
- void set_default_controls_interpolation ();
-
double _last_ev_time_beats;
sframes_t _last_ev_time_frames;
/** end time (start + duration) of last call to read_unlocked */
diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc
index bed99d5660..40b6eb2a07 100644
--- a/libs/ardour/automatable.cc
+++ b/libs/ardour/automatable.cc
@@ -58,7 +58,7 @@ Automatable::Automatable (const Automatable& other)
for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
- _controls[ac->parameter()] = ac;
+ add_control (ac);
}
}
int
diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc
index 6e2c477a99..b5d5d24713 100644
--- a/libs/ardour/midi_model.cc
+++ b/libs/ardour/midi_model.cc
@@ -41,8 +41,9 @@ using namespace PBD;
MidiModel::MidiModel(MidiSource* s)
: AutomatableSequence<TimeType>(s->session())
- , _midi_source(s)
+ , _midi_source (0)
{
+ set_midi_source (s);
}
/** Start a new Diff command.
@@ -761,6 +762,9 @@ MidiModel::DiffCommand::get_state ()
* user can switch a recorded track (with note durations from some instrument)
* to percussive, save, reload, then switch it back to sustained without
* destroying the original note durations.
+ *
+ * Similarly, control events are written without interpolation (as with the
+ * `Discrete' mode).
*/
bool
MidiModel::write_to (boost::shared_ptr<MidiSource> source)
@@ -773,7 +777,7 @@ MidiModel::write_to (boost::shared_ptr<MidiSource> source)
source->drop_model();
source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
- for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
+ for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
source->append_event_unlocked_beats(*i);
}
@@ -800,7 +804,7 @@ MidiModel::sync_to_source ()
_midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
- for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
+ for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
_midi_source->append_event_unlocked_beats(*i);
}
@@ -832,7 +836,7 @@ MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::Music
source->drop_model();
source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
- for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
+ for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
const Evoral::Event<Evoral::MusicalTime>& ev (*i);
if (ev.time() >= begin_time && ev.time() < end_time) {
@@ -1138,6 +1142,54 @@ MidiModel::insert_merge_policy () const
void
MidiModel::set_midi_source (MidiSource* s)
{
- _midi_source->invalidate ();
+ if (_midi_source) {
+ _midi_source->invalidate ();
+ }
+
+ _midi_source_connections.drop_connections ();
+
_midi_source = s;
+
+ _midi_source->InterpolationChanged.connect_same_thread (
+ _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
+ );
+}
+
+/** The source has signalled that the interpolation style for a parameter has changed. In order to
+ * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
+ * appropriate ControlList.
+ *
+ * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and the
+ * MidiSource's InterpolationChanged signal is listened to by the GUI.
+ */
+void
+MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
+{
+ Glib::Mutex::Lock lm (_control_lock);
+ control(p)->list()->set_interpolation (s);
+}
+
+/** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
+ * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
+ */
+void
+MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
+{
+ _midi_source->set_interpolation_of (p, s);
+}
+
+boost::shared_ptr<Evoral::Control>
+MidiModel::control_factory (Evoral::Parameter const & p)
+{
+ boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
+
+ /* Set up newly created control's lists to the appropriate interpolation state
+ from our source.
+ */
+
+ assert (_midi_source);
+
+ c->list()->set_interpolation (_midi_source->interpolation_of (p));
+
+ return c;
}
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
index 3f831b348d..dbc41c8ab6 100644
--- a/libs/ardour/midi_source.cc
+++ b/libs/ardour/midi_source.cc
@@ -97,6 +97,12 @@ MidiSource::get_state ()
node.add_property ("captured-for", _captured_for);
}
+ for (InterpolationStyleMap::const_iterator i = _interpolation_style.begin(); i != _interpolation_style.end(); ++i) {
+ XMLNode* child = node.add_child (X_("InterpolationStyle"));
+ child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
+ child->add_property (X_("style"), enum_2_string (i->second));
+ }
+
return node;
}
@@ -109,6 +115,28 @@ MidiSource::set_state (const XMLNode& node, int /*version*/)
_captured_for = prop->value();
}
+ XMLNodeList children = node.children ();
+ for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == X_("InterpolationStyle")) {
+ XMLProperty* prop;
+
+ if ((prop = (*i)->property (X_("parameter"))) == 0) {
+ error << _("Missing parameter property on InterpolationStyle") << endmsg;
+ return -1;
+ }
+
+ Evoral::Parameter p = EventTypeMap::instance().new_parameter (prop->value());
+
+ if ((prop = (*i)->property (X_("style"))) == 0) {
+ error << _("Missing style property on InterpolationStyle") << endmsg;
+ return -1;
+ }
+
+ Evoral::ControlList::InterpolationStyle s = static_cast<Evoral::ControlList::InterpolationStyle> (string_2_enum (prop->value(), s));
+ set_interpolation_of (p, s);
+ }
+ }
+
return 0;
}
@@ -160,7 +188,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(0, filtered); i != _model->end(); ++i) {
+ for (i = _model->begin(0, false, filtered); i != _model->end(); ++i) {
if (converter.to(i->time()) >= start) {
break;
}
@@ -260,6 +288,7 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
newpath, false, _session.frame_rate()));
newsrc->set_timeline_position(_timeline_position);
+ newsrc->copy_interpolation_from (this);
if (_model) {
if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
@@ -344,3 +373,49 @@ MidiSource::set_model (boost::shared_ptr<MidiModel> m)
_model = m;
ModelChanged (); /* EMIT SIGNAL */
}
+
+/** @return Interpolation style that should be used for control parameter \a p */
+Evoral::ControlList::InterpolationStyle
+MidiSource::interpolation_of (Evoral::Parameter p) const
+{
+ InterpolationStyleMap::const_iterator i = _interpolation_style.find (p);
+ if (i == _interpolation_style.end()) {
+ return EventTypeMap::instance().interpolation_of (p);
+ }
+
+ return i->second;
+}
+
+/** Set interpolation style to be used for a given parameter. This change will be
+ * propagated to anyone who needs to know.
+ */
+void
+MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
+{
+ if (interpolation_of (p) == s) {
+ return;
+ }
+
+ if (EventTypeMap::instance().interpolation_of (p) == s) {
+ /* interpolation type is being set to the default, so we don't need a note in our map */
+ _interpolation_style.erase (p);
+ } else {
+ _interpolation_style[p] = s;
+ }
+
+ InterpolationChanged (p, s); /* EMIT SIGNAL */
+}
+
+void
+MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
+{
+ copy_interpolation_from (s.get ());
+}
+
+void
+MidiSource::copy_interpolation_from (MidiSource* s)
+{
+ _interpolation_style = s->_interpolation_style;
+
+ /* XXX: should probably emit signals here */
+}
diff --git a/libs/ardour/midi_stretch.cc b/libs/ardour/midi_stretch.cc
index 585e0a07b2..21b5453da4 100644
--- a/libs/ardour/midi_stretch.cc
+++ b/libs/ardour/midi_stretch.cc
@@ -90,7 +90,10 @@ MidiStretch::run (boost::shared_ptr<Region> r)
boost::shared_ptr<MidiModel> new_model = new_src->model();
new_model->start_write();
- for (Evoral::Sequence<MidiModel::TimeType>::const_iterator i = old_model->begin();
+ /* Note: pass true into force_discrete for the begin() iterator so that the model doesn't
+ * do interpolation of controller data when we stretch.
+ */
+ for (Evoral::Sequence<MidiModel::TimeType>::const_iterator i = old_model->begin (0, true);
i != old_model->end(); ++i) {
const double new_time = i->time() * _request.time_fraction;
@@ -103,6 +106,8 @@ MidiStretch::run (boost::shared_ptr<Region> r)
new_model->end_write();
new_model->set_edited(true);
+ new_src->copy_interpolation_from (src);
+
const int ret = finish (region, nsrcs, new_name);
results[0]->set_length((nframes_t) floor (r->length() * _request.time_fraction), NULL);
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index e7f5e28b94..e8ddf678c9 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -109,7 +109,7 @@ Route::init ()
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
_mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-
+
add_control (_solo_control);
add_control (_mute_control);
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
index fe5a0f7c8f..bbdf958815 100644
--- a/libs/ardour/smf_source.cc
+++ b/libs/ardour/smf_source.cc
@@ -257,10 +257,6 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& source, sframes_t position
append_event_unlocked_frames(ev, position);
}
- if (_model) {
- set_default_controls_interpolation();
- }
-
Evoral::SMF::flush();
free(buf);
@@ -471,8 +467,6 @@ SMFSource::load_model (bool lock, bool force_reload)
_length_beats = max(_length_beats, ev.time());
}
- set_default_controls_interpolation();
-
_model->end_write(false);
_model->set_edited(false);
@@ -482,18 +476,6 @@ SMFSource::load_model (bool lock, bool force_reload)
}
void
-SMFSource::set_default_controls_interpolation ()
-{
- // set interpolation style to defaults, can be changed by the GUI later
- Evoral::ControlSet::Controls controls = _model->controls();
- for (Evoral::ControlSet::Controls::iterator c = controls.begin(); c != controls.end(); ++c) {
- (*c).second->list()->set_interpolation(
- EventTypeMap::instance().interpolation_of((*c).first));
- }
-}
-
-
-void
SMFSource::destroy_model ()
{
//cerr << _name << " destroying model " << _model.get() << endl;
diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp
index 5f842775ee..d207c76925 100644
--- a/libs/evoral/evoral/ControlList.hpp
+++ b/libs/evoral/evoral/ControlList.hpp
@@ -218,6 +218,7 @@ public:
bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
+ bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
void create_curve();
void destroy_curve();
@@ -234,10 +235,12 @@ public:
};
InterpolationStyle interpolation() const { return _interpolation; }
- void set_interpolation(InterpolationStyle style) { _interpolation = style; }
+ void set_interpolation (InterpolationStyle);
/** Emitted when mark_dirty() is called on this object */
mutable PBD::Signal0<void> Dirty;
+ /** Emitted when our interpolation style changes */
+ PBD::Signal1<void, InterpolationStyle> InterpolationChanged;
protected:
@@ -246,7 +249,6 @@ protected:
void build_search_cache_if_necessary(double start, double end) const;
- bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
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);
diff --git a/libs/evoral/evoral/ControlSet.hpp b/libs/evoral/evoral/ControlSet.hpp
index b775bb3b4b..95de58dead 100644
--- a/libs/evoral/evoral/ControlSet.hpp
+++ b/libs/evoral/evoral/ControlSet.hpp
@@ -27,11 +27,11 @@
#include "pbd/signals.h"
#include "evoral/types.hpp"
#include "evoral/Parameter.hpp"
+#include "evoral/ControlList.hpp"
namespace Evoral {
class Control;
-class ControlList;
class ControlEvent;
class ControlSet : public boost::noncopyable {
@@ -69,12 +69,15 @@ public:
protected:
virtual void control_list_marked_dirty () {}
+ virtual void control_list_interpolation_changed (Parameter, ControlList::InterpolationStyle) {}
mutable Glib::Mutex _control_lock;
Controls _controls;
private:
+
PBD::ScopedConnectionList _control_connections;
+ PBD::ScopedConnectionList _list_connections;
};
diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp
index 3cddeb38ca..24a3c44625 100644
--- a/libs/evoral/evoral/Sequence.hpp
+++ b/libs/evoral/evoral/Sequence.hpp
@@ -185,7 +185,7 @@ public:
class const_iterator {
public:
const_iterator();
- const_iterator(const Sequence<Time>& seq, Time t, std::set<Evoral::Parameter> const &);
+ const_iterator(const Sequence<Time>& seq, Time t, bool, std::set<Evoral::Parameter> const &);
~const_iterator();
inline bool valid() const { return !_is_end && _event; }
@@ -220,10 +220,11 @@ public:
typename SysExes::const_iterator _sysex_iter;
ControlIterators _control_iters;
ControlIterators::iterator _control_iter;
+ bool _force_discrete;
};
- const_iterator begin (Time t=0, std::set<Evoral::Parameter> const & f = std::set<Evoral::Parameter> ()) const {
- return const_iterator (*this, t, f);
+ const_iterator begin (Time t = 0, bool force_discrete = false, std::set<Evoral::Parameter> const & f = std::set<Evoral::Parameter> ()) const {
+ return const_iterator (*this, t, force_discrete, f);
}
const const_iterator& end() const { return _end_iter; }
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
index 3eb6c7d4ce..bfe4c95365 100644
--- a/libs/evoral/src/ControlList.cpp
+++ b/libs/evoral/src/ControlList.cpp
@@ -954,10 +954,11 @@ ControlList::rt_safe_earliest_event(double start, double end, double& x, double&
bool
ControlList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const
{
- if (_interpolation == Discrete)
+ if (_interpolation == Discrete) {
return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
- else
+ } else {
return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive);
+ }
}
@@ -1356,5 +1357,16 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
maybe_signal_changed ();
}
+void
+ControlList::set_interpolation (InterpolationStyle s)
+{
+ if (_interpolation == s) {
+ return;
+ }
+
+ _interpolation = s;
+ InterpolationChanged (s); /* EMIT SIGNAL */
+}
+
} // namespace Evoral
diff --git a/libs/evoral/src/ControlSet.cpp b/libs/evoral/src/ControlSet.cpp
index d985e347d7..6142f08870 100644
--- a/libs/evoral/src/ControlSet.cpp
+++ b/libs/evoral/src/ControlSet.cpp
@@ -16,6 +16,7 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <iostream>
#include <limits>
#include "evoral/ControlSet.hpp"
#include "evoral/ControlList.hpp"
@@ -43,6 +44,10 @@ ControlSet::add_control(boost::shared_ptr<Control> ac)
_controls[ac->parameter()] = ac;
ac->ListMarkedDirty.connect_same_thread (_control_connections, boost::bind (&ControlSet::control_list_marked_dirty, this));
+
+ ac->list()->InterpolationChanged.connect_same_thread (
+ _list_connections, boost::bind (&ControlSet::control_list_interpolation_changed, this, ac->parameter(), _1)
+ );
}
void
@@ -111,10 +116,10 @@ ControlSet::clear_controls ()
Glib::Mutex::Lock lm (_control_lock);
_control_connections.drop_connections ();
+ _list_connections.drop_connections ();
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
li->second->list()->clear();
}
-
} // namespace Evoral
diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp
index a67f32e993..a9d55056bd 100644
--- a/libs/evoral/src/Sequence.cpp
+++ b/libs/evoral/src/Sequence.cpp
@@ -52,14 +52,16 @@ Sequence<Time>::const_iterator::const_iterator()
_event = boost::shared_ptr< Event<Time> >(new Event<Time>());
}
+/** @param force_discrete true to force ControlLists to use discrete evaluation, otherwise false to get them to use their configured mode */
template<typename Time>
-Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t, std::set<Evoral::Parameter> const & filtered)
+Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t, bool force_discrete, std::set<Evoral::Parameter> const & filtered)
: _seq(&seq)
, _type(NIL)
, _is_end((t == DBL_MAX) || seq.empty())
, _note_iter(seq.notes().end())
, _sysex_iter(seq.sysexes().end())
, _control_iter(_control_iters.end())
+ , _force_discrete (force_discrete)
{
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Created Iterator @ %1 (is end: %2)\n)", t, _is_end));
@@ -98,7 +100,12 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
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);
+ bool ret;
+ if (_force_discrete) {
+ ret = i->second->list()->rt_safe_earliest_event_discrete_unlocked (t, DBL_MAX, x, y, true);
+ } else {
+ ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y, true);
+ }
if (!ret) {
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: CC %1 (size %2) has no events past %3\n",
i->first.id(), i->second->list()->size(), t));
@@ -246,8 +253,11 @@ Sequence<Time>::const_iterator::operator++()
break;
case CONTROL:
// Increment current controller iterator
- ret = _control_iter->list->rt_safe_earliest_event_unlocked(
- _control_iter->x, DBL_MAX, x, y, false);
+ if (_force_discrete) {
+ ret = _control_iter->list->rt_safe_earliest_event_discrete_unlocked (_control_iter->x, DBL_MAX, x, y, false);
+ } else {
+ ret = _control_iter->list->rt_safe_earliest_event_unlocked (_control_iter->x, DBL_MAX, x, y, false);
+ }
assert(!ret || x > _control_iter->x);
if (ret) {
_control_iter->x = x;
@@ -366,6 +376,7 @@ Sequence<Time>::const_iterator::operator=(const const_iterator& other)
_note_iter = other._note_iter;
_sysex_iter = other._sysex_iter;
_control_iters = other._control_iters;
+ _force_discrete = other._force_discrete;
if (other._lock)
_lock = _seq->read_lock();
@@ -391,7 +402,7 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
, _overlap_pitch_resolution (FirstOnFirstOff)
, _writing(false)
, _type_map(type_map)
- , _end_iter(*this, DBL_MAX, std::set<Evoral::Parameter> ())
+ , _end_iter(*this, DBL_MAX, false, std::set<Evoral::Parameter> ())
, _percussive(false)
, _lowest_note(127)
, _highest_note(0)
@@ -409,7 +420,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, std::set<Evoral::Parameter> ())
+ , _end_iter(*this, DBL_MAX, false, std::set<Evoral::Parameter> ())
, _percussive(other._percussive)
, _lowest_note(other._lowest_note)
, _highest_note(other._highest_note)