From d357eca668044badcb4bab318e2e74cfffa9a0b0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 19 Sep 2008 00:47:49 +0000 Subject: [PATCH] Factor out sequencing related things into an independant new library: "evoral". Anything related to the storage of events/values over a range of time lives in evoral. This includes MidiModel (Evoral::Sequence) and automation data (AutomationList (Evoral::ControlList), Automatable (Evoral::ControlSet), etc). libs/evoral synced with http://svn.drobilla.net/lad/trunk/evoral r1511. git-svn-id: svn://localhost/ardour2/branches/3.0@3754 d708f5d6-7413-0410-9779-e7cbd77b26cf --- SConstruct | 3 + gtk2_ardour/SConscript | 1 + gtk2_ardour/audio_time_axis.cc | 2 +- gtk2_ardour/automation_controller.cc | 17 +- gtk2_ardour/automation_controller.h | 2 +- gtk2_ardour/automation_line.cc | 4 +- gtk2_ardour/automation_line.h | 2 +- gtk2_ardour/automation_region_view.cc | 2 +- gtk2_ardour/automation_selection.h | 2 +- gtk2_ardour/automation_streamview.cc | 6 +- gtk2_ardour/automation_time_axis.cc | 28 +- gtk2_ardour/canvas-hit.h | 2 +- gtk2_ardour/canvas-note-event.cc | 2 +- gtk2_ardour/canvas-note-event.h | 6 +- gtk2_ardour/canvas-note.cc | 2 +- gtk2_ardour/canvas-note.h | 2 +- gtk2_ardour/canvas-program-change.cc | 2 +- gtk2_ardour/canvas-program-change.h | 4 +- gtk2_ardour/crossfade_edit.cc | 14 +- gtk2_ardour/curvetest.cc | 3 +- gtk2_ardour/gain_meter.cc | 21 +- gtk2_ardour/generic_pluginui.cc | 12 +- gtk2_ardour/midi_region_view.cc | 23 +- gtk2_ardour/midi_region_view.h | 8 +- gtk2_ardour/midi_streamview.cc | 2 +- gtk2_ardour/midi_time_axis.cc | 10 +- gtk2_ardour/mixer_strip.cc | 6 +- gtk2_ardour/panner_ui.cc | 4 +- gtk2_ardour/route_time_axis.cc | 7 +- gtk2_ardour/route_ui.cc | 4 +- gtk2_ardour/selection.cc | 19 +- gtk2_ardour/selection.h | 10 +- libs/ardour/SConscript | 2 +- libs/ardour/ardour/automatable.h | 34 +- libs/ardour/ardour/automation_control.h | 40 +- libs/ardour/ardour/automation_event.h | 240 +--- libs/ardour/ardour/midi_buffer.h | 14 +- libs/ardour/ardour/midi_model.h | 161 +-- libs/ardour/ardour/midi_region.h | 4 +- libs/ardour/ardour/midi_ring_buffer.h | 11 +- libs/ardour/ardour/midi_source.h | 2 +- libs/ardour/ardour/midi_track.h | 2 +- libs/ardour/ardour/note.h | 82 -- libs/ardour/ardour/panner.h | 10 +- libs/ardour/ardour/parameter.h | 168 +-- libs/ardour/ardour/plugin_insert.h | 2 +- libs/ardour/ardour/smf_source.h | 4 +- libs/ardour/audio_track.cc | 6 +- libs/ardour/audioregion.cc | 48 +- libs/ardour/automatable.cc | 201 +-- libs/ardour/automation_control.cc | 41 +- libs/ardour/automation_event.cc | 1264 +---------------- libs/ardour/crossfade.cc | 12 +- libs/ardour/gain.cc | 2 +- libs/ardour/import.cc | 2 +- libs/ardour/io.cc | 21 +- libs/ardour/jack_midi_port.cc | 2 +- libs/ardour/meter.cc | 2 +- libs/ardour/midi_buffer.cc | 14 +- libs/ardour/midi_diskstream.cc | 2 +- libs/ardour/midi_model.cc | 689 +-------- libs/ardour/midi_source.cc | 2 +- libs/ardour/midi_stretch.cc | 2 +- libs/ardour/midi_track.cc | 4 +- libs/ardour/panner.cc | 22 +- libs/ardour/parameter.cc | 8 +- libs/ardour/plugin_insert.cc | 41 +- libs/ardour/route.cc | 4 +- libs/ardour/smf_source.cc | 6 +- libs/evoral/SConscript | 42 + libs/evoral/evoral/Control.hpp | 58 + libs/evoral/evoral/ControlList.hpp | 274 ++++ libs/evoral/evoral/ControlSet.hpp | 69 + libs/evoral/evoral/Curve.hpp | 58 + libs/evoral/evoral/Event.hpp | 219 +++ libs/evoral/evoral/EventSink.hpp | 40 + libs/evoral/evoral/MIDIParameters.hpp | 52 + libs/evoral/evoral/Note.hpp | 79 ++ libs/evoral/evoral/Parameter.hpp | 135 ++ libs/evoral/evoral/Sequence.hpp | 215 +++ libs/evoral/evoral/midi_events.h | 133 ++ libs/evoral/evoral/types.hpp | 31 + libs/evoral/src/Control.cpp | 82 ++ libs/evoral/src/ControlList.cpp | 1310 ++++++++++++++++++ libs/evoral/src/ControlSet.cpp | 139 ++ libs/evoral/src/Curve.cpp | 401 ++++++ libs/evoral/src/Event.cpp | 107 ++ libs/{ardour/note.cc => evoral/src/Note.cpp} | 49 +- libs/evoral/src/Sequence.cpp | 643 +++++++++ libs/evoral/test/sequence.cpp | 12 + libs/midi++2/SConscript | 2 +- libs/midi++2/event.cc | 97 -- libs/midi++2/jack_midiport.cc | 8 +- libs/midi++2/midi++/event.h | 183 +-- libs/midi++2/midi++/jack.h | 2 +- libs/midi++2/midi++/midnam_patch.h | 2 +- libs/midi++2/midnam_patch.cc | 7 +- libs/surfaces/control_protocol/SConscript | 1 + libs/surfaces/frontier/tranzport/SConscript | 1 + libs/surfaces/generic_midi/SConscript | 1 + libs/surfaces/mackie/SConscript | 1 + libs/surfaces/powermate/SConscript | 1 + 102 files changed, 4640 insertions(+), 3217 deletions(-) delete mode 100644 libs/ardour/ardour/note.h create mode 100644 libs/evoral/SConscript create mode 100644 libs/evoral/evoral/Control.hpp create mode 100644 libs/evoral/evoral/ControlList.hpp create mode 100644 libs/evoral/evoral/ControlSet.hpp create mode 100644 libs/evoral/evoral/Curve.hpp create mode 100644 libs/evoral/evoral/Event.hpp create mode 100644 libs/evoral/evoral/EventSink.hpp create mode 100644 libs/evoral/evoral/MIDIParameters.hpp create mode 100644 libs/evoral/evoral/Note.hpp create mode 100644 libs/evoral/evoral/Parameter.hpp create mode 100644 libs/evoral/evoral/Sequence.hpp create mode 100644 libs/evoral/evoral/midi_events.h create mode 100644 libs/evoral/evoral/types.hpp create mode 100644 libs/evoral/src/Control.cpp create mode 100644 libs/evoral/src/ControlList.cpp create mode 100644 libs/evoral/src/ControlSet.cpp create mode 100644 libs/evoral/src/Curve.cpp create mode 100644 libs/evoral/src/Event.cpp rename libs/{ardour/note.cc => evoral/src/Note.cpp} (65%) create mode 100644 libs/evoral/src/Sequence.cpp create mode 100644 libs/evoral/test/sequence.cpp delete mode 100644 libs/midi++2/event.cc diff --git a/SConstruct b/SConstruct index 35a67d1879..b41b609820 100644 --- a/SConstruct +++ b/SConstruct @@ -628,6 +628,7 @@ libraries['ardour_cp'] = LibraryInfo (LIBS='ardour_cp', LIBPATH='#libs/surfaces/ libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour') libraries['midi++2'] = LibraryInfo (LIBS='midi++', LIBPATH='#libs/midi++2', CPPPATH='#libs/midi++2') +libraries['evoral'] = LibraryInfo (LIBS='evoral', LIBPATH='#libs/evoral', CPPPATH='#libs/evoral') libraries['pbd'] = LibraryInfo (LIBS='pbd', LIBPATH='#libs/pbd', CPPPATH='#libs/pbd') libraries['gtkmm2ext'] = LibraryInfo (LIBS='gtkmm2ext', LIBPATH='#libs/gtkmm2ext', CPPPATH='#libs/gtkmm2ext') @@ -1083,6 +1084,7 @@ if env['SYSLIBS']: subdirs = [ 'libs/pbd', 'libs/midi++2', + 'libs/evoral', 'libs/ardour', 'libs/vamp-sdk', 'libs/vamp-plugins/', @@ -1159,6 +1161,7 @@ else: 'libs/taglib', 'libs/pbd', 'libs/midi++2', + 'libs/evoral', 'libs/ardour', 'libs/vamp-sdk', 'libs/vamp-plugins/', diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index bffb28e66a..009b2f6702 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -48,6 +48,7 @@ gtkardour.Merge ([ libraries['libgnomecanvasmm'], libraries['lrdf'], libraries['midi++2'], + libraries['evoral'], libraries['pangomm'], libraries['pbd'], libraries['samplerate'], diff --git a/gtk2_ardour/audio_time_axis.cc b/gtk2_ardour/audio_time_axis.cc index 124c639367..8c77da3443 100644 --- a/gtk2_ardour/audio_time_axis.cc +++ b/gtk2_ardour/audio_time_axis.cc @@ -345,7 +345,7 @@ AudioTimeAxisView::create_automation_child (Parameter param, bool show) update_pans (show); } else { - error << "AudioTimeAxisView: unknown automation child " << param.to_string() << endmsg; + error << "AudioTimeAxisView: unknown automation child " << param.symbol() << endmsg; } } diff --git a/gtk2_ardour/automation_controller.cc b/gtk2_ardour/automation_controller.cc index 0bf1482224..dc9b8bb2c4 100644 --- a/gtk2_ardour/automation_controller.cc +++ b/gtk2_ardour/automation_controller.cc @@ -61,12 +61,15 @@ AutomationController::~AutomationController() } boost::shared_ptr -AutomationController::create(boost::shared_ptr parent, boost::shared_ptr al, boost::shared_ptr ac) +AutomationController::create( + boost::shared_ptr parent, + boost::shared_ptr cl, + boost::shared_ptr ac) { - Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(al->default_value(), al->get_min_y(), al->get_max_y())); + Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(cl->default_value(), cl->get_min_y(), cl->get_max_y())); if (!ac) { - PBD::warning << "Creating AutomationController for " << al->parameter().to_string() << endmsg; - ac = parent->control_factory(al); + PBD::warning << "Creating AutomationController for " << cl->parameter().symbol() << endmsg; + ac = boost::dynamic_pointer_cast(parent->control_factory(cl)); } return boost::shared_ptr(new AutomationController(ac, adjustment)); } @@ -109,13 +112,13 @@ AutomationController::value_adjusted() void AutomationController::start_touch() { - _controllable->list()->start_touch(); + _controllable->start_touch(); } void AutomationController::end_touch() { - _controllable->list()->stop_touch(); + _controllable->stop_touch(); } void @@ -123,7 +126,7 @@ AutomationController::automation_state_changed () { ENSURE_GUI_THREAD(mem_fun(*this, &AutomationController::automation_state_changed)); - bool x = (_controllable->list()->automation_state() != Off); + bool x = (_controllable->automation_state() != Off); /* start watching automation so that things move */ diff --git a/gtk2_ardour/automation_controller.h b/gtk2_ardour/automation_controller.h index 8a244113c4..0da24b3588 100644 --- a/gtk2_ardour/automation_controller.h +++ b/gtk2_ardour/automation_controller.h @@ -37,7 +37,7 @@ class AutomationController : public Gtkmm2ext::BarController { public: static boost::shared_ptr create( boost::shared_ptr parent, - boost::shared_ptr al, + boost::shared_ptr cl, boost::shared_ptr ac); ~AutomationController(); diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index b112a9481e..de3e2d9873 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -1048,7 +1048,7 @@ AutomationLine::list_changed () } void -AutomationLine::reset_callback (const AutomationList& events) +AutomationLine::reset_callback (const Evoral::ControlList& events) { ALPoints tmp_points; uint32_t npoints = events.size(); @@ -1064,7 +1064,7 @@ AutomationLine::reset_callback (const AutomationList& events) AutomationList::const_iterator ai; - for (ai = events.const_begin(); ai != events.const_end(); ++ai) { + for (ai = events.begin(); ai != events.end(); ++ai) { double translated_y = (*ai)->value; model_to_view_y (translated_y); diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 9e35903671..ace2145ff3 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -165,7 +165,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y); virtual void change_model_range (ARDOUR::AutomationList::iterator,ARDOUR::AutomationList::iterator, double delta, float ydelta); - void reset_callback (const ARDOUR::AutomationList&); + void reset_callback (const Evoral::ControlList&); void list_changed (); virtual bool event_handler (GdkEvent*); diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc index 0ba14398e6..00cb8be5c4 100644 --- a/gtk2_ardour/automation_region_view.cc +++ b/gtk2_ardour/automation_region_view.cc @@ -33,7 +33,7 @@ AutomationRegionView::AutomationRegionView(ArdourCanvas::Group* { if (list) { _line = boost::shared_ptr(new AutomationLine( - list->parameter().to_string(), time_axis, *group, list)); + list->parameter().symbol(), time_axis, *group, list)); _line->set_colors(); _line->show(); _line->show_all_control_points(); diff --git a/gtk2_ardour/automation_selection.h b/gtk2_ardour/automation_selection.h index 05063e144e..fb19c0b5e0 100644 --- a/gtk2_ardour/automation_selection.h +++ b/gtk2_ardour/automation_selection.h @@ -26,6 +26,6 @@ namespace ARDOUR { class AutomationList; } -struct AutomationSelection : list {}; +struct AutomationSelection : list< boost::shared_ptr > {}; #endif /* __ardour_gtk_automation_selection_h__ */ diff --git a/gtk2_ardour/automation_streamview.cc b/gtk2_ardour/automation_streamview.cc index 5803d283dc..68da972ba0 100644 --- a/gtk2_ardour/automation_streamview.cc +++ b/gtk2_ardour/automation_streamview.cc @@ -83,11 +83,13 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr region mr->midi_source()->load_model(); } - const boost::shared_ptr control = region->control(_controller->controllable()->parameter()); + const boost::shared_ptr control + = boost::dynamic_pointer_cast( + region->control(_controller->controllable()->parameter())); boost::shared_ptr list; if (control) - list = control->list(); + list = boost::dynamic_pointer_cast(control->list()); AutomationRegionView *region_view; std::list::iterator i; diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index bc371a5826..07a47f274e 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -208,10 +208,10 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr line(new AutomationLine ( - _control->parameter().to_string(), + _control->parameter().symbol(), *this, *canvas_display, - _control->list())); + _control->alist())); line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get()); line->queue_reset (); @@ -261,7 +261,7 @@ AutomationTimeAxisView::set_automation_state (AutoState state) state); } - _control->list()->set_automation_state(state); + _control->alist()->set_automation_state(state); } } @@ -276,7 +276,7 @@ AutomationTimeAxisView::automation_state_changed () if (!_line) { state = Off; } else { - state = _control->list()->automation_state (); + state = _control->alist()->automation_state (); } switch (state & (Off|Play|Touch|Write)) { @@ -578,12 +578,12 @@ AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEvent _line->view_to_model_y (y); _session.begin_reversible_command (_("add automation event")); - XMLNode& before = _control->list()->get_state(); + XMLNode& before = _control->alist()->get_state(); - _control->list()->add (when, y); + _control->alist()->add (when, y); - XMLNode& after = _control->list()->get_state(); - _session.commit_reversible_command (new MementoCommand(*_control->list().get(), &before, &after)); + XMLNode& after = _control->alist()->get_state(); + _session.commit_reversible_command (new MementoCommand(*_control->alist(), &before, &after)); _session.set_dirty (); } @@ -598,7 +598,7 @@ AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) bool AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op) { - AutomationList* what_we_got = 0; + boost::shared_ptr what_we_got; boost::shared_ptr alist (line.the_list()); bool ret = false; @@ -621,8 +621,6 @@ AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& sel case Clear: if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) { _session.add_command(new MementoCommand(*alist.get(), &before, &alist->get_state())); - delete what_we_got; - what_we_got = 0; ret = true; } break; @@ -671,7 +669,7 @@ AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCo bool AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op) { - AutomationList* what_we_got = 0; + boost::shared_ptr what_we_got; boost::shared_ptr alist(line.the_list()); bool ret = false; @@ -700,8 +698,6 @@ AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointS case Clear: if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) { _session.add_command (new MementoCommand(*alist.get(), new XMLNode (before), &alist->get_state())); - delete what_we_got; - what_we_got = 0; ret = true; } break; @@ -822,7 +818,7 @@ AutomationTimeAxisView::add_line (boost::shared_ptr line) assert(!_line); assert(line->the_list() == _control->list()); - automation_connection = _control->list()->automation_state_changed.connect + automation_connection = _control->alist()->automation_state_changed.connect (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed)); _line = line; @@ -884,7 +880,7 @@ AutomationTimeAxisView::set_state (const XMLNode& node) if ((*iter)->name() == state_node_name) { XMLProperty* type = (*iter)->property("automation-id"); - if (type && type->value() == _control->parameter().to_string()) { + if (type && type->value() == _control->parameter().symbol()) { XMLProperty *shown = (*iter)->property("shown_editor"); if (shown && shown->value() == "yes") { diff --git a/gtk2_ardour/canvas-hit.h b/gtk2_ardour/canvas-hit.h index e77ef37380..fa90075a0c 100644 --- a/gtk2_ardour/canvas-hit.h +++ b/gtk2_ardour/canvas-hit.h @@ -33,7 +33,7 @@ public: MidiRegionView& region, Group& group, double size, - const boost::shared_ptr note = boost::shared_ptr()) + const boost::shared_ptr note = boost::shared_ptr()) : Diamond(group, size), CanvasNoteEvent(region, this, note) { diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index c370530cfc..1d01554883 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -32,7 +32,7 @@ namespace Canvas { CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item, - const boost::shared_ptr note) + const boost::shared_ptr note) : _region(region) , _item(item) , _text(0) diff --git a/gtk2_ardour/canvas-note-event.h b/gtk2_ardour/canvas-note-event.h index 776d89160d..9e48db8c24 100644 --- a/gtk2_ardour/canvas-note-event.h +++ b/gtk2_ardour/canvas-note-event.h @@ -49,7 +49,7 @@ public: CanvasNoteEvent( MidiRegionView& region, Item* item, - const boost::shared_ptr note = boost::shared_ptr()); + const boost::shared_ptr note = boost::shared_ptr()); virtual ~CanvasNoteEvent(); @@ -81,7 +81,7 @@ public: virtual double x2() = 0; virtual double y2() = 0; - const boost::shared_ptr note() const { return _note; } + const boost::shared_ptr note() const { return _note; } protected: enum State { None, Pressed, Dragging }; @@ -91,7 +91,7 @@ protected: Text* _text; Widget* _channel_selector_widget; State _state; - const boost::shared_ptr _note; + const boost::shared_ptr _note; bool _own_note; bool _selected; }; diff --git a/gtk2_ardour/canvas-note.cc b/gtk2_ardour/canvas-note.cc index 30b1e3807b..89916dbf4a 100644 --- a/gtk2_ardour/canvas-note.cc +++ b/gtk2_ardour/canvas-note.cc @@ -1,7 +1,7 @@ #include "canvas-note.h" #include "midi_region_view.h" #include "public_editor.h" -#include "ardour/note.h" +#include "evoral/Note.hpp" using namespace ARDOUR; diff --git a/gtk2_ardour/canvas-note.h b/gtk2_ardour/canvas-note.h index 97739b4207..30cbad6c25 100644 --- a/gtk2_ardour/canvas-note.h +++ b/gtk2_ardour/canvas-note.h @@ -55,7 +55,7 @@ public: CanvasNote( MidiRegionView& region, Group& group, - const boost::shared_ptr note = boost::shared_ptr()) + const boost::shared_ptr note = boost::shared_ptr()) : SimpleRect(group), CanvasNoteEvent(region, this, note), _note_state(None) { diff --git a/gtk2_ardour/canvas-program-change.cc b/gtk2_ardour/canvas-program-change.cc index 6f7182e2cd..e5a7768f34 100644 --- a/gtk2_ardour/canvas-program-change.cc +++ b/gtk2_ardour/canvas-program-change.cc @@ -8,7 +8,7 @@ using namespace std; CanvasProgramChange::CanvasProgramChange( MidiRegionView& region, Group& parent, - boost::shared_ptr event, + boost::shared_ptr event, double height, double x, double y) diff --git a/gtk2_ardour/canvas-program-change.h b/gtk2_ardour/canvas-program-change.h index 3ac9b10383..f2a912d506 100644 --- a/gtk2_ardour/canvas-program-change.h +++ b/gtk2_ardour/canvas-program-change.h @@ -19,7 +19,7 @@ public: CanvasProgramChange( MidiRegionView& region, Group& parent, - boost::shared_ptr event, + boost::shared_ptr event, double height, double x = 0.0, double y = 0.0 @@ -29,7 +29,7 @@ public: private: MidiRegionView& _region; - boost::shared_ptr _event; + boost::shared_ptr _event; Text* _text; SimpleLine* _line; SimpleRect* _rect; diff --git a/gtk2_ardour/crossfade_edit.cc b/gtk2_ardour/crossfade_edit.cc index 82869af396..80b6224bd3 100644 --- a/gtk2_ardour/crossfade_edit.cc +++ b/gtk2_ardour/crossfade_edit.cc @@ -65,8 +65,8 @@ CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0; CrossfadeEditor::Half::Half () : line (0), - normative_curve (Parameter(GainAutomation), 0.0, 1.0, 1.0), // FIXME: GainAutomation? - gain_curve (Parameter(GainAutomation), 0.0, 2.0, 1.0) + normative_curve (Parameter(GainAutomation, 0.0, 1.0, 1.0)), // FIXME: GainAutomation? + gain_curve (Parameter(GainAutomation)) { } @@ -343,13 +343,13 @@ CrossfadeEditor::set (const ARDOUR::AutomationList& curve, WhichFade which) goto out; } - the_end = curve.const_end(); + the_end = curve.end(); --the_end; - firstx = (*curve.const_begin())->when; + firstx = (*curve.begin())->when; endx = (*the_end)->when; - for (ARDOUR::AutomationList::const_iterator i = curve.const_begin(); i != curve.const_end(); ++i) { + for (ARDOUR::AutomationList::const_iterator i = curve.begin(); i != curve.end(); ++i) { double xfract = ((*i)->when - firstx) / (endx - firstx); double yfract = ((*i)->value - miny) / (maxy - miny); @@ -771,7 +771,7 @@ CrossfadeEditor::_apply_to (boost::shared_ptr xf) /* IN */ - ARDOUR::AutomationList::const_iterator the_end = in.const_end(); + ARDOUR::AutomationList::const_iterator the_end = in.end(); --the_end; double firstx = (*in.begin())->when; @@ -791,7 +791,7 @@ CrossfadeEditor::_apply_to (boost::shared_ptr xf) /* OUT */ - the_end = out.const_end(); + the_end = out.end(); --the_end; firstx = (*out.begin())->when; diff --git a/gtk2_ardour/curvetest.cc b/gtk2_ardour/curvetest.cc index 48650c0353..ec552848d2 100644 --- a/gtk2_ardour/curvetest.cc +++ b/gtk2_ardour/curvetest.cc @@ -33,7 +33,8 @@ curvetest (string filename) { ifstream in (filename.c_str()); stringstream line; - AutomationList al (Parameter(), -1.0, +1.0, 0); + Parameter param(GainAutomation, -1.0, +1.0, 0.0); + AutomationList al (param); double minx = DBL_MAX; double maxx = DBL_MIN; diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc index 107b0eedfc..182cf2d89d 100644 --- a/gtk2_ardour/gain_meter.cc +++ b/gtk2_ardour/gain_meter.cc @@ -168,8 +168,8 @@ GainMeterBase::GainMeterBase (boost::shared_ptr io, Session& s, gain_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_style_button_event), false); gain_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_state_button_event), false); - r->gain_control()->list()->automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed)); - r->gain_control()->list()->automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed)); + r->gain_control()->alist()->automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed)); + r->gain_control()->alist()->automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed)); gain_automation_state_changed (); } @@ -408,7 +408,8 @@ GainMeterBase::set_fader_name (const char * name) void GainMeterBase::update_gain_sensitive () { - static_cast(gain_slider)->set_sensitive (!(_io->gain_control()->list()->automation_state() & Play)); + static_cast(gain_slider)->set_sensitive ( + !(_io->gain_control()->alist()->automation_state() & Play)); } @@ -556,14 +557,14 @@ GainMeterBase::meter_point_clicked () gint GainMeterBase::start_gain_touch (GdkEventButton* ev) { - _io->gain_control()->list()->start_touch (); + _io->gain_control()->alist()->start_touch (); return FALSE; } gint GainMeterBase::end_gain_touch (GdkEventButton* ev) { - _io->gain_control()->list()->stop_touch (); + _io->gain_control()->alist()->stop_touch (); return FALSE; } @@ -666,10 +667,10 @@ GainMeterBase::gain_automation_style_changed () { switch (_width) { case Wide: - gain_automation_style_button.set_label (astyle_string(_io->gain_control()->list()->automation_style())); + gain_automation_style_button.set_label (astyle_string(_io->gain_control()->alist()->automation_style())); break; case Narrow: - gain_automation_style_button.set_label (short_astyle_string(_io->gain_control()->list()->automation_style())); + gain_automation_style_button.set_label (short_astyle_string(_io->gain_control()->alist()->automation_style())); break; } } @@ -683,14 +684,14 @@ GainMeterBase::gain_automation_state_changed () switch (_width) { case Wide: - gain_automation_state_button.set_label (astate_string(_io->gain_control()->list()->automation_state())); + gain_automation_state_button.set_label (astate_string(_io->gain_control()->alist()->automation_state())); break; case Narrow: - gain_automation_state_button.set_label (short_astate_string(_io->gain_control()->list()->automation_state())); + gain_automation_state_button.set_label (short_astate_string(_io->gain_control()->alist()->automation_state())); break; } - x = (_io->gain_control()->list()->automation_state() != Off); + x = (_io->gain_control()->alist()->automation_state() != Off); if (gain_automation_state_button.get_active() != x) { ignore_toggle = true; diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc index 4b64098dc0..34ab571c8c 100644 --- a/gtk2_ardour/generic_pluginui.cc +++ b/gtk2_ardour/generic_pluginui.cc @@ -209,7 +209,11 @@ GenericPluginUI::build () } } - if ((cui = build_control_ui (i, insert->control(Parameter(PluginAutomation, i)))) == 0) { + boost::shared_ptr c + = boost::dynamic_pointer_cast( + insert->control(Parameter(PluginAutomation, i))); + + if ((cui = build_control_ui (i, c)) == 0) { error << string_compose(_("Plugin Editor: could not build control element for port %1"), i) << endmsg; continue; } @@ -526,7 +530,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptrChanged.connect (bind (mem_fun (*this, &GenericPluginUI::parameter_changed), control_ui)); - mcontrol->list()->automation_state_changed.connect + mcontrol->alist()->automation_state_changed.connect (bind (mem_fun(*this, &GenericPluginUI::automation_state_changed), control_ui)); } else if (plugin->parameter_is_output (port_index)) { @@ -584,13 +588,13 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptrcontrol->list()->start_touch (); + cui->control->start_touch (); } void GenericPluginUI::stop_touch (GenericPluginUI::ControlUI* cui) { - cui->control->list()->stop_touch (); + cui->control->stop_touch (); } void diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 49e5e0fb5d..63117beee2 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -397,7 +397,8 @@ MidiRegionView::create_note_at(double x, double y, double duration) new_note_duration = snap_to_frame(new_note_time_position_relative + new_note_duration) + _region->start() - new_note_time; - const boost::shared_ptr new_note(new Note(0, new_note_time, new_note_duration, (uint8_t)note, 0x40)); + const boost::shared_ptr new_note(new Evoral::Note( + 0, new_note_time, new_note_duration, (uint8_t)note, 0x40)); view->update_bounds(new_note->note()); MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note"); @@ -444,7 +445,7 @@ MidiRegionView::start_delta_command(string name) } void -MidiRegionView::command_add_note(const boost::shared_ptr note, bool selected) +MidiRegionView::command_add_note(const boost::shared_ptr note, bool selected) { if (_delta_command) _delta_command->add(note); @@ -524,8 +525,8 @@ MidiRegionView::redisplay_model() for (AutomationList::const_iterator event = control->second->list()->begin(); event != control->second->list()->end(); ++event) { - MidiControlIterator iter(control->second->list(), (*event)->when, (*event)->value); - boost::shared_ptr event(new MIDI::Event()); + Evoral::ControlIterator iter(control->second->list(), (*event)->when, (*event)->value); + boost::shared_ptr event(new Evoral::Event()); _model->control_to_midi_event(event, iter); add_pgm_change(event); } @@ -774,7 +775,7 @@ MidiRegionView::extend_active_notes() * event arrives, to properly display the note. */ void -MidiRegionView::add_note(const boost::shared_ptr note) +MidiRegionView::add_note(const boost::shared_ptr note) { assert(note->time() >= 0); assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive); @@ -816,7 +817,7 @@ MidiRegionView::add_note(const boost::shared_ptr note) // finish the old note rectangle if (_active_notes[note->note()]) { CanvasNote* const old_rect = _active_notes[note->note()]; - boost::shared_ptr old_note = old_rect->note(); + boost::shared_ptr old_note = old_rect->note(); cerr << "MidiModel: WARNING: Note has duration 0: chan " << old_note->channel() << "note " << (int)old_note->note() << " @ " << old_note->time() << endl; /* FIXME: How large to make it? Make it a diamond? */ @@ -870,7 +871,7 @@ MidiRegionView::add_note(const boost::shared_ptr note) } void -MidiRegionView::add_pgm_change(boost::shared_ptr event) +MidiRegionView::add_pgm_change(boost::shared_ptr event) { assert(event->time() >= 0); @@ -1061,7 +1062,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) Selection::iterator next = i; ++next; - const boost::shared_ptr copy(new Note(*(*i)->note().get())); + const boost::shared_ptr copy(new Evoral::Note(*(*i)->note().get())); // we need to snap here again in nframes64_t in order to be sample accurate double new_note_time = (*i)->note()->time(); @@ -1253,7 +1254,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo // transform to region start relative current_frame += _region->start(); - const boost::shared_ptr copy(new Note(*(canvas_note->note().get()))); + const boost::shared_ptr copy(new Evoral::Note(*(canvas_note->note().get()))); // resize beginning of note if (note_end == CanvasNote::NOTE_ON && current_frame < copy->end_time()) { @@ -1286,7 +1287,7 @@ MidiRegionView::change_velocity(uint8_t velocity, bool relative) ++next; CanvasNoteEvent *event = *i; - const boost::shared_ptr copy(new Note(*(event->note().get()))); + const boost::shared_ptr copy(new Evoral::Note(*(event->note().get()))); if (relative) { uint8_t new_velocity = copy->velocity() + velocity; @@ -1315,7 +1316,7 @@ MidiRegionView::change_channel(uint8_t channel) ++next; CanvasNoteEvent *event = *i; - const boost::shared_ptr copy(new Note(*(event->note().get()))); + const boost::shared_ptr copy(new Evoral::Note(*(event->note().get()))); copy->set_channel(channel); diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 50cac66d77..1bdccd4986 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -79,10 +79,10 @@ class MidiRegionView : public RegionView GhostRegion* add_ghost (TimeAxisView&); - void add_note(const boost::shared_ptr note); + void add_note(const boost::shared_ptr note); void resolve_note(uint8_t note_num, double end_time); - void add_pgm_change(boost::shared_ptr event); + void add_pgm_change(boost::shared_ptr event); void begin_write(); void end_write(); @@ -93,7 +93,7 @@ class MidiRegionView : public RegionView void display_model(boost::shared_ptr model); void start_delta_command(string name = "midi edit"); - void command_add_note(const boost::shared_ptr note, bool selected); + void command_add_note(const boost::shared_ptr note, bool selected); void command_remove_note(ArdourCanvas::CanvasNoteEvent* ev); void apply_command(); @@ -240,7 +240,7 @@ class MidiRegionView : public RegionView /** New notes (created in the current command) which should be selected * when they appear after the command is applied. */ - std::set< boost::shared_ptr > _marked_for_selection; + std::set< boost::shared_ptr > _marked_for_selection; std::vector _resize_data; }; diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index d53150fd91..2266ddd064 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -554,7 +554,7 @@ MidiStreamView::update_rec_regions (boost::shared_ptr data, nframes_t // FIXME: slooooooooow! - const boost::shared_ptr note = data->note_at(i); + const boost::shared_ptr note = data->note_at(i); if (note->duration() > 0 && note->end_time() + region->position() > start) mrv->resolve_note(note->note(), note->end_time()); diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 5802cc7e20..53da98d829 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -334,12 +334,12 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show) /* FIXME: don't create AutomationList for track itself * (not actually needed or used, since the automation is region-ey) */ - boost::shared_ptr c = _route->control(param); + boost::shared_ptr c + = boost::dynamic_pointer_cast(_route->control(param)); if (!c) { - boost::shared_ptr al(new ARDOUR::AutomationList(param, - param.min(), param.max(), (param.max() - param.min() / 2))); - c = boost::shared_ptr(_route->control_factory(al)); + boost::shared_ptr al(new ARDOUR::AutomationList(param)); + c = boost::dynamic_pointer_cast(_route->control_factory(al)); _route->add_control(c); } @@ -358,7 +358,7 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show) add_automation_child(param, track, show); } else { - error << "MidiTimeAxisView: unknown automation child " << param.to_string() << endmsg; + error << "MidiTimeAxisView: unknown automation child " << param.symbol() << endmsg; } } diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 1a8949d1b8..2459ac57a8 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -450,7 +450,7 @@ MixerStrip::set_width (Width w, void* owner) pre_processor_box.set_width (w); post_processor_box.set_width (w); - boost::shared_ptr gain_automation = _route->gain_control()->list(); + boost::shared_ptr gain_automation = _route->gain_control()->alist(); _width_owner = owner; @@ -748,8 +748,8 @@ MixerStrip::connect_to_pan () if (!_route->panner().empty()) { StreamPanner* sp = _route->panner().front(); - panstate_connection = sp->pan_control()->list()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed)); - panstyle_connection = sp->pan_control()->list()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed)); + panstate_connection = sp->pan_control()->alist()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed)); + panstyle_connection = sp->pan_control()->alist()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed)); } panners.pan_changed (this); diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc index 3fe43850a3..fec4cbf464 100644 --- a/gtk2_ardour/panner_ui.cc +++ b/gtk2_ardour/panner_ui.cc @@ -593,7 +593,7 @@ PannerUI::update_pan_bars (bool only_if_aplay) float xpos, val; if (only_if_aplay) { - boost::shared_ptr alist (_io->panner()[n]->pan_control()->list()); + boost::shared_ptr alist (_io->panner()[n]->pan_control()->alist()); if (!alist->automation_playback()) { continue; @@ -727,7 +727,7 @@ PannerUI::pan_automation_state_changed () return; } - x = (_io->panner().front()->pan_control()->list()->automation_state() != Off); + x = (_io->panner().front()->pan_control()->alist()->automation_state() != Off); if (pan_automation_state_button.get_active() != x) { ignore_toggle = true; diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index b3a988fd92..61c87d01de 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -1741,7 +1741,8 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr char state_name[256]; snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id()); - boost::shared_ptr control = processor->control(what, true); + boost::shared_ptr control + = boost::dynamic_pointer_cast(processor->control(what, true)); pan->view = boost::shared_ptr( new AutomationTimeAxisView (_session, _route, processor, control, @@ -1782,7 +1783,7 @@ RouteTimeAxisView::add_existing_processor_automation_curves (boost::shared_ptr

s; boost::shared_ptr al; - processor->what_has_visible_automation (s); + processor->what_has_visible_data (s); for (set::iterator i = s.begin(); i != s.end(); ++i) { @@ -1843,7 +1844,7 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::shared_ptr const std::set& automatable = processor->what_can_be_automated (); std::set has_visible_automation; - processor->what_has_visible_automation(has_visible_automation); + processor->what_has_visible_data(has_visible_automation); if (automatable.empty()) { return; diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 4d1d92ac5a..c02c4109b1 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -776,14 +776,14 @@ RouteUI::get_automation_child_xml_node (Parameter param) for (iter = kids.begin(); iter != kids.end(); ++iter) { if ((*iter)->name() == AutomationTimeAxisView::state_node_name) { XMLProperty* type = (*iter)->property("automation-id"); - if (type && type->value() == param.to_string()) + if (type && type->value() == param.symbol()) return *iter; } } // Didn't find it, make a new one XMLNode* child = new XMLNode (AutomationTimeAxisView::state_node_name); - child->add_property("automation-id", param.to_string()); + child->add_property("automation-id", param.symbol()); xml_node->add_child_nocopy (*child); return child; diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index 910cebc7fe..2f9601fcec 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -389,10 +389,17 @@ Selection::replace (uint32_t sid, nframes_t start, nframes_t end) } void -Selection::add (AutomationList* ac) +Selection::add (boost::shared_ptr cl) { - if (find (lines.begin(), lines.end(), ac) == lines.end()) { - lines.push_back (ac); + boost::shared_ptr al + = boost::dynamic_pointer_cast(cl); + if (!al) { + warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg; + return; + return; + } + if (find (lines.begin(), lines.end(), al) == lines.end()) { + lines.push_back (al); LinesChanged(); } } @@ -493,9 +500,9 @@ Selection::remove (nframes_t start, nframes_t end) } void -Selection::remove (AutomationList *ac) +Selection::remove (boost::shared_ptr ac) { - list::iterator i; + AutomationSelection::iterator i; if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) { lines.erase (i); LinesChanged(); @@ -595,7 +602,7 @@ Selection::set (TimeAxisView* track, nframes_t start, nframes_t end) } void -Selection::set (AutomationList *ac) +Selection::set (boost::shared_ptr ac) { lines.clear(); add (ac); diff --git a/gtk2_ardour/selection.h b/gtk2_ardour/selection.h index 17862e127b..c6923e663c 100644 --- a/gtk2_ardour/selection.h +++ b/gtk2_ardour/selection.h @@ -47,6 +47,10 @@ namespace ARDOUR { class AutomationList; } +namespace Evoral { + class ControlList; +} + /// Lists of selected things /** The Selection class holds lists of selected items (tracks, regions, etc. etc.). */ @@ -105,7 +109,7 @@ class Selection : public sigc::trackable void set (RegionView*, bool also_clear_tracks = true); void set (std::vector&); long set (TimeAxisView*, nframes_t, nframes_t); - void set (ARDOUR::AutomationList*); + void set (boost::shared_ptr); void set (boost::shared_ptr); void set (const std::list >&); void set (AutomationSelectable*); @@ -128,7 +132,7 @@ class Selection : public sigc::trackable void add (RegionView*); void add (std::vector&); long add (nframes_t, nframes_t); - void add (ARDOUR::AutomationList*); + void add (boost::shared_ptr); void add (boost::shared_ptr); void add (const std::list >&); void add (Marker*); @@ -139,7 +143,7 @@ class Selection : public sigc::trackable void remove (RegionView*); void remove (uint32_t selection_id); void remove (nframes_t, nframes_t); - void remove (ARDOUR::AutomationList*); + void remove (boost::shared_ptr); void remove (boost::shared_ptr); void remove (const std::list >&); void remove (const list&); diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 4bdbf7d88a..b51e81c2ca 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -109,7 +109,6 @@ mix.cc mtc_slave.cc midi_clock_slave.cc named_selection.cc -note.cc onset_detector.cc panner.cc parameter.cc @@ -330,6 +329,7 @@ ardour.Merge ([ libraries['glibmm2'], libraries['lrdf'], libraries['midi++2'], + libraries['evoral'], libraries['pbd'], libraries['raptor'], libraries['samplerate'], diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index a2c1d98ae7..ce31721802 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -27,49 +27,33 @@ #include #include #include +#include namespace ARDOUR { class Session; class AutomationControl; -class Automatable : public SessionObject +class Automatable : public SessionObject, virtual public Evoral::ControlSet { public: Automatable(Session&, const std::string& name); virtual ~Automatable() {} - // shorthand for gain, pan, etc - inline boost::shared_ptr - control(AutomationType type, bool create_if_missing=false) { - return control(Parameter(type), create_if_missing); - } - - virtual boost::shared_ptr control(Parameter id, bool create_if_missing=false); - virtual boost::shared_ptr control(Parameter id) const; + boost::shared_ptr control_factory(boost::shared_ptr list) const; + boost::shared_ptr control_list_factory(const Evoral::Parameter& param) const; - boost::shared_ptr control_factory(boost::shared_ptr list); + virtual void add_control(boost::shared_ptr); - typedef std::map > Controls; - Controls& controls() { return _controls; } - const Controls& controls() const { return _controls; } - - virtual void add_control(boost::shared_ptr); - virtual void automation_snapshot(nframes_t now, bool force); bool should_snapshot (nframes_t now) { return (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval); } virtual void transport_stopped(nframes_t now); - virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const; - virtual string describe_parameter(Parameter param); - virtual float default_parameter_value(Parameter param) { return 1.0f; } - virtual void clear_automation(); - AutoState get_parameter_automation_state (Parameter param, bool lock = true); virtual void set_parameter_automation_state (Parameter param, AutoState); @@ -78,14 +62,11 @@ public: void protect_automation (); - void what_has_automation(std::set&) const; - void what_has_visible_automation(std::set&) const; + void what_has_visible_data(std::set&) const; const std::set& what_can_be_automated() const { return _can_automate_list; } void mark_automation_visible(Parameter, bool); - Glib::Mutex& automation_lock() const { return _automation_lock; } - static void set_automation_interval (jack_nframes_t frames) { _automation_interval = frames; } @@ -106,9 +87,6 @@ protected: int load_automation (const std::string& path); int old_set_automation_state(const XMLNode&); - mutable Glib::Mutex _automation_lock; - - Controls _controls; std::set _visible_controls; std::set _can_automate_list; diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 68ac5797dc..c414f7bc40 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include namespace ARDOUR { @@ -34,28 +36,42 @@ class Automatable; /** A PBD:Controllable with associated automation data (AutomationList) */ -class AutomationControl : public PBD::Controllable +class AutomationControl : public PBD::Controllable, public Evoral::Control { public: AutomationControl(ARDOUR::Session&, boost::shared_ptr, std::string name="unnamed controllable"); + + boost::shared_ptr alist() { return boost::dynamic_pointer_cast(_list); } + + void set_list(boost::shared_ptr); + + inline bool automation_playback() const { + return ((ARDOUR::AutomationList*)_list.get())->automation_playback(); + } + + inline bool automation_write() const { + return ((ARDOUR::AutomationList*)_list.get())->automation_write(); + } + + inline AutoState automation_state() { + return ((ARDOUR::AutomationList*)_list.get())->automation_state(); + } + + inline void start_touch() { + return ((ARDOUR::AutomationList*)_list.get())->start_touch(); + } + + inline void stop_touch() { + return ((ARDOUR::AutomationList*)_list.get())->stop_touch(); + } void set_value(float val); float get_value() const; - float user_value() const; - - void set_list(boost::shared_ptr); - - boost::shared_ptr list() { return _list; } - boost::shared_ptr list() const { return _list; } - - Parameter parameter() const; protected: - ARDOUR::Session& _session; - boost::shared_ptr _list; - float _user_value; + ARDOUR::Session& _session; }; diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index 4362b9c867..ed3379bc15 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -37,105 +37,29 @@ #include #include +#include + +using Evoral::ControlEvent; + namespace ARDOUR { -class Curve; - -struct ControlEvent { - - ControlEvent (double w, double v) - : when (w), value (v), coeff (0) { - } - - ControlEvent (const ControlEvent& other) - : when (other.when), value (other.value), coeff (0) { - if (other.coeff) { - create_coeffs(); - for (size_t i=0; i < 4; ++i) - coeff[i] = other.coeff[i]; - } - } - - ~ControlEvent() { if (coeff) delete[] coeff; } - - void create_coeffs() { - if (!coeff) - coeff = new double[4]; - - coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0; - } - - double when; - double value; - double* coeff; ///< double[4] allocated by Curve as needed -}; - -/* automation lists use a pool allocator that does not use a lock and - allocates 8k of new pointers at a time -*/ - -typedef boost::fast_pool_allocator ControlEventAllocator; - -class AutomationList : public PBD::StatefulDestructible +class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlList { public: - typedef std::list EventList; - typedef EventList::iterator iterator; - typedef EventList::reverse_iterator reverse_iterator; - typedef EventList::const_iterator const_iterator; - - AutomationList (Parameter id, double min_val, double max_val, double default_val); + AutomationList (Parameter id); AutomationList (const XMLNode&, Parameter id); ~AutomationList(); + virtual boost::shared_ptr create(Evoral::Parameter id); + AutomationList (const AutomationList&); AutomationList (const AutomationList&, double start, double end); AutomationList& operator= (const AutomationList&); bool operator== (const AutomationList&); - - const Parameter& parameter() const { return _parameter; } - void set_parameter(Parameter p) { _parameter = p; } - + void freeze(); void thaw (); - - EventList::size_type size() const { return _events.size(); } - bool empty() const { return _events.empty(); } - - void reset_default (double val) { - _default_value = val; - } - - void clear (); - void x_scale (double factor); - bool extend_to (double); - void slide (iterator before, double distance); - - void reposition_for_rt_add (double when); - void rt_add (double when, double value); - void add (double when, double value); - /* this should be private but old-school automation loading needs it in IO/IOProcessor */ - void fast_simple_add (double when, double value); - - void reset_range (double start, double end); - void erase_range (double start, double end); - void erase (iterator); - void erase (iterator, iterator); - void move_range (iterator start, iterator end, double, double); - void modify (iterator, double, double); - - AutomationList* cut (double, double); - AutomationList* copy (double, double); - void clear (double, double); - - AutomationList* cut (iterator, iterator); - AutomationList* copy (iterator, iterator); - void clear (iterator, iterator); - - bool paste (AutomationList&, double position, float times); + void mark_dirty () const; void set_automation_state (AutoState); AutoState automation_state() const { return _state; } @@ -151,157 +75,29 @@ class AutomationList : public PBD::StatefulDestructible bool automation_write () const { return (_state & Write) || ((_state & Touch) && _touching); } + + sigc::signal StateChanged; + + static sigc::signal AutomationListCreated; + mutable sigc::signal Dirty; void start_touch (); void stop_touch (); bool touching() const { return _touching; } - void set_yrange (double min, double max) { - _min_yval = min; - _max_yval = max; - } - - double get_max_y() const { return _max_yval; } - double get_min_y() const { return _min_yval; } - - void truncate_end (double length); - void truncate_start (double length); - - iterator begin() { return _events.begin(); } - iterator end() { return _events.end(); } - - ControlEvent* back() { return _events.back(); } - ControlEvent* front() { return _events.front(); } - - const_iterator const_begin() const { return _events.begin(); } - const_iterator const_end() const { return _events.end(); } - - std::pair control_points_adjacent (double when); - - template void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) { - Glib::Mutex::Lock lm (_lock); - (obj.*method)(*this); - } - - sigc::signal StateChanged; - XMLNode& get_state(void); int set_state (const XMLNode &s); XMLNode& state (bool full); XMLNode& serialize_events (); - void set_max_xval (double); - double get_max_xval() const { return _max_xval; } - - double eval (double where) { - Glib::Mutex::Lock lm (_lock); - return unlocked_eval (where); - } - - double rt_safe_eval (double where, bool& ok) { - - Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK); - - if ((ok = lm.locked())) { - return unlocked_eval (where); - } else { - return 0.0; - } - } - - static inline bool time_comparator (const ControlEvent* a, const ControlEvent* b) { - return a->when < b->when; - } - - /** Lookup cache for eval functions, range contains equivalent values */ - struct LookupCache { - LookupCache() : left(-1) {} - double left; /* leftmost x coordinate used when finding "range" */ - std::pair range; - }; - - /** Lookup cache for point finding, range contains points between left and right */ - struct SearchCache { - SearchCache() : left(-1), right(-1) {} - double left; /* leftmost x coordinate used when finding "range" */ - double right; /* rightmost x coordinate used when finding "range" */ - std::pair range; - }; - - static sigc::signal AutomationListCreated; - - const EventList& events() const { return _events; } - double default_value() const { return _default_value; } - - // teeny const violations for Curve - mutable sigc::signal Dirty; - Glib::Mutex& lock() const { return _lock; } - LookupCache& lookup_cache() const { return _lookup_cache; } - SearchCache& search_cache() const { return _search_cache; } - - /** Called by locked entry point and various private - * locations where we already hold the lock. - * - * FIXME: Should this be private? Curve needs it.. - */ - double unlocked_eval (double x) 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; - - Curve& curve() { return *_curve; } - const Curve& curve() const { return *_curve; } - - enum InterpolationStyle { - Discrete, - Linear, - Curved - }; - - InterpolationStyle interpolation() const { return _interpolation; } - void set_interpolation(InterpolationStyle style) { _interpolation = style; } - private: - - /** Called by unlocked_eval() to handle cases of 3 or more control points. - */ - double multipoint_eval (double x) 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; - - AutomationList* cut_copy_clear (double, double, int op); - int deserialize_events (const XMLNode&); void maybe_signal_changed (); - void mark_dirty (); - void _x_scale (double factor); - - mutable LookupCache _lookup_cache; - mutable SearchCache _search_cache; - Parameter _parameter; - InterpolationStyle _interpolation; - EventList _events; - mutable Glib::Mutex _lock; - int8_t _frozen; - bool _changed_when_thawed; - AutoState _state; - AutoStyle _style; - bool _touching; - bool _new_touch; - double _max_xval; - double _min_yval; - double _max_yval; - double _default_value; - bool _sort_pending; - iterator _rt_insertion_point; - double _rt_pos; - - Curve* _curve; + AutoState _state; + AutoStyle _style; + bool _touching; }; } // namespace diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 699f461b17..0c13199825 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -39,7 +39,7 @@ public: void copy(const MidiBuffer& copy); - bool push_back(const MIDI::Event& event); + bool push_back(const Evoral::Event& event); bool push_back(const jack_midi_event_t& event); uint8_t* reserve(double time, size_t size); @@ -50,7 +50,7 @@ public: struct iterator { iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {} - inline MIDI::Event& operator*() const { return buffer[index]; } + inline Evoral::Event& operator*() const { return buffer[index]; } inline iterator& operator++() { ++index; return *this; } // prefix inline bool operator!=(const iterator& other) const { return index != other.index; } @@ -61,7 +61,7 @@ public: struct const_iterator { const_iterator(const MidiBuffer& b, size_t i) : buffer(b), index(i) {} - inline const MIDI::Event& operator*() const { return buffer[index]; } + inline const Evoral::Event& operator*() const { return buffer[index]; } inline const_iterator& operator++() { ++index; return *this; } // prefix inline bool operator!=(const const_iterator& other) const { return index != other.index; } @@ -80,8 +80,8 @@ private: friend class iterator; friend class const_iterator; - const MIDI::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; } - MIDI::Event& operator[](size_t i) { assert(i < _size); return _events[i]; } + const Evoral::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; } + Evoral::Event& operator[](size_t i) { assert(i < _size); return _events[i]; } // FIXME: Eliminate this static const size_t MAX_EVENT_SIZE = 4; // bytes @@ -92,8 +92,8 @@ private: /* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */ - MIDI::Event* _events; ///< Event structs that point to offsets in _data - uint8_t* _data; ///< MIDI, straight up. No time stamps. + Evoral::Event* _events; ///< Event structs that point to offsets in _data + uint8_t* _data; ///< MIDI, straight up. No time stamps. }; diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 2481aa8d34..51bddfde44 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -31,84 +31,28 @@ #include #include #include -#include #include +#include +#include namespace ARDOUR { class Session; class MidiSource; -/** - * This class keeps track of the current x and y for a control - */ -class MidiControlIterator { -public: - boost::shared_ptr automation_list; - double x; - double y; - - MidiControlIterator(boost::shared_ptr a_list, - double a_x, - double a_y) - : automation_list(a_list) - , x(a_x) - , y(a_y) - {} -}; - - /** This is a higher level (than MidiBuffer) model of MIDI data, with separate * representations for notes (instead of just unassociated note on/off events) * and controller data. Controller data is represented as part of the * Automatable base (i.e. in a map of AutomationList, keyed by Parameter). + * Because of this MIDI controllers and automatable controllers/widgets/etc + * are easily interchangeable. */ -class MidiModel : public boost::noncopyable, public Automatable { +class MidiModel : public Automatable, public Evoral::Sequence { public: MidiModel(MidiSource* s, size_t size=0); - void write_lock(); - void write_unlock(); - - void read_lock() const; - void read_unlock() const; - - void clear(); - - NoteMode note_mode() const { return _note_mode; } - void set_note_mode(NoteMode mode) { _note_mode = mode; } - - void start_write(); - bool writing() const { return _writing; } - void end_write(bool delete_stuck=false); - - size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const; - - /** Resizes vector if necessary (NOT realtime safe) */ - void append(const MIDI::Event& ev); - - inline const boost::shared_ptr note_at(unsigned i) const { return _notes[i]; } - inline const boost::shared_ptr note_at(unsigned i) { return _notes[i]; } - - inline size_t n_notes() const { return _notes.size(); } - inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; } - - inline static bool note_time_comparator (const boost::shared_ptr a, - const boost::shared_ptr b) { - return a->time() < b->time(); - } - - struct LaterNoteEndComparator { - typedef const Note* value_type; - inline bool operator()(const boost::shared_ptr a, - const boost::shared_ptr b) const { - return a->end_time() > b->end_time(); - } - }; - - typedef std::vector< boost::shared_ptr > Notes; - inline Notes& notes() { return _notes; } - inline const Notes& notes() const { return _notes; } + NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); } + void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); }; /** Add/Remove notes. * Technically all operations can be implemented as one of these. @@ -127,17 +71,17 @@ public: int set_state (const XMLNode&); XMLNode& get_state (); - void add(const boost::shared_ptr note); - void remove(const boost::shared_ptr note); + void add(const boost::shared_ptr note); + void remove(const boost::shared_ptr note); private: - XMLNode &marshal_note(const boost::shared_ptr note); - boost::shared_ptr unmarshal_note(XMLNode *xml_note); + XMLNode &marshal_note(const boost::shared_ptr note); + boost::shared_ptr unmarshal_note(XMLNode *xml_note); boost::shared_ptr _model; const std::string _name; - typedef std::list< boost::shared_ptr > NoteList; + typedef std::list< boost::shared_ptr > NoteList; NoteList _added_notes; NoteList _removed_notes; @@ -146,8 +90,6 @@ public: MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); void apply_command(Command* cmd); - bool edited() const { return _edited; } - void set_edited(bool yn) { _edited = yn; } bool write_to(boost::shared_ptr source); // MidiModel doesn't use the normal AutomationList serialisation code @@ -157,90 +99,11 @@ public: sigc::signal ContentsChanged; - /** Read iterator */ - class const_iterator { - public: - const_iterator(const MidiModel& model, double t); - ~const_iterator(); - - inline bool locked() const { return _locked; } - - const MIDI::Event& operator*() const { return *_event; } - const boost::shared_ptr operator->() const { return _event; } - const boost::shared_ptr get_event_pointer() { return _event; } - - const const_iterator& operator++(); // prefix only - bool operator==(const const_iterator& other) const; - bool operator!=(const const_iterator& other) const { return ! operator==(other); } - - const_iterator& operator=(const const_iterator& other); - - private: - friend class MidiModel; - - const MidiModel* _model; - boost::shared_ptr _event; - - typedef std::priority_queue< - boost::shared_ptr, std::deque< boost::shared_ptr >, - LaterNoteEndComparator> - ActiveNotes; - - mutable ActiveNotes _active_notes; - - bool _is_end; - bool _locked; - Notes::const_iterator _note_iter; - std::vector _control_iters; - std::vector::iterator _control_iter; - }; - - const_iterator begin() const { return const_iterator(*this, 0); } - const const_iterator& end() const { return _end_iter; } - const MidiSource* midi_source() const { return _midi_source; } void set_midi_source(MidiSource* source) { _midi_source = source; } - bool control_to_midi_event(boost::shared_ptr& ev, const MidiControlIterator& iter) const; private: friend class DeltaCommand; - void add_note_unlocked(const boost::shared_ptr note); - void remove_note_unlocked(const boost::shared_ptr note); - - friend class const_iterator; - -#ifndef NDEBUG - bool is_sorted() const; -#endif - - void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity); - void append_note_off_unlocked(uint8_t chan, double time, uint8_t note); - void append_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte); - void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number); - - mutable Glib::RWLock _lock; - - Notes _notes; - - NoteMode _note_mode; - - typedef std::vector WriteNotes; - WriteNotes _write_notes[16]; - bool _writing; - bool _edited; - - typedef std::vector< boost::shared_ptr > AutomationLists; - AutomationLists _dirty_automations; - - const const_iterator _end_iter; - - mutable nframes_t _next_read; - mutable const_iterator _read_iter; - - typedef std::priority_queue< - boost::shared_ptr, std::deque< boost::shared_ptr >, - LaterNoteEndComparator> - ActiveNotes; // We cannot use a boost::shared_ptr here to avoid a retain cycle MidiSource* _midi_source; diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 44f775dc1c..66257372a9 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -80,10 +80,10 @@ class MidiRegion : public Region Controls& controls() { return midi_source()->model()->controls(); } const Controls& controls() const { return midi_source()->model()->controls(); } - boost::shared_ptr control(Parameter id, bool create_if_missing=false) + boost::shared_ptr control(Evoral::Parameter id, bool create_if_missing=false) { return midi_source()->model()->control(id, create_if_missing); } - boost::shared_ptr control(Parameter id) const + boost::shared_ptr control(Evoral::Parameter id) const { return midi_source()->model()->control(id); } int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&); diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index ff0be5c997..db0c3029a8 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace ARDOUR { @@ -243,7 +244,7 @@ MidiRingBufferBase::write(size_t size, const T* src) * * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..] */ -class MidiRingBuffer : public MidiRingBufferBase { +class MidiRingBuffer : public MidiRingBufferBase, public Evoral::EventSink { public: /** @param size Size in bytes. */ @@ -251,8 +252,8 @@ public: : MidiRingBufferBase(size), _channel_mask(0x0000FFFF) {} - size_t write(double time, size_t size, const uint8_t* buf); - bool read(double* time, size_t* size, uint8_t* buf); + size_t write(double time, uint32_t size, const uint8_t* buf); + bool read(double* time, uint32_t* size, uint8_t* buf); bool read_prefix(double* time, size_t* size); bool read_contents(size_t size, uint8_t* buf); @@ -292,7 +293,7 @@ private: inline bool -MidiRingBuffer::read(double* time, size_t* size, uint8_t* buf) +MidiRingBuffer::read(double* time, uint32_t* size, uint8_t* buf) { bool success = MidiRingBufferBase::full_read(sizeof(double), (uint8_t*)time); @@ -333,7 +334,7 @@ MidiRingBuffer::read_contents(size_t size, uint8_t* buf) inline size_t -MidiRingBuffer::write(double time, size_t size, const uint8_t* buf) +MidiRingBuffer::write(double time, uint32_t size, const uint8_t* buf) { /*fprintf(stderr, "MRB %p write (t = %f) ", this, time); for (size_t i = 0; i < size; ++i) diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 997f3f9d69..9cb222d207 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -58,7 +58,7 @@ class MidiSource : public Source virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const; virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt); - virtual void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev) = 0; + virtual void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev) = 0; virtual void mark_for_remove() = 0; virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time); diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 6e4677df22..7eda6f904b 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -75,7 +75,7 @@ public: struct MidiControl : public AutomationControl { MidiControl(MidiTrack* route, boost::shared_ptr al) - : AutomationControl (route->session(), al, al->parameter().to_string()) + : AutomationControl (route->session(), al, al->parameter().symbol()) , _route (route) {} diff --git a/libs/ardour/ardour/note.h b/libs/ardour/ardour/note.h deleted file mode 100644 index 0f649b3370..0000000000 --- a/libs/ardour/ardour/note.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (C) 2007 Paul Davis - Author: Dave Robillard - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __ardour_note_h__ -#define __ardour_note_h__ - -#include -#include - -namespace ARDOUR { - - -/** A MIDI Note. - * - * A note is (unfortunately) special and not just another MIDI::Event as it - * has a duration and two separate MIDI events (on and off). - */ -class Note { -public: - Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40); - Note(const Note& copy); - ~Note(); - - const Note& operator=(const Note& copy); - - inline bool operator==(const Note& other) - { return time() == other.time() && - note() == other.note() && - duration() == other.duration() && - velocity() == other.velocity() && - channel() == other.channel(); - } - - inline double time() const { return _on_event.time(); } - inline double end_time() const { return _off_event.time(); } - inline uint8_t note() const { return _on_event.note(); } - inline uint8_t velocity() const { return _on_event.velocity(); } - inline double duration() const { return _off_event.time() - _on_event.time(); } - inline uint8_t channel() const { - assert(_on_event.channel() == _off_event.channel()); - return _on_event.channel(); - } - - inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; } - inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; } - inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; } - inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; } - inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); } - - inline MIDI::Event& on_event() { return _on_event; } - inline MIDI::Event& off_event() { return _off_event; } - - inline const MIDI::Event& on_event() const { return _on_event; } - inline const MIDI::Event& off_event() const { return _off_event; } - -private: - // Event buffers are self-contained - MIDI::Event _on_event; - MIDI::Event _off_event; -}; - - -} // namespace ARDOUR - -#endif /* __ardour_note_h__ */ diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 2559eed003..1b85495d7a 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -101,14 +101,16 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful float effective_y; float effective_z; - bool _muted; + bool _muted; struct PanControllable : public AutomationControl { PanControllable (Session& s, std::string name, StreamPanner& p, Parameter param) - : AutomationControl (s, boost::shared_ptr(new AutomationList( - param, 0.0, 1.0, 0.5)), name) - , panner (p) { assert(param.type() != NullAutomation); } + : AutomationControl (s, + boost::shared_ptr(new AutomationList(param)), name) + , panner (p) + { assert(param.type() != NullAutomation); } + AutomationList* alist() { return (AutomationList*)_list.get(); } StreamPanner& panner; void set_value (float); diff --git a/libs/ardour/ardour/parameter.h b/libs/ardour/ardour/parameter.h index a5dd9cdca9..dbcccd811f 100644 --- a/libs/ardour/ardour/parameter.h +++ b/libs/ardour/ardour/parameter.h @@ -24,140 +24,86 @@ #include #include #include +#include +#include namespace ARDOUR { - /** ID of an automatable parameter. * * A given automatable object has a number of automatable parameters. This is * the unique ID for those parameters. Anything automatable (AutomationList, - * Curve) must have an ID unique with respect to it's Automatable parent. + * Curve) must have unique Parameter ID with respect to it's Automatable parent. * - * A parameter ID has two parts, a type and an int (only used by some types). + * These are fast to compare, but passing a (const) reference around is + * probably more efficient than copying because the Parameter contains + * metadata not used for comparison. * - * This is a bit more ugly than it could be, due to using the existing/legacy - * ARDOUR::AutomationType: GainAutomation, PanAutomation, SoloAutomation, - * and MuteAutomation use only the type(), but PluginAutomation and - * MidiCCAutomation use the id() as port number and CC number, respectively. - * - * Future types may use a string or URI or whatever, as long as these are - * comparable anything may be added. ints are best as these should be fast to - * copy and compare with one another. + * See evoral/Parameter.hpp for precise definition. */ -class Parameter +class Parameter : public Evoral::Parameter { public: Parameter(AutomationType type = NullAutomation, uint32_t id=0, uint8_t channel=0) - : _type(type), _id(id), _channel(channel) + : Evoral::Parameter((uint32_t)type, id, channel) + { + init(type); + } + + Parameter(AutomationType type, double min, double max, double normal) + : Evoral::Parameter((uint32_t)type, 0, 0, min, max, normal) {} + Parameter(const Evoral::Parameter& copy) + : Evoral::Parameter(copy) + { + init((AutomationType)_type); + } + + void init(AutomationType type) { + _normal = 0.0f; + switch(type) { + case NullAutomation: + case GainAutomation: + _min = 0.0f; + _max = 2.0f; + _normal = 1.0f; + break; + case PanAutomation: + _min = 0.0f; + _max = 1.0f; + _normal = 0.5f; + case PluginAutomation: + case SoloAutomation: + case MuteAutomation: + case FadeInAutomation: + case FadeOutAutomation: + case EnvelopeAutomation: + _min = 0.0f; + _max = 2.0f; + _normal = 1.0f; + case MidiCCAutomation: + Evoral::MIDI::ContinuousController::set_range(*this); break; + case MidiPgmChangeAutomation: + Evoral::MIDI::ProgramChange::set_range(*this); break; + case MidiPitchBenderAutomation: + Evoral::MIDI::PitchBender::set_range(*this); break; + case MidiChannelAftertouchAutomation: + Evoral::MIDI::ChannelAftertouch::set_range(*this); break; + } + } + Parameter(const std::string& str); - inline AutomationType type() const { return _type; } - inline uint32_t id() const { return _id; } - inline uint8_t channel() const { return _channel; } + inline AutomationType type() const { return (AutomationType)_type; } - /** - * Equivalence operator - * It is obvious from the definition that this operator - * is transitive, as required by stict weak ordering - * (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html) - */ - inline bool operator==(const Parameter& id) const { - return (_type == id._type && _id == id._id && _channel == id._channel); - } - - /** Strict weak ordering - * (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html) - * This is necessary so that std::set works): - * Sort Parameters first according to type then to id and lastly to channel. - * - * Proof: - *

    - *
  1. Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.
  2. - * - *
  3. Antisymmetry: given x != y, f(x, y) implies !f(y, x) because of the same - * property of \c < in each branch and the symmetry of operator==.
  4. - * - *
  5. Transitivity: let f(x, y) and f(y, z) be true. We prove by assuming the contrary, - * that f(x, z) does not hold. - * That would imply exactly one of the following: - *
      - *
    1. x == z which contradicts the assumption f(x, y) and f(y, x) - * because of antisymmetry. - *
    2. - *
    3. f(z, x) is true. That would imply that one of the ivars (we call it i) - * of x is greater than the same ivar in z while all "previous" ivars - * are equal. That would imply that also in y all those "previous" - * ivars are equal and because if x.i > z.i it is impossible - * that there is an y that satisfies x.i < y.i < z.i at the same - * time which contradicts the assumption. - *
    4. - *
    - *
  6. - *
- */ - inline bool operator<(const Parameter& id) const { -#ifndef NDEBUG - if (_type == NullAutomation) - PBD::warning << "Uninitialized Parameter compared." << endmsg; -#endif - if (_type < id._type) { - return true; - } else if (_type == id._type && _id < id._id) { - return true; - } else if (_id == id._id && _channel < id._channel) { - return true; - } - - return false; - } - - inline operator bool() const { return (_type != 0); } - - std::string to_string() const; - - /* The below properties are only used for CC right now, but unchanging properties - * of parameters (rather than changing parameters of automation lists themselves) - * should be moved here */ - - inline double min() const { - switch(_type) { - case MidiCCAutomation: - case MidiPgmChangeAutomation: - case MidiPitchBenderAutomation: - case MidiChannelAftertouchAutomation: - return 0.0; - - default: - return DBL_MIN; - } - } - - inline double max() const { - switch(_type) { - case MidiCCAutomation: - case MidiPgmChangeAutomation: - case MidiChannelAftertouchAutomation: - return 127.0; - case MidiPitchBenderAutomation: - return 16383.0; - - default: - return DBL_MAX; - } - } + std::string symbol() const; inline bool is_integer() const { return (_type >= MidiCCAutomation && _type <= MidiChannelAftertouchAutomation); } -private: - // default copy constructor is ok - AutomationType _type; - uint32_t _id; - uint8_t _channel; + inline operator Parameter() { return (Parameter)*this; } }; diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index c19d113256..8db9fb14fe 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -77,7 +77,7 @@ class PluginInsert : public Processor void set_parameter (Parameter param, float val); float get_parameter (Parameter param); - float default_parameter_value (Parameter param); + float default_parameter_value (Evoral::Parameter param); struct PluginControl : public AutomationControl { diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 88bf1e5d13..3217c8e3e8 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -26,6 +26,8 @@ #include +namespace Evoral { class Event; } + namespace ARDOUR { class MidiRingBuffer; @@ -71,7 +73,7 @@ class SMFSource : public MidiSource { void set_allow_remove_if_empty (bool yn); void mark_for_remove(); - void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev); + void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev); int flush_header (); int flush_footer (); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index cd05e5fc86..532abeb123 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -604,9 +604,9 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */ if (!diskstream->record_enabled() && _session.transport_rolling()) { - Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK); + Glib::Mutex::Lock am (_control_lock, Glib::TRY_LOCK); - if (am.locked() && gain_control()->list()->automation_playback()) { + if (am.locked() && gain_control()->automation_playback()) { apply_gain_automation = gain_control()->list()->curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); } } @@ -702,7 +702,7 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes } } - if (gain_control()->list()->automation_state() == Play) { + if (gain_control()->automation_state() == Play) { gain_control()->list()->curve().get_vector (start, start + nframes, gain_automation, nframes); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 271544297d..707f10e91a 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -77,9 +77,9 @@ AudioRegion::init () /* constructor for use by derived types only */ AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name) : Region (s, start, length, name, DataType::AUDIO) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { init (); } @@ -87,9 +87,9 @@ AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string /** Basic AudioRegion constructor (one channel) */ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, nframes_t length) : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0, Region::Flag(Region::DefaultFlags|Region::External)) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { boost::shared_ptr afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -102,9 +102,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n /* Basic AudioRegion constructor (one channel) */ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (src, start, length, name, DataType::AUDIO, layer, flags) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { boost::shared_ptr afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -117,9 +117,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n /* Basic AudioRegion constructor (many channels) */ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (srcs, start, length, name, DataType::AUDIO, layer, flags) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { init (); listen_to_my_sources (); @@ -128,9 +128,9 @@ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t len /** Create a new AudioRegion, that is part of an existing one */ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (other, offset, length, name, layer, flags) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { set > unique_srcs; @@ -180,9 +180,9 @@ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t AudioRegion::AudioRegion (boost::shared_ptr other) : Region (other) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { assert(_type == DataType::AUDIO); _scale_amplitude = other->_scale_amplitude; @@ -196,9 +196,9 @@ AudioRegion::AudioRegion (boost::shared_ptr other) AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& node) : Region (src, node) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { boost::shared_ptr afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -217,9 +217,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& nod AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) : Region (srcs, node) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { init (); diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index cacebe59a4..349c09e136 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -92,7 +92,7 @@ Automatable::load_automation (const string& path) return 1; } - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); set tosave; _controls.clear (); @@ -108,7 +108,7 @@ Automatable::load_automation (const string& path) in >> value; if (!in) goto bad; /* FIXME: this is legacy and only used for plugin inserts? I think? */ - boost::shared_ptr c = control (Parameter(PluginAutomation, port), true); + boost::shared_ptr c = control (Parameter(PluginAutomation, port), true); c->list()->add (when, value); tosave.insert (Parameter(PluginAutomation, port)); } @@ -122,12 +122,10 @@ Automatable::load_automation (const string& path) } void -Automatable::add_control(boost::shared_ptr ac) +Automatable::add_control(boost::shared_ptr ac) { Parameter param = ac->parameter(); - _controls[param] = ac; - _can_automate_list.insert(param); // Sync everything (derived classes) up to initial values @@ -135,21 +133,9 @@ Automatable::add_control(boost::shared_ptr ac) } void -Automatable::what_has_automation (set& s) const +Automatable::what_has_visible_data (set& s) const { - Glib::Mutex::Lock lm (_automation_lock); - Controls::const_iterator li; - - // FIXME: correct semantics? - for (li = _controls.begin(); li != _controls.end(); ++li) { - s.insert ((*li).first); - } -} - -void -Automatable::what_has_visible_automation (set& s) const -{ - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); set::const_iterator li; for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) { @@ -157,42 +143,6 @@ Automatable::what_has_visible_automation (set& s) const } } -/** Returns NULL if we don't have an AutomationList for \a parameter. - */ -boost::shared_ptr -Automatable::control (Parameter parameter, bool create_if_missing) -{ - Controls::iterator i = _controls.find(parameter); - - if (i != _controls.end()) { - return i->second; - - } else if (create_if_missing) { - boost::shared_ptr al (new AutomationList ( - parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter))); - boost::shared_ptr ac(control_factory(al)); - add_control(ac); - return ac; - - } else { - //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg; - return boost::shared_ptr(); - } -} - -boost::shared_ptr -Automatable::control (Parameter parameter) const -{ - Controls::const_iterator i = _controls.find(parameter); - - if (i != _controls.end()) { - return i->second; - } else { - //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg; - return boost::shared_ptr(); - } -} - string Automatable::describe_parameter (Parameter param) { @@ -213,7 +163,7 @@ Automatable::describe_parameter (Parameter param) } else if (param.type() == MidiChannelAftertouchAutomation) { return string_compose("Aftertouch [%1]", int(param.channel()) + 1); } else { - return param.to_string(); + return param.symbol(); } } @@ -237,37 +187,6 @@ Automatable::mark_automation_visible (Parameter what, bool yn) } } -bool -Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const -{ - Controls::const_iterator li; - - next_event.when = max_frames; - - for (li = _controls.begin(); li != _controls.end(); ++li) { - - AutomationList::const_iterator i; - boost::shared_ptr alist (li->second->list()); - ControlEvent cp (now, 0.0f); - - for (i = lower_bound (alist->const_begin(), alist->const_end(), &cp, AutomationList::time_comparator); - i != alist->const_end() && (*i)->when < end; ++i) { - if ((*i)->when > now) { - break; - } - } - - if (i != alist->const_end() && (*i)->when < end) { - - if ((*i)->when < next_event.when) { - next_event.when = (*i)->when; - } - } - } - - return next_event.when != max_frames; -} - /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner) * had a single automation parameter, with it's type implicit. Derived objects should * pass that type and it will be used for the untyped AutomationList found. @@ -275,7 +194,7 @@ Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_e int Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); /* Don't clear controls, since some may be special derived Controllable classes */ @@ -301,11 +220,11 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param) if (!id_prop) { warning << "AutomationList node without automation-id property, " - << "using default: " << legacy_param.to_string() << endmsg; + << "using default: " << legacy_param.symbol() << endmsg; al->set_parameter(legacy_param); } - boost::shared_ptr existing = control(param); + boost::shared_ptr existing = control(param); if (existing) existing->set_list(al); else @@ -324,7 +243,7 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param) XMLNode& Automatable::get_automation_state () { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); XMLNode* node = new XMLNode (X_("Automation")); if (_controls.empty()) { @@ -332,30 +251,24 @@ Automatable::get_automation_state () } for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) { - node->add_child_nocopy (li->second->list()->get_state ()); + boost::shared_ptr l + = boost::dynamic_pointer_cast(li->second->list()); + node->add_child_nocopy (l->get_state ()); } return *node; } -void -Automatable::clear_automation () -{ - Glib::Mutex::Lock lm (_automation_lock); - - for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) - li->second->list()->clear(); -} - void Automatable::set_parameter_automation_state (Parameter param, AutoState s) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); - boost::shared_ptr c = control (param, true); + boost::shared_ptr c = control (param, true); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); - if (s != c->list()->automation_state()) { - c->list()->set_automation_state (s); + if (s != l->automation_state()) { + l->set_automation_state (s); _session.set_dirty (); } } @@ -366,15 +279,16 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock) AutoState result = Off; if (lock) - _automation_lock.lock(); + _control_lock.lock(); - boost::shared_ptr c = control(param); + boost::shared_ptr c = control(param); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); if (c) - result = c->list()->automation_state(); + result = l->automation_state(); if (lock) - _automation_lock.unlock(); + _control_lock.unlock(); return result; } @@ -382,12 +296,13 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock) void Automatable::set_parameter_automation_style (Parameter param, AutoStyle s) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); - boost::shared_ptr c = control(param, true); + boost::shared_ptr c = control(param, true); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); - if (s != c->list()->automation_style()) { - c->list()->set_automation_style (s); + if (s != l->automation_style()) { + l->set_automation_style (s); _session.set_dirty (); } } @@ -395,12 +310,13 @@ Automatable::set_parameter_automation_style (Parameter param, AutoStyle s) AutoStyle Automatable::get_parameter_automation_style (Parameter param) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); - boost::shared_ptr c = control(param); + boost::shared_ptr c = control(param); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); if (c) { - return c->list()->automation_style(); + return l->automation_style(); } else { return Absolute; // whatever } @@ -409,20 +325,22 @@ Automatable::get_parameter_automation_style (Parameter param) void Automatable::protect_automation () { - set automated_params; + typedef set ParameterSet; + ParameterSet automated_params; - what_has_automation (automated_params); + what_has_data (automated_params); - for (set::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { + for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { - boost::shared_ptr c = control(*i); + boost::shared_ptr c = control(*i); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); - switch (c->list()->automation_state()) { + switch (l->automation_state()) { case Write: - c->list()->set_automation_state (Off); + l->set_automation_state (Off); break; case Touch: - c->list()->set_automation_state (Play); + l->set_automation_state (Play); break; default: break; @@ -436,8 +354,10 @@ Automatable::automation_snapshot (nframes_t now, bool force) if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) { - if (i->second->list()->automation_write()) { - i->second->list()->rt_add (now, i->second->user_value()); + boost::shared_ptr c + = boost::dynamic_pointer_cast(i->second); + if (c->automation_write()) { + c->list()->rt_add (now, i->second->user_value()); } } @@ -450,29 +370,34 @@ Automatable::transport_stopped (nframes_t now) { for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) { - boost::shared_ptr c = li->second; + boost::shared_ptr c + = boost::dynamic_pointer_cast(li->second); + boost::shared_ptr l + = boost::dynamic_pointer_cast(c->list()); c->list()->reposition_for_rt_add (now); - if (c->list()->automation_state() != Off) { + if (c->automation_state() != Off) { c->set_value(c->list()->eval(now)); } } } -/* FIXME: this probably doesn't belong here */ -boost::shared_ptr -Automatable::control_factory(boost::shared_ptr list) +boost::shared_ptr +Automatable::control_factory(boost::shared_ptr list) const { - if ( - list->parameter().type() == MidiCCAutomation || - list->parameter().type() == MidiPgmChangeAutomation || - list->parameter().type() == MidiChannelAftertouchAutomation - ) { - // FIXME: this will die horribly if this is not a MidiTrack - return boost::shared_ptr(new MidiTrack::MidiControl((MidiTrack*)this, list)); + boost::shared_ptr l = boost::dynamic_pointer_cast(list); + assert(l); + if (l->parameter().type() >= MidiCCAutomation + && l->parameter().type() <= MidiChannelAftertouchAutomation) { + return boost::shared_ptr(new MidiTrack::MidiControl((MidiTrack*)this, l)); } else { - return boost::shared_ptr(new AutomationControl(_session, list)); + return boost::shared_ptr(new AutomationControl(_session, l)); } } +boost::shared_ptr +Automatable::control_list_factory(const Evoral::Parameter& param) const +{ + return boost::shared_ptr(new AutomationList(param)); +} diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index 4885a6fed9..a16306838a 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -30,10 +30,9 @@ using namespace PBD; AutomationControl::AutomationControl(Session& session, boost::shared_ptr list, string name) - : Controllable((name == "unnamed controllable") ? list->parameter().to_string() : name) + : Controllable((name == "unnamed controllable") ? list->parameter().symbol() : name) + , Evoral::Control(list) , _session(session) - , _list(list) - , _user_value(list->default_value()) { } @@ -43,49 +42,27 @@ AutomationControl::AutomationControl(Session& session, boost::shared_ptrautomation_playback()) - return _list->eval(_session.transport_frame()); - else - return _user_value; + bool from_list = ((AutomationList*)_list.get())->automation_playback(); + return Control::get_value(from_list, _session.transport_frame()); } void AutomationControl::set_value(float value) { - _user_value = value; + bool to_list = _session.transport_stopped() + && ((AutomationList*)_list.get())->automation_playback(); - if (_session.transport_stopped() && _list->automation_write()) - _list->add(_session.transport_frame(), value); + Control::set_value(value, to_list, _session.transport_frame()); Changed(); /* EMIT SIGNAL */ } -/** Get the latest user-set value, which may not equal get_value() when automation - * is playing back, etc. - * - * Automation write/touch works by periodically sampling this value and adding it - * to the AutomationList. - */ -float -AutomationControl::user_value() const -{ - return _user_value; -} - - void -AutomationControl::set_list(boost::shared_ptr list) +AutomationControl::set_list(boost::shared_ptr list) { - _list = list; - _user_value = list->default_value(); + Control::set_list(list); Changed(); /* EMIT SIGNAL */ } - -Parameter -AutomationControl::parameter() const -{ - return _list->parameter(); -} diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index af390953f4..2d98f1c27c 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -39,11 +39,6 @@ using namespace PBD; sigc::signal AutomationList::AutomationListCreated; -static bool sort_events_by_time (ControlEvent* a, ControlEvent* b) -{ - return a->when < b->when; -} - #if 0 static void dumpit (const AutomationList& al, string prefix = "") { @@ -56,92 +51,33 @@ static void dumpit (const AutomationList& al, string prefix = "") #endif /* XXX: min_val max_val redundant? (param.min() param.max()) */ -AutomationList::AutomationList (Parameter id, double min_val, double max_val, double default_val) - : _parameter(id) - , _interpolation(Linear) - , _curve(new Curve(*this)) -{ - _parameter = id; - _frozen = 0; - _changed_when_thawed = false; +AutomationList::AutomationList (Parameter id) + : ControlList(id) +{ _state = Off; _style = Absolute; - _min_yval = min_val; - _max_yval = max_val; _touching = false; - _max_xval = 0; // means "no limit" - _default_value = default_val; - _rt_insertion_point = _events.end(); - _lookup_cache.left = -1; - _lookup_cache.range.first = _events.end(); - _search_cache.left = -1; - _search_cache.range.first = _events.end(); - _sort_pending = false; assert(_parameter.type() != NullAutomation); AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other) - : _parameter(other._parameter) - , _interpolation(Linear) - , _curve(new Curve(*this)) + : ControlList(other) { - _frozen = 0; - _changed_when_thawed = false; - _style = other._style; - _min_yval = other._min_yval; - _max_yval = other._max_yval; - _max_xval = other._max_xval; - _default_value = other._default_value; _state = other._state; _touching = other._touching; - _rt_insertion_point = _events.end(); - _lookup_cache.range.first = _events.end(); - _search_cache.range.first = _events.end(); - _sort_pending = false; - - for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) { - _events.push_back (new ControlEvent (**i)); - } - - mark_dirty (); + assert(_parameter.type() != NullAutomation); AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other, double start, double end) - : _parameter(other._parameter) - , _interpolation(Linear) - , _curve(new Curve(*this)) + : ControlList(other) { - _frozen = 0; - _changed_when_thawed = false; _style = other._style; - _min_yval = other._min_yval; - _max_yval = other._max_yval; - _max_xval = other._max_xval; - _default_value = other._default_value; _state = other._state; _touching = other._touching; - _rt_insertion_point = _events.end(); - _lookup_cache.range.first = _events.end(); - _search_cache.range.first = _events.end(); - _sort_pending = false; - - /* now grab the relevant points, and shift them back if necessary */ - - AutomationList* section = const_cast(&other)->copy (start, end); - - if (!section->empty()) { - for (iterator i = section->begin(); i != section->end(); ++i) { - _events.push_back (new ControlEvent ((*i)->when, (*i)->value)); - } - } - - delete section; - - mark_dirty (); assert(_parameter.type() != NullAutomation); AutomationListCreated(this); @@ -151,21 +87,11 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl * in or below the node. It is used if \a id is non-null. */ AutomationList::AutomationList (const XMLNode& node, Parameter id) - : _interpolation(Linear) - , _curve(new Curve(*this)) + : ControlList(id) { - _frozen = 0; - _changed_when_thawed = false; _touching = false; - _min_yval = FLT_MIN; - _max_yval = FLT_MAX; - _max_xval = 0; // means "no limit" _state = Off; _style = Absolute; - _rt_insertion_point = _events.end(); - _lookup_cache.range.first = _events.end(); - _search_cache.range.first = _events.end(); - _sort_pending = false; set_state (node); @@ -179,10 +105,12 @@ AutomationList::AutomationList (const XMLNode& node, Parameter id) AutomationList::~AutomationList() { GoingAway (); - - for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) { - delete (*x); - } +} + +boost::shared_ptr +AutomationList::create(Evoral::Parameter id) +{ + return boost::shared_ptr(new AutomationList(id)); } bool @@ -217,12 +145,10 @@ AutomationList::operator= (const AutomationList& other) void AutomationList::maybe_signal_changed () { - mark_dirty (); + ControlList::maybe_signal_changed (); - if (_frozen) { - _changed_when_thawed = true; - } else { - StateChanged (); + if (!_frozen) { + StateChanged (); /* EMIT SIGNAL */ } } @@ -248,390 +174,14 @@ void AutomationList::start_touch () { _touching = true; - _new_touch = true; + _new_value = true; } void AutomationList::stop_touch () { _touching = false; - _new_touch = false; -} - -void -AutomationList::clear () -{ - { - Glib::Mutex::Lock lm (_lock); - _events.clear (); - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::x_scale (double factor) -{ - Glib::Mutex::Lock lm (_lock); - _x_scale (factor); -} - -bool -AutomationList::extend_to (double when) -{ - Glib::Mutex::Lock lm (_lock); - if (_events.empty() || _events.back()->when == when) { - return false; - } - double factor = when / _events.back()->when; - _x_scale (factor); - return true; -} - -void AutomationList::_x_scale (double factor) -{ - for (iterator i = _events.begin(); i != _events.end(); ++i) { - (*i)->when = floor ((*i)->when * factor); - } - - mark_dirty (); -} - -void -AutomationList::reposition_for_rt_add (double when) -{ - _rt_insertion_point = _events.end(); -} - -void -AutomationList::rt_add (double when, double value) -{ - /* this is for automation recording */ - - if ((_state & Touch) && !_touching) { - return; - } - - // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl; - - { - Glib::Mutex::Lock lm (_lock); - - iterator where; - ControlEvent cp (when, 0.0); - bool done = false; - - if ((_rt_insertion_point != _events.end()) && ((*_rt_insertion_point)->when < when) ) { - - /* we have a previous insertion point, so we should delete - everything between it and the position where we are going - to insert this point. - */ - - iterator after = _rt_insertion_point; - - if (++after != _events.end()) { - iterator far = after; - - while (far != _events.end()) { - if ((*far)->when > when) { - break; - } - ++far; - } - - if (_new_touch) { - where = far; - _rt_insertion_point = where; - - if ((*where)->when == when) { - (*where)->value = value; - done = true; - } - } else { - where = _events.erase (after, far); - } - - } else { - - where = after; - - } - - iterator previous = _rt_insertion_point; - --previous; - - if (_rt_insertion_point != _events.begin() && (*_rt_insertion_point)->value == value && (*previous)->value == value) { - (*_rt_insertion_point)->when = when; - done = true; - - } - - } else { - - where = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); - - if (where != _events.end()) { - if ((*where)->when == when) { - (*where)->value = value; - done = true; - } - } - } - - if (!done) { - _rt_insertion_point = _events.insert (where, new ControlEvent (when, value)); - } - - _new_touch = false; - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::fast_simple_add (double when, double value) -{ - /* to be used only for loading pre-sorted data from saved state */ - _events.insert (_events.end(), new ControlEvent (when, value)); - assert(_events.back()); -} - -void -AutomationList::add (double when, double value) -{ - /* this is for graphical editing */ - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (when, 0.0f); - bool insert = true; - iterator insertion_point; - - for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); insertion_point != _events.end(); ++insertion_point) { - - /* only one point allowed per time point */ - - if ((*insertion_point)->when == when) { - (*insertion_point)->value = value; - insert = false; - break; - } - - if ((*insertion_point)->when >= when) { - break; - } - } - - if (insert) { - - _events.insert (insertion_point, new ControlEvent (when, value)); - reposition_for_rt_add (0); - - } - - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::erase (iterator i) -{ - { - Glib::Mutex::Lock lm (_lock); - _events.erase (i); - reposition_for_rt_add (0); - mark_dirty (); - } - maybe_signal_changed (); -} - -void -AutomationList::erase (iterator start, iterator end) -{ - { - Glib::Mutex::Lock lm (_lock); - _events.erase (start, end); - reposition_for_rt_add (0); - mark_dirty (); - } - maybe_signal_changed (); -} - -void -AutomationList::reset_range (double start, double endt) -{ - bool reset = false; - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (start, 0.0f); - iterator s; - iterator e; - - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) { - - cp.when = endt; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - - for (iterator i = s; i != e; ++i) { - (*i)->value = _default_value; - } - - reset = true; - - mark_dirty (); - } - } - - if (reset) { - maybe_signal_changed (); - } -} - -void -AutomationList::erase_range (double start, double endt) -{ - bool erased = false; - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (start, 0.0f); - iterator s; - iterator e; - - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) { - cp.when = endt; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - _events.erase (s, e); - reposition_for_rt_add (0); - erased = true; - mark_dirty (); - } - - } - - if (erased) { - maybe_signal_changed (); - } -} - -void -AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta) -{ - /* note: we assume higher level logic is in place to avoid this - reordering the time-order of control events in the list. ie. all - points after end are later than (end)->when. - */ - - { - Glib::Mutex::Lock lm (_lock); - - while (start != end) { - (*start)->when += xdelta; - (*start)->value += ydelta; - if (isnan ((*start)->value)) { - abort (); - } - ++start; - } - - if (!_frozen) { - _events.sort (sort_events_by_time); - } else { - _sort_pending = true; - } - - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::slide (iterator before, double distance) -{ - { - Glib::Mutex::Lock lm (_lock); - - if (before == _events.end()) { - return; - } - - while (before != _events.end()) { - (*before)->when += distance; - ++before; - } - } - - maybe_signal_changed (); -} - -void -AutomationList::modify (iterator iter, double when, double val) -{ - /* note: we assume higher level logic is in place to avoid this - reordering the time-order of control events in the list. ie. all - points after *iter are later than when. - */ - - { - Glib::Mutex::Lock lm (_lock); - - (*iter)->when = when; - (*iter)->value = val; - - if (isnan (val)) { - abort (); - } - - if (!_frozen) { - _events.sort (sort_events_by_time); - } else { - _sort_pending = true; - } - - mark_dirty (); - } - - maybe_signal_changed (); -} - -std::pair -AutomationList::control_points_adjacent (double xval) -{ - Glib::Mutex::Lock lm (_lock); - iterator i; - ControlEvent cp (xval, 0.0f); - std::pair ret; - - ret.first = _events.end(); - ret.second = _events.end(); - - for (i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); i != _events.end(); ++i) { - - if (ret.first == _events.end()) { - if ((*i)->when >= xval) { - if (i != _events.begin()) { - ret.first = i; - --ret.first; - } else { - return ret; - } - } - } - - if ((*i)->when > xval) { - ret.second = i; - break; - } - } - - return ret; + _new_value = false; } void @@ -643,790 +193,20 @@ AutomationList::freeze () void AutomationList::thaw () { - if (_frozen == 0) { - PBD::stacktrace (cerr); - fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg; - /*NOTREACHED*/ - } - - if (--_frozen > 0) { - return; - } - - { - Glib::Mutex::Lock lm (_lock); - - if (_sort_pending) { - _events.sort (sort_events_by_time); - _sort_pending = false; - } - } + ControlList::thaw(); if (_changed_when_thawed) { StateChanged(); /* EMIT SIGNAL */ } } -void -AutomationList::set_max_xval (double x) -{ - _max_xval = x; -} - void -AutomationList::mark_dirty () +AutomationList::mark_dirty () const { - _lookup_cache.left = -1; - _search_cache.left = -1; + ControlList::mark_dirty (); Dirty (); /* EMIT SIGNAL */ } -void -AutomationList::truncate_end (double last_coordinate) -{ - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (last_coordinate, 0); - AutomationList::reverse_iterator i; - double last_val; - - if (_events.empty()) { - return; - } - - if (last_coordinate == _events.back()->when) { - return; - } - - if (last_coordinate > _events.back()->when) { - - /* extending end: - */ - - iterator foo = _events.begin(); - bool lessthantwo; - - if (foo == _events.end()) { - lessthantwo = true; - } else if (++foo == _events.end()) { - lessthantwo = true; - } else { - lessthantwo = false; - } - - if (lessthantwo) { - /* less than 2 points: add a new point */ - _events.push_back (new ControlEvent (last_coordinate, _events.back()->value)); - } else { - - /* more than 2 points: check to see if the last 2 values - are equal. if so, just move the position of the - last point. otherwise, add a new point. - */ - - iterator penultimate = _events.end(); - --penultimate; /* points at last point */ - --penultimate; /* points at the penultimate point */ - - if (_events.back()->value == (*penultimate)->value) { - _events.back()->when = last_coordinate; - } else { - _events.push_back (new ControlEvent (last_coordinate, _events.back()->value)); - } - } - - } else { - - /* shortening end */ - - last_val = unlocked_eval (last_coordinate); - last_val = max ((double) _min_yval, last_val); - last_val = min ((double) _max_yval, last_val); - - i = _events.rbegin(); - - /* make i point to the last control point */ - - ++i; - - /* now go backwards, removing control points that are - beyond the new last coordinate. - */ - - uint32_t sz = _events.size(); - - while (i != _events.rend() && sz > 2) { - AutomationList::reverse_iterator tmp; - - tmp = i; - ++tmp; - - if ((*i)->when < last_coordinate) { - break; - } - - _events.erase (i.base()); - --sz; - - i = tmp; - } - - _events.back()->when = last_coordinate; - _events.back()->value = last_val; - } - - reposition_for_rt_add (0); - mark_dirty(); - } - - maybe_signal_changed (); -} - -void -AutomationList::truncate_start (double overall_length) -{ - { - Glib::Mutex::Lock lm (_lock); - iterator i; - double first_legal_value; - double first_legal_coordinate; - - if (_events.empty()) { - fatal << _("programming error:") - << "AutomationList::truncate_start() called on an empty list" - << endmsg; - /*NOTREACHED*/ - return; - } - - if (overall_length == _events.back()->when) { - /* no change in overall length */ - return; - } - - if (overall_length > _events.back()->when) { - - /* growing at front: duplicate first point. shift all others */ - - double shift = overall_length - _events.back()->when; - uint32_t np; - - for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) { - (*i)->when += shift; - } - - if (np < 2) { - - /* less than 2 points: add a new point */ - _events.push_front (new ControlEvent (0, _events.front()->value)); - - } else { - - /* more than 2 points: check to see if the first 2 values - are equal. if so, just move the position of the - first point. otherwise, add a new point. - */ - - iterator second = _events.begin(); - ++second; /* points at the second point */ - - if (_events.front()->value == (*second)->value) { - /* first segment is flat, just move start point back to zero */ - _events.front()->when = 0; - } else { - /* leave non-flat segment in place, add a new leading point. */ - _events.push_front (new ControlEvent (0, _events.front()->value)); - } - } - - } else { - - /* shrinking at front */ - - first_legal_coordinate = _events.back()->when - overall_length; - first_legal_value = unlocked_eval (first_legal_coordinate); - first_legal_value = max (_min_yval, first_legal_value); - first_legal_value = min (_max_yval, first_legal_value); - - /* remove all events earlier than the new "front" */ - - i = _events.begin(); - - while (i != _events.end() && !_events.empty()) { - AutomationList::iterator tmp; - - tmp = i; - ++tmp; - - if ((*i)->when > first_legal_coordinate) { - break; - } - - _events.erase (i); - - i = tmp; - } - - - /* shift all remaining points left to keep their same - relative position - */ - - for (i = _events.begin(); i != _events.end(); ++i) { - (*i)->when -= first_legal_coordinate; - } - - /* add a new point for the interpolated new value */ - - _events.push_front (new ControlEvent (0, first_legal_value)); - } - - reposition_for_rt_add (0); - - mark_dirty(); - } - - maybe_signal_changed (); -} - -double -AutomationList::unlocked_eval (double x) const -{ - pair range; - int32_t npoints; - double lpos, upos; - double lval, uval; - double fraction; - - npoints = _events.size(); - - switch (npoints) { - case 0: - return _default_value; - - case 1: - if (x >= _events.front()->when) { - return _events.front()->value; - } else { - // return _default_value; - return _events.front()->value; - } - - case 2: - if (x >= _events.back()->when) { - return _events.back()->value; - } else if (x == _events.front()->when) { - return _events.front()->value; - } else if (x < _events.front()->when) { - // return _default_value; - return _events.front()->value; - } - - lpos = _events.front()->when; - lval = _events.front()->value; - upos = _events.back()->when; - uval = _events.back()->value; - - if (_interpolation == Discrete) - return lval; - - /* linear interpolation betweeen the two points - */ - - fraction = (double) (x - lpos) / (double) (upos - lpos); - return lval + (fraction * (uval - lval)); - - default: - - if (x >= _events.back()->when) { - return _events.back()->value; - } else if (x == _events.front()->when) { - return _events.front()->value; - } else if (x < _events.front()->when) { - // return _default_value; - return _events.front()->value; - } - - return multipoint_eval (x); - break; - } - - /*NOTREACHED*/ /* stupid gcc */ - return 0.0; -} - -double -AutomationList::multipoint_eval (double x) const -{ - double upos, lpos; - double uval, lval; - double fraction; - - /* "Stepped" lookup (no interpolation) */ - /* FIXME: no cache. significant? */ - if (_interpolation == Discrete) { - const ControlEvent cp (x, 0); - EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); - - // shouldn't have made it to multipoint_eval - assert(i != _events.end()); - - if (i == _events.begin() || (*i)->when == x) - return (*i)->value; - else - return (*(--i))->value; - } - - /* Only do the range lookup if x is in a different range than last time - * this was called (or if the lookup cache has been marked "dirty" (left<0) */ - if ((_lookup_cache.left < 0) || - ((_lookup_cache.left > x) || - (_lookup_cache.range.first == _events.end()) || - ((*_lookup_cache.range.second)->when < x))) { - - const ControlEvent cp (x, 0); - - _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, time_comparator); - } - - pair range = _lookup_cache.range; - - if (range.first == range.second) { - - /* x does not exist within the list as a control point */ - - _lookup_cache.left = x; - - if (range.first != _events.begin()) { - --range.first; - lpos = (*range.first)->when; - lval = (*range.first)->value; - } else { - /* we're before the first point */ - // return _default_value; - return _events.front()->value; - } - - if (range.second == _events.end()) { - /* we're after the last point */ - return _events.back()->value; - } - - upos = (*range.second)->when; - uval = (*range.second)->value; - - /* linear interpolation betweeen the two points - on either side of x - */ - - fraction = (double) (x - lpos) / (double) (upos - lpos); - return lval + (fraction * (uval - lval)); - - } - - /* x is a control point in the data */ - _lookup_cache.left = -1; - return (*range.first)->value; -} - -void -AutomationList::build_search_cache_if_necessary(double start, double end) const -{ - /* Only do the range lookup if x is in a different range than last time - * this was called (or if the search cache has been marked "dirty" (left<0) */ - if (!_events.empty() && ((_search_cache.left < 0) || - ((_search_cache.left > start) || - (_search_cache.right < end)))) { - - const ControlEvent start_point (start, 0); - const ControlEvent end_point (end, 0); - - //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") := (" - // << start << ".." << end << ")" << endl; - - _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, time_comparator); - _search_cache.range.second = upper_bound (_events.begin(), _events.end(), &end_point, time_comparator); - - _search_cache.left = start; - _search_cache.right = end; - } -} - -/** Get the earliest event between \a start and \a end, using the current interpolation style. - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y, bool inclusive) const -{ - // FIXME: It would be nice if this was unnecessary.. - Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK); - if (!lm.locked()) { - return false; - } - - return rt_safe_earliest_event_unlocked(start, end, x, y, inclusive); -} - - -/** Get the earliest event between \a start and \a end, using the current interpolation style. - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const -{ - if (_interpolation == Discrete) - return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive); - else - return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive); -} - - -/** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation) - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const -{ - build_search_cache_if_necessary(start, end); - - const pair& range = _search_cache.range; - - if (range.first != _events.end()) { - const ControlEvent* const first = *range.first; - - const bool past_start = (inclusive ? first->when >= start : first->when > start); - - /* Earliest points is in range, return it */ - if (past_start >= start && first->when < end) { - - x = first->when; - y = first->value; - - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - ++_search_cache.range.first; - - assert(x >= start); - assert(x < end); - return true; - - } else { - return false; - } - - /* No points in range */ - } else { - return false; - } -} - -/** Get the earliest time the line crosses an integer (Linear interpolation). - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const -{ - //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl; - - if (_events.size() == 0) - return false; - else if (_events.size() == 1) - return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive); - - // Hack to avoid infinitely repeating the same event - build_search_cache_if_necessary(start, end); - - pair range = _search_cache.range; - - if (range.first != _events.end()) { - - const ControlEvent* first = NULL; - const ControlEvent* next = NULL; - - /* Step is after first */ - if (range.first == _events.begin() || (*range.first)->when == start) { - first = *range.first; - next = *(++range.first); - ++_search_cache.range.first; - - /* Step is before first */ - } else { - const_iterator prev = range.first; - --prev; - first = *prev; - next = *range.first; - } - - if (inclusive && first->when == start) { - x = first->when; - y = first->value; - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - //++_search_cache.range.first; - return true; - } - - if (abs(first->value - next->value) <= 1) { - if (next->when <= end && (!inclusive || next->when > start)) { - x = next->when; - y = next->value; - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - //++_search_cache.range.first; - return true; - } else { - return false; - } - } - - const double slope = (next->value - first->value) / (double)(next->when - first->when); - //cerr << "start y: " << start_y << endl; - - //y = first->value + (slope * fabs(start - first->when)); - y = first->value; - - if (first->value < next->value) // ramping up - y = ceil(y); - else // ramping down - y = floor(y); - - x = first->when + (y - first->value) / (double)slope; - - while ((inclusive && x < start) || (x <= start && y != next->value)) { - - if (first->value < next->value) // ramping up - y += 1.0; - else // ramping down - y -= 1.0; - - x = first->when + (y - first->value) / (double)slope; - } - - /*cerr << first->value << " @ " << first->when << " ... " - << next->value << " @ " << next->when - << " = " << y << " @ " << x << endl;*/ - - assert( (y >= first->value && y <= next->value) - || (y <= first->value && y >= next->value) ); - - - const bool past_start = (inclusive ? x >= start : x > start); - if (past_start && x < end) { - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - - return true; - - } else { - return false; - } - - /* No points in the future, so no steps (towards them) in the future */ - } else { - return false; - } -} - -AutomationList* -AutomationList::cut (iterator start, iterator end) -{ - AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value); - - { - Glib::Mutex::Lock lm (_lock); - - for (iterator x = start; x != end; ) { - iterator tmp; - - tmp = x; - ++tmp; - - nal->_events.push_back (new ControlEvent (**x)); - _events.erase (x); - - reposition_for_rt_add (0); - - x = tmp; - } - - mark_dirty (); - } - - maybe_signal_changed (); - - return nal; -} - -AutomationList* -AutomationList::cut_copy_clear (double start, double end, int op) -{ - AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value); - iterator s, e; - ControlEvent cp (start, 0.0); - bool changed = false; - - { - Glib::Mutex::Lock lm (_lock); - - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) == _events.end()) { - return nal; - } - - cp.when = end; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - - if (op != 2 && (*s)->when != start) { - nal->_events.push_back (new ControlEvent (0, unlocked_eval (start))); - } - - for (iterator x = s; x != e; ) { - iterator tmp; - - tmp = x; - ++tmp; - - changed = true; - - /* adjust new points to be relative to start, which - has been set to zero. - */ - - if (op != 2) { - nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value)); - } - - if (op != 1) { - _events.erase (x); - } - - x = tmp; - } - - if (op != 2 && nal->_events.back()->when != end - start) { - nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end))); - } - - if (changed) { - reposition_for_rt_add (0); - } - - mark_dirty (); - } - - maybe_signal_changed (); - - return nal; - -} - -AutomationList* -AutomationList::copy (iterator start, iterator end) -{ - AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value); - - { - Glib::Mutex::Lock lm (_lock); - - for (iterator x = start; x != end; ) { - iterator tmp; - - tmp = x; - ++tmp; - - nal->_events.push_back (new ControlEvent (**x)); - - x = tmp; - } - } - - return nal; -} - -AutomationList* -AutomationList::cut (double start, double end) -{ - return cut_copy_clear (start, end, 0); -} - -AutomationList* -AutomationList::copy (double start, double end) -{ - return cut_copy_clear (start, end, 1); -} - -void -AutomationList::clear (double start, double end) -{ - (void) cut_copy_clear (start, end, 2); -} - -bool -AutomationList::paste (AutomationList& alist, double pos, float times) -{ - if (alist._events.empty()) { - return false; - } - - { - Glib::Mutex::Lock lm (_lock); - iterator where; - iterator prev; - double end = 0; - ControlEvent cp (pos, 0.0); - - where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - - for (iterator i = alist.begin();i != alist.end(); ++i) { - _events.insert (where, new ControlEvent( (*i)->when+pos,( *i)->value)); - end = (*i)->when + pos; - } - - - /* move all points after the insertion along the timeline by - the correct amount. - */ - - while (where != _events.end()) { - iterator tmp; - if ((*where)->when <= end) { - tmp = where; - ++tmp; - _events.erase(where); - where = tmp; - - } else { - break; - } - } - - reposition_for_rt_add (0); - mark_dirty (); - } - - maybe_signal_changed (); - return true; -} - XMLNode& AutomationList::get_state () { @@ -1440,7 +220,7 @@ AutomationList::state (bool full) char buf[64]; LocaleGuard lg (X_("POSIX")); - root->add_property ("automation-id", _parameter.to_string()); + root->add_property ("automation-id", _parameter.symbol()); root->add_property ("id", _id.to_s()); diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 213e7504b0..b6ca322c73 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -78,8 +78,8 @@ Crossfade::Crossfade (boost::shared_ptr in, boost::shared_ptr