A few fixes to interpolation of MIDI controller data. Don't interpolate
when writing these data back to a source, otherwise surprising new interpolated points appear in MIDI automation. Similarly don't interpolate when reading the model during MIDI stretch. Fix handling of interpolation state; controllers that have been set by the user to use a different interpolation style are noted in the <Source> tag of the session file and this state is sprayed around to MidiModel and the GUI as necessary. git-svn-id: svn://localhost/ardour2/branches/3.0@7409 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
b75977920e
commit
593b421180
@ -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()
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
@ -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 ();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
@ -481,18 +475,6 @@ SMFSource::load_model (bool lock, bool force_reload)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user