mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 14:54:56 +01:00
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
22 changed files with 285 additions and 96 deletions
|
|
@ -67,7 +67,6 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv
|
||||||
, _parent_group (parent)
|
, _parent_group (parent)
|
||||||
, _time_converter (converter ? (*converter) : default_converter)
|
, _time_converter (converter ? (*converter) : default_converter)
|
||||||
{
|
{
|
||||||
_interpolation = al->interpolation();
|
|
||||||
points_visible = false;
|
points_visible = false;
|
||||||
update_pending = false;
|
update_pending = false;
|
||||||
_uses_gain_mapping = 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));
|
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);
|
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_uses_gain_mapping (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_interpolation(alist->interpolation());
|
interpolation_changed (alist->interpolation ());
|
||||||
|
|
||||||
|
connect_to_list ();
|
||||||
}
|
}
|
||||||
|
|
||||||
AutomationLine::~AutomationLine ()
|
AutomationLine::~AutomationLine ()
|
||||||
|
|
@ -122,7 +123,7 @@ AutomationLine::queue_reset ()
|
||||||
void
|
void
|
||||||
AutomationLine::show ()
|
AutomationLine::show ()
|
||||||
{
|
{
|
||||||
if (_interpolation != AutomationList::Discrete) {
|
if (alist->interpolation() != AutomationList::Discrete) {
|
||||||
line->show();
|
line->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +149,7 @@ AutomationLine::hide ()
|
||||||
double
|
double
|
||||||
AutomationLine::control_point_box_size ()
|
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()),
|
return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
|
||||||
4.0);
|
4.0);
|
||||||
}
|
}
|
||||||
|
|
@ -470,7 +471,7 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
|
||||||
|
|
||||||
line->property_points() = line_points;
|
line->property_points() = line_points;
|
||||||
|
|
||||||
if (_visible && _interpolation != AutomationList::Discrete) {
|
if (_visible && alist->interpolation() != AutomationList::Discrete) {
|
||||||
line->show();
|
line->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1117,10 +1118,11 @@ AutomationLine::change_model (AutomationList::iterator /*i*/, double /*x*/, doub
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
|
AutomationLine::set_list (boost::shared_ptr<ARDOUR::AutomationList> list)
|
||||||
{
|
{
|
||||||
alist = list;
|
alist = list;
|
||||||
queue_reset();
|
queue_reset ();
|
||||||
|
connect_to_list ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1222,13 +1224,10 @@ AutomationLine::model_to_view_coord (double& x, double& y) const
|
||||||
x = _time_converter.to(x);
|
x = _time_converter.to(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called when our list has announced that its interpolation style has changed */
|
||||||
void
|
void
|
||||||
AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
|
AutomationLine::interpolation_changed (AutomationList::InterpolationStyle style)
|
||||||
{
|
{
|
||||||
_interpolation = style;
|
|
||||||
alist->set_interpolation (_interpolation);
|
|
||||||
|
|
||||||
if (style == AutomationList::Discrete) {
|
if (style == AutomationList::Discrete) {
|
||||||
show_all_control_points();
|
show_all_control_points();
|
||||||
line->hide();
|
line->hide();
|
||||||
|
|
@ -1301,3 +1300,14 @@ AutomationLine::clear_always_in_view ()
|
||||||
alist->apply_to_points (*this, &AutomationLine::reset_callback);
|
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
|
class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -91,8 +92,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
|
||||||
void set_line_color (uint32_t);
|
void set_line_color (uint32_t);
|
||||||
uint32_t get_line_color() const { return _line_color; }
|
uint32_t get_line_color() const { return _line_color; }
|
||||||
|
|
||||||
void set_interpolation(ARDOUR::AutomationList::InterpolationStyle style);
|
|
||||||
|
|
||||||
void show ();
|
void show ();
|
||||||
void hide ();
|
void hide ();
|
||||||
void set_height (guint32);
|
void set_height (guint32);
|
||||||
|
|
@ -174,7 +173,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
|
||||||
|
|
||||||
void reset_callback (const Evoral::ControlList&);
|
void reset_callback (const Evoral::ControlList&);
|
||||||
void list_changed ();
|
void list_changed ();
|
||||||
PBD::ScopedConnection _state_connection;
|
|
||||||
|
|
||||||
virtual bool event_handler (GdkEvent*);
|
virtual bool event_handler (GdkEvent*);
|
||||||
virtual void add_model_point (ALPoints& tmp_points, double frame, double yfract);
|
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;
|
std::list<double> _always_in_view;
|
||||||
|
|
||||||
const Evoral::TimeConverter<double, ARDOUR::sframes_t>& _time_converter;
|
const Evoral::TimeConverter<double, ARDOUR::sframes_t>& _time_converter;
|
||||||
ARDOUR::AutomationList::InterpolationStyle _interpolation;
|
|
||||||
|
|
||||||
void reset_line_coords (ControlPoint&);
|
void reset_line_coords (ControlPoint&);
|
||||||
void add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t);
|
void add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t);
|
||||||
|
|
||||||
double control_point_box_size ();
|
double control_point_box_size ();
|
||||||
|
void connect_to_list ();
|
||||||
|
void interpolation_changed (ARDOUR::AutomationList::InterpolationStyle);
|
||||||
|
|
||||||
struct ModelRepresentation {
|
struct ModelRepresentation {
|
||||||
ARDOUR::AutomationList::iterator start;
|
ARDOUR::AutomationList::iterator start;
|
||||||
|
|
@ -211,6 +209,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
|
||||||
|
|
||||||
void model_representation (ControlPoint&, ModelRepresentation&);
|
void model_representation (ControlPoint&, ModelRepresentation&);
|
||||||
|
|
||||||
|
PBD::ScopedConnectionList _list_connections;
|
||||||
|
|
||||||
friend class AudioRegionGainLine;
|
friend class AudioRegionGainLine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,6 @@ AutomationRegionView::create_line (boost::shared_ptr<ARDOUR::AutomationList> lis
|
||||||
ARDOUR::EventTypeMap::instance().to_symbol(list->parameter()),
|
ARDOUR::EventTypeMap::instance().to_symbol(list->parameter()),
|
||||||
trackview, *get_canvas_group(), list, &_time_converter));
|
trackview, *get_canvas_group(), list, &_time_converter));
|
||||||
_line->set_colors();
|
_line->set_colors();
|
||||||
_line->set_interpolation(list->interpolation());
|
|
||||||
_line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE));
|
_line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE));
|
||||||
_line->show();
|
_line->show();
|
||||||
_line->show_all_control_points();
|
_line->show_all_control_points();
|
||||||
|
|
|
||||||
|
|
@ -237,14 +237,28 @@ AutomationStreamView::has_automation () const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Our parent AutomationTimeAxisView calls this when the user requests a particular
|
||||||
|
* InterpolationStyle; tell the AutomationLists in our regions.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
AutomationStreamView::set_interpolation (AutomationList::InterpolationStyle s)
|
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);
|
AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*i);
|
||||||
assert (arv);
|
assert (arv);
|
||||||
if (arv->line()) {
|
arv->line()->the_list()->set_interpolation (s);
|
||||||
arv->line()->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;
|
bool has_automation () const;
|
||||||
|
|
||||||
void set_interpolation (ARDOUR::AutomationList::InterpolationStyle);
|
void set_interpolation (ARDOUR::AutomationList::InterpolationStyle);
|
||||||
|
ARDOUR::AutomationList::InterpolationStyle interpolation () const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setup_rec_box ();
|
void setup_rec_box ();
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,6 @@ AutomationTimeAxisView::set_automation_state (AutoState state)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << "_view = " << _view << "\n";
|
|
||||||
if (_view) {
|
if (_view) {
|
||||||
_view->set_automation_state (state);
|
_view->set_automation_state (state);
|
||||||
|
|
||||||
|
|
@ -347,13 +346,12 @@ AutomationTimeAxisView::automation_state_changed ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The interpolation style of our AutomationList has changed, so update */
|
||||||
void
|
void
|
||||||
AutomationTimeAxisView::interpolation_changed ()
|
AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
|
||||||
{
|
{
|
||||||
AutomationList::InterpolationStyle style = _control->list()->interpolation();
|
|
||||||
|
|
||||||
if (mode_line_item && mode_discrete_item) {
|
if (mode_line_item && mode_discrete_item) {
|
||||||
if (style == AutomationList::Discrete) {
|
if (s == AutomationList::Discrete) {
|
||||||
mode_discrete_item->set_active(true);
|
mode_discrete_item->set_active(true);
|
||||||
mode_line_item->set_active(false);
|
mode_line_item->set_active(false);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -361,25 +359,20 @@ AutomationTimeAxisView::interpolation_changed ()
|
||||||
mode_discrete_item->set_active(false);
|
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
|
void
|
||||||
AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
|
AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
|
||||||
{
|
{
|
||||||
_control->list()->set_interpolation(style);
|
/* Tell our view's list, if we have one, otherwise tell our own.
|
||||||
if (_line) {
|
* Everything else will be signalled back from that.
|
||||||
_line->set_interpolation(style);
|
*/
|
||||||
}
|
|
||||||
if (_view) {
|
if (_view) {
|
||||||
_view->set_interpolation (style);
|
_view->set_interpolation (style);
|
||||||
|
} else {
|
||||||
|
_control->list()->set_interpolation (style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -546,6 +539,9 @@ AutomationTimeAxisView::build_display_menu ()
|
||||||
|
|
||||||
/* mode menu */
|
/* mode menu */
|
||||||
|
|
||||||
|
/* current interpolation state */
|
||||||
|
AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
|
||||||
|
|
||||||
if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
|
if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
|
||||||
|
|
||||||
Menu* auto_mode_menu = manage (new Menu);
|
Menu* auto_mode_menu = manage (new Menu);
|
||||||
|
|
@ -558,17 +554,13 @@ AutomationTimeAxisView::build_display_menu ()
|
||||||
sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
|
sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
|
||||||
AutomationList::Discrete)));
|
AutomationList::Discrete)));
|
||||||
mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
|
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 (
|
am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
|
||||||
sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
|
sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
|
||||||
AutomationList::Linear)));
|
AutomationList::Linear)));
|
||||||
mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
|
mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
|
||||||
|
mode_line_item->set_active (s == AutomationList::Linear);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
|
items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
|
||||||
}
|
}
|
||||||
|
|
@ -576,7 +568,7 @@ AutomationTimeAxisView::build_display_menu ()
|
||||||
/* make sure the automation menu state is correct */
|
/* make sure the automation menu state is correct */
|
||||||
|
|
||||||
automation_state_changed ();
|
automation_state_changed ();
|
||||||
interpolation_changed ();
|
interpolation_changed (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -834,7 +826,7 @@ void
|
||||||
AutomationTimeAxisView::clear_lines ()
|
AutomationTimeAxisView::clear_lines ()
|
||||||
{
|
{
|
||||||
_line.reset();
|
_line.reset();
|
||||||
automation_connection.disconnect ();
|
_list_connections.drop_connections ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -844,7 +836,13 @@ AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
|
||||||
assert(!_line);
|
assert(!_line);
|
||||||
assert(line->the_list() == _control->list());
|
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;
|
_line = line;
|
||||||
//_controller = AutomationController::create(_session, line->the_list(), _control);
|
//_controller = AutomationController::create(_session, line->the_list(), _control);
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@ class AutomationTimeAxisView : public TimeAxisView {
|
||||||
|
|
||||||
ArdourCanvas::SimpleRect* _base_rect;
|
ArdourCanvas::SimpleRect* _base_rect;
|
||||||
boost::shared_ptr<AutomationLine> _line;
|
boost::shared_ptr<AutomationLine> _line;
|
||||||
AutomationStreamView* _view;
|
/** AutomationStreamView if we are editing region-based automation (for MIDI), otherwise 0 */
|
||||||
|
AutomationStreamView* _view;
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
bool ignore_toggle;
|
bool ignore_toggle;
|
||||||
|
|
@ -156,9 +157,9 @@ class AutomationTimeAxisView : public TimeAxisView {
|
||||||
void automation_state_changed ();
|
void automation_state_changed ();
|
||||||
|
|
||||||
void set_interpolation (ARDOUR::AutomationList::InterpolationStyle);
|
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);
|
void update_extra_xml_shown (bool editor_shown);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,8 @@ public:
|
||||||
InsertMergePolicy insert_merge_policy () const;
|
InsertMergePolicy insert_merge_policy () const;
|
||||||
void set_insert_merge_policy (InsertMergePolicy);
|
void set_insert_merge_policy (InsertMergePolicy);
|
||||||
|
|
||||||
|
boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
|
int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
|
||||||
|
|
||||||
|
|
@ -170,6 +172,11 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class DeltaCommand;
|
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
|
// We cannot use a boost::shared_ptr here to avoid a retain cycle
|
||||||
MidiSource* _midi_source;
|
MidiSource* _midi_source;
|
||||||
InsertMergePolicy _insert_merge_policy;
|
InsertMergePolicy _insert_merge_policy;
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,15 @@ class MidiSource : virtual public Source
|
||||||
void set_model (boost::shared_ptr<MidiModel>);
|
void set_model (boost::shared_ptr<MidiModel>);
|
||||||
void drop_model();
|
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 */
|
/** Emitted when a different MidiModel is set */
|
||||||
PBD::Signal0<void> ModelChanged;
|
PBD::Signal0<void> ModelChanged;
|
||||||
|
/** Emitted when a parameter's interpolation style is changed */
|
||||||
|
PBD::Signal2<void, Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationChanged;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void flush_midi() = 0;
|
virtual void flush_midi() = 0;
|
||||||
|
|
@ -146,6 +153,12 @@ class MidiSource : virtual public Source
|
||||||
mutable double _length_beats;
|
mutable double _length_beats;
|
||||||
mutable sframes_t _last_read_end;
|
mutable sframes_t _last_read_end;
|
||||||
sframes_t _last_write_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,
|
sframes_t position,
|
||||||
nframes_t cnt);
|
nframes_t cnt);
|
||||||
|
|
||||||
void set_default_controls_interpolation ();
|
|
||||||
|
|
||||||
double _last_ev_time_beats;
|
double _last_ev_time_beats;
|
||||||
sframes_t _last_ev_time_frames;
|
sframes_t _last_ev_time_frames;
|
||||||
/** end time (start + duration) of last call to read_unlocked */
|
/** 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) {
|
for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
|
||||||
boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
|
boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
|
||||||
_controls[ac->parameter()] = ac;
|
add_control (ac);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int
|
int
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,9 @@ using namespace PBD;
|
||||||
|
|
||||||
MidiModel::MidiModel(MidiSource* s)
|
MidiModel::MidiModel(MidiSource* s)
|
||||||
: AutomatableSequence<TimeType>(s->session())
|
: AutomatableSequence<TimeType>(s->session())
|
||||||
, _midi_source(s)
|
, _midi_source (0)
|
||||||
{
|
{
|
||||||
|
set_midi_source (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start a new Diff command.
|
/** 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)
|
* user can switch a recorded track (with note durations from some instrument)
|
||||||
* to percussive, save, reload, then switch it back to sustained without
|
* to percussive, save, reload, then switch it back to sustained without
|
||||||
* destroying the original note durations.
|
* destroying the original note durations.
|
||||||
|
*
|
||||||
|
* Similarly, control events are written without interpolation (as with the
|
||||||
|
* `Discrete' mode).
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
MidiModel::write_to (boost::shared_ptr<MidiSource> source)
|
MidiModel::write_to (boost::shared_ptr<MidiSource> source)
|
||||||
|
|
@ -773,7 +777,7 @@ MidiModel::write_to (boost::shared_ptr<MidiSource> source)
|
||||||
source->drop_model();
|
source->drop_model();
|
||||||
source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
|
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);
|
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());
|
_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);
|
_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->drop_model();
|
||||||
source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
|
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);
|
const Evoral::Event<Evoral::MusicalTime>& ev (*i);
|
||||||
|
|
||||||
if (ev.time() >= begin_time && ev.time() < end_time) {
|
if (ev.time() >= begin_time && ev.time() < end_time) {
|
||||||
|
|
@ -1138,6 +1142,54 @@ MidiModel::insert_merge_policy () const
|
||||||
void
|
void
|
||||||
MidiModel::set_midi_source (MidiSource* s)
|
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 = 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);
|
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;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +115,28 @@ MidiSource::set_state (const XMLNode& node, int /*version*/)
|
||||||
_captured_for = prop->value();
|
_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;
|
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 the cached iterator is invalid, search for the first event past start
|
||||||
if (_last_read_end == 0 || start != _last_read_end || !_model_iter_valid) {
|
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));
|
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) {
|
if (converter.to(i->time()) >= start) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +288,7 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
||||||
newpath, false, _session.frame_rate()));
|
newpath, false, _session.frame_rate()));
|
||||||
|
|
||||||
newsrc->set_timeline_position(_timeline_position);
|
newsrc->set_timeline_position(_timeline_position);
|
||||||
|
newsrc->copy_interpolation_from (this);
|
||||||
|
|
||||||
if (_model) {
|
if (_model) {
|
||||||
if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
|
if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
|
||||||
|
|
@ -344,3 +373,49 @@ MidiSource::set_model (boost::shared_ptr<MidiModel> m)
|
||||||
_model = m;
|
_model = m;
|
||||||
ModelChanged (); /* EMIT SIGNAL */
|
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();
|
boost::shared_ptr<MidiModel> new_model = new_src->model();
|
||||||
new_model->start_write();
|
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) {
|
i != old_model->end(); ++i) {
|
||||||
const double new_time = i->time() * _request.time_fraction;
|
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->end_write();
|
||||||
new_model->set_edited(true);
|
new_model->set_edited(true);
|
||||||
|
|
||||||
|
new_src->copy_interpolation_from (src);
|
||||||
|
|
||||||
const int ret = finish (region, nsrcs, new_name);
|
const int ret = finish (region, nsrcs, new_name);
|
||||||
|
|
||||||
results[0]->set_length((nframes_t) floor (r->length() * _request.time_fraction), NULL);
|
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));
|
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
|
||||||
_mute_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 (_solo_control);
|
||||||
add_control (_mute_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);
|
append_event_unlocked_frames(ev, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_model) {
|
|
||||||
set_default_controls_interpolation();
|
|
||||||
}
|
|
||||||
|
|
||||||
Evoral::SMF::flush();
|
Evoral::SMF::flush();
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
|
|
@ -471,8 +467,6 @@ SMFSource::load_model (bool lock, bool force_reload)
|
||||||
_length_beats = max(_length_beats, ev.time());
|
_length_beats = max(_length_beats, ev.time());
|
||||||
}
|
}
|
||||||
|
|
||||||
set_default_controls_interpolation();
|
|
||||||
|
|
||||||
_model->end_write(false);
|
_model->end_write(false);
|
||||||
_model->set_edited(false);
|
_model->set_edited(false);
|
||||||
|
|
||||||
|
|
@ -481,18 +475,6 @@ SMFSource::load_model (bool lock, bool force_reload)
|
||||||
free(buf);
|
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
|
void
|
||||||
SMFSource::destroy_model ()
|
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 (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_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 create_curve();
|
||||||
void destroy_curve();
|
void destroy_curve();
|
||||||
|
|
@ -234,10 +235,12 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
InterpolationStyle interpolation() const { return _interpolation; }
|
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 */
|
/** Emitted when mark_dirty() is called on this object */
|
||||||
mutable PBD::Signal0<void> Dirty;
|
mutable PBD::Signal0<void> Dirty;
|
||||||
|
/** Emitted when our interpolation style changes */
|
||||||
|
PBD::Signal1<void, InterpolationStyle> InterpolationChanged;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
@ -246,7 +249,6 @@ protected:
|
||||||
|
|
||||||
void build_search_cache_if_necessary(double start, double end) const;
|
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;
|
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);
|
boost::shared_ptr<ControlList> cut_copy_clear (double, double, int op);
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@
|
||||||
#include "pbd/signals.h"
|
#include "pbd/signals.h"
|
||||||
#include "evoral/types.hpp"
|
#include "evoral/types.hpp"
|
||||||
#include "evoral/Parameter.hpp"
|
#include "evoral/Parameter.hpp"
|
||||||
|
#include "evoral/ControlList.hpp"
|
||||||
|
|
||||||
namespace Evoral {
|
namespace Evoral {
|
||||||
|
|
||||||
class Control;
|
class Control;
|
||||||
class ControlList;
|
|
||||||
class ControlEvent;
|
class ControlEvent;
|
||||||
|
|
||||||
class ControlSet : public boost::noncopyable {
|
class ControlSet : public boost::noncopyable {
|
||||||
|
|
@ -69,12 +69,15 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void control_list_marked_dirty () {}
|
virtual void control_list_marked_dirty () {}
|
||||||
|
virtual void control_list_interpolation_changed (Parameter, ControlList::InterpolationStyle) {}
|
||||||
|
|
||||||
mutable Glib::Mutex _control_lock;
|
mutable Glib::Mutex _control_lock;
|
||||||
Controls _controls;
|
Controls _controls;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
PBD::ScopedConnectionList _control_connections;
|
PBD::ScopedConnectionList _control_connections;
|
||||||
|
PBD::ScopedConnectionList _list_connections;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ public:
|
||||||
class const_iterator {
|
class const_iterator {
|
||||||
public:
|
public:
|
||||||
const_iterator();
|
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();
|
~const_iterator();
|
||||||
|
|
||||||
inline bool valid() const { return !_is_end && _event; }
|
inline bool valid() const { return !_is_end && _event; }
|
||||||
|
|
@ -220,10 +220,11 @@ public:
|
||||||
typename SysExes::const_iterator _sysex_iter;
|
typename SysExes::const_iterator _sysex_iter;
|
||||||
ControlIterators _control_iters;
|
ControlIterators _control_iters;
|
||||||
ControlIterators::iterator _control_iter;
|
ControlIterators::iterator _control_iter;
|
||||||
|
bool _force_discrete;
|
||||||
};
|
};
|
||||||
|
|
||||||
const_iterator begin (Time t=0, std::set<Evoral::Parameter> const & f = std::set<Evoral::Parameter> ()) const {
|
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, f);
|
return const_iterator (*this, t, force_discrete, f);
|
||||||
}
|
}
|
||||||
const const_iterator& end() const { return _end_iter; }
|
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
|
bool
|
||||||
ControlList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const
|
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);
|
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);
|
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 ();
|
maybe_signal_changed ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ControlList::set_interpolation (InterpolationStyle s)
|
||||||
|
{
|
||||||
|
if (_interpolation == s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_interpolation = s;
|
||||||
|
InterpolationChanged (s); /* EMIT SIGNAL */
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Evoral
|
} // namespace Evoral
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include "evoral/ControlSet.hpp"
|
#include "evoral/ControlSet.hpp"
|
||||||
#include "evoral/ControlList.hpp"
|
#include "evoral/ControlList.hpp"
|
||||||
|
|
@ -43,6 +44,10 @@ ControlSet::add_control(boost::shared_ptr<Control> ac)
|
||||||
_controls[ac->parameter()] = ac;
|
_controls[ac->parameter()] = ac;
|
||||||
|
|
||||||
ac->ListMarkedDirty.connect_same_thread (_control_connections, boost::bind (&ControlSet::control_list_marked_dirty, this));
|
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
|
void
|
||||||
|
|
@ -111,10 +116,10 @@ ControlSet::clear_controls ()
|
||||||
Glib::Mutex::Lock lm (_control_lock);
|
Glib::Mutex::Lock lm (_control_lock);
|
||||||
|
|
||||||
_control_connections.drop_connections ();
|
_control_connections.drop_connections ();
|
||||||
|
_list_connections.drop_connections ();
|
||||||
|
|
||||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
|
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
|
||||||
li->second->list()->clear();
|
li->second->list()->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Evoral
|
} // namespace Evoral
|
||||||
|
|
|
||||||
|
|
@ -52,14 +52,16 @@ Sequence<Time>::const_iterator::const_iterator()
|
||||||
_event = boost::shared_ptr< Event<Time> >(new Event<Time>());
|
_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>
|
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)
|
: _seq(&seq)
|
||||||
, _type(NIL)
|
, _type(NIL)
|
||||||
, _is_end((t == DBL_MAX) || seq.empty())
|
, _is_end((t == DBL_MAX) || seq.empty())
|
||||||
, _note_iter(seq.notes().end())
|
, _note_iter(seq.notes().end())
|
||||||
, _sysex_iter(seq.sysexes().end())
|
, _sysex_iter(seq.sysexes().end())
|
||||||
, _control_iter(_control_iters.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));
|
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)));
|
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: control: %1\n", seq._type_map.to_symbol(i->first)));
|
||||||
double x, y;
|
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) {
|
if (!ret) {
|
||||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: CC %1 (size %2) has no events past %3\n",
|
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));
|
i->first.id(), i->second->list()->size(), t));
|
||||||
|
|
@ -246,8 +253,11 @@ Sequence<Time>::const_iterator::operator++()
|
||||||
break;
|
break;
|
||||||
case CONTROL:
|
case CONTROL:
|
||||||
// Increment current controller iterator
|
// Increment current controller iterator
|
||||||
ret = _control_iter->list->rt_safe_earliest_event_unlocked(
|
if (_force_discrete) {
|
||||||
_control_iter->x, DBL_MAX, x, y, false);
|
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);
|
assert(!ret || x > _control_iter->x);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
_control_iter->x = x;
|
_control_iter->x = x;
|
||||||
|
|
@ -366,6 +376,7 @@ Sequence<Time>::const_iterator::operator=(const const_iterator& other)
|
||||||
_note_iter = other._note_iter;
|
_note_iter = other._note_iter;
|
||||||
_sysex_iter = other._sysex_iter;
|
_sysex_iter = other._sysex_iter;
|
||||||
_control_iters = other._control_iters;
|
_control_iters = other._control_iters;
|
||||||
|
_force_discrete = other._force_discrete;
|
||||||
|
|
||||||
if (other._lock)
|
if (other._lock)
|
||||||
_lock = _seq->read_lock();
|
_lock = _seq->read_lock();
|
||||||
|
|
@ -391,7 +402,7 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
|
||||||
, _overlap_pitch_resolution (FirstOnFirstOff)
|
, _overlap_pitch_resolution (FirstOnFirstOff)
|
||||||
, _writing(false)
|
, _writing(false)
|
||||||
, _type_map(type_map)
|
, _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)
|
, _percussive(false)
|
||||||
, _lowest_note(127)
|
, _lowest_note(127)
|
||||||
, _highest_note(0)
|
, _highest_note(0)
|
||||||
|
|
@ -409,7 +420,7 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
|
||||||
, _overlap_pitch_resolution (other._overlap_pitch_resolution)
|
, _overlap_pitch_resolution (other._overlap_pitch_resolution)
|
||||||
, _writing(false)
|
, _writing(false)
|
||||||
, _type_map(other._type_map)
|
, _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)
|
, _percussive(other._percussive)
|
||||||
, _lowest_note(other._lowest_note)
|
, _lowest_note(other._lowest_note)
|
||||||
, _highest_note(other._highest_note)
|
, _highest_note(other._highest_note)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue