Factor out sequencing related things into an independant new library: "evoral".
authorDavid Robillard <d@drobilla.net>
Fri, 19 Sep 2008 00:47:49 +0000 (00:47 +0000)
committerDavid Robillard <d@drobilla.net>
Fri, 19 Sep 2008 00:47:49 +0000 (00:47 +0000)
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

103 files changed:
SConstruct
gtk2_ardour/SConscript
gtk2_ardour/audio_time_axis.cc
gtk2_ardour/automation_controller.cc
gtk2_ardour/automation_controller.h
gtk2_ardour/automation_line.cc
gtk2_ardour/automation_line.h
gtk2_ardour/automation_region_view.cc
gtk2_ardour/automation_selection.h
gtk2_ardour/automation_streamview.cc
gtk2_ardour/automation_time_axis.cc
gtk2_ardour/canvas-hit.h
gtk2_ardour/canvas-note-event.cc
gtk2_ardour/canvas-note-event.h
gtk2_ardour/canvas-note.cc
gtk2_ardour/canvas-note.h
gtk2_ardour/canvas-program-change.cc
gtk2_ardour/canvas-program-change.h
gtk2_ardour/crossfade_edit.cc
gtk2_ardour/curvetest.cc
gtk2_ardour/gain_meter.cc
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/midi_streamview.cc
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/mixer_strip.cc
gtk2_ardour/panner_ui.cc
gtk2_ardour/route_time_axis.cc
gtk2_ardour/route_ui.cc
gtk2_ardour/selection.cc
gtk2_ardour/selection.h
libs/ardour/SConscript
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_control.h
libs/ardour/ardour/automation_event.h
libs/ardour/ardour/midi_buffer.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_region.h
libs/ardour/ardour/midi_ring_buffer.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/note.h [deleted file]
libs/ardour/ardour/panner.h
libs/ardour/ardour/parameter.h
libs/ardour/ardour/plugin_insert.h
libs/ardour/ardour/smf_source.h
libs/ardour/audio_track.cc
libs/ardour/audioregion.cc
libs/ardour/automatable.cc
libs/ardour/automation_control.cc
libs/ardour/automation_event.cc
libs/ardour/crossfade.cc
libs/ardour/gain.cc
libs/ardour/import.cc
libs/ardour/io.cc
libs/ardour/jack_midi_port.cc
libs/ardour/meter.cc
libs/ardour/midi_buffer.cc
libs/ardour/midi_diskstream.cc
libs/ardour/midi_model.cc
libs/ardour/midi_source.cc
libs/ardour/midi_stretch.cc
libs/ardour/midi_track.cc
libs/ardour/note.cc [deleted file]
libs/ardour/panner.cc
libs/ardour/parameter.cc
libs/ardour/plugin_insert.cc
libs/ardour/route.cc
libs/ardour/smf_source.cc
libs/evoral/SConscript [new file with mode: 0644]
libs/evoral/evoral/Control.hpp [new file with mode: 0644]
libs/evoral/evoral/ControlList.hpp [new file with mode: 0644]
libs/evoral/evoral/ControlSet.hpp [new file with mode: 0644]
libs/evoral/evoral/Curve.hpp [new file with mode: 0644]
libs/evoral/evoral/Event.hpp [new file with mode: 0644]
libs/evoral/evoral/EventSink.hpp [new file with mode: 0644]
libs/evoral/evoral/MIDIParameters.hpp [new file with mode: 0644]
libs/evoral/evoral/Note.hpp [new file with mode: 0644]
libs/evoral/evoral/Parameter.hpp [new file with mode: 0644]
libs/evoral/evoral/Sequence.hpp [new file with mode: 0644]
libs/evoral/evoral/midi_events.h [new file with mode: 0644]
libs/evoral/evoral/types.hpp [new file with mode: 0644]
libs/evoral/src/Control.cpp [new file with mode: 0644]
libs/evoral/src/ControlList.cpp [new file with mode: 0644]
libs/evoral/src/ControlSet.cpp [new file with mode: 0644]
libs/evoral/src/Curve.cpp [new file with mode: 0644]
libs/evoral/src/Event.cpp [new file with mode: 0644]
libs/evoral/src/Note.cpp [new file with mode: 0644]
libs/evoral/src/Sequence.cpp [new file with mode: 0644]
libs/evoral/test/sequence.cpp [new file with mode: 0644]
libs/midi++2/SConscript
libs/midi++2/event.cc [deleted file]
libs/midi++2/jack_midiport.cc
libs/midi++2/midi++/event.h
libs/midi++2/midi++/jack.h
libs/midi++2/midi++/midnam_patch.h
libs/midi++2/midnam_patch.cc
libs/surfaces/control_protocol/SConscript
libs/surfaces/frontier/tranzport/SConscript
libs/surfaces/generic_midi/SConscript
libs/surfaces/mackie/SConscript
libs/surfaces/powermate/SConscript

index 35a67d18791bbd5ef002f3ff4b0fda82e82144f8..b41b609820c0e081d5fc1b36eeb9241b6895ae5f 100644 (file)
@@ -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/',
index bffb28e66a151d7e36d03604889d4a44cccd01bf..009b2f67021668f7270341888a0113e08fd46768 100644 (file)
@@ -48,6 +48,7 @@ gtkardour.Merge ([
     libraries['libgnomecanvasmm'],
     libraries['lrdf'],
     libraries['midi++2'],
+    libraries['evoral'],
     libraries['pangomm'],
     libraries['pbd'],
     libraries['samplerate'],
index 124c63936791b9106cf0afa6d4e646c7f0986161..8c77da34437049f9a2196825c59818a81a93782b 100644 (file)
@@ -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;
        }
 }
 
index 0bf1482224c2a5f983d6d1c56552c972ebf3c97e..dc9b8bb2c40e494bac82209367e0cbf9a0a86bf6 100644 (file)
@@ -61,12 +61,15 @@ AutomationController::~AutomationController()
 }
 
 boost::shared_ptr<AutomationController>
-AutomationController::create(boost::shared_ptr<Automatable> parent, boost::shared_ptr<AutomationList> al, boost::shared_ptr<AutomationControl> ac)
+AutomationController::create(
+               boost::shared_ptr<Automatable> parent,
+               boost::shared_ptr<Evoral::ControlList> cl,
+               boost::shared_ptr<AutomationControl> 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<AutomationControl>(parent->control_factory(cl));
        }
        return boost::shared_ptr<AutomationController>(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 */
        
index 8a244113c4056d5e65becd37fba700f565a9f76d..0da24b3588c5cc38bacc5a43866c2b0b959ec381 100644 (file)
@@ -37,7 +37,7 @@ class AutomationController : public Gtkmm2ext::BarController {
 public:
        static boost::shared_ptr<AutomationController> create(
                        boost::shared_ptr<ARDOUR::Automatable> parent,
-                       boost::shared_ptr<ARDOUR::AutomationList> al,
+                       boost::shared_ptr<Evoral::ControlList> cl,
                        boost::shared_ptr<ARDOUR::AutomationControl> ac);
 
        ~AutomationController();
index b112a9481e15fcaab16a2b8df483e0db11775dcb..de3e2d98732f62f99cd40be96b3c288ce08b275f 100644 (file)
@@ -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);
index 9e35903671a0cc3248ca8319c8ffa0de23dbc173..ace2145ff367566b1a9b90f08bbc548227733a10 100644 (file)
@@ -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*);
index 0ba14398e62e5eb7ea8c9ff8a09a5cd3a674b446..00cb8be5c426b8df531c4209f588c5a649b9f264 100644 (file)
@@ -33,7 +33,7 @@ AutomationRegionView::AutomationRegionView(ArdourCanvas::Group*
 { 
        if (list) {
                _line = boost::shared_ptr<AutomationLine>(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();
index 05063e144e7b736fe9385d074f79660ce24c594b..fb19c0b5e0d3697de4a49cc3792df22a1a9f69e7 100644 (file)
@@ -26,6 +26,6 @@ namespace ARDOUR {
        class AutomationList;
 }
 
-struct AutomationSelection : list<ARDOUR::AutomationList*> {};
+struct AutomationSelection : list< boost::shared_ptr<ARDOUR::AutomationList> > {};
 
 #endif /* __ardour_gtk_automation_selection_h__ */
index 5803d283dca8dc8dac7bed840c8a6a19d829b2d7..68da972ba01bafc58f7d41d568699cee89c5437f 100644 (file)
@@ -83,11 +83,13 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region
                        mr->midi_source()->load_model();
        }
 
-       const boost::shared_ptr<AutomationControl> control = region->control(_controller->controllable()->parameter());
+       const boost::shared_ptr<AutomationControl> control
+               = boost::dynamic_pointer_cast<AutomationControl>(
+                               region->control(_controller->controllable()->parameter()));
 
        boost::shared_ptr<AutomationList> list;
        if (control)
-               list = control->list();
+               list = boost::dynamic_pointer_cast<AutomationList>(control->list());
 
        AutomationRegionView *region_view;
        std::list<RegionView *>::iterator i;
index bc371a5826e3a1090006a8360692fd59291398db..07a47f274ea30f1de6763c4c16832b636bbe161d 100644 (file)
@@ -208,10 +208,10 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Ro
        } else {
        
                boost::shared_ptr<AutomationLine> 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<ARDOUR::AutomationList>(*_control->list().get(), &before, &after));
+       XMLNode& after = _control->alist()->get_state();
+       _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_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<Evoral::ControlList> what_we_got;
        boost::shared_ptr<AutomationList> 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<AutomationList>(*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<Evoral::ControlList> what_we_got;
        boost::shared_ptr<AutomationList> 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<AutomationList>(*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<AutomationLine> 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") {
index e77ef3738031364552205600bfdef23eb7193ec4..fa90075a0c0dde7fe73a8b0b34b48cef77294bd4 100644 (file)
@@ -33,7 +33,7 @@ public:
                        MidiRegionView&                       region,
                        Group&                                group,
                        double                                size,
-                       const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>())
+                       const boost::shared_ptr<Evoral::Note> note = boost::shared_ptr<Evoral::Note>())
 
                : Diamond(group, size), CanvasNoteEvent(region, this, note)
        {
index c370530cfcc123ff1e19a0ee4b37c132d302651e..1d015548832a06f6e5ee08f992665abd6f108e69 100644 (file)
@@ -32,7 +32,7 @@ namespace Canvas {
 
 
 CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item,
-               const boost::shared_ptr<ARDOUR::Note> note)
+               const boost::shared_ptr<Evoral::Note> note)
        : _region(region)
        , _item(item)
        , _text(0)
index 776d89160d5e794fdbf25154dc3ef25f582b3849..9e48db8c24d5c0c7a359d02c9853b08a11a4dff5 100644 (file)
@@ -49,7 +49,7 @@ public:
        CanvasNoteEvent(
                        MidiRegionView&                       region,
                        Item*                                 item,
-                       const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
+                       const boost::shared_ptr<Evoral::Note> note = boost::shared_ptr<Evoral::Note>());
 
        virtual ~CanvasNoteEvent();
 
@@ -81,7 +81,7 @@ public:
        virtual double x2() = 0;
        virtual double y2() = 0;
 
-       const boost::shared_ptr<ARDOUR::Note> note() const { return _note; }
+       const boost::shared_ptr<Evoral::Note> 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<ARDOUR::Note> _note;
+       const boost::shared_ptr<Evoral::Note> _note;
        bool                                  _own_note;
        bool                                  _selected;
 };
index 30b1e3807b292ab59087fb3fb77092f18b9c09bd..89916dbf4aeacccd7cf25a4a8a70a8f24ffcc36c 100644 (file)
@@ -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;
 
index 97739b42076625525aaddbea1e16929b52407fb3..30cbad6c25a9d0f0a6e9f6e7f356ba555d733ff0 100644 (file)
@@ -55,7 +55,7 @@ public:
        CanvasNote(
                        MidiRegionView&                       region,
                        Group&                                group,
-                       const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>())
+                       const boost::shared_ptr<Evoral::Note> note = boost::shared_ptr<Evoral::Note>())
 
                : SimpleRect(group), CanvasNoteEvent(region, this, note), _note_state(None)
        {
index 6f7182e2cda6f9f91e1c64c4602169cc80c0b74d..e5a7768f348d05ca1d5060220f64218b9ac74265 100644 (file)
@@ -8,7 +8,7 @@ using namespace std;
 CanvasProgramChange::CanvasProgramChange(
                MidiRegionView&                       region,
                Group&                                parent,
-               boost::shared_ptr<MIDI::Event>        event,
+               boost::shared_ptr<Evoral::Event>      event,
                double                                height,
                double                                x,
                double                                y)
index 3ac9b103838321958207e7b911060846600ebc77..f2a912d5061730fa958a2ff2625628ba70f35960 100644 (file)
@@ -19,7 +19,7 @@ public:
        CanvasProgramChange(
                MidiRegionView&                       region,
                Group&                                parent,
-               boost::shared_ptr<MIDI::Event>        event,
+               boost::shared_ptr<Evoral::Event>      event,
                double                                height,
                double                                x = 0.0,
                double                                y = 0.0
@@ -29,7 +29,7 @@ public:
        
 private:
        MidiRegionView&                   _region;
-       boost::shared_ptr<MIDI::Event>    _event;
+       boost::shared_ptr<Evoral::Event>  _event;
        Text*                             _text;
        SimpleLine*                       _line;
        SimpleRect*                       _rect;
index 82869af396d6d53ddd4fd004263793b83df664e4..80b6224bd34232aadd0ffca0842fe5f4da0b1e26 100644 (file)
@@ -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<Crossfade> 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<Crossfade> xf)
 
        /* OUT */
 
-       the_end = out.const_end();
+       the_end = out.end();
        --the_end;
 
        firstx = (*out.begin())->when;
index 48650c0353b09e7e34531a1ec6fa18766ab6a09a..ec552848d24138f6a1c78399d9a44720525f3bb1 100644 (file)
@@ -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;
 
index 107b0eedfc9bf67dea492b42db0bc7c4daec87f2..182cf2d89da4445ee26321d3a6bb96523e8d74a1 100644 (file)
@@ -168,8 +168,8 @@ GainMeterBase::GainMeterBase (boost::shared_ptr<IO> 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<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (!(_io->gain_control()->list()->automation_state() & Play));
+       static_cast<Gtkmm2ext::SliderController*>(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;
index 4b64098dc0f21862086804d7bc8466274080de8d..34ab571c8ccde9467056c45afc0a17b3f9bbb41b 100644 (file)
@@ -209,7 +209,11 @@ GenericPluginUI::build ()
                                }
                        }
 
-                       if ((cui = build_control_ui (i, insert->control(Parameter(PluginAutomation, i)))) == 0) {
+                       boost::shared_ptr<ARDOUR::AutomationControl> c
+                               = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
+                                       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_ptr<Automat
                automation_state_changed (control_ui);
 
                mcontrol->Changed.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_ptr<Automat
 void
 GenericPluginUI::start_touch (GenericPluginUI::ControlUI* cui)
 {
-       cui->control->list()->start_touch ();
+       cui->control->start_touch ();
 }
 
 void
 GenericPluginUI::stop_touch (GenericPluginUI::ControlUI* cui)
 {
-       cui->control->list()->stop_touch ();
+       cui->control->stop_touch ();
 }
 
 void
index 49e5e0fb5d6c811c42d740268689b736e42d229e..63117beee2c3ecaf2755a99d322c719f63b8d484 100644 (file)
@@ -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<Note> new_note(new Note(0, new_note_time, new_note_duration, (uint8_t)note, 0x40));
+       const boost::shared_ptr<Evoral::Note> 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<ARDOUR::Note> note, bool selected)
+MidiRegionView::command_add_note(const boost::shared_ptr<Evoral::Note> 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<MIDI::Event> event(new MIDI::Event());
+                                       Evoral::ControlIterator iter(control->second->list(), (*event)->when, (*event)->value);
+                                       boost::shared_ptr<Evoral::Event> 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> note)
+MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> 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> note)
                                // finish the old note rectangle
                                if (_active_notes[note->note()]) {
                                        CanvasNote* const old_rect = _active_notes[note->note()];
-                                       boost::shared_ptr<ARDOUR::Note> old_note = old_rect->note();
+                                       boost::shared_ptr<Evoral::Note> 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> note)
 }
 
 void
-MidiRegionView::add_pgm_change(boost::shared_ptr<MIDI::Event> event)
+MidiRegionView::add_pgm_change(boost::shared_ptr<Evoral::Event> 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<Note> copy(new Note(*(*i)->note().get()));
+                       const boost::shared_ptr<Evoral::Note> 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<Note> copy(new Note(*(canvas_note->note().get())));
+               const boost::shared_ptr<Evoral::Note> 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<Note> copy(new Note(*(event->note().get())));
+               const boost::shared_ptr<Evoral::Note> 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<Note> copy(new Note(*(event->note().get())));
+               const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
 
                copy->set_channel(channel);
                
index 50cac66d778552a1801d02bf51b277af2b387ff2..1bdccd4986fb20a9ee56b353e58c25d0d006b07a 100644 (file)
@@ -79,10 +79,10 @@ class MidiRegionView : public RegionView
 
        GhostRegion* add_ghost (TimeAxisView&);
 
-       void add_note(const boost::shared_ptr<ARDOUR::Note> note);
+       void add_note(const boost::shared_ptr<Evoral::Note> note);
        void resolve_note(uint8_t note_num, double end_time);
        
-       void add_pgm_change(boost::shared_ptr<MIDI::Event> event);
+       void add_pgm_change(boost::shared_ptr<Evoral::Event> event);
 
        void begin_write();
        void end_write();
@@ -93,7 +93,7 @@ class MidiRegionView : public RegionView
        void display_model(boost::shared_ptr<ARDOUR::MidiModel> model);
 
        void start_delta_command(string name = "midi edit");
-       void command_add_note(const boost::shared_ptr<ARDOUR::Note> note, bool selected);
+       void command_add_note(const boost::shared_ptr<Evoral::Note> 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<ARDOUR::Note> > _marked_for_selection;
+       std::set< boost::shared_ptr<Evoral::Note> > _marked_for_selection;
 
        std::vector<NoteResizeData *> _resize_data;
 };
index d53150fd91cab88c61404ba9649568056699671e..2266ddd064714735e64e487dccf701ed9fd22c6e 100644 (file)
@@ -554,7 +554,7 @@ MidiStreamView::update_rec_regions (boost::shared_ptr<MidiModel> data, nframes_t
 
                                                        // FIXME: slooooooooow!
 
-                                                       const boost::shared_ptr<Note> note = data->note_at(i);
+                                                       const boost::shared_ptr<Evoral::Note> note = data->note_at(i);
                                                        
                                                        if (note->duration() > 0 && note->end_time() + region->position() > start)
                                                                mrv->resolve_note(note->note(), note->end_time());
index 5802cc7e20a76a73b5dc77a86d7ae57b1757a4e3..53da98d829ee40dce88139c2eed9037c58f51e32 100644 (file)
@@ -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<AutomationControl> c = _route->control(param);
+               boost::shared_ptr<AutomationControl> c
+                       = boost::dynamic_pointer_cast<AutomationControl>(_route->control(param));
 
                if (!c) {
-                       boost::shared_ptr<AutomationList> al(new ARDOUR::AutomationList(param,
-                                               param.min(), param.max(), (param.max() - param.min() / 2)));
-                       c = boost::shared_ptr<AutomationControl>(_route->control_factory(al));
+                       boost::shared_ptr<AutomationList> al(new ARDOUR::AutomationList(param));
+                       c = boost::dynamic_pointer_cast<AutomationControl>(_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;
        }
 }
 
index 1a8949d1b8d719ff5c86400d76d291fa5679b344..2459ac57a83dc8862bdcff9d63b409d1cdba241c 100644 (file)
@@ -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<AutomationList> gain_automation = _route->gain_control()->list();
+       boost::shared_ptr<AutomationList> 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);
index 3fe43850a353ee09ffda8d66f697e435ef9c5542..fec4cbf464b49419f3ee0974e485d9f4108ecc94 100644 (file)
@@ -593,7 +593,7 @@ PannerUI::update_pan_bars (bool only_if_aplay)
                float xpos, val;
 
                if (only_if_aplay) {
-                       boost::shared_ptr<AutomationList> alist (_io->panner()[n]->pan_control()->list());
+                       boost::shared_ptr<AutomationList> 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;
index b3a988fd927318660ae47e787d2a44698eedc192..61c87d01dea69f2b5fa3e6ec875e69cb85994b8f 100644 (file)
@@ -1741,7 +1741,8 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor>
        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<AutomationControl> control = processor->control(what, true);
+       boost::shared_ptr<AutomationControl> control
+                       = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
 
        pan->view = boost::shared_ptr<AutomationTimeAxisView>(
                        new AutomationTimeAxisView (_session, _route, processor, control,
@@ -1782,7 +1783,7 @@ RouteTimeAxisView::add_existing_processor_automation_curves (boost::shared_ptr<P
        set<Parameter> s;
        boost::shared_ptr<AutomationLine> al;
 
-       processor->what_has_visible_automation (s);
+       processor->what_has_visible_data (s);
 
        for (set<Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
                
@@ -1843,7 +1844,7 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::shared_ptr<Processor>
        const std::set<Parameter>& automatable = processor->what_can_be_automated ();
        std::set<Parameter> has_visible_automation;
 
-       processor->what_has_visible_automation(has_visible_automation);
+       processor->what_has_visible_data(has_visible_automation);
 
        if (automatable.empty()) {
                return;
index 4d1d92ac5a3ff0eb8d55098b6cb63ddff0c2776f..c02c4109b172367c77d1d75b66d07a76806eaa88 100644 (file)
@@ -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;
index 910cebc7fe718007954d67c23c8253a8afa89f48..2f9601fceca5355373c420acfa97f6f6ba3eb0ff 100644 (file)
@@ -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<Evoral::ControlList> cl)
 {
-       if (find (lines.begin(), lines.end(), ac) == lines.end()) {
-               lines.push_back (ac);
+       boost::shared_ptr<ARDOUR::AutomationList> al
+               = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(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<ARDOUR::AutomationList> ac)
 {
-       list<AutomationList*>::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<Evoral::ControlList> ac)
 {
        lines.clear();
        add (ac);
index 17862e127b0aeb3529b405360c3252bc0aba5b7b..c6923e663c6add8d22a39e2bbcb636267894830f 100644 (file)
@@ -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<RegionView*>&);
        long set (TimeAxisView*, nframes_t, nframes_t);
-       void set (ARDOUR::AutomationList*);
+       void set (boost::shared_ptr<Evoral::ControlList>);
        void set (boost::shared_ptr<ARDOUR::Playlist>);
        void set (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
        void set (AutomationSelectable*);
@@ -128,7 +132,7 @@ class Selection : public sigc::trackable
        void add (RegionView*);
        void add (std::vector<RegionView*>&);
        long add (nframes_t, nframes_t);
-       void add (ARDOUR::AutomationList*);
+       void add (boost::shared_ptr<Evoral::ControlList>);
        void add (boost::shared_ptr<ARDOUR::Playlist>);
        void add (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
        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<ARDOUR::AutomationList>);
        void remove (boost::shared_ptr<ARDOUR::Playlist>);
        void remove (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
        void remove (const list<Selectable*>&);
index 4bdbf7d88ab33c1ee30020bb9f26a40a5ba6dd7d..b51e81c2ca9be835af7125e5c1566aac030812a9 100644 (file)
@@ -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'],
index a2c1d98ae77b82b30e9f794180f95b77383c53ad..ce31721802e50b0d4b28c907386f0b3099be76c7 100644 (file)
 #include <ardour/automation_event.h>
 #include <ardour/automation_control.h>
 #include <ardour/parameter.h>
+#include <evoral/ControlSet.hpp>
 
 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<AutomationControl>
-       control(AutomationType type, bool create_if_missing=false) {
-               return control(Parameter(type), create_if_missing);
-       }
-
-       virtual boost::shared_ptr<AutomationControl> control(Parameter id, bool create_if_missing=false);
-       virtual boost::shared_ptr<const AutomationControl> control(Parameter id) const;
+       boost::shared_ptr<Evoral::Control> control_factory(boost::shared_ptr<Evoral::ControlList> list) const;
+       boost::shared_ptr<Evoral::ControlList> control_list_factory(const Evoral::Parameter& param) const;
        
-       boost::shared_ptr<AutomationControl> control_factory(boost::shared_ptr<AutomationList> list);
+       virtual void add_control(boost::shared_ptr<Evoral::Control>);
        
-       typedef std::map<Parameter,boost::shared_ptr<AutomationControl> > Controls;
-       Controls&       controls()       { return _controls; }
-       const Controls& controls() const { return _controls; }
-
-       virtual void add_control(boost::shared_ptr<AutomationControl>);
-
        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<Parameter>&) const;
-       void what_has_visible_automation(std::set<Parameter>&) const;
+       void what_has_visible_data(std::set<Parameter>&) const;
        const std::set<Parameter>& 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<Parameter> _visible_controls;
        std::set<Parameter> _can_automate_list;
        
index 68ac5797dc10a5528b74f36e4196e0347143f486..c414f7bc40a741ec099322a428e9dd6c00959ecf 100644 (file)
@@ -24,6 +24,8 @@
 #include <boost/shared_ptr.hpp>
 #include <pbd/controllable.h>
 #include <ardour/parameter.h>
+#include <evoral/Control.hpp>
+#include <ardour/automation_event.h>
 
 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<ARDOUR::AutomationList>,
                        std::string name="unnamed controllable");
+       
+       boost::shared_ptr<AutomationList> alist() { return boost::dynamic_pointer_cast<AutomationList>(_list); }
+
+       void set_list(boost::shared_ptr<Evoral::ControlList>);
+
+       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<ARDOUR::AutomationList>);
-
-       boost::shared_ptr<ARDOUR::AutomationList>       list()       { return _list; }
-       boost::shared_ptr<const ARDOUR::AutomationList> list() const { return _list; }
-
-       Parameter parameter() const;
 
 protected:
-       ARDOUR::Session&                          _session;
-       boost::shared_ptr<ARDOUR::AutomationList> _list;
-       float                                     _user_value;
+       ARDOUR::Session& _session;
 };
 
 
index 4362b9c867cd283493bccf86b9baab1af9779615..ed3379bc150a33e90e4236c78c84ab44d4d847cb 100644 (file)
 #include <ardour/ardour.h>
 #include <ardour/parameter.h>
 
-namespace ARDOUR {
-
-class Curve;
-
-struct ControlEvent {
-
-    ControlEvent (double w, double v)
-           : when (w), value (v), coeff (0) { 
-       }
+#include <evoral/ControlList.hpp>
 
-    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; }
+using Evoral::ControlEvent;
 
-       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<ControlEvent*,
-       boost::default_user_allocator_new_delete,
-       boost::details::pool::null_mutex,
-       8192> ControlEventAllocator;
+namespace ARDOUR {
 
-class AutomationList : public PBD::StatefulDestructible
+class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlList
 {
   public:
-       typedef std::list<ControlEvent*,ControlEventAllocator> 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<Evoral::ControlList> 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<void> StateChanged;
+       
+       static sigc::signal<void, AutomationList*> AutomationListCreated;
+       mutable sigc::signal<void> 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<AutomationList::iterator,AutomationList::iterator> control_points_adjacent (double when);
-
-       template<class T> void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) {
-               Glib::Mutex::Lock lm (_lock);
-               (obj.*method)(*this);
-       }
-       
-       sigc::signal<void> 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<AutomationList::const_iterator,AutomationList::const_iterator> 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<AutomationList::const_iterator,AutomationList::const_iterator> range;
-       };
-
-       static sigc::signal<void, AutomationList*> AutomationListCreated;
-
-       const EventList& events() const { return _events; }
-       double default_value() const { return _default_value; }
-
-       // teeny const violations for Curve
-       mutable sigc::signal<void> 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
index 699f461b17ee90bb940fb8aebfbf87c84edec516..0c131998254bdf80363e5411130e80f1c2fcc242 100644 (file)
@@ -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.
 };
 
 
index 2481aa8d342f63d2a6cdd4bc78cdcc120a77c75d..51bddfde44b7f7cb51abb21787493806a033a3f1 100644 (file)
 #include <ardour/midi_buffer.h>
 #include <ardour/midi_ring_buffer.h>
 #include <ardour/automatable.h>
-#include <ardour/note.h>
 #include <ardour/types.h>
+#include <evoral/Note.hpp>
+#include <evoral/Sequence.hpp>
 
 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<const AutomationList> automation_list;
-       double x;
-       double y;
-       
-       MidiControlIterator(boost::shared_ptr<const AutomationList> 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<const Note> note_at(unsigned i) const { return _notes[i]; }
-       inline const boost::shared_ptr<Note>       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<const Note> a,
-                                                const boost::shared_ptr<const Note> b) { 
-               return a->time() < b->time();
-       }
-
-       struct LaterNoteEndComparator {
-               typedef const Note* value_type;
-               inline bool operator()(const boost::shared_ptr<const Note> a,
-                                      const boost::shared_ptr<const Note> b) const { 
-                       return a->end_time() > b->end_time();
-               }
-       };
-
-       typedef std::vector< boost::shared_ptr<Note> > 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> note);
-               void remove(const boost::shared_ptr<Note> note);
+               void add(const boost::shared_ptr<Evoral::Note> note);
+               void remove(const boost::shared_ptr<Evoral::Note> note);
 
        private:
-               XMLNode &marshal_note(const boost::shared_ptr<Note> note);
-               boost::shared_ptr<Note> unmarshal_note(XMLNode *xml_note);
+               XMLNode &marshal_note(const boost::shared_ptr<Evoral::Note> note);
+               boost::shared_ptr<Evoral::Note> unmarshal_note(XMLNode *xml_note);
                
                boost::shared_ptr<MidiModel>         _model;
                const std::string                    _name;
                
-               typedef std::list< boost::shared_ptr<Note> > NoteList;
+               typedef std::list< boost::shared_ptr<Evoral::Note> > 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<MidiSource> source);
                
        // MidiModel doesn't use the normal AutomationList serialisation code
@@ -157,90 +99,11 @@ public:
 
        sigc::signal<void> 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<MIDI::Event> operator->() const  { return _event; }
-               const boost::shared_ptr<MIDI::Event> 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<MIDI::Event> _event;
-
-               typedef std::priority_queue<
-                               boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
-                               LaterNoteEndComparator>
-                       ActiveNotes;
-               
-               mutable ActiveNotes _active_notes;
-
-               bool                                       _is_end;
-               bool                                       _locked;
-               Notes::const_iterator                      _note_iter;
-               std::vector<MidiControlIterator>           _control_iters;
-               std::vector<MidiControlIterator>::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<MIDI::Event>& ev, const MidiControlIterator& iter) const;
        
 private:
        friend class DeltaCommand;
-       void add_note_unlocked(const boost::shared_ptr<Note> note);
-       void remove_note_unlocked(const boost::shared_ptr<const Note> 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<size_t> WriteNotes;
-       WriteNotes _write_notes[16];
-       bool       _writing;
-       bool       _edited;
-       
-       typedef std::vector< boost::shared_ptr<const ARDOUR::AutomationList> > 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<Note>, std::deque< boost::shared_ptr<Note> >,
-                       LaterNoteEndComparator>
-               ActiveNotes;
        
        // We cannot use a boost::shared_ptr here to avoid a retain cycle
        MidiSource* _midi_source;
index 44f775dc1c84e5fea459bb308d454dc4dea3e9dc..66257372a92fb805a1cd3886728e84601e7d8912 100644 (file)
@@ -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<AutomationControl> control(Parameter id, bool create_if_missing=false)
+       boost::shared_ptr<Evoral::Control> control(Evoral::Parameter id, bool create_if_missing=false)
                        { return midi_source()->model()->control(id, create_if_missing); }
 
-       boost::shared_ptr<const AutomationControl> control(Parameter id) const
+       boost::shared_ptr<const Evoral::Control> control(Evoral::Parameter id) const
                        { return midi_source()->model()->control(id); }
        
        int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&);
index ff0be5c9978b5927168cdf9761a3628e3529f0a7..db0c3029a8aa1fd81bf1a0f268bb2a50bc2839b4 100644 (file)
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <ardour/types.h>
 #include <ardour/buffer.h>
+#include <evoral/EventSink.hpp>
 
 namespace ARDOUR {
 
@@ -243,7 +244,7 @@ MidiRingBufferBase<T>::write(size_t size, const T* src)
  *
  * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..]
  */
-class MidiRingBuffer : public MidiRingBufferBase<uint8_t> {
+class MidiRingBuffer : public MidiRingBufferBase<uint8_t>, public Evoral::EventSink {
 public:
        /** @param size Size in bytes.
         */
@@ -251,8 +252,8 @@ public:
                : MidiRingBufferBase<uint8_t>(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<uint8_t>::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)
index 997f3f9d6948a456c4d8e6ea09027978563e4dba..9cb222d2077e8253f2123251211e1931ffb59bc1 100644 (file)
@@ -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);
index 6e4677df22372ccae5697305a598d26356305bb3..7eda6f904babe16357330f36a1cfbc8ccb7356d0 100644 (file)
@@ -75,7 +75,7 @@ public:
        
        struct MidiControl : public AutomationControl {
            MidiControl(MidiTrack* route, boost::shared_ptr<AutomationList> 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 (file)
index 0f649b3..0000000
+++ /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 <stdint.h>
-#include <midi++/event.h>
-
-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__ */
index 2559eed003c7a89693d3494335dac634b61a7716..1b85495d7a9887edacd2df0e8fea8536a3cab4b6 100644 (file)
@@ -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<AutomationList>(new AutomationList(
-                                       param, 0.0, 1.0, 0.5)), name)
-                       , panner (p) { assert(param.type() != NullAutomation); }
+                       : AutomationControl (s,
+                                       boost::shared_ptr<AutomationList>(new AutomationList(param)), name)
+                       , panner (p)
+               { assert(param.type() != NullAutomation); }
            
+               AutomationList* alist() { return (AutomationList*)_list.get(); }
            StreamPanner& panner;
            
            void set_value (float);
index a5dd9cdca977afc945195d8d820d3c095e7290e9..dbcccd811f3d7189b2367649fc2deb2f502e5239 100644 (file)
 #include <pbd/compose.h>
 #include <pbd/error.h>
 #include <ardour/types.h>
+#include <evoral/Parameter.hpp>
+#include <evoral/MIDIParameters.hpp>
 
 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.
- *
- * A parameter ID has two parts, a type and an int (only used by some types).
+ * Curve) must have unique Parameter ID with respect to it's Automatable parent.
  *
- * 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.
+ * 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.
  *
- * 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)
-       {}
-       
-       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; }
-
-       /**
-        * 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);
+               : Evoral::Parameter((uint32_t)type, id, channel)
+       {
+               init(type);
        }
        
-       /** 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:
-        * <ol>
-        * <li>Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.</li>
-        * 
-        * <li>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==. </li>
-        * 
-        * <li>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:
-        *        <ol>
-        *      <li> x == z which contradicts the assumption f(x, y) and f(y, x)
-        *                 because of antisymmetry.
-        *      </li>
-        *      <li> 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.
-        *      </li>
-        *    </ol> 
-        * </li>
-        * </ol>
-        */
-       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;
+       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);
        }
        
-       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) {
+       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:
-                       return 0.0;
-                       
-               default:
-                       return DBL_MIN;
+                       Evoral::MIDI::ChannelAftertouch::set_range(*this); break;
                }
        }
        
-       inline double max() const {
-               switch(_type) {
-               case MidiCCAutomation:
-               case MidiPgmChangeAutomation:
-               case MidiChannelAftertouchAutomation:
-                       return 127.0;
-               case MidiPitchBenderAutomation:
-                       return 16383.0;
-                       
-               default:
-                       return DBL_MAX;
-               }
-       }
+       Parameter(const std::string& str);
+
+       inline AutomationType type() const { return (AutomationType)_type; }
+
+       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; }
 };
 
 
index c19d1132569de3fca4b762e7ab9fc8869bd29882..8db9fb14fee3ece7d2f422c65ab6459333eceb13 100644 (file)
@@ -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 
        {
index 88bf1e5d1370157a97ec5888594b0b3b7224124a..3217c8e3e8c141a8e30eb699800c95f39ae3fdc3 100644 (file)
@@ -26,6 +26,8 @@
 
 #include <ardour/midi_source.h>
 
+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 ();
index cd05e5fc86b6200c1d7fa52288bf111e657e0b46..532abeb123474fd8699abba86b84aecff00a02a7 100644 (file)
@@ -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);
 
index 271544297d6fa106bfec3485e1f9a18cd77673c1..707f10e91a8669d8e264068fb39833de678166a4 100644 (file)
@@ -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<AudioSource> 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<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
        if (afs) {
@@ -102,9 +102,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
 /* Basic AudioRegion constructor (one channel) */
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> 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<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
        if (afs) {
@@ -117,9 +117,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> 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<const AudioRegion> 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<boost::shared_ptr<Source> > unique_srcs;
 
@@ -180,9 +180,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
 
 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> 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<const AudioRegion> other)
 
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> 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<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
        if (afs) {
@@ -217,9 +217,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> 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 ();
 
index cacebe59a476e77e00e41eb1fa70e54d6a870288..349c09e136b74b4e34c925a9c15d0753a91f7880 100644 (file)
@@ -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<Parameter> 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<AutomationControl> c = control (Parameter(PluginAutomation, port), true);
+               boost::shared_ptr<Evoral::Control> 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<AutomationControl> ac)
+Automatable::add_control(boost::shared_ptr<Evoral::Control> 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<AutomationControl> ac)
 }
 
 void
-Automatable::what_has_automation (set<Parameter>& 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<Parameter>& s) const
+Automatable::what_has_visible_data (set<Parameter>& s) const
 {
-       Glib::Mutex::Lock lm (_automation_lock);
+       Glib::Mutex::Lock lm (_control_lock);
        set<Parameter>::const_iterator li;
        
        for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
@@ -157,42 +143,6 @@ Automatable::what_has_visible_automation (set<Parameter>& s) const
        }
 }
 
-/** Returns NULL if we don't have an AutomationList for \a parameter.
- */
-boost::shared_ptr<AutomationControl>
-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<AutomationList> al (new AutomationList (
-                                       parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter)));
-               boost::shared_ptr<AutomationControl> ac(control_factory(al));
-               add_control(ac);
-               return ac;
-
-       } else {
-               //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
-               return boost::shared_ptr<AutomationControl>();
-       }
-}
-
-boost::shared_ptr<const AutomationControl>
-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<AutomationControl>();
-       }
-}
-
 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<const AutomationList> 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<AutomationControl> existing = control(param);
+                       boost::shared_ptr<Evoral::Control> 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<AutomationList> l
+                               = boost::dynamic_pointer_cast<AutomationList>(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<AutomationControl> c = control (param, true);
+       boost::shared_ptr<Evoral::Control> c = control (param, true);
+       boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(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<AutomationControl> c = control(param);
+       boost::shared_ptr<Evoral::Control> c = control(param);
+       boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(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<AutomationControl> c = control(param, true);
+       boost::shared_ptr<Evoral::Control> c = control(param, true);
+       boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(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<AutomationControl> c = control(param);
+       boost::shared_ptr<Evoral::Control> c = control(param);
+       boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(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<Parameter> automated_params;
+       typedef set<Evoral::Parameter> ParameterSet;
+       ParameterSet automated_params;
 
-       what_has_automation (automated_params);
+       what_has_data (automated_params);
 
-       for (set<Parameter>::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<AutomationControl> c = control(*i);
+               boost::shared_ptr<Evoral::Control> c = control(*i);
+               boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(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<AutomationControl> c
+                                       = boost::dynamic_pointer_cast<AutomationControl>(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<AutomationControl> c = li->second;
+               boost::shared_ptr<AutomationControl> c
+                               = boost::dynamic_pointer_cast<AutomationControl>(li->second);
+               boost::shared_ptr<AutomationList> l
+                               = boost::dynamic_pointer_cast<AutomationList>(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<AutomationControl>
-Automatable::control_factory(boost::shared_ptr<AutomationList> list)
+boost::shared_ptr<Evoral::Control>
+Automatable::control_factory(boost::shared_ptr<Evoral::ControlList> 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<AutomationControl>(new MidiTrack::MidiControl((MidiTrack*)this, list));
+       boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(list);
+       assert(l);
+       if (l->parameter().type() >= MidiCCAutomation
+                       && l->parameter().type() <= MidiChannelAftertouchAutomation) {
+               return boost::shared_ptr<Evoral::Control>(new MidiTrack::MidiControl((MidiTrack*)this, l));
        } else {
-               return boost::shared_ptr<AutomationControl>(new AutomationControl(_session, list));
+               return boost::shared_ptr<Evoral::Control>(new AutomationControl(_session, l));
        }
 }
 
+boost::shared_ptr<Evoral::ControlList>
+Automatable::control_list_factory(const Evoral::Parameter& param) const
+{
+       return boost::shared_ptr<Evoral::ControlList>(new AutomationList(param));
+}
index 4885a6fed94cb383671cf077088bcae15ff7e330..a16306838a6d5f165c9c7db62bd3fd2f5bbe6e4c 100644 (file)
@@ -30,10 +30,9 @@ using namespace PBD;
 
 
 AutomationControl::AutomationControl(Session& session, boost::shared_ptr<AutomationList> 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_ptr<Automat
 float
 AutomationControl::get_value() const
 {
-       if (_list->automation_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<ARDOUR::AutomationList> list)
+AutomationControl::set_list(boost::shared_ptr<Evoral::ControlList> list)
 {
-       _list = list;
-       _user_value = list->default_value();
+       Control::set_list(list);
        Changed();  /* EMIT SIGNAL */
 }
 
-       
-Parameter
-AutomationControl::parameter() const
-{
-       return _list->parameter();
-}
index af390953f424aa660b773efa64501a130e2f85f4..2d98f1c27c20a4d7aa32a8f3a712182ffb7fa55c 100644 (file)
@@ -39,11 +39,6 @@ using namespace PBD;
 
 sigc::signal<void,AutomationList *> 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<AutomationList*>(&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 <AutomationList> 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<Evoral::ControlList>
+AutomationList::create(Evoral::Parameter id)
+{
+       return boost::shared_ptr<Evoral::ControlList>(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::iterator,AutomationList::iterator>
-AutomationList::control_points_adjacent (double xval)
-{
-       Glib::Mutex::Lock lm (_lock);
-       iterator i;
-       ControlEvent cp (xval, 0.0f);
-       std::pair<iterator,iterator> 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<EventList::iterator,EventList::iterator> 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<const_iterator,const_iterator> 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<const_iterator,const_iterator>& 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<const_iterator,const_iterator> 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());
 
index 213e7504b0f5f997d69d699edc9fe622192ec510..b6ca322c73278752c36336c4c5b2b30a2c5689b9 100644 (file)
@@ -78,8 +78,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
                      nframes_t position,
                      AnchorPoint ap)
        : AudioRegion (in->session(), position, length, "foobar"),
-         _fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
-         _fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+         _fade_in (Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB
+         _fade_out (Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
 
 {
        _in = in;
@@ -95,8 +95,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
 
 Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act)
        : AudioRegion (a->session(), 0, 0, "foobar"),
-         _fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
-         _fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+         _fade_in (Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB
+         _fade_out (Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
 {
        _in_update = false;
        _fixed = false;
@@ -114,8 +114,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioR
 
 Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
        : AudioRegion (playlist.session(), 0, 0, "foobar"),
-         _fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
-         _fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+         _fade_in (Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB
+         _fade_out (Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
 
 {
        boost::shared_ptr<Region> r;
index 49596d661480735873e02863a3b08e22d8d382d0..741ac2b57a3e08824aa943c1307d339c21116138 100644 (file)
@@ -22,7 +22,7 @@
 using namespace ARDOUR;
 
 Gain::Gain ()
-       : AutomationList (Parameter(GainAutomation), 0.0, 2.0, 1.0f)   /* XXX yuck; clamps gain to -inf .. +6db */
+       : AutomationList (Parameter(GainAutomation))   /* XXX yuck; clamps gain to -inf .. +6db */
 {
 }
 
index d4afda8b5a3a39df2c114549892029a86bfe7658..199a2fd1a43e0f0a2d08e1cbfdf234a987bdf595 100644 (file)
@@ -305,7 +305,7 @@ static void
 write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
                               vector<boost::shared_ptr<Source> >& newfiles)
 {
-       MIDI::Event ev(0.0, 4, NULL, true);
+       Evoral::Event ev(0.0, 4, NULL, true);
 
        status.progress = 0.0f;
 
index 1b3c2e237808c85c14c4d902e1e5189cd48d4cd7..69c74f5f03cb3be933967471858c4e56c4ecbdda 100644 (file)
@@ -137,7 +137,7 @@ IO::IO (Session& s, const string& name,
        deferred_state = 0;
 
        boost::shared_ptr<AutomationList> gl(
-                       new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
+                       new AutomationList(Parameter(GainAutomation)));
 
        _gain_control = boost::shared_ptr<GainControl>(
                        new GainControl(X_("gaincontrol"), *this, gl));
@@ -178,7 +178,7 @@ IO::IO (Session& s, const XMLNode& node, DataType dt)
        apply_gain_automation = false;
        
        boost::shared_ptr<AutomationList> gl(
-                       new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
+                       new AutomationList(Parameter(GainAutomation)));
 
        _gain_control = boost::shared_ptr<GainControl>(
                        new GainControl(X_("gaincontrol"), *this, gl));
@@ -1153,7 +1153,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
 gain_t
 IO::effective_gain () const
 {
-       if (_gain_control->list()->automation_playback()) {
+       if (_gain_control->automation_playback()) {
                return _gain_control->get_value();
        } else {
                return _desired_gain;
@@ -2266,7 +2266,7 @@ IO::meter ()
 void
 IO::clear_automation ()
 {
-       Automatable::clear_automation (); // clears gain automation
+       Automatable::clear (); // clears gain automation
        _panner->clear_automation ();
 }
 
@@ -2280,9 +2280,10 @@ IO::set_parameter_automation_state (Parameter param, AutoState state)
                bool changed = false;
 
                { 
-                       Glib::Mutex::Lock lm (_automation_lock);
+                       Glib::Mutex::Lock lm (_control_lock);
 
-                       boost::shared_ptr<AutomationList> gain_auto = _gain_control->list();
+                       boost::shared_ptr<AutomationList> gain_auto
+                               = boost::dynamic_pointer_cast<AutomationList>(_gain_control->list());
 
                        if (state != gain_auto->automation_state()) {
                                changed = true;
@@ -2337,7 +2338,7 @@ IO::set_gain (gain_t val, void *src)
                _gain = val;
        }
        
-       if (_session.transport_stopped() && src != 0 && src != this && _gain_control->list()->automation_write()) {
+       if (_session.transport_stopped() && src != 0 && src != this && _gain_control->automation_write()) {
                _gain_control->list()->add (_session.transport_frame(), val);
                
        }
@@ -2349,7 +2350,7 @@ void
 IO::start_pan_touch (uint32_t which)
 {
        if (which < _panner->size()) {
-               (*_panner)[which]->pan_control()->list()->start_touch();
+               (*_panner)[which]->pan_control()->start_touch();
        }
 }
 
@@ -2357,7 +2358,7 @@ void
 IO::end_pan_touch (uint32_t which)
 {
        if (which < _panner->size()) {
-               (*_panner)[which]->pan_control()->list()->stop_touch();
+               (*_panner)[which]->pan_control()->stop_touch();
        }
 
 }
@@ -2380,7 +2381,7 @@ IO::transport_stopped (nframes_t frame)
 {
        _gain_control->list()->reposition_for_rt_add (frame);
 
-       if (_gain_control->list()->automation_state() != Off) {
+       if (_gain_control->automation_state() != Off) {
                
                /* the src=0 condition is a special signal to not propagate 
                   automation gain changes into the mix group when locating.
index 6d8e4c8c5d24966bcd0770e4b5067b623d98802d..d831864ec962e432eb15384bf572716b9d97029f 100644 (file)
@@ -82,7 +82,7 @@ JackMidiPort::cycle_end (nframes_t nframes, nframes_t offset)
        jack_midi_clear_buffer (jack_buffer);
 
        for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
-               const MIDI::Event& ev = *i;
+               const Evoral::Event& ev = *i;
                // event times should be frames, relative to cycle start
                assert(ev.time() >= 0);
                assert(ev.time() < nframes);
index aedfd17be865201fa1340f17c59089591478c9d2..363cbe242f8b5c27e0d2d228d813b5a564cafb38 100644 (file)
@@ -47,7 +47,7 @@ PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_f
                // GUI needs a better MIDI meter, not much information can be
                // expressed through peaks alone
                for (MidiBuffer::iterator i = bufs.get_midi(n).begin(); i != bufs.get_midi(n).end(); ++i) {
-                       const MIDI::Event& ev = *i;
+                       const Evoral::Event& ev = *i;
                        if (ev.is_note_on()) {
                                const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0;
                                //printf("V %d -> %f\n", (int)((Byte)ev.buffer[2]), this_vel);
index 1530babe34323285f9591a5cc4848ea225c75bb8..afd8481795f5937ad96f748967adda0534a0de04 100644 (file)
@@ -74,10 +74,10 @@ MidiBuffer::resize (size_t size)
        _capacity = size;
 
 #ifdef NO_POSIX_MEMALIGN
-       _events = (MIDI::Event *) malloc(sizeof(MIDI::Event) * _capacity);
+       _events = (Evoral::Event *) malloc(sizeof(Evoral::Event) * _capacity);
        _data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
 #else
-       posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(MIDI::Event) * _capacity);
+       posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(Evoral::Event) * _capacity);
        posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
 #endif 
        assert(_data);
@@ -115,7 +115,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
        
        // FIXME: slow
        for (size_t i=0; i < msrc.size(); ++i) {
-               const MIDI::Event& ev = msrc[i];
+               const Evoral::Event& ev = msrc[i];
                if (ev.time() >= offset && ev.time() < offset+nframes) {
                        //cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
                        push_back(ev);
@@ -136,7 +136,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
  * @return false if operation failed (not enough room)
  */
 bool
-MidiBuffer::push_back(const MIDI::Event& ev)
+MidiBuffer::push_back(const Evoral::Event& ev)
 {
        if (_size == _capacity)
                return false;
@@ -223,7 +223,7 @@ MidiBuffer::silence(nframes_t dur, nframes_t offset)
        if (offset != 0)
                cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
 
-       memset(_events, 0, sizeof(MIDI::Event) * _capacity);
+       memset(_events, 0, sizeof(Evoral::Event) * _capacity);
        memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
        _size = 0;
        _silent = true;
@@ -262,8 +262,8 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
                        push_back(b[b_index]);
                        ++b_index;
                } else {
-                       const MIDI::Event& a_ev = a[a_index];
-                       const MIDI::Event& b_ev = b[b_index];
+                       const Evoral::Event& a_ev = a[a_index];
+                       const Evoral::Event& b_ev = b[b_index];
 
                        if (a_ev.time() <= b_ev.time()) {
                                push_back(a_ev);
index 5b4716d51e52331a45d384252dd5920d3ce2b3a0..b2991c1a7f11eb8d8ed96e18ee518bc2f01c3747 100644 (file)
@@ -520,7 +520,7 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
                MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
 
                for (size_t i=0; i < to_write; ++i) {
-                       const MIDI::Event& ev = *port_iter;
+                       const Evoral::Event& ev = *port_iter;
                        assert(ev.buffer());
                        _capture_buf->write(ev.time() + transport_frame, ev.size(), ev.buffer());
                        ++port_iter;
index b21bf38873a080ef00ee0b7f6572c6e3449e731e..da0fa364b980cd7a97a14938acdb7a10b64aaa16 100644 (file)
 using namespace std;
 using namespace ARDOUR;
 
-void MidiModel::write_lock() {
-       _lock.writer_lock();
-       _automation_lock.lock();
-}
-
-void MidiModel::write_unlock() {
-       _lock.writer_unlock();
-       _automation_lock.unlock();
-}
-
-void MidiModel::read_lock() const {
-       _lock.reader_lock();
-       /*_automation_lock.lock();*/
-}
-
-void MidiModel::read_unlock() const {
-       _lock.reader_unlock();
-       /*_automation_lock.unlock();*/
-}
-
-// Read iterator (const_iterator)
-
-MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
-       : _model(&model)
-       , _is_end( (t == DBL_MAX) || model.empty() )
-       , _locked( !_is_end )
-{
-       //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
-
-       if (_is_end) {
-               return;
-       }
-
-       model.read_lock();
-
-       _note_iter = model.notes().end();
-       // find first note which begins after t
-       for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
-               if ((*i)->time() >= t) {
-                       _note_iter = i;
-                       break;
-               }
-       }
-
-       MidiControlIterator earliest_control(boost::shared_ptr<AutomationList>(), DBL_MAX, 0.0);
-
-       _control_iters.reserve(model.controls().size());
-       
-       // find the earliest control event available
-       for (Automatable::Controls::const_iterator i = model.controls().begin();
-                       i != model.controls().end(); ++i) {
-
-               assert(
-                       i->first.type() == MidiCCAutomation ||
-                       i->first.type() == MidiPgmChangeAutomation ||
-                       i->first.type() == MidiPitchBenderAutomation ||
-                       i->first.type() == MidiChannelAftertouchAutomation);
-
-               double x, y;
-               bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
-               if (!ret) {
-                       //cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
-                       //      << ") has no events past " << t << endl;
-                       continue;
-               }
-
-               assert(x >= 0);
-
-               if (y < i->first.min() || y > i->first.max()) {
-                       cerr << "ERROR: Controller (" << i->first.to_string() << ") value '" << y
-                               << "' out of range [" << i->first.min() << "," << i->first.max()
-                               << "], event ignored" << endl;
-                       continue;
-               }
-
-               const MidiControlIterator new_iter(i->second->list(), x, y);
-
-               //cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
-               _control_iters.push_back(new_iter);
-
-               // if the x of the current control is less than earliest_control
-               // we have a new earliest_control
-               if (x < earliest_control.x) {
-                       earliest_control = new_iter;
-                       _control_iter = _control_iters.end();
-                       --_control_iter;
-                       // now _control_iter points to the last Element in _control_iters
-               }
-       }
-
-       if (_note_iter != model.notes().end()) {
-               _event = boost::shared_ptr<MIDI::Event>(new MIDI::Event((*_note_iter)->on_event(), true));
-       }
-
-       double time = DBL_MAX;
-       // in case we have no notes in the region, we still want to get controller messages
-       if (_event.get()) {
-               time = _event->time();
-               // if the note is going to make it this turn, advance _note_iter
-               if (earliest_control.x > time) {
-                       _active_notes.push(*_note_iter);
-                       ++_note_iter;
-               }
-       }
-       
-       // <=, because we probably would want to send control events first 
-       if (earliest_control.automation_list.get() && earliest_control.x <= time) {
-               model.control_to_midi_event(_event, earliest_control);
-       } else {
-               _control_iter = _control_iters.end();
-       }
-
-       if ( (! _event.get()) || _event->size() == 0) {
-               //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
-               _is_end = true;
-
-               // eliminate possible race condition here (ugly)
-               static Glib::Mutex mutex;
-               Glib::Mutex::Lock lock(mutex);
-               if (_locked) {
-                       _model->read_unlock();
-                       _locked = false;
-               }
-       } else {
-               //printf("New MIDI Iterator = %X @ %lf\n", _event->type(), _event->time());
-       }
-
-       assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0'));
-}
-
-MidiModel::const_iterator::~const_iterator()
-{
-       if (_locked) {
-               _model->read_unlock();
-       }
-}
-
-const MidiModel::const_iterator& MidiModel::const_iterator::operator++()
-{
-       if (_is_end) {
-               throw std::logic_error("Attempt to iterate past end of MidiModel");
-       }
-       
-       assert(_event->buffer() && _event->buffer()[0] != '\0');
-
-       /*cerr << "const_iterator::operator++: " << _event->to_string() << endl;*/
-
-       if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
-               cerr << "FAILED event buffer: " << hex << int(_event->buffer()[0]) << int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl;
-       }
-       assert((_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()));
-
-       // Increment past current control event
-       if (!_event->is_note() && _control_iter != _control_iters.end() && _control_iter->automation_list.get()) {
-               double x = 0.0, y = 0.0;
-               const bool ret = _control_iter->automation_list->rt_safe_earliest_event_unlocked(
-                               _control_iter->x, DBL_MAX, x, y, false);
-
-               if (ret) {
-                       _control_iter->x = x;
-                       _control_iter->y = y;
-               } else {
-                       _control_iter->automation_list.reset();
-                       _control_iter->x = DBL_MAX;
-               }
-       }
-
-       const std::vector<MidiControlIterator>::iterator old_control_iter = _control_iter;
-       _control_iter = _control_iters.begin();
-
-       // find the _control_iter with the earliest event time
-       for (std::vector<MidiControlIterator>::iterator i = _control_iters.begin();
-                       i != _control_iters.end(); ++i) {
-               if (i->x < _control_iter->x) {
-                       _control_iter = i;
-               }
-       }
-
-       enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
-
-       Type type = NIL;
-       double t = 0;
-
-       // Next earliest note on
-       if (_note_iter != _model->notes().end()) {
-               type = NOTE_ON;
-               t = (*_note_iter)->time();
-       }
-
-       // Use the next earliest note off iff it's earlier than the note on
-       if (_model->note_mode() == Sustained && (! _active_notes.empty())) {
-               if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
-                       type = NOTE_OFF;
-                       t = _active_notes.top()->end_time();
-               }
-       }
-
-       // Use the next earliest controller iff it's earlier than the note event
-       if (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX /*&& _control_iter != old_control_iter */) {
-               if (type == NIL || _control_iter->x < t) {
-                       type = AUTOMATION;
-               }
-       }
-
-       if (type == NOTE_ON) {
-               //cerr << "********** MIDI Iterator = note on" << endl;
-               *_event = (*_note_iter)->on_event();
-               cerr << "Event contents on note on: " << _event->to_string() << endl;
-               _active_notes.push(*_note_iter);
-               ++_note_iter;
-       } else if (type == NOTE_OFF) {
-               //cerr << "********** MIDI Iterator = note off" << endl;
-               *_event = _active_notes.top()->off_event();
-               _active_notes.pop();
-       } else if (type == AUTOMATION) {
-               //cerr << "********** MIDI Iterator = Automation" << endl;
-               _model->control_to_midi_event(_event, *_control_iter);
-       } else {
-               //cerr << "********** MIDI Iterator = End" << endl;
-               _is_end = true;
-       }
-
-       assert(_is_end || _event->size() > 0);
-
-       return *this;
-}
-
-bool MidiModel::const_iterator::operator==(const const_iterator& other) const
-{
-       if (_is_end || other._is_end) {
-               return (_is_end == other._is_end);
-       } else {
-               return (_event == other._event);
-       }
-}
-
-MidiModel::const_iterator& MidiModel::const_iterator::operator=(const const_iterator& other)
-{
-       if (_locked && _model != other._model) {
-               _model->read_unlock();
-       }
-
-       _model         = other._model;
-       _active_notes  = other._active_notes;
-       _is_end        = other._is_end;
-       _locked        = other._locked;
-       _note_iter     = other._note_iter;
-       _control_iters = other._control_iters;
-       size_t index   = other._control_iter - other._control_iters.begin();
-       _control_iter  = _control_iters.begin() + index;
-       
-       if (!_is_end) {
-               _event =  boost::shared_ptr<MIDI::Event>(new MIDI::Event(*other._event, true));
-       }
-
-       return *this;
-}
-
-// MidiModel
 
 MidiModel::MidiModel(MidiSource *s, size_t size)
-       : Automatable(s->session(), "midi model")
-       , _notes(size)
-       , _note_mode(Sustained)
-       , _writing(false)
-       , _edited(false)
-       , _end_iter(*this, DBL_MAX)
-       , _next_read(UINT32_MAX)
-       , _read_iter(*this, DBL_MAX)
+       : ControlSet()
+       , Automatable(s->session(), "midi model")
+       , Sequence(size)
        , _midi_source(s)
 {
-       assert(_end_iter._is_end);
-       assert( ! _end_iter._locked);
 }
 
-/** Read events in frame range \a start .. \a start+cnt into \a dst,
- * adding \a stamp_offset to each event's timestamp.
- * \return number of events written to \a dst
- */
-size_t MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes,
-               nframes_t stamp_offset, nframes_t negative_stamp_offset) const
-{
-       //cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
-       //cerr << this << " MM # notes: " << n_notes() << endl;
-
-       size_t read_events = 0;
-
-       if (start != _next_read) {
-               _read_iter = const_iterator(*this, (double)start);
-               //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
-       } else {
-               //cerr << "Using cached iterator at " << _next_read << endl;
-       }
-
-       _next_read = start + nframes;
-
-       while (_read_iter != end() && _read_iter->time() < start + nframes) {
-               assert(_read_iter->size() > 0);
-               assert(_read_iter->buffer());
-               dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset,
-                         _read_iter->size(), 
-                         _read_iter->buffer());
-               
-                /*cerr << this << " MidiModel::read event @ " << _read_iter->time()  
-                << " type: " << hex << int(_read_iter->type()) << dec 
-                << " note: " << int(_read_iter->note()) 
-                << " velocity: " << int(_read_iter->velocity()) 
-                << endl;*/
-               
-               ++_read_iter;
-               ++read_events;
-       }
-
-       return read_events;
-}
-
-/** Write the controller event pointed to by \a iter to \a ev.
- * The buffer of \a ev will be allocated or resized as necessary.
- * \return true on success
- */
-bool
-MidiModel::control_to_midi_event(boost::shared_ptr<MIDI::Event>& ev, const MidiControlIterator& iter) const
-{
-       assert(iter.automation_list.get());
-       if (!ev) {
-               ev = boost::shared_ptr<MIDI::Event>(new MIDI::Event(0, 3, NULL, true));
-       }
-       
-       switch (iter.automation_list->parameter().type()) {
-       case MidiCCAutomation:
-               assert(iter.automation_list.get());
-               assert(iter.automation_list->parameter().channel() < 16);
-               assert(iter.automation_list->parameter().id() <= INT8_MAX);
-               assert(iter.y <= INT8_MAX);
-               
-               ev->time() = iter.x;
-               ev->realloc(3);
-               ev->buffer()[0] = MIDI_CMD_CONTROL + iter.automation_list->parameter().channel();
-               ev->buffer()[1] = (uint8_t)iter.automation_list->parameter().id();
-               ev->buffer()[2] = (uint8_t)iter.y;
-               break;
-
-       case MidiPgmChangeAutomation:
-               assert(iter.automation_list.get());
-               assert(iter.automation_list->parameter().channel() < 16);
-               assert(iter.automation_list->parameter().id() == 0);
-               assert(iter.y <= INT8_MAX);
-               
-               ev->time() = iter.x;
-               ev->realloc(2);
-               ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.automation_list->parameter().channel();
-               ev->buffer()[1] = (uint8_t)iter.y;
-               break;
-
-       case MidiPitchBenderAutomation:
-               assert(iter.automation_list.get());
-               assert(iter.automation_list->parameter().channel() < 16);
-               assert(iter.automation_list->parameter().id() == 0);
-               assert(iter.y < (1<<14));
-               
-               ev->time() = iter.x;
-               ev->realloc(3);
-               ev->buffer()[0] = MIDI_CMD_BENDER + iter.automation_list->parameter().channel();
-               ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB
-               ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
-               //cerr << "Pitch bender event: " << ev->to_string() << " value: " << ev->pitch_bender_value() << " original value: " << iter.y << std::endl;
-               break;
-
-       case MidiChannelAftertouchAutomation:
-               assert(iter.automation_list.get());
-               assert(iter.automation_list->parameter().channel() < 16);
-               assert(iter.automation_list->parameter().id() == 0);
-               assert(iter.y <= INT8_MAX);
-
-               ev->time() = iter.x;
-               ev->realloc(2);
-               ev->buffer()[0]
-                               = MIDI_CMD_CHANNEL_PRESSURE + iter.automation_list->parameter().channel();
-               ev->buffer()[1] = (uint8_t)iter.y;
-               break;
-
-       default:
-               return false;
-       }
-
-       return true;
-}
-
-
-/** Clear all events from the model.
- */
-void MidiModel::clear()
-{
-       _lock.writer_lock();
-       _notes.clear();
-       clear_automation();
-       _next_read = 0;
-       _read_iter = end();
-       _lock.writer_unlock();
-}
-
-
-/** Begin a write of events to the model.
- *
- * If \a mode is Sustained, complete notes with duration are constructed as note
- * on/off events are received.  Otherwise (Percussive), only note on events are
- * stored; note off events are discarded entirely and all contained notes will
- * have duration 0.
- */
-void MidiModel::start_write()
-{
-       //cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
-       write_lock();
-       _writing = true;
-       for (int i = 0; i < 16; ++i)
-               _write_notes[i].clear();
-       
-       _dirty_automations.clear();
-       write_unlock();
-}
-
-/** Finish a write of events to the model.
- *
- * If \a delete_stuck is true and the current mode is Sustained, note on events
- * that were never resolved with a corresonding note off will be deleted.
- * Otherwise they will remain as notes with duration 0.
- */
-void MidiModel::end_write(bool delete_stuck)
-{
-       write_lock();
-       assert(_writing);
-
-       //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
-
-       if (_note_mode == Sustained && delete_stuck) {
-               for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
-                       if ((*n)->duration() == 0) {
-                               cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
-                               n = _notes.erase(n);
-                               // we have to break here because erase invalidates the iterator
-                               break;
-                       } else {
-                               ++n;
-                       }
-               }
-       }
-
-       for (int i = 0; i < 16; ++i) {
-               if (!_write_notes[i].empty()) {
-                       cerr << "WARNING: MidiModel::end_write: Channel " << i << " has "
-                                       << _write_notes[i].size() << " stuck notes" << endl;
-               }
-               _write_notes[i].clear();
-       }
-
-       for (AutomationLists::const_iterator i = _dirty_automations.begin(); i != _dirty_automations.end(); ++i) {
-               (*i)->Dirty.emit();
-               (*i)->lookup_cache().left = -1;
-               (*i)->search_cache().left = -1;
-       }
-       
-       _writing = false;
-       write_unlock();
-}
-
-/** Append \a in_event to model.  NOT realtime safe.
- *
- * Timestamps of events in \a buf are expected to be relative to
- * the start of this model (t=0) and MUST be monotonically increasing
- * and MUST be >= the latest event currently in the model.
- */
-void MidiModel::append(const MIDI::Event& ev)
-{
-       write_lock();
-       _edited = true;
-
-       assert(_notes.empty() || ev.time() >= _notes.back()->time());
-       assert(_writing);
-
-       if (ev.is_note_on()) {
-               append_note_on_unlocked(ev.channel(), ev.time(), ev.note(),
-                               ev.velocity());
-       } else if (ev.is_note_off()) {
-               append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
-       } else if (ev.is_cc()) {
-               append_automation_event_unlocked(MidiCCAutomation, ev.channel(),
-                               ev.time(), ev.cc_number(), ev.cc_value());
-       } else if (ev.is_pgm_change()) {
-               append_automation_event_unlocked(MidiPgmChangeAutomation, ev.channel(),
-                               ev.time(), ev.pgm_number(), 0);
-       } else if (ev.is_pitch_bender()) {
-               append_automation_event_unlocked(MidiPitchBenderAutomation,
-                               ev.channel(), ev.time(), ev.pitch_bender_lsb(),
-                               ev.pitch_bender_msb());
-       } else if (ev.is_channel_aftertouch()) {
-               append_automation_event_unlocked(MidiChannelAftertouchAutomation,
-                               ev.channel(), ev.time(), ev.channel_aftertouch(), 0);
-       } else {
-               printf("WARNING: MidiModel: Unknown event type %X\n", ev.type());
-       }
-
-       write_unlock();
-}
-
-void MidiModel::append_note_on_unlocked(uint8_t chan, double time,
-               uint8_t note_num, uint8_t velocity)
-{
-       /*cerr << "MidiModel " << this << " chan " << (int)chan <<
-        " note " << (int)note_num << " on @ " << time << endl;*/
-
-       assert(note_num <= 127);
-       assert(chan < 16);
-       assert(_writing);
-       _edited = true;
-
-       boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
-       _notes.push_back(new_note);
-       if (_note_mode == Sustained) {
-               //cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
-               _write_notes[chan].push_back(_notes.size() - 1);
-       }/* else {
-        cerr << "MM Percussive: NOT appending active note on" << endl;
-        }*/
-}
-
-void MidiModel::append_note_off_unlocked(uint8_t chan, double time,
-               uint8_t note_num)
-{
-       /*cerr << "MidiModel " << this << " chan " << (int)chan <<
-        " note " << (int)note_num << " off @ " << time << endl;*/
-
-       assert(note_num <= 127);
-       assert(chan < 16);
-       assert(_writing);
-       _edited = true;
-
-       if (_note_mode == Percussive) {
-               cerr << "MidiModel Ignoring note off (percussive mode)" << endl;
-               return;
-       }
-
-       /* FIXME: make _write_notes fixed size (127 noted) for speed */
-
-       /* FIXME: note off velocity for that one guy out there who actually has
-        * keys that send it */
-
-       bool resolved = false;
-
-       for (WriteNotes::iterator n = _write_notes[chan].begin(); n
-                       != _write_notes[chan].end(); ++n) {
-               Note& note = *_notes[*n].get();
-               if (note.note() == note_num) {
-                       assert(time >= note.time());
-                       note.set_duration(time - note.time());
-                       _write_notes[chan].erase(n);
-                       //cerr << "MM resolved note, duration: " << note.duration() << endl;
-                       resolved = true;
-                       break;
-               }
-       }
-
-       if (!resolved) {
-               cerr << "MidiModel " << this << " spurious note off chan " << (int)chan
-                               << ", note " << (int)note_num << " @ " << time << endl;
-       }
-}
-
-void MidiModel::append_automation_event_unlocked(AutomationType type,
-               uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte)
-{
-       //cerr << "MidiModel " << this << " chan " << (int)chan <<
-       //              " CC " << (int)number << " = " << (int)value << " @ " << time << endl;
-
-       assert(chan < 16);
-       assert(_writing);
-       _edited = true;
-       double value;
-
-       uint32_t id = 0;
-
-       switch (type) {
-       case MidiCCAutomation:
-               id = first_byte;
-               value = double(second_byte);
-               break;
-       case MidiChannelAftertouchAutomation:
-       case MidiPgmChangeAutomation:
-               id = 0;
-               value = double(first_byte);
-               break;
-       case MidiPitchBenderAutomation:
-               id = 0;
-               value = double((0x7F & second_byte) << 7 | (0x7F & first_byte));
-               break;
-       default:
-               assert(false);
-       }
-
-       Parameter param(type, id, chan);
-       boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
-       control->list()->rt_add(time, value);
-}
-
-void MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
-{
-       //cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
-       _edited = true;
-       Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note,
-                       note_time_comparator);
-       _notes.insert(i, note);
-}
-
-void MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
-{
-       _edited = true;
-       //cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
-       for (Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
-               Note& _n = *(*n);
-               const Note& _note = *note;
-               // TODO: There is still the issue, that after restarting ardour
-               // persisted undo does not work, because of rounding errors in the
-               // event times after saving/restoring to/from MIDI files
-               /*cerr << "======================================= " << endl;
-               cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl;
-               cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl;
-               cerr << "Equal: " << bool(_n == _note) << endl;
-               cerr << endl << endl;*/
-               if (_n == _note) {
-                       _notes.erase(n);
-                       // we have to break here, because erase invalidates all iterators, ie. n itself
-                       break;
-               }
-       }
-}
-
-/** Slow!  for debugging only. */
-#ifndef NDEBUG
-bool MidiModel::is_sorted() const {
-       bool t = 0;
-       for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
-               if ((*n)->time() < t)
-                       return false;
-               else
-                       t = (*n)->time();
-
-       return true;
-}
-#endif
-
 /** Start a new command.
  *
  * This has no side-effects on the model or Session, the returned command
@@ -701,7 +61,8 @@ MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name)
  * Ownership of cmd is taken, it must not be deleted by the caller.
  * The command will constitute one item on the undo stack.
  */
-void MidiModel::apply_command(Command* cmd)
+void
+MidiModel::apply_command(Command* cmd)
 {
        _session.begin_reversible_command(cmd->name());
        (*cmd)();
@@ -710,7 +71,8 @@ void MidiModel::apply_command(Command* cmd)
        _edited = true;
 }
 
-// MidiEditCommand
+
+// DeltaCommand
 
 MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
                const std::string& name)
@@ -727,21 +89,24 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
        set_state(node);
 }
 
-void MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note)
+void
+MidiModel::DeltaCommand::add(const boost::shared_ptr<Evoral::Note> note)
 {
        //cerr << "MEC: apply" << endl;
        _removed_notes.remove(note);
        _added_notes.push_back(note);
 }
 
-void MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note)
+void
+MidiModel::DeltaCommand::remove(const boost::shared_ptr<Evoral::Note> note)
 {
        //cerr << "MEC: remove" << endl;
        _added_notes.remove(note);
        _removed_notes.push_back(note);
 }
 
-void MidiModel::DeltaCommand::operator()()
+void
+MidiModel::DeltaCommand::operator()()
 {
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
@@ -763,10 +128,10 @@ void MidiModel::DeltaCommand::operator()()
 
        _model->write_lock();
 
-       for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
+       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
                _model->add_note_unlocked(*i);
 
-       for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
+       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
                _model->remove_note_unlocked(*i);
 
        _model->write_unlock();
@@ -778,7 +143,8 @@ void MidiModel::DeltaCommand::operator()()
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
-void MidiModel::DeltaCommand::undo()
+void
+MidiModel::DeltaCommand::undo()
 {
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
@@ -800,11 +166,11 @@ void MidiModel::DeltaCommand::undo()
 
        _model->write_lock();
 
-       for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i
+       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _added_notes.begin(); i
                        != _added_notes.end(); ++i)
                _model->remove_note_unlocked(*i);
 
-       for (std::list< boost::shared_ptr<Note> >::iterator i =
+       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i =
                        _removed_notes.begin(); i != _removed_notes.end(); ++i)
                _model->add_note_unlocked(*i);
 
@@ -817,7 +183,8 @@ void MidiModel::DeltaCommand::undo()
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
-XMLNode & MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> note)
+XMLNode&
+MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Evoral::Note> note)
 {
        XMLNode *xml_note = new XMLNode("note");
        ostringstream note_str(ios::ate);
@@ -843,7 +210,7 @@ XMLNode & MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> no
        return *xml_note;
 }
 
-boost::shared_ptr<Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
+boost::shared_ptr<Evoral::Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
 {
        unsigned int note;
        istringstream note_str(xml_note->property("note")->value());
@@ -865,7 +232,7 @@ boost::shared_ptr<Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_not
        istringstream velocity_str(xml_note->property("velocity")->value());
        velocity_str >> velocity;
 
-       boost::shared_ptr<Note> note_ptr(new Note(channel, time, duration, note, velocity));
+       boost::shared_ptr<Evoral::Note> note_ptr(new Evoral::Note(channel, time, duration, note, velocity));
        return note_ptr;
 }
 
@@ -913,8 +280,8 @@ XMLNode& MidiModel::DeltaCommand::get_state()
 }
 
 struct EventTimeComparator {
-       typedef const MIDI::Event* value_type;
-       inline bool operator()(const MIDI::Event& a, const MIDI::Event& b) const {
+       typedef const Evoral::Event* value_type;
+       inline bool operator()(const Evoral::Event& a, const Evoral::Event& b) const {
                return a.time() >= b.time();
        }
 };
@@ -930,14 +297,14 @@ bool MidiModel::write_to(boost::shared_ptr<MidiSource> source)
 {
        read_lock();
 
-       const NoteMode old_note_mode = _note_mode;
-       _note_mode = Sustained;
+       const bool old_percussive = percussive();
+       set_percussive(false);
        
        for (const_iterator i = begin(); i != end(); ++i) {
                source->append_event_unlocked(Frames, *i);
        }
                
-       _note_mode = old_note_mode;
+       set_percussive(old_percussive);
        
        read_unlock();
        _edited = false;
index 4e83413c135fe9a0bbd630295b23783f1a22509e..d6d27a1f89d84a118f670f0523ce294f47f109c4 100644 (file)
@@ -106,7 +106,7 @@ MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nfra
        Glib::Mutex::Lock lm (_lock);
        if (_model) {
                //const size_t n_events =
-               _model->read(dst, start, cnt, stamp_offset, negative_stamp_offset);
+               _model->read(dst, start, cnt, stamp_offset - negative_stamp_offset);
                //cout << "Read " << n_events << " events from model." << endl;
                return cnt;
        } else {
index f33c58a0fd2f6c5e1dea6599c967af2aa4e7b954..c11963c9d50846832d38fd01ae7572cd7d2ec0d9 100644 (file)
@@ -91,7 +91,7 @@ MidiStretch::run (boost::shared_ptr<Region> r)
                const double new_time = i->time() * _request.time_fraction;
                
                // FIXME: double copy
-               MIDI::Event ev = MIDI::Event(*i, true);
+               Evoral::Event ev = Evoral::Event(*i, true);
                ev.time() = new_time;
                new_model->append(ev);
        }
index 4588d6f5b910a1cdb8e14f3e4f4085f39d5e6ac9..c1203d7ba953456e49153ee016153c52e56c109f 100644 (file)
@@ -590,7 +590,7 @@ MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_fra
        
        uint8_t buf[3]; // CC = 3 bytes
        buf[0] = MIDI_CMD_CONTROL;
-       MIDI::Event ev(0, 3, buf, false);
+       Evoral::Event ev(0, 3, buf, false);
 
        // Write track controller automation
        // This now lives in MidiModel.  Any need for track automation like this?
@@ -733,7 +733,7 @@ MidiTrack::MidiControl::set_value(float val)
        assert(val <= _list->parameter().max());
        size_t size = 3;
 
-       if ( ! _list->automation_playback()) {
+       if ( ! automation_playback()) {
                uint8_t ev[3] = { _list->parameter().channel(), int(val), 0 };
                switch(_list->parameter().type()) {
                case MidiCCAutomation:
diff --git a/libs/ardour/note.cc b/libs/ardour/note.cc
deleted file mode 100644 (file)
index ea1e713..0000000
+++ /dev/null
@@ -1,110 +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.
-
-*/
-
-#include <ardour/note.h>
-#include <iostream>
-
-namespace ARDOUR {
-
-Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
-       : _on_event(t, 3, NULL, true)
-       , _off_event(t + d, 3, NULL, true)
-{
-       assert(chan < 16);
-
-       _on_event.buffer()[0] = MIDI_CMD_NOTE_ON + chan;
-       _on_event.buffer()[1] = n;
-       _on_event.buffer()[2] = v;
-       
-       _off_event.buffer()[0] = MIDI_CMD_NOTE_OFF + chan;
-       _off_event.buffer()[1] = n;
-       _off_event.buffer()[2] = 0x40;
-       
-       assert(time() == t);
-       assert(duration() == d);
-       assert(note() == n);
-       assert(velocity() == v);
-       assert(_on_event.channel() == _off_event.channel());
-       assert(channel() == chan);
-}
-
-
-Note::Note(const Note& copy)
-       : _on_event(copy._on_event, true)
-       , _off_event(copy._off_event, true)
-{
-       assert(_on_event.buffer());
-       assert(_off_event.buffer());
-       /*
-       assert(copy._on_event.size == 3);
-       _on_event.buffer = _on_event_buffer;
-       memcpy(_on_event_buffer, copy._on_event_buffer, 3);
-       
-       assert(copy._off_event.size == 3);
-       _off_event.buffer = _off_event_buffer;
-       memcpy(_off_event_buffer, copy._off_event_buffer, 3);
-       */
-
-       assert(time() == copy.time());
-       assert(end_time() == copy.end_time());
-       assert(note() == copy.note());
-       assert(velocity() == copy.velocity());
-       assert(duration() == copy.duration());
-       assert(_on_event.channel() == _off_event.channel());
-       assert(channel() == copy.channel());
-}
-
-Note::~Note()
-{
-       std::cerr << "Note::~Note() Note time: " << time()
-                << " pitch: " << int(note()) 
-            << " duration: " << duration() 
-            << "   end-time: " << end_time() 
-            << "   velocity: " << int(velocity()) 
-            << std::endl;
-}
-
-
-const Note&
-Note::operator=(const Note& copy)
-{
-       _on_event = copy._on_event;
-       _off_event = copy._off_event;
-       /*_on_event.time = copy._on_event.time;
-       assert(copy._on_event.size == 3);
-       memcpy(_on_event_buffer, copy._on_event_buffer, 3);
-       
-       _off_event.time = copy._off_event.time;
-       assert(copy._off_event.size == 3);
-       memcpy(_off_event_buffer, copy._off_event_buffer, 3);
-       */
-       
-       assert(time() == copy.time());
-       assert(end_time() == copy.end_time());
-       assert(note() == copy.note());
-       assert(velocity() == copy.velocity());
-       assert(duration() == copy.duration());
-       assert(_on_event.channel() == _off_event.channel());
-       assert(channel() == copy.channel());
-       
-       return *this;
-}
-
-} // namespace ARDOUR
index 2163ba5cc0a4554a8cdcede85af0fad45db3c6b6..b89c021042346113ca3b0f153f69419b4d473294 100644 (file)
@@ -228,7 +228,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
 
        /* now that we are done loading */
 
-       _control->list()->StateChanged ();
+       ((AutomationList*)_control->list().get())->StateChanged ();
 
        return 0;
 }
@@ -486,7 +486,7 @@ EqualPowerStereoPanner::state (bool full_state)
        root->add_property (X_("type"), EqualPowerStereoPanner::name);
 
        XMLNode* autonode = new XMLNode (X_("Automation"));
-       autonode->add_child_nocopy (_control->list()->state (full_state));
+       autonode->add_child_nocopy (((AutomationList*)_control->list().get())->state (full_state));
        root->add_child_nocopy (*autonode);
 
        StreamPanner::add_state (*root);
@@ -519,9 +519,9 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
 
                } else if ((*iter)->name() == X_("Automation")) {
 
-                       _control->list()->set_state (*((*iter)->children().front()));
+                       _control->alist()->set_state (*((*iter)->children().front()));
 
-                       if (_control->list()->automation_state() != Off) {
+                       if (_control->alist()->automation_state() != Off) {
                                set_position (_control->list()->eval (parent.session().transport_frame()));
                        }
                }
@@ -917,7 +917,7 @@ void
 Panner::set_automation_style (AutoStyle style)
 {
        for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
-               (*i)->pan_control()->list()->set_automation_style (style);
+               ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
        }
        _session.set_dirty ();
 }      
@@ -926,7 +926,7 @@ void
 Panner::set_automation_state (AutoState state)
 {
        for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
-               (*i)->pan_control()->list()->set_automation_state (state);
+               ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
        }
        _session.set_dirty ();
 }      
@@ -935,7 +935,7 @@ AutoState
 Panner::automation_state () const
 {
        if (!empty()) {
-               return front()->pan_control()->list()->automation_state ();
+               return ((AutomationList*)front()->pan_control()->list().get())->automation_state ();
        } else {
                return Off;
        }
@@ -945,7 +945,7 @@ AutoStyle
 Panner::automation_style () const
 {
        if (!empty()) {
-               return front()->pan_control()->list()->automation_style ();
+               return ((AutomationList*)front()->pan_control()->list().get())->automation_style ();
        } else {
                return Absolute;
        }
@@ -955,7 +955,7 @@ void
 Panner::transport_stopped (nframes_t frame)
 {
        for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
-               (*i)->pan_control()->list()->reposition_for_rt_add (frame);
+               ((AutomationList*)(*i)->pan_control()->list().get())->reposition_for_rt_add (frame);
        }
 }      
 
@@ -963,7 +963,7 @@ void
 Panner::snapshot (nframes_t now)
 {
        for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
-               boost::shared_ptr<AutomationList> list = (*i)->pan_control()->list();
+               AutomationList* list = ((AutomationList*)(*i)->pan_control()->list().get());
                if (list->automation_write())
                        list->rt_add(now, (*i)->pan_control()->get_value());
        }
@@ -1127,7 +1127,7 @@ bool
 Panner::touching () const
 {
        for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) {
-               if ((*i)->pan_control()->list()->touching ()) {
+               if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
                        return true;
                }
        }
index 1b94e5dc4d38d424fa7b390fc4bcba21c992d372..ea70ea7927e94e640f170ab3084349df2ea4b6a7 100644 (file)
@@ -26,9 +26,7 @@ using namespace ARDOUR;
  * (AutomationList automation-id property)
  */
 Parameter::Parameter(const std::string& str)
-       : _type(NullAutomation)
-       , _id(0)
-       , _channel(0)
+       : Evoral::Parameter (NullAutomation, 0)
 {
        if (str == "gain") {
                _type = GainAutomation;
@@ -80,6 +78,8 @@ Parameter::Parameter(const std::string& str)
        } else {
                PBD::warning << "Unknown Parameter '" << str << "'" << endmsg;
        }
+
+       init((AutomationType)_type); // set min/max/normal
 }
 
 
@@ -87,7 +87,7 @@ Parameter::Parameter(const std::string& str)
  * e.g. <AutomationList automation-id="whatthisreturns">
  */
 std::string
-Parameter::to_string() const
+Parameter::symbol() const
 {
        if (_type == GainAutomation) {
                return "gain";
index f44a5df003b1f3329337bbefeaee749d4b044cdc..8158e55cc295bc65e54960d66540791ef0f479ef 100644 (file)
@@ -152,9 +152,10 @@ PluginInsert::auto_state_changed (Parameter which)
        if (which.type() != PluginAutomation)
                return;
 
-       boost::shared_ptr<AutomationControl> c = control (which);
+       boost::shared_ptr<AutomationControl> c
+                       = boost::dynamic_pointer_cast<AutomationControl>(control (which));
 
-       if (c && c->list()->automation_state() != Off) {
+       if (c && ((AutomationList*)c->list().get())->automation_state() != Off) {
                _plugins[0]->set_parameter (which.id(), c->list()->eval (_session.transport_frame()));
        }
 }
@@ -225,15 +226,10 @@ PluginInsert::set_automatable ()
                if (i->type() == PluginAutomation) {
                        can_automate (*i);
                        _plugins.front()->get_parameter_descriptor(i->id(), desc);
-                       boost::shared_ptr<AutomationList> list(new AutomationList(
-                                       *i,
-                                       //(desc.min_unbound ? FLT_MIN : desc.lower),
-                                       //(desc.max_unbound ? FLT_MAX : desc.upper),
-                                       desc.lower, desc.upper,
-                                       _plugins.front()->default_value(i->id())));
-
-                       add_control(boost::shared_ptr<AutomationControl>(
-                                       new PluginControl(*this, list)));
+                       Parameter param(*i);
+                       param.set_range(desc.lower, desc.upper, _plugins.front()->default_value(i->id()));
+                       boost::shared_ptr<AutomationList> list(new AutomationList(param));
+                       add_control(boost::shared_ptr<AutomationControl>(new PluginControl(*this, list)));
                }
        }
 }
@@ -296,9 +292,10 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off
                
                for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li, ++n) {
                        
-                       boost::shared_ptr<AutomationControl> c = li->second;
+                       boost::shared_ptr<AutomationControl> c
+                               = boost::dynamic_pointer_cast<AutomationControl>(li->second);
 
-                       if (c->parameter().type() == PluginAutomation && c->list()->automation_playback()) {
+                       if (c->parameter().type() == PluginAutomation && c->automation_playback()) {
                                bool valid;
 
                                const float val = c->list()->rt_safe_eval (now, valid);                         
@@ -371,8 +368,7 @@ PluginInsert::set_parameter (Parameter param, float val)
 
        _plugins[0]->set_parameter (param.id(), val);
        
-       boost::shared_ptr<AutomationControl> c = control (param);
-       
+       boost::shared_ptr<Evoral::Control> c = control (param);
        if (c)
                c->set_value(val);
 
@@ -396,7 +392,7 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs
        nframes_t now = _session.transport_frame ();
        nframes_t end = now + nframes;
 
-       Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK);
+       Glib::Mutex::Lock lm (_control_lock, Glib::TRY_LOCK);
 
        if (!lm.locked()) {
                connect_and_run (bufs, nframes, offset, false);
@@ -434,7 +430,7 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs
 }      
 
 float
-PluginInsert::default_parameter_value (Parameter param)
+PluginInsert::default_parameter_value (Evoral::Parameter param)
 {
        if (param.type() != PluginAutomation)
                return 1.0;
@@ -640,7 +636,7 @@ PluginInsert::state (bool full)
                child->add_child_nocopy (automation_list (*x).state (full));
                autonode->add_child_nocopy (*child);
                */
-               autonode->add_child_nocopy (control(*x)->list()->state (full));
+               autonode->add_child_nocopy (((AutomationList*)control(*x)->list().get())->state (full));
        }
 
        node.add_child_nocopy (*autonode);
@@ -763,8 +759,11 @@ PluginInsert::set_state(const XMLNode& node)
                                continue;
                        }
 
+                       boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(
+                                       control(Parameter(PluginAutomation, port_id), true));
+
                        if (!child->children().empty()) {
-                               control (Parameter(PluginAutomation, port_id), true)->list()->set_state (*child->children().front());
+                               c->alist()->set_state (*child->children().front());
                        } else {
                                if ((cprop = child->property("auto")) != 0) {
                                        
@@ -772,13 +771,13 @@ PluginInsert::set_state(const XMLNode& node)
 
                                        int x;
                                        sscanf (cprop->value().c_str(), "0x%x", &x);
-                                       control (Parameter(PluginAutomation, port_id), true)->list()->set_automation_state (AutoState (x));
+                                       c->alist()->set_automation_state (AutoState (x));
 
                                } else {
                                        
                                        /* missing */
                                        
-                                       control (Parameter(PluginAutomation, port_id), true)->list()->set_automation_state (Off);
+                                       c->alist()->set_automation_state (Off);
                                }
                        }
 
index 638c0260569df83a6a7849ed39f92e28115d7221..79d2b4f9b6da17d479a7f3d2451443c88620cd28 100644 (file)
@@ -2528,11 +2528,11 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra
        apply_gain_automation = false;
 
        { 
-               Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
+               Glib::Mutex::Lock am (_control_lock, Glib::TRY_LOCK);
                
                if (am.locked() && _session.transport_rolling()) {
                        
-                       if (_gain_control->list()->automation_playback()) {
+                       if (_gain_control->alist()->automation_playback()) {
                                apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
                                                start_frame, end_frame, _session.gain_automation_buffer(), nframes);
                        }
index bb43d4791a55e4da634f8787626bb1406e9266fa..2d5c0af0d599276f3fe6d4ca8d9f348c1d6819fb 100644 (file)
@@ -450,7 +450,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
        if (_model && ! _model->writing())
                _model->start_write();
 
-       MIDI::Event ev(0.0, 4, NULL, true);
+       Evoral::Event ev(0.0, 4, NULL, true);
 
        while (true) {
                bool ret = src.full_peek(sizeof(double), (uint8_t*)&time);
@@ -500,7 +500,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
                
 
 void
-SMFSource::append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev)
+SMFSource::append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev)
 {
        if (ev.size() == 0)
                return;
@@ -925,7 +925,7 @@ SMFSource::load_model(bool lock, bool force_reload)
        fseek(_fd, _header_size, SEEK_SET);
 
        uint64_t time = 0; /* in SMF ticks */
-       MIDI::Event ev;
+       Evoral::Event ev;
        
        size_t scratch_size = 0; // keep track of scratch and minimize reallocs
        
diff --git a/libs/evoral/SConscript b/libs/evoral/SConscript
new file mode 100644 (file)
index 0000000..fcc29c9
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- python -*-
+
+import os
+import os.path
+import glob
+
+Import('env libraries install_prefix')
+
+evoral = env.Clone()
+evoral.Merge([
+       libraries['glibmm2'],
+       libraries['xml'], 
+       libraries['pbd'],       
+       ])
+
+if evoral['IS_OSX']:
+       evoral.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
+
+domain = 'evoral'
+
+evoral.Append(DOMAIN=domain, MAJOR=1, MINOR=0, MICRO=0)
+evoral.Append(CXXFLAGS="-DEVENT_WITH_XML")
+
+sources = Split("""
+src/Control.cpp
+src/ControlList.cpp
+src/ControlSet.cpp
+src/Event.cpp
+src/Note.cpp
+src/Sequence.cpp
+src/Curve.cpp
+""")
+
+libevoral = evoral.SharedLibrary('evoral', [ sources ])
+
+Default(libevoral)
+
+env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour3'), libevoral))
+
+env.Alias('tarball', env.Distribute (env['DISTTREE'],
+                                     [ 'SConscript' ] + sources +
+                                     glob.glob('midi++/*.h')))
diff --git a/libs/evoral/evoral/Control.hpp b/libs/evoral/evoral/Control.hpp
new file mode 100644 (file)
index 0000000..ed95752
--- /dev/null
@@ -0,0 +1,58 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_CONTROL_HPP
+#define EVORAL_CONTROL_HPP
+
+#include <set>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <glibmm/thread.h>
+#include <evoral/types.hpp>
+#include <evoral/Parameter.hpp>
+
+namespace Evoral {
+
+class ControlList;
+class Transport;
+
+class Control
+{
+public:
+       Control(boost::shared_ptr<ControlList>);
+       virtual ~Control() {}
+
+       void  set_value(float val, bool to_list=false, nframes_t frame=0);
+       float get_value(bool from_list=false, nframes_t frame=0) const;
+       float user_value() const;
+
+       void set_list(boost::shared_ptr<ControlList>);
+
+       boost::shared_ptr<ControlList>       list()       { return _list; }
+       boost::shared_ptr<const ControlList> list() const { return _list; }
+
+       Parameter parameter() const;
+
+protected:
+       boost::shared_ptr<ControlList> _list;
+       float                          _user_value;
+};
+
+} // namespace Evoral
+
+#endif // EVORAL_CONTROL_HPP
diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp
new file mode 100644 (file)
index 0000000..c035b6d
--- /dev/null
@@ -0,0 +1,274 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_CONTROL_LIST_HPP
+#define EVORAL_CONTROL_LIST_HPP
+
+#include <list>
+#include <boost/pool/pool.hpp>
+#include <boost/pool/pool_alloc.hpp>
+#include <glibmm/thread.h>
+#include <evoral/types.hpp>
+#include <evoral/Parameter.hpp>
+#include <evoral/Curve.hpp>
+
+namespace Evoral {
+
+
+/** A single event (time-stamped value) for a control
+ */
+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
+};
+
+
+/** Pool allocator for control lists that does not use a lock
+ * and allocates 8k blocks of new pointers at a time
+ */
+typedef boost::fast_pool_allocator<
+               ControlEvent*,
+               boost::default_user_allocator_new_delete,
+               boost::details::pool::null_mutex,
+               8192>
+       ControlEventAllocator;
+
+
+/** A list (sequence) of time-stamped values for a control
+ */
+class ControlList
+{
+public:
+       typedef std::list<ControlEvent*,ControlEventAllocator> EventList;
+       typedef EventList::iterator iterator;
+       typedef EventList::reverse_iterator reverse_iterator;
+       typedef EventList::const_iterator const_iterator;
+
+       ControlList (Parameter id);
+       //ControlList (const XMLNode&, Parameter id);
+       ~ControlList();
+       
+       virtual boost::shared_ptr<ControlList> create(Parameter id);
+
+       ControlList (const ControlList&);
+       ControlList (const ControlList&, double start, double end);
+       ControlList& operator= (const ControlList&);
+       bool operator== (const ControlList&);
+       
+       void freeze();
+       void thaw ();
+
+       const Parameter& parameter() const          { return _parameter; }
+       void             set_parameter(Parameter p) { _parameter = p; }
+
+       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);
+       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);
+
+       boost::shared_ptr<ControlList> cut (double, double);
+       boost::shared_ptr<ControlList> copy (double, double);
+       void clear (double, double);
+
+       boost::shared_ptr<ControlList> cut (iterator, iterator);
+       boost::shared_ptr<ControlList> copy (iterator, iterator);
+       void clear (iterator, iterator);
+
+       bool paste (ControlList&, double position, float times);
+       
+       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(); }
+       const_iterator      begin() const { return _events.begin(); }
+       iterator            end()         { return _events.end(); }
+       const_iterator      end()   const { return _events.end(); }
+       ControlEvent*       back()        { return _events.back(); }
+       const ControlEvent* back()  const { return _events.back(); }
+       ControlEvent*       front()       { return _events.front(); }
+       const ControlEvent* front() const { return _events.front(); }
+
+       std::pair<ControlList::iterator,ControlList::iterator> control_points_adjacent (double when);
+
+       template<class T> void apply_to_points (T& obj, void (T::*method)(const ControlList&)) {
+               Glib::Mutex::Lock lm (_lock);
+               (obj.*method)(*this);
+       }
+
+       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<ControlList::const_iterator,ControlList::const_iterator> 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<ControlList::const_iterator,ControlList::const_iterator> range;
+       };
+
+       const EventList& events() const { return _events; }
+       double default_value() const { return _parameter.normal(); }
+
+       // FIXME: const violations for Curve
+       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; }
+       
+       virtual void mark_dirty () const;
+
+       enum InterpolationStyle {
+               Discrete,
+               Linear,
+               Curved
+       };
+
+       InterpolationStyle interpolation() const { return _interpolation; }
+       void set_interpolation(InterpolationStyle style) { _interpolation = style; }
+
+protected:
+
+       /** 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;
+
+       boost::shared_ptr<ControlList> cut_copy_clear (double, double, int op);
+
+       virtual void maybe_signal_changed ();
+       
+       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;
+       bool                _new_value;
+       double              _max_xval;
+       double              _min_yval;
+       double              _max_yval;
+       double              _default_value;
+       bool                _sort_pending;
+       iterator            _rt_insertion_point;
+       double              _rt_pos;
+
+       Curve* _curve;
+};
+
+} // namespace Evoral
+
+#endif // EVORAL_CONTROL_LIST_HPP
+
diff --git a/libs/evoral/evoral/ControlSet.hpp b/libs/evoral/evoral/ControlSet.hpp
new file mode 100644 (file)
index 0000000..ba6e5e5
--- /dev/null
@@ -0,0 +1,69 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_CONTROLLABLE_HPP
+#define EVORAL_CONTROLLABLE_HPP
+
+#include <set>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <glibmm/thread.h>
+#include <evoral/types.hpp>
+#include <evoral/Parameter.hpp>
+
+namespace Evoral {
+
+class Control;
+class ControlList;
+class ControlEvent;
+
+class ControlSet {
+public:
+       ControlSet();
+       virtual ~ControlSet() {}
+
+       virtual boost::shared_ptr<Control> control(Evoral::Parameter id, bool create_if_missing=false);
+       virtual boost::shared_ptr<const Control> control(Evoral::Parameter id) const;
+       
+       virtual boost::shared_ptr<Control> control_factory(boost::shared_ptr<ControlList> list) const;
+       virtual boost::shared_ptr<ControlList> control_list_factory(const Parameter& param) const;
+       
+       typedef std::map< Parameter, boost::shared_ptr<Control> > Controls;
+       Controls&       controls()       { return _controls; }
+       const Controls& controls() const { return _controls; }
+
+       virtual void add_control(boost::shared_ptr<Control>);
+
+       virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
+       
+       virtual float default_parameter_value(Parameter param) { return 1.0f; }
+
+       virtual void clear();
+
+       void what_has_data(std::set<Parameter>&) const;
+       
+       Glib::Mutex& control_lock() const { return _control_lock; }
+
+protected:
+       mutable Glib::Mutex _control_lock;
+       Controls            _controls;
+};
+
+} // namespace Evoral
+
+#endif // EVORAL_CONTROLLABLE_HPP
diff --git a/libs/evoral/evoral/Curve.hpp b/libs/evoral/evoral/Curve.hpp
new file mode 100644 (file)
index 0000000..7cfc6bc
--- /dev/null
@@ -0,0 +1,58 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_CURVE_HPP
+#define EVORAL_CURVE_HPP
+
+#include <inttypes.h>
+#include <boost/utility.hpp>
+
+namespace Evoral {
+
+class ControlList;
+
+class Curve : public boost::noncopyable
+{
+public:
+       Curve (const ControlList& cl);
+
+       bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen);
+       void get_vector (double x0, double x1, float *arg, int32_t veclen);
+
+       void solve ();
+       
+       void mark_dirty() const { _dirty = true; }
+
+private:
+       double unlocked_eval (double where);
+       double multipoint_eval (double x);
+
+       void _get_vector (double x0, double x1, float *arg, int32_t veclen);
+
+       mutable bool       _dirty;
+       const ControlList& _list;
+};
+
+} // namespace Evoral
+
+extern "C" {
+       void curve_get_vector_from_c (void *arg, double, double, float*, int32_t);
+}
+
+#endif // EVORAL_CURVE_HPP
+
diff --git a/libs/evoral/evoral/Event.hpp b/libs/evoral/evoral/Event.hpp
new file mode 100644 (file)
index 0000000..beffb01
--- /dev/null
@@ -0,0 +1,219 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_EVENT_HPP
+#define EVORAL_EVENT_HPP
+
+#include <stdint.h>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+#include <assert.h>
+#include <evoral/midi_events.h>
+#ifdef EVENT_WITH_XML
+       #include <pbd/xml++.h>
+#endif
+
+/** If this is not defined, all methods of MidiEvent are RT safe
+ * but MidiEvent will never deep copy and (depending on the scenario)
+ * may not be usable in STL containers, signals, etc. 
+ */
+#define EVENT_ALLOW_ALLOC 1
+
+//#define EVENT_WITH_XML
+
+namespace Evoral {
+
+
+/** Identical to jack_midi_event_t, but with double timestamp
+ *
+ * time is either a frame time (from/to Jack) or a beat time (internal
+ * tempo time, used in MidiModel) depending on context.
+ */
+struct Event {
+#ifdef EVENT_ALLOW_ALLOC
+       Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false);
+       
+       /** Copy \a copy.
+        * 
+        * If \a owns_buffer is true, the buffer will be copied and this method
+        * is NOT REALTIME SAFE.  Otherwise both events share a buffer and
+        * memory management semantics are the caller's problem.
+        */
+       Event(const Event& copy, bool owns_buffer);
+       
+#ifdef EVENT_WITH_XML
+       /** Event from XML ala http://www.midi.org/dtds/MIDIEvents10.dtd
+        */
+       Event(const XMLNode& event);
+       
+       /** Event to XML ala http://www.midi.org/dtds/MIDIEvents10.dtd
+        */
+       boost::shared_ptr<XMLNode> to_xml() const;
+#endif
+       
+       ~Event();
+
+       inline const Event& operator=(const Event& copy) {
+               _time = copy._time;
+               if (_owns_buffer) {
+                       if (copy._buffer) {
+                               if (copy._size > _size) {
+                                       _buffer = (uint8_t*)::realloc(_buffer, copy._size);
+                               }
+                               memcpy(_buffer, copy._buffer, copy._size);
+                       } else {
+                               free(_buffer);
+                               _buffer = NULL;
+                       }
+               } else {
+                       _buffer = copy._buffer;
+               }
+
+               _size = copy._size;
+               return *this;
+       }
+
+       inline void shallow_copy(const Event& copy) {
+               if (_owns_buffer) {
+                       free(_buffer);
+                       _buffer = false;
+                       _owns_buffer = false;
+               }
+
+               _time = copy._time;
+               _size = copy._size;
+               _buffer = copy._buffer;
+       }
+       
+       inline void set(uint8_t* buf, size_t size, double t) {
+               if (_owns_buffer) {
+                       if (_size < size) {
+                               _buffer = (uint8_t*) ::realloc(_buffer, size);
+                       }
+                       memcpy (_buffer, buf, size);
+               } else {
+                       _buffer = buf;
+               }
+
+               _size = size;
+               _time = t;
+       }
+
+       inline bool operator==(const Event& other) const {
+               if (_time != other._time)
+                       return false;
+
+               if (_size != other._size)
+                       return false;
+
+               if (_buffer == other._buffer)
+                       return true;
+
+               for (size_t i=0; i < _size; ++i)
+                       if (_buffer[i] != other._buffer[i])
+                               return false;
+
+               return true;
+       }
+       
+       inline bool operator!=(const Event& other) const { return ! operator==(other); }
+
+       inline bool owns_buffer() const { return _owns_buffer; }
+       
+       inline void set_buffer(size_t size, uint8_t* buf, bool own) {
+               if (_owns_buffer) {
+                       free(_buffer);
+                       _buffer = NULL;
+               }
+               _size = size;
+               _buffer = buf;
+               _owns_buffer = own;
+       }
+
+       inline void realloc(size_t size) {
+               if (_owns_buffer) {
+                       if (size > _size)
+                               _buffer = (uint8_t*) ::realloc(_buffer, size);
+               } else {
+                       _buffer = (uint8_t*) ::malloc(size);
+                       _owns_buffer = true;
+               }
+
+               _size = size;
+       }
+
+
+#else
+
+       inline void set_buffer(uint8_t* buf) { _buffer = buf; }
+
+#endif // EVENT_ALLOW_ALLOC
+
+       inline double      time()                  const { return _time; }
+       inline double&     time()                        { return _time; }
+       inline uint32_t    size()                  const { return _size; }
+       inline uint32_t&   size()                        { return _size; }
+       inline uint8_t     type()                  const { return (_buffer[0] & 0xF0); }
+       inline void        set_type(uint8_t type)        { _buffer[0] =   (0x0F & _buffer[0])
+                                                                       | (0xF0 & type); }
+       inline uint8_t     channel()               const { return (_buffer[0] & 0x0F); }
+       inline void        set_channel(uint8_t channel)  { _buffer[0] =   (0xF0 & _buffer[0])
+                                                                       | (0x0F & channel); }
+       inline bool        is_note_on()            const { return (type() == MIDI_CMD_NOTE_ON); }
+       inline bool        is_note_off()           const { return (type() == MIDI_CMD_NOTE_OFF); }
+       inline bool        is_cc()                 const { return (type() == MIDI_CMD_CONTROL); }
+       inline bool        is_pitch_bender()       const { return (type() == MIDI_CMD_BENDER); }
+       inline bool        is_pgm_change()         const { return (type() == MIDI_CMD_PGM_CHANGE); }
+       inline bool        is_note()               const { return (is_note_on() || is_note_off()); }
+       inline bool        is_aftertouch()         const { return (type() == MIDI_CMD_NOTE_PRESSURE); }
+       inline bool        is_channel_aftertouch() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); }
+       inline uint8_t     note()                  const { return (_buffer[1]); }
+       inline uint8_t     velocity()              const { return (_buffer[2]); }
+       inline uint8_t     cc_number()             const { return (_buffer[1]); }
+       inline uint8_t     cc_value()              const { return (_buffer[2]); }
+       inline uint8_t     pitch_bender_lsb()      const { return (_buffer[1]); }
+       inline uint8_t     pitch_bender_msb()      const { return (_buffer[2]); }
+       inline uint16_t    pitch_bender_value()    const { return ( ((0x7F & _buffer[2]) << 7)
+                                                                  | (0x7F & _buffer[1]) ); }
+       inline uint8_t     pgm_number()            const { return (_buffer[1]); }
+       inline void        set_pgm_number(uint8_t number){ _buffer[1] = number; }
+       inline uint8_t     aftertouch()            const { return (_buffer[1]); }
+       inline uint8_t     channel_aftertouch()    const { return (_buffer[1]); }
+       inline bool        is_channel_event()      const { return (0x80 <= type()) && (type() <= 0xE0); }
+       inline bool        is_smf_meta_event()     const { return _buffer[0] == 0xFF; }
+       inline bool        is_sysex()              const { return    _buffer[0] == 0xF0
+                                                                 || _buffer[0] == 0xF7; }
+       inline const uint8_t* buffer()             const { return _buffer; }
+       inline uint8_t*&      buffer()                   { return _buffer; }
+
+private:
+       double   _time;   /**< Sample index (or beat time) at which event is valid */
+       uint32_t _size;   /**< Number of uint8_ts of data in \a buffer */
+       uint8_t* _buffer; /**< Raw MIDI data */
+
+#ifdef EVENT_ALLOW_ALLOC
+       bool _owns_buffer; /**< Whether buffer is locally allocated */
+#endif
+};
+
+
+} // namespace Evoral
+
+#endif // EVORAL_EVENT_HPP
+
diff --git a/libs/evoral/evoral/EventSink.hpp b/libs/evoral/evoral/EventSink.hpp
new file mode 100644 (file)
index 0000000..fde6399
--- /dev/null
@@ -0,0 +1,40 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_EVENT_SINK_HPP
+#define EVORAL_EVENT_SINK_HPP
+
+#include <evoral/types.hpp>
+
+namespace Evoral {
+
+
+/** Pure virtual base for anything you can write events to.
+ */
+class EventSink {
+public:
+       virtual size_t write(timestamp_t    time,
+                            uint32_t       size,
+                            const uint8_t* buf) = 0;
+};
+
+
+} // namespace Evoral
+
+#endif // EVORAL_EVENT_SINK_HPP
+
diff --git a/libs/evoral/evoral/MIDIParameters.hpp b/libs/evoral/evoral/MIDIParameters.hpp
new file mode 100644 (file)
index 0000000..c21a862
--- /dev/null
@@ -0,0 +1,52 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_MIDI_PARAMETERS_HPP
+#define EVORAL_MIDI_PARAMETERS_HPP
+
+namespace Evoral {
+namespace MIDI {
+
+struct ContinuousController : public Parameter {
+       ContinuousController(uint32_t cc_type, uint32_t channel, uint32_t controller)
+          : Parameter(cc_type, controller, channel) { set_range(*this); }
+       static void set_range(Parameter& p) { p.set_range(0.0, 127.0, 0.0); }
+};
+
+struct ProgramChange : public Parameter {
+       ProgramChange(uint32_t pc_type, uint32_t channel)
+          : Parameter(pc_type, 0, channel) { set_range(*this); }
+       static void set_range(Parameter& p) { p.set_range(0.0, 127.0, 0.0); }
+};
+
+struct ChannelAftertouch : public Parameter {
+       ChannelAftertouch(uint32_t ca_type, uint32_t channel)
+          : Parameter(ca_type, 0, channel) { set_range(*this); }
+       static void set_range(Parameter& p) { p.set_range(0.0, 127.0, 0.0); }
+};
+
+struct PitchBender : public Parameter {
+       PitchBender(uint32_t pb_type, uint32_t channel)
+          : Parameter(pb_type, 0, channel) { set_range(*this); }
+       static void set_range(Parameter& p) { p.set_range(0.0, 16383.0, 8192.0); }
+};
+
+} // namespace MIDI
+} // namespace Evoral
+
+#endif // EVORAL_MIDI_PARAMETERS_HPP
diff --git a/libs/evoral/evoral/Note.hpp b/libs/evoral/evoral/Note.hpp
new file mode 100644 (file)
index 0000000..76095e7
--- /dev/null
@@ -0,0 +1,79 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_NOTE_HPP 
+#define EVORAL_NOTE_HPP
+
+#include <stdint.h>
+#include <evoral/Event.hpp>
+
+namespace Evoral {
+
+
+/** An abstract (protocol agnostic) note.
+ *
+ * Currently a note is defined as (on event, duration, off event).
+ */
+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       Event& on_event()        { return _on_event; }
+       inline const Event& on_event()  const { return _on_event; }
+       inline       Event& off_event()       { return _off_event; }
+       inline const Event& off_event() const { return _off_event; }
+
+private:
+       // Event buffers are self-contained
+       Event _on_event;
+       Event _off_event;
+};
+
+
+} // namespace Evoral
+
+#endif // EVORAL_NOTE_HPP
+
diff --git a/libs/evoral/evoral/Parameter.hpp b/libs/evoral/evoral/Parameter.hpp
new file mode 100644 (file)
index 0000000..ae405ec
--- /dev/null
@@ -0,0 +1,135 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_PARAMETER_HPP
+#define EVORAL_PARAMETER_HPP
+
+#include <string>
+#include <boost/format.hpp>
+
+namespace Evoral {
+
+
+/** ID of a [play|record|automate]able parameter.
+ *
+ * A parameter is defined by (type, id, channel).  Type is an integer which
+ * can be used in any way by the application (e.g. cast to a custom enum,
+ * map to/from a URI, etc).  ID is type specific (e.g. MIDI controller #).
+ *
+ * This class defines a < operator which is a strict weak ordering, so
+ * Parameter may be stored in a std::set, used as a std::map key, etc.
+ */
+class Parameter
+{
+public:
+       Parameter(uint32_t type, uint32_t id, int8_t channel=0,
+                       double min=0.0f, double max=0.0f, double def=0.0f)
+               : _type(type), _id(id), _channel(channel), _min(min), _max(max), _normal(def)
+       {}
+       
+       //Parameter(const std::string& str);
+
+       inline uint32_t type()    const { return _type; }
+       inline uint32_t id()      const { return _id; }
+       inline uint8_t  channel() const { return _channel; }
+
+       /**
+        * 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
+        * Sort Parameters first according to type then to id and lastly to channel.
+        *  
+        * Proof:
+        * <ol>
+        * <li>Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.</li>
+        * <li>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==. </li>
+        * <li>Transitivity: let f(x, y) and f(y, z) be true.
+        *    We prove by contradiction, assuming the contrary (f(x, z) is false).
+        *    That would imply exactly one of the following:
+        *        <ol>
+        *      <li> x == z which contradicts the assumption f(x, y) and f(y, x)
+        *                 because of antisymmetry.
+        *      </li>
+        *      <li> 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.
+        *      </li>
+        *      Therefore f(x, z) is true (transitivity)
+        *    </ol> 
+        * </li>
+        * </ol>
+        */
+       inline bool operator<(const Parameter& id) const {
+               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); }
+       
+       virtual std::string symbol() const {
+               return (boost::format("%1%_c%2%_n%3%\n") % _type % _channel % _id).str();
+       }
+       
+       inline void set_range(double min, double max, double normal) {
+               _min = min;
+               _max = max;
+               _normal = normal;
+       }
+       
+       inline const double min()    const { return _min; }
+       inline const double max()    const { return _max; }
+       inline const double normal() const { return _normal; }
+
+protected:
+       // Default copy constructor is ok
+       
+       // ID (used in comparison)
+       uint32_t _type;
+       uint32_t _id;
+       uint8_t  _channel;
+
+       // Metadata (not used in comparison)
+       double _min;
+       double _max;
+       double _normal;
+};
+
+
+} // namespace Evoral
+
+#endif // EVORAL_PARAMETER_HPP
+
diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp
new file mode 100644 (file)
index 0000000..ef3ad39
--- /dev/null
@@ -0,0 +1,215 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_SEQUENCE_HPP
+#define EVORAL_SEQUENCE_HPP
+
+#include <vector>
+#include <queue>
+#include <deque>
+#include <map>
+#include <utility>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <glibmm/thread.h>
+#include <evoral/types.hpp>
+#include <evoral/Note.hpp>
+#include <evoral/Parameter.hpp>
+#include <evoral/ControlSet.hpp>
+
+namespace Evoral {
+
+class EventSink;
+class Note;
+class Event;
+class ControlList;
+
+/** This class keeps track of the current x and y for a control
+ */
+class ControlIterator {
+public:
+       boost::shared_ptr<const ControlList> list;
+       double x;
+       double y;
+       
+       ControlIterator(boost::shared_ptr<const ControlList> a_list,
+                       double a_x,
+                       double a_y)
+               : list(a_list)
+               , x(a_x)
+               , y(a_y)
+       {}
+};
+
+
+/** This is a higher level view of events, with separate representations for
+ * notes (instead of just unassociated note on/off events) and controller data.
+ * Controller data is represented as a list of time-stamped float values.
+ */
+class Sequence : public boost::noncopyable, virtual public ControlSet {
+public:
+       Sequence(size_t size);
+       
+       void write_lock();
+       void write_unlock();
+
+       void read_lock()   const;
+       void read_unlock() const;
+
+       void clear();
+
+       bool percussive() const     { return _percussive; }
+       void set_percussive(bool p) { _percussive = p; }
+
+       void start_write();
+       bool writing() const { return _writing; }
+       void end_write(bool delete_stuck=false);
+
+       size_t read(EventSink&  dst,
+                   timestamp_t start,
+                   timedur_t   length,
+                   timestamp_t stamp_offset) const;
+
+       /** Resizes vector if necessary (NOT realtime safe) */
+       void append(const Event& ev);
+       
+       inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
+       inline const boost::shared_ptr<Note>       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<const Note> a,
+                                               const boost::shared_ptr<const Note> b) { 
+               return a->time() < b->time();
+       }
+
+       struct LaterNoteEndComparator {
+               typedef const Note* value_type;
+               inline bool operator()(const boost::shared_ptr<const Note> a,
+                                      const boost::shared_ptr<const Note> b) const { 
+                       return a->end_time() > b->end_time();
+               }
+       };
+
+       typedef std::vector< boost::shared_ptr<Note> > Notes;
+       inline       Notes& notes()       { return _notes; }
+       inline const Notes& notes() const { return _notes; }
+
+       /** Read iterator */
+       class const_iterator {
+       public:
+               const_iterator(const Sequence& seq, double t);
+               ~const_iterator();
+
+               inline bool locked() const { return _locked; }
+
+               const Event& operator*()  const { return *_event;  }
+               const boost::shared_ptr<Event> operator->() const  { return _event; }
+               const boost::shared_ptr<Event> 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 Sequence;
+
+               const Sequence*          _seq;
+               boost::shared_ptr<Event> _event;
+
+               typedef std::priority_queue< boost::shared_ptr<Note>,
+                                            std::deque< boost::shared_ptr<Note> >,
+                                            LaterNoteEndComparator >
+                       ActiveNotes;
+               
+               mutable ActiveNotes _active_notes;
+
+               bool                                   _is_end;
+               bool                                   _locked;
+               Notes::const_iterator                  _note_iter;
+               std::vector<ControlIterator>           _control_iters;
+               std::vector<ControlIterator>::iterator _control_iter;
+       };
+       
+       const_iterator        begin() const { return const_iterator(*this, 0); }
+       const const_iterator& end()   const { return _end_iter; }
+       
+       bool control_to_midi_event(boost::shared_ptr<Event>& ev,
+                                  const ControlIterator&    iter) const;
+       
+       typedef std::map< Parameter, boost::shared_ptr<Control> > Controls;
+       Controls&       controls()       { return _controls; }
+       const Controls& controls() const { return _controls; }
+       
+       bool edited() const      { return _edited; }
+       void set_edited(bool yn) { _edited = yn; }
+       
+protected:
+       void add_note_unlocked(const boost::shared_ptr<Note> note);
+       void remove_note_unlocked(const boost::shared_ptr<const Note> note);
+       
+       mutable const_iterator _read_iter;
+       bool                   _edited;
+#ifndef NDEBUG
+       bool is_sorted() const;
+#endif
+
+private:
+       friend class const_iterator;
+       
+       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_control_unlocked(Parameter param, double time, double value);
+
+       mutable Glib::RWLock _lock;
+
+       Notes    _notes;
+       Controls _controls;
+       
+       typedef std::vector<size_t> WriteNotes;
+       WriteNotes _write_notes[16];
+       bool       _writing;
+       
+       typedef std::vector< boost::shared_ptr<const ControlList> > ControlLists;
+       ControlLists _dirty_controls;
+
+       const   const_iterator _end_iter;
+       mutable nframes_t      _next_read;
+       bool                   _percussive;
+
+       /** FIXME: Make fully dynamic, map to URIs */
+       enum EventTypes {
+               midi_cc_type,
+               midi_pc_type,
+               midi_pb_type,
+               midi_ca_type
+       };
+
+       typedef std::priority_queue<
+                       boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
+                       LaterNoteEndComparator>
+               ActiveNotes;
+};
+
+} // namespace Evoral
+
+#endif // EVORAL_SEQUENCE_HPP
+
diff --git a/libs/evoral/evoral/midi_events.h b/libs/evoral/evoral/midi_events.h
new file mode 100644 (file)
index 0000000..1c786aa
--- /dev/null
@@ -0,0 +1,133 @@
+/* Definitions to ease working with raw MIDI.
+ *
+ * Adapted from ALSA's asounddef.h
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef RAUL_MIDI_EVENTS_H
+#define RAUL_MIDI_EVENTS_H
+
+
+/**
+ * \defgroup midi MIDI Definitions
+ * MIDI command and controller number definitions.
+ * \{
+ */
+
+
+// Controllers
+#define MIDI_CTL_MSB_BANK               0x00 /**< Bank Selection */
+#define MIDI_CTL_MSB_MODWHEEL           0x01 /**< Modulation */
+#define MIDI_CTL_MSB_BREATH             0x02 /**< Breath */
+#define MIDI_CTL_MSB_FOOT               0x04 /**< Foot */
+#define MIDI_CTL_MSB_PORTAMENTO_TIME    0x05 /**< Portamento Time */
+#define MIDI_CTL_MSB_DATA_ENTRY         0x06 /**< Data Entry */
+#define MIDI_CTL_MSB_MAIN_VOLUME        0x07 /**< Main Volume */
+#define MIDI_CTL_MSB_BALANCE            0x08 /**< Balance */
+#define MIDI_CTL_MSB_PAN                0x0A /**< Panpot */
+#define MIDI_CTL_MSB_EXPRESSION         0x0B /**< Expression */
+#define MIDI_CTL_MSB_EFFECT1            0x0C /**< Effect1 */
+#define MIDI_CTL_MSB_EFFECT2            0x0D /**< Effect2 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE1   0x10 /**< General Purpose 1 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE2   0x11 /**< General Purpose 2 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE3   0x12 /**< General Purpose 3 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE4   0x13 /**< General Purpose 4 */
+#define MIDI_CTL_LSB_BANK               0x20 /**< Bank Selection */
+#define MIDI_CTL_LSB_MODWHEEL           0x21 /**< Modulation */
+#define MIDI_CTL_LSB_BREATH             0x22 /**< Breath */
+#define MIDI_CTL_LSB_FOOT               0x24 /**< Foot */
+#define MIDI_CTL_LSB_PORTAMENTO_TIME    0x25 /**< Portamento Time */
+#define MIDI_CTL_LSB_DATA_ENTRY         0x26 /**< Data Entry */
+#define MIDI_CTL_LSB_MAIN_VOLUME        0x27 /**< Main Volume */
+#define MIDI_CTL_LSB_BALANCE            0x28 /**< Balance */
+#define MIDI_CTL_LSB_PAN                0x2A /**< Panpot */
+#define MIDI_CTL_LSB_EXPRESSION         0x2B /**< Expression */
+#define MIDI_CTL_LSB_EFFECT1            0x2C /**< Effect1 */
+#define MIDI_CTL_LSB_EFFECT2            0x2D /**< Effect2 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE1   0x30 /**< General Purpose 1 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE2   0x31 /**< General Purpose 2 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE3   0x32 /**< General Purpose 3 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE4   0x33 /**< General Purpose 4 */
+#define MIDI_CTL_SUSTAIN                0x40 /**< Sustain Pedal */
+#define MIDI_CTL_PORTAMENTO             0x41 /**< Portamento */
+#define MIDI_CTL_SOSTENUTO              0x42 /**< Sostenuto */
+#define MIDI_CTL_SOFT_PEDAL             0x43 /**< Soft Pedal */
+#define MIDI_CTL_LEGATO_FOOTSWITCH      0x44 /**< Legato Foot Switch */
+#define MIDI_CTL_HOLD2                  0x45 /**< Hold2 */
+#define MIDI_CTL_SC1_SOUND_VARIATION    0x46 /**< SC1 Sound Variation */
+#define MIDI_CTL_SC2_TIMBRE             0x47 /**< SC2 Timbre */
+#define MIDI_CTL_SC3_RELEASE_TIME       0x48 /**< SC3 Release Time */
+#define MIDI_CTL_SC4_ATTACK_TIME        0x49 /**< SC4 Attack Time */
+#define MIDI_CTL_SC5_BRIGHTNESS         0x4A /**< SC5 Brightness */
+#define MIDI_CTL_SC6                    0x4B /**< SC6 */
+#define MIDI_CTL_SC7                    0x4C /**< SC7 */
+#define MIDI_CTL_SC8                    0x4D /**< SC8 */
+#define MIDI_CTL_SC9                    0x4E /**< SC9 */
+#define MIDI_CTL_SC10                   0x4F /**< SC10 */
+#define MIDI_CTL_GENERAL_PURPOSE5       0x50 /**< General Purpose 5 */
+#define MIDI_CTL_GENERAL_PURPOSE6       0x51 /**< General Purpose 6 */
+#define MIDI_CTL_GENERAL_PURPOSE7       0x52 /**< General Purpose 7 */
+#define MIDI_CTL_GENERAL_PURPOSE8       0x53 /**< General Purpose 8 */
+#define MIDI_CTL_PORTAMENTO_CONTROL     0x54 /**< Portamento Control */
+#define MIDI_CTL_E1_REVERB_DEPTH        0x5B /**< E1 Reverb Depth */
+#define MIDI_CTL_E2_TREMOLO_DEPTH       0x5C /**< E2 Tremolo Depth */
+#define MIDI_CTL_E3_CHORUS_DEPTH        0x5D /**< E3 Chorus Depth */
+#define MIDI_CTL_E4_DETUNE_DEPTH        0x5E /**< E4 Detune Depth */
+#define MIDI_CTL_E5_PHASER_DEPTH        0x5F /**< E5 Phaser Depth */
+#define MIDI_CTL_DATA_INCREMENT         0x60 /**< Data Increment */
+#define MIDI_CTL_DATA_DECREMENT         0x61 /**< Data Decrement */
+#define MIDI_CTL_NONREG_PARM_NUM_LSB    0x62 /**< Non-registered Parameter Number */
+#define MIDI_CTL_NONREG_PARM_NUM_MSB    0x63 /**< Non-registered Parameter Number */
+#define MIDI_CTL_REGIST_PARM_NUM_LSB    0x64 /**< Registered Parameter Number */
+#define MIDI_CTL_REGIST_PARM_NUM_MSB    0x65 /**< Registered Parameter Number */
+#define MIDI_CTL_ALL_SOUNDS_OFF         0x78 /**< All Sounds Off */
+#define MIDI_CTL_RESET_CONTROLLERS      0x79 /**< Reset Controllers */
+#define MIDI_CTL_LOCAL_CONTROL_SWITCH   0x7A /**< Local Control Switch */
+#define MIDI_CTL_ALL_NOTES_OFF          0x7B /**< All Notes Off */
+#define MIDI_CTL_OMNI_OFF               0x7C /**< Omni Off */
+#define MIDI_CTL_OMNI_ON                0x7D /**< Omni On */
+#define MIDI_CTL_MONO1                  0x7E /**< Mono1 */
+#define MIDI_CTL_MONO2                  0x7F /**< Mono2 */
+
+// Commands
+#define MIDI_CMD_NOTE_OFF               0x80 /**< Note Off */
+#define MIDI_CMD_NOTE_ON                0x90 /**< Note On */
+#define MIDI_CMD_NOTE_PRESSURE          0xA0 /**< Key Pressure */
+#define MIDI_CMD_CONTROL                0xB0 /**< Control Change */
+#define MIDI_CMD_PGM_CHANGE             0xC0 /**< Program Change */
+#define MIDI_CMD_CHANNEL_PRESSURE       0xD0 /**< Channel Pressure */
+#define MIDI_CMD_BENDER                 0xE0 /**< Pitch Bender */
+#define MIDI_CMD_COMMON_SYSEX           0xF0 /**< Sysex (System Exclusive) Begin */
+#define MIDI_CMD_COMMON_MTC_QUARTER     0xF1 /**< MTC Quarter Frame */
+#define MIDI_CMD_COMMON_SONG_POS        0xF2 /**< Song Position */
+#define MIDI_CMD_COMMON_SONG_SELECT     0xF3 /**< Song Select */
+#define MIDI_CMD_COMMON_TUNE_REQUEST    0xF6 /**< Tune Request */
+#define MIDI_CMD_COMMON_SYSEX_END       0xF7 /**< End of Sysex */
+#define MIDI_CMD_COMMON_CLOCK           0xF8 /**< Clock */
+#define MIDI_CMD_COMMON_TICK            0xF9 /**< Tick */
+#define MIDI_CMD_COMMON_START           0xFA /**< Start */
+#define MIDI_CMD_COMMON_CONTINUE        0xFB /**< Continue */
+#define MIDI_CMD_COMMON_STOP            0xFC /**< Stop */
+#define MIDI_CMD_COMMON_SENSING         0xFE /**< Active Sensing */
+#define MIDI_CMD_COMMON_RESET           0xFF /**< Reset */
+
+//@}
+
+
+/** \} */
+
+#endif /* RAUL_MIDI_EVENTS_H */
diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp
new file mode 100644 (file)
index 0000000..e078a69
--- /dev/null
@@ -0,0 +1,31 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_TYPES_HPP
+#define EVORAL_TYPES_HPP
+
+/** Frame count (i.e. length of time in audio frames) */
+typedef uint32_t nframes_t;
+
+/** Time-stamp of an event */
+typedef double timestamp_t;
+
+/** Duration of time in timestamp_t units */
+typedef timestamp_t timedur_t;
+
+#endif // EVORAL_TYPES_HPP
diff --git a/libs/evoral/src/Control.cpp b/libs/evoral/src/Control.cpp
new file mode 100644 (file)
index 0000000..8efc2a6
--- /dev/null
@@ -0,0 +1,82 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <evoral/Control.hpp>
+#include <evoral/ControlList.hpp>
+
+namespace Evoral {
+
+Control::Control(boost::shared_ptr<ControlList> list)
+       : _list(list)
+       , _user_value(list->default_value())
+{
+}
+
+
+/** Get the currently effective value (ie the one that corresponds to current output)
+ */
+float
+Control::get_value(bool from_list, nframes_t frame) const
+{
+       if (from_list)
+               return _list->eval(frame);
+       else
+               return _user_value;
+}
+
+
+void
+Control::set_value(float value, bool to_list, nframes_t frame)
+{
+       _user_value = value;
+       
+       if (to_list)
+               _list->add(frame, value);
+}
+
+
+/** 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
+Control::user_value() const
+{
+       return _user_value;
+}
+       
+
+void
+Control::set_list(boost::shared_ptr<ControlList> list)
+{
+       _list = list;
+       _user_value = list->default_value();
+}
+
+       
+Parameter
+Control::parameter() const
+{
+       return _list->parameter();
+}
+
+} // namespace Evoral
+
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
new file mode 100644 (file)
index 0000000..8f6ea18
--- /dev/null
@@ -0,0 +1,1310 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cmath>
+#include <cassert>
+#include <utility>
+#include <iostream>
+#include <evoral/ControlList.hpp>
+
+using namespace std;
+
+namespace Evoral {
+
+
+inline bool event_time_less_than (ControlEvent* a, ControlEvent* b)
+{
+       return a->when < b->when;
+}
+
+
+ControlList::ControlList (Parameter id)
+       : _parameter(id)
+       , _interpolation(Linear)
+       , _curve(new Curve(*this))
+{      
+       _frozen = 0;
+       _changed_when_thawed = false;
+       _min_yval = id.min();
+       _max_yval = id.max();
+       _max_xval = 0; // means "no limit" 
+       _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;
+}
+
+ControlList::ControlList (const ControlList& other)
+       : _parameter(other._parameter)
+       , _interpolation(Linear)
+       , _curve(new Curve(*this))
+{
+       _frozen = 0;
+       _changed_when_thawed = false;
+       _min_yval = other._min_yval;
+       _max_yval = other._max_yval;
+       _max_xval = other._max_xval;
+       _default_value = other._default_value;
+       _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 ();
+}
+
+ControlList::ControlList (const ControlList& other, double start, double end)
+       : _parameter(other._parameter)
+       , _interpolation(Linear)
+       , _curve(new Curve(*this))
+{
+       _frozen = 0;
+       _changed_when_thawed = false;
+       _min_yval = other._min_yval;
+       _max_yval = other._max_yval;
+       _max_xval = other._max_xval;
+       _default_value = other._default_value;
+       _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 */
+
+       boost::shared_ptr<ControlList> section = const_cast<ControlList*>(&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));
+               }
+       }
+
+       mark_dirty ();
+}
+
+ControlList::~ControlList()
+{
+       for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
+               delete (*x);
+       }
+}
+       
+
+boost::shared_ptr<ControlList>
+ControlList::create(Parameter id)
+{
+       return boost::shared_ptr<ControlList>(new ControlList(id));
+}
+
+
+bool
+ControlList::operator== (const ControlList& other)
+{
+       return _events == other._events;
+}
+
+ControlList&
+ControlList::operator= (const ControlList& other)
+{
+       if (this != &other) {
+               
+               _events.clear ();
+               
+               for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
+                       _events.push_back (new ControlEvent (**i));
+               }
+               
+               _min_yval = other._min_yval;
+               _max_yval = other._max_yval;
+               _max_xval = other._max_xval;
+               _default_value = other._default_value;
+               
+               mark_dirty ();
+               maybe_signal_changed ();
+       }
+
+       return *this;
+}
+
+void
+ControlList::maybe_signal_changed ()
+{
+       mark_dirty ();
+       
+       if (_frozen)
+               _changed_when_thawed = true;
+}
+
+void
+ControlList::clear ()
+{
+       {
+               Glib::Mutex::Lock lm (_lock);
+               _events.clear ();
+               mark_dirty ();
+       }
+
+       maybe_signal_changed ();
+}
+
+void
+ControlList::x_scale (double factor)
+{
+       Glib::Mutex::Lock lm (_lock);
+       _x_scale (factor);
+}
+
+bool
+ControlList::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 ControlList::_x_scale (double factor)
+{
+       for (iterator i = _events.begin(); i != _events.end(); ++i) {
+               (*i)->when = floor ((*i)->when * factor);
+       }
+
+       mark_dirty ();
+}
+
+void
+ControlList::reposition_for_rt_add (double when)
+{
+       _rt_insertion_point = _events.end();
+}
+
+void
+ControlList::rt_add (double when, double value)
+{
+       // 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_value) {
+                                       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_value = false;
+               mark_dirty ();
+       }
+
+       maybe_signal_changed ();
+}
+
+void
+ControlList::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
+ControlList::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
+ControlList::erase (iterator i)
+{
+       {
+               Glib::Mutex::Lock lm (_lock);
+               _events.erase (i);
+               reposition_for_rt_add (0);
+               mark_dirty ();
+       }
+       maybe_signal_changed ();
+}
+
+void
+ControlList::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
+ControlList::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
+ControlList::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
+ControlList::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 (event_time_less_than);
+               } else {
+                       _sort_pending = true;
+               }
+
+               mark_dirty ();
+       }
+
+       maybe_signal_changed ();
+}
+
+void
+ControlList::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
+ControlList::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 (event_time_less_than);
+               } else {
+                       _sort_pending = true;
+               }
+
+               mark_dirty ();
+       }
+
+       maybe_signal_changed ();
+}
+
+std::pair<ControlList::iterator,ControlList::iterator>
+ControlList::control_points_adjacent (double xval)
+{
+       Glib::Mutex::Lock lm (_lock);
+       iterator i;
+       ControlEvent cp (xval, 0.0f);
+       std::pair<iterator,iterator> 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;
+}
+
+void
+ControlList::set_max_xval (double x)
+{
+       _max_xval = x;
+}
+
+void
+ControlList::freeze ()
+{
+       _frozen++;
+}
+
+void
+ControlList::thaw ()
+{
+       assert(_frozen > 0);
+
+       if (--_frozen > 0) {
+               return;
+       }
+
+       {
+               Glib::Mutex::Lock lm (_lock);
+
+               if (_sort_pending) {
+                       _events.sort (event_time_less_than);
+                       _sort_pending = false;
+               }
+       }
+}
+
+void 
+ControlList::mark_dirty () const
+{
+       _lookup_cache.left = -1;
+       _search_cache.left = -1;
+       if (_curve)
+               _curve->mark_dirty();
+}
+
+void
+ControlList::truncate_end (double last_coordinate)
+{
+       {
+               Glib::Mutex::Lock lm (_lock);
+               ControlEvent cp (last_coordinate, 0);
+               ControlList::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) {
+                               ControlList::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
+ControlList::truncate_start (double overall_length)
+{
+       {
+               Glib::Mutex::Lock lm (_lock);
+               iterator i;
+               double first_legal_value;
+               double first_legal_coordinate;
+
+               assert(!_events.empty());
+               
+               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()) {
+                               ControlList::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
+ControlList::unlocked_eval (double x) const
+{
+       pair<EventList::iterator,EventList::iterator> 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
+ControlList::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<const_iterator,const_iterator> 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
+ControlList::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
+ControlList::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
+ControlList::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
+ControlList::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<const_iterator,const_iterator>& 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
+ControlList::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<const_iterator,const_iterator> 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;
+       }
+}
+
+boost::shared_ptr<ControlList>
+ControlList::cut (iterator start, iterator end)
+{
+       boost::shared_ptr<ControlList> nal = create (_parameter);
+
+       {
+               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;
+}
+
+boost::shared_ptr<ControlList>
+ControlList::cut_copy_clear (double start, double end, int op)
+{
+       boost::shared_ptr<ControlList> nal = create (_parameter);
+       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;
+
+}
+
+boost::shared_ptr<ControlList>
+ControlList::copy (iterator start, iterator end)
+{
+       boost::shared_ptr<ControlList> nal = create (_parameter);
+
+       {
+               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;
+}
+
+boost::shared_ptr<ControlList>
+ControlList::cut (double start, double end)
+{
+       return cut_copy_clear (start, end, 0);
+}
+
+boost::shared_ptr<ControlList>
+ControlList::copy (double start, double end)
+{
+       return cut_copy_clear (start, end, 1);
+}
+
+void
+ControlList::clear (double start, double end)
+{
+       (void) cut_copy_clear (start, end, 2);
+}
+
+bool
+ControlList::paste (ControlList& 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;
+}
+
+} // namespace Evoral
+
diff --git a/libs/evoral/src/ControlSet.cpp b/libs/evoral/src/ControlSet.cpp
new file mode 100644 (file)
index 0000000..93a0b0e
--- /dev/null
@@ -0,0 +1,139 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits>
+#include <evoral/ControlSet.hpp>
+#include <evoral/ControlList.hpp>
+#include <evoral/Control.hpp>
+#include <evoral/Event.hpp>
+
+using namespace std;
+
+namespace Evoral {
+
+ControlSet::ControlSet()
+{
+}
+
+void
+ControlSet::add_control(boost::shared_ptr<Control> ac)
+{
+       _controls[ac->parameter()] = ac;
+}
+
+void
+ControlSet::what_has_data (set<Parameter>& s) const
+{
+       Glib::Mutex::Lock lm (_control_lock);
+       Controls::const_iterator li;
+       
+       // FIXME: correct semantics?
+       for (li = _controls.begin(); li != _controls.end(); ++li) {
+               s.insert  ((*li).first);
+       }
+}
+
+/** If \a create_if_missing is true, a control list will be created and returned
+ * if one does not already exists.  Otherwise NULL will be returned if a control list
+ * for \a parameter does not exist.
+ */
+boost::shared_ptr<Control>
+ControlSet::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<ControlList> al (control_list_factory(parameter));
+               boost::shared_ptr<Control> ac(control_factory(al));
+               add_control(ac);
+               return ac;
+
+       } else {
+               //warning << "ControlList " << parameter.to_string() << " not found for " << _name << endmsg;
+               return boost::shared_ptr<Control>();
+       }
+}
+
+boost::shared_ptr<const Control>
+ControlSet::control (Parameter parameter) const
+{
+       Controls::const_iterator i = _controls.find(parameter);
+
+       if (i != _controls.end()) {
+               return i->second;
+       } else {
+               //warning << "ControlList " << parameter.to_string() << " not found for " << _name << endmsg;
+               return boost::shared_ptr<Control>();
+       }
+}
+
+bool
+ControlSet::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
+{
+       Controls::const_iterator li;    
+
+       next_event.when = std::numeric_limits<nframes_t>::max();
+       
+       for (li = _controls.begin(); li != _controls.end(); ++li) {
+               ControlList::const_iterator i;
+               boost::shared_ptr<const ControlList> alist (li->second->list());
+               ControlEvent cp (now, 0.0f);
+               
+               for (i = lower_bound (alist->begin(), alist->end(), &cp, ControlList::time_comparator);
+                               i != alist->end() && (*i)->when < end; ++i) {
+                       if ((*i)->when > now) {
+                               break; 
+                       }
+               }
+               
+               if (i != alist->end() && (*i)->when < end) {
+                       if ((*i)->when < next_event.when) {
+                               next_event.when = (*i)->when;
+                       }
+               }
+       }
+
+       return next_event.when != std::numeric_limits<nframes_t>::max();
+}
+
+void
+ControlSet::clear ()
+{
+       Glib::Mutex::Lock lm (_control_lock);
+
+       for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
+               li->second->list()->clear();
+}
+       
+boost::shared_ptr<Control>
+ControlSet::control_factory(boost::shared_ptr<ControlList> list) const
+{
+       return boost::shared_ptr<Control>(new Control(list));
+}
+
+boost::shared_ptr<ControlList>
+ControlSet::control_list_factory(const Parameter& param) const
+{
+       return boost::shared_ptr<ControlList>(new ControlList(param));
+}
+
+
+} // namespace Evoral
diff --git a/libs/evoral/src/Curve.cpp b/libs/evoral/src/Curve.cpp
new file mode 100644 (file)
index 0000000..c463022
--- /dev/null
@@ -0,0 +1,401 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <float.h>
+#include <cmath>
+#include <climits>
+#include <cfloat>
+#include <cmath>
+
+#include <glibmm/thread.h>
+
+#include <evoral/Curve.hpp>
+#include <evoral/ControlList.hpp>
+
+using namespace std;
+using namespace sigc;
+
+namespace Evoral {
+
+
+Curve::Curve (const ControlList& cl)
+       : _dirty (true)
+       , _list (cl)
+{
+}
+
+void
+Curve::solve ()
+{
+       uint32_t npoints;
+
+       if (!_dirty) {
+               return;
+       }
+       
+       if ((npoints = _list.events().size()) > 2) {
+               
+               /* Compute coefficients needed to efficiently compute a constrained spline
+                  curve. See "Constrained Cubic Spline Interpolation" by CJC Kruger
+                  (www.korf.co.uk/spline.pdf) for more details.
+               */
+
+               double x[npoints];
+               double y[npoints];
+               uint32_t i;
+               ControlList::EventList::const_iterator xx;
+
+               for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
+                       x[i] = (double) (*xx)->when;
+                       y[i] = (double) (*xx)->value;
+               }
+
+               double lp0, lp1, fpone;
+
+               lp0 = (x[1] - x[0])/(y[1] - y[0]);
+               lp1 = (x[2] - x[1])/(y[2] - y[1]);
+
+               if (lp0*lp1 < 0) {
+                       fpone = 0;
+               } else {
+                       fpone = 2 / (lp1 + lp0);
+               }
+
+               double fplast = 0;
+
+               for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
+                       
+                       double xdelta;   /* gcc is wrong about possible uninitialized use */
+                       double xdelta2;  /* ditto */
+                       double ydelta;   /* ditto */
+                       double fppL, fppR;
+                       double fpi;
+
+                       if (i > 0) {
+                               xdelta = x[i] - x[i-1];
+                               xdelta2 = xdelta * xdelta;
+                               ydelta = y[i] - y[i-1];
+                       }
+
+                       /* compute (constrained) first derivatives */
+                       
+                       if (i == 0) {
+
+                               /* first segment */
+                               
+                               fplast = ((3 * (y[1] - y[0]) / (2 * (x[1] - x[0]))) - (fpone * 0.5));
+
+                               /* we don't store coefficients for i = 0 */
+
+                               continue;
+
+                       } else if (i == npoints - 1) {
+
+                               /* last segment */
+
+                               fpi = ((3 * ydelta) / (2 * xdelta)) - (fplast * 0.5);
+                               
+                       } else {
+
+                               /* all other segments */
+
+                               double slope_before = ((x[i+1] - x[i]) / (y[i+1] - y[i]));
+                               double slope_after = (xdelta / ydelta);
+
+                               if (slope_after * slope_before < 0.0) {
+                                       /* slope changed sign */
+                                       fpi = 0.0;
+                               } else {
+                                       fpi = 2 / (slope_before + slope_after);
+                               }
+                               
+                       }
+
+                       /* compute second derivative for either side of control point `i' */
+                       
+                       fppL = (((-2 * (fpi + (2 * fplast))) / (xdelta))) +
+                               ((6 * ydelta) / xdelta2);
+                       
+                       fppR = (2 * ((2 * fpi) + fplast) / xdelta) -
+                               ((6 * ydelta) / xdelta2);
+                       
+                       /* compute polynomial coefficients */
+
+                       double b, c, d;
+
+                       d = (fppR - fppL) / (6 * xdelta);   
+                       c = ((x[i] * fppL) - (x[i-1] * fppR))/(2 * xdelta);
+                       
+                       double xim12, xim13;
+                       double xi2, xi3;
+                       
+                       xim12 = x[i-1] * x[i-1];  /* "x[i-1] squared" */
+                       xim13 = xim12 * x[i-1];   /* "x[i-1] cubed" */
+                       xi2 = x[i] * x[i];        /* "x[i] squared" */
+                       xi3 = xi2 * x[i];         /* "x[i] cubed" */
+                       
+                       b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta;
+
+                       /* store */
+
+                       (*xx)->create_coeffs();
+                       (*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
+                       (*xx)->coeff[1] = b;
+                       (*xx)->coeff[2] = c;
+                       (*xx)->coeff[3] = d;
+
+                       fplast = fpi;
+               }
+               
+       }
+
+       _dirty = false;
+}
+
+bool
+Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen)
+{
+       Glib::Mutex::Lock lm(_list.lock(), Glib::TRY_LOCK);
+
+       if (!lm.locked()) {
+               return false;
+       } else {
+               _get_vector (x0, x1, vec, veclen);
+               return true;
+       }
+}
+
+void
+Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
+{
+       Glib::Mutex::Lock lm(_list.lock());
+       _get_vector (x0, x1, vec, veclen);
+}
+
+void
+Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
+{
+       double rx, dx, lx, hx, max_x, min_x;
+       int32_t i;
+       int32_t original_veclen;
+       int32_t npoints;
+
+       if ((npoints = _list.events().size()) == 0) {
+               for (i = 0; i < veclen; ++i) {
+                       vec[i] = _list.default_value();
+               }
+               return;
+       }
+
+       /* events is now known not to be empty */
+
+       max_x = _list.events().back()->when;
+       min_x = _list.events().front()->when;
+
+       lx = max (min_x, x0);
+
+       if (x1 < 0) {
+               x1 = _list.events().back()->when;
+       }
+
+       hx = min (max_x, x1);
+
+       original_veclen = veclen;
+
+       if (x0 < min_x) {
+
+               /* fill some beginning section of the array with the 
+                  initial (used to be default) value 
+               */
+
+               double frac = (min_x - x0) / (x1 - x0);
+               int32_t subveclen = (int32_t) floor (veclen * frac);
+               
+               subveclen = min (subveclen, veclen);
+
+               for (i = 0; i < subveclen; ++i) {
+                       vec[i] = _list.events().front()->value;
+               }
+
+               veclen -= subveclen;
+               vec += subveclen;
+       }
+
+       if (veclen && x1 > max_x) {
+
+               /* fill some end section of the array with the default or final value */
+
+               double frac = (x1 - max_x) / (x1 - x0);
+
+               int32_t subveclen = (int32_t) floor (original_veclen * frac);
+
+               float val;
+               
+               subveclen = min (subveclen, veclen);
+
+               val = _list.events().back()->value;
+
+               i = veclen - subveclen;
+
+               for (i = veclen - subveclen; i < veclen; ++i) {
+                       vec[i] = val;
+               }
+
+               veclen -= subveclen;
+       }
+
+       if (veclen == 0) {
+               return;
+       }
+
+       if (npoints == 1 ) {
+       
+               for (i = 0; i < veclen; ++i) {
+                       vec[i] = _list.events().front()->value;
+               }
+               return;
+       }
+       if (npoints == 2) {
+               /* linear interpolation between 2 points */
+               /* XXX I'm not sure that this is the right thing to
+                  do here. but its not a common case for the envisaged
+                  uses.
+               */
+       
+               if (veclen > 1) {
+                       dx = (hx - lx) / (veclen - 1) ;
+               } else {
+                       dx = 0; // not used
+               }
+       
+               double slope = (_list.events().back()->value - _list.events().front()->value)/  
+                       (_list.events().back()->when - _list.events().front()->when);
+               double yfrac = dx*slope;
+               vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
+               for (i = 1; i < veclen; ++i) {
+                       vec[i] = vec[i-1] + yfrac;
+               }
+               return;
+       }
+       if (_dirty) {
+               solve ();
+       }
+
+       rx = lx;
+
+       if (veclen > 1) {
+
+               dx = (hx - lx) / veclen;
+
+               for (i = 0; i < veclen; ++i, rx += dx) {
+                       vec[i] = multipoint_eval (rx);
+               }
+       }
+}
+
+double
+Curve::unlocked_eval (double x)
+{
+       // I don't see the point of this...
+
+       if (_dirty) {
+               solve ();
+       }
+
+       return _list.unlocked_eval (x);
+}
+
+double
+Curve::multipoint_eval (double x)
+{      
+       pair<ControlList::EventList::const_iterator,ControlList::EventList::const_iterator> range;
+
+       ControlList::LookupCache& lookup_cache = _list.lookup_cache();
+
+       if ((lookup_cache.left < 0) ||
+           ((lookup_cache.left > x) || 
+            (lookup_cache.range.first == _list.events().end()) || 
+            ((*lookup_cache.range.second)->when < x))) {
+               
+               ControlEvent cp (x, 0.0);
+
+               lookup_cache.range = equal_range (_list.events().begin(), _list.events().end(), &cp, ControlList::time_comparator);
+       }
+
+       range = lookup_cache.range;
+
+       /* EITHER 
+          
+          a) x is an existing control point, so first == existing point, second == next point
+
+          OR
+
+          b) x is between control points, so range is empty (first == second, points to where
+              to insert x)
+          
+       */
+
+       if (range.first == range.second) {
+
+               /* x does not exist within the list as a control point */
+               
+               lookup_cache.left = x;
+
+               if (range.first == _list.events().begin()) {
+                       /* we're before the first point */
+                       // return default_value;
+                       _list.events().front()->value;
+               }
+               
+               if (range.second == _list.events().end()) {
+                       /* we're after the last point */
+                       return _list.events().back()->value;
+               }
+
+               double x2 = x * x;
+               ControlEvent* ev = *range.second;
+
+               return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
+       } 
+
+       /* x is a control point in the data */
+       /* invalidate the cached range because its not usable */
+       lookup_cache.left = -1;
+       return (*range.first)->value;
+}
+
+} // namespace Evoral
+
+extern "C" {
+
+void 
+curve_get_vector_from_c (void *arg, double x0, double x1, float* vec, int32_t vecsize)
+{
+       static_cast<Evoral::Curve*>(arg)->get_vector (x0, x1, vec, vecsize);
+}
+
+}
diff --git a/libs/evoral/src/Event.cpp b/libs/evoral/src/Event.cpp
new file mode 100644 (file)
index 0000000..71d1808
--- /dev/null
@@ -0,0 +1,107 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <evoral/Event.hpp>
+
+namespace Evoral {
+
+#ifdef EVENT_ALLOW_ALLOC
+Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer)
+       : _time(t)
+       , _size(s)
+       , _buffer(b)
+       , _owns_buffer(owns_buffer)
+{
+       if (owns_buffer) {
+               _buffer = (uint8_t*)malloc(_size);
+               if (b) {
+                       memcpy(_buffer, b, _size);
+               } else {
+                       memset(_buffer, 0, _size);
+               }
+       }
+}
+
+Event::Event(const Event& copy, bool owns_buffer)
+       : _time(copy._time)
+       , _size(copy._size)
+       , _buffer(copy._buffer)
+       , _owns_buffer(owns_buffer)
+{
+       if (owns_buffer) {
+               _buffer = (uint8_t*)malloc(_size);
+               if (copy._buffer) {
+                       memcpy(_buffer, copy._buffer, _size);
+               } else {
+                       memset(_buffer, 0, _size);
+               }
+       }
+}
+
+Event::~Event() {
+       if (_owns_buffer) {
+               free(_buffer);
+       }
+}
+
+#endif // EVENT_ALLOW_ALLOC
+
+#ifdef EVENT_WITH_XML
+
+Event::Event(const XMLNode& event)
+{
+       string name = event.name();
+       
+       if (name == "ControlChange") {
+               
+       } else if (name == "ProgramChange") {
+               
+       }
+}
+
+
+boost::shared_ptr<XMLNode> 
+Event::to_xml() const
+{
+       XMLNode *result = 0;
+       
+       switch (type()) {
+       case MIDI_CMD_CONTROL:
+               result = new XMLNode("ControlChange");
+               result->add_property("Channel", channel());
+               result->add_property("Control", cc_number());
+               result->add_property("Value",   cc_value());
+               break;
+                       
+       case MIDI_CMD_PGM_CHANGE:
+               result = new XMLNode("ProgramChange");
+               result->add_property("Channel", channel());
+               result->add_property("Number",  pgm_number());
+               break;
+               
+       default:
+               // The implementation is continued as needed
+               break;
+       }
+       
+       return boost::shared_ptr<XMLNode>(result);
+}
+#endif // EVENT_WITH_XML
+
+} // namespace MIDI
+
diff --git a/libs/evoral/src/Note.cpp b/libs/evoral/src/Note.cpp
new file mode 100644 (file)
index 0000000..88be34f
--- /dev/null
@@ -0,0 +1,103 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <evoral/Note.hpp>
+
+namespace Evoral {
+
+Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
+       : _on_event(t, 3, NULL, true)
+       , _off_event(t + d, 3, NULL, true)
+{
+       assert(chan < 16);
+
+       _on_event.buffer()[0] = MIDI_CMD_NOTE_ON + chan;
+       _on_event.buffer()[1] = n;
+       _on_event.buffer()[2] = v;
+       
+       _off_event.buffer()[0] = MIDI_CMD_NOTE_OFF + chan;
+       _off_event.buffer()[1] = n;
+       _off_event.buffer()[2] = 0x40;
+       
+       assert(time() == t);
+       assert(duration() == d);
+       assert(note() == n);
+       assert(velocity() == v);
+       assert(_on_event.channel() == _off_event.channel());
+       assert(channel() == chan);
+}
+
+
+Note::Note(const Note& copy)
+       : _on_event(copy._on_event, true)
+       , _off_event(copy._off_event, true)
+{
+       assert(_on_event.buffer());
+       assert(_off_event.buffer());
+       /*
+       assert(copy._on_event.size == 3);
+       _on_event.buffer = _on_event_buffer;
+       memcpy(_on_event_buffer, copy._on_event_buffer, 3);
+       
+       assert(copy._off_event.size == 3);
+       _off_event.buffer = _off_event_buffer;
+       memcpy(_off_event_buffer, copy._off_event_buffer, 3);
+       */
+
+       assert(time() == copy.time());
+       assert(end_time() == copy.end_time());
+       assert(note() == copy.note());
+       assert(velocity() == copy.velocity());
+       assert(duration() == copy.duration());
+       assert(_on_event.channel() == _off_event.channel());
+       assert(channel() == copy.channel());
+}
+
+
+Note::~Note()
+{
+}
+
+
+const Note&
+Note::operator=(const Note& copy)
+{
+       _on_event = copy._on_event;
+       _off_event = copy._off_event;
+       /*_on_event.time = copy._on_event.time;
+       assert(copy._on_event.size == 3);
+       memcpy(_on_event_buffer, copy._on_event_buffer, 3);
+       
+       _off_event.time = copy._off_event.time;
+       assert(copy._off_event.size == 3);
+       memcpy(_off_event_buffer, copy._off_event_buffer, 3);
+       */
+       
+       assert(time() == copy.time());
+       assert(end_time() == copy.end_time());
+       assert(note() == copy.note());
+       assert(velocity() == copy.velocity());
+       assert(duration() == copy.duration());
+       assert(_on_event.channel() == _off_event.channel());
+       assert(channel() == copy.channel());
+       
+       return *this;
+}
+
+} // namespace Evoral
diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp
new file mode 100644 (file)
index 0000000..81502fd
--- /dev/null
@@ -0,0 +1,643 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ * 
+ * Evoral 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.
+ * 
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define __STDC_LIMIT_MACROS 1
+
+#include <iostream>
+#include <algorithm>
+#include <stdexcept>
+#include <stdint.h>
+#include <evoral/Sequence.hpp>
+#include <evoral/ControlList.hpp>
+#include <evoral/Control.hpp>
+#include <evoral/ControlSet.hpp>
+#include <evoral/EventSink.hpp>
+#include <evoral/MIDIParameters.hpp>
+
+using namespace std;
+
+namespace Evoral {
+
+void Sequence::write_lock() {
+       _lock.writer_lock();
+       _control_lock.lock();
+}
+
+void Sequence::write_unlock() {
+       _lock.writer_unlock();
+       _control_lock.unlock();
+}
+
+void Sequence::read_lock() const {
+       _lock.reader_lock();
+}
+
+void Sequence::read_unlock() const {
+       _lock.reader_unlock();
+}
+
+// Read iterator (const_iterator)
+
+Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
+       : _seq(&seq)
+       , _is_end( (t == DBL_MAX) || seq.empty() )
+       , _locked( !_is_end )
+{
+       //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
+
+       if (_is_end) {
+               return;
+       }
+
+       seq.read_lock();
+
+       _note_iter = seq.notes().end();
+       // find first note which begins after t
+       for (Sequence::Notes::const_iterator i = seq.notes().begin(); i != seq.notes().end(); ++i) {
+               if ((*i)->time() >= t) {
+                       _note_iter = i;
+                       break;
+               }
+       }
+
+       ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
+
+       _control_iters.reserve(seq.controls().size());
+       
+       // find the earliest control event available
+       for (Controls::const_iterator i = seq.controls().begin(); i != seq.controls().end(); ++i) {
+               double x, y;
+               bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
+               if (!ret) {
+                       //cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
+                       //      << ") has no events past " << t << endl;
+                       continue;
+               }
+
+               assert(x >= 0);
+
+               if (y < i->first.min() || y > i->first.max()) {
+                       cerr << "ERROR: Controller (" << i->first.type() << ") value '" << y
+                               << "' out of range [" << i->first.min() << "," << i->first.max()
+                               << "], event ignored" << endl;
+                       continue;
+               }
+
+               const ControlIterator new_iter(i->second->list(), x, y);
+
+               //cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
+               _control_iters.push_back(new_iter);
+
+               // if the x of the current control is less than earliest_control
+               // we have a new earliest_control
+               if (x < earliest_control.x) {
+                       earliest_control = new_iter;
+                       _control_iter = _control_iters.end();
+                       --_control_iter;
+                       // now _control_iter points to the last Element in _control_iters
+               }
+       }
+
+       if (_note_iter != seq.notes().end()) {
+               _event = boost::shared_ptr<Event>(new Event((*_note_iter)->on_event(), true));
+       }
+
+       double time = DBL_MAX;
+       // in case we have no notes in the region, we still want to get controller messages
+       if (_event.get()) {
+               time = _event->time();
+               // if the note is going to make it this turn, advance _note_iter
+               if (earliest_control.x > time) {
+                       _active_notes.push(*_note_iter);
+                       ++_note_iter;
+               }
+       }
+       
+       // <=, because we probably would want to send control events first 
+       if (earliest_control.list.get() && earliest_control.x <= time) {
+               seq.control_to_midi_event(_event, earliest_control);
+       } else {
+               _control_iter = _control_iters.end();
+       }
+
+       if ( (! _event.get()) || _event->size() == 0) {
+               //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
+               _is_end = true;
+
+               // eliminate possible race condition here (ugly)
+               static Glib::Mutex mutex;
+               Glib::Mutex::Lock lock(mutex);
+               if (_locked) {
+                       _seq->read_unlock();
+                       _locked = false;
+               }
+       } else {
+               //printf("New MIDI Iterator = %X @ %lf\n", _event->type(), _event->time());
+       }
+
+       assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0'));
+}
+
+Sequence::const_iterator::~const_iterator()
+{
+       if (_locked) {
+               _seq->read_unlock();
+       }
+}
+
+const Sequence::const_iterator& Sequence::const_iterator::operator++()
+{
+       if (_is_end) {
+               throw std::logic_error("Attempt to iterate past end of Sequence");
+       }
+       
+       assert(_event->buffer() && _event->buffer()[0] != '\0');
+
+       /*cerr << "const_iterator::operator++: " << _event->to_string() << endl;*/
+
+       if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
+               cerr << "FAILED event buffer: " << hex << int(_event->buffer()[0]) << int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl;
+       }
+       assert((_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()));
+
+       // Increment past current control event
+       if (!_event->is_note() && _control_iter != _control_iters.end() && _control_iter->list.get()) {
+               double x = 0.0, y = 0.0;
+               const bool ret = _control_iter->list->rt_safe_earliest_event_unlocked(
+                               _control_iter->x, DBL_MAX, x, y, false);
+
+               if (ret) {
+                       _control_iter->x = x;
+                       _control_iter->y = y;
+               } else {
+                       _control_iter->list.reset();
+                       _control_iter->x = DBL_MAX;
+               }
+       }
+
+       const std::vector<ControlIterator>::iterator old_control_iter = _control_iter;
+       _control_iter = _control_iters.begin();
+
+       // find the _control_iter with the earliest event time
+       for (std::vector<ControlIterator>::iterator i = _control_iters.begin();
+                       i != _control_iters.end(); ++i) {
+               if (i->x < _control_iter->x) {
+                       _control_iter = i;
+               }
+       }
+
+       enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
+
+       Type type = NIL;
+       double t = 0;
+
+       // Next earliest note on
+       if (_note_iter != _seq->notes().end()) {
+               type = NOTE_ON;
+               t = (*_note_iter)->time();
+       }
+
+       // Use the next earliest note off iff it's earlier than the note on
+       if (!_seq->percussive() && (! _active_notes.empty())) {
+               if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
+                       type = NOTE_OFF;
+                       t = _active_notes.top()->end_time();
+               }
+       }
+
+       // Use the next earliest controller iff it's earlier than the note event
+       if (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX /*&& _control_iter != old_control_iter */) {
+               if (type == NIL || _control_iter->x < t) {
+                       type = AUTOMATION;
+               }
+       }
+
+       if (type == NOTE_ON) {
+               //cerr << "********** MIDI Iterator = note on" << endl;
+               *_event = (*_note_iter)->on_event();
+               _active_notes.push(*_note_iter);
+               ++_note_iter;
+       } else if (type == NOTE_OFF) {
+               //cerr << "********** MIDI Iterator = note off" << endl;
+               *_event = _active_notes.top()->off_event();
+               _active_notes.pop();
+       } else if (type == AUTOMATION) {
+               //cerr << "********** MIDI Iterator = Automation" << endl;
+               _seq->control_to_midi_event(_event, *_control_iter);
+       } else {
+               //cerr << "********** MIDI Iterator = End" << endl;
+               _is_end = true;
+       }
+
+       assert(_is_end || _event->size() > 0);
+
+       return *this;
+}
+
+bool Sequence::const_iterator::operator==(const const_iterator& other) const
+{
+       if (_is_end || other._is_end) {
+               return (_is_end == other._is_end);
+       } else {
+               return (_event == other._event);
+       }
+}
+
+Sequence::const_iterator& Sequence::const_iterator::operator=(const const_iterator& other)
+{
+       if (_locked && _seq != other._seq) {
+               _seq->read_unlock();
+       }
+
+       _seq           = other._seq;
+       _active_notes  = other._active_notes;
+       _is_end        = other._is_end;
+       _locked        = other._locked;
+       _note_iter     = other._note_iter;
+       _control_iters = other._control_iters;
+       size_t index   = other._control_iter - other._control_iters.begin();
+       _control_iter  = _control_iters.begin() + index;
+       
+       if (!_is_end) {
+               _event =  boost::shared_ptr<Event>(new Event(*other._event, true));
+       }
+
+       return *this;
+}
+
+// Sequence
+
+Sequence::Sequence(size_t size)
+       : _read_iter(*this, DBL_MAX)
+       , _edited(false)
+       , _notes(size)
+       , _writing(false)
+       , _end_iter(*this, DBL_MAX)
+       , _next_read(UINT32_MAX)
+       , _percussive(false)
+{
+       assert(_end_iter._is_end);
+       assert( ! _end_iter._locked);
+}
+
+/** Read events in frame range \a start .. \a start+cnt into \a dst,
+ * adding \a offset to each event's timestamp.
+ * \return number of events written to \a dst
+ */
+size_t Sequence::read(EventSink& dst, timestamp_t start, timestamp_t nframes, timedur_t offset) const
+{
+       //cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
+       //cerr << this << " MM # notes: " << n_notes() << endl;
+
+       size_t read_events = 0;
+
+       if (start != _next_read) {
+               _read_iter = const_iterator(*this, (double)start);
+               //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
+       } else {
+               //cerr << "Using cached iterator at " << _next_read << endl;
+       }
+
+       _next_read = start + nframes;
+
+       while (_read_iter != end() && _read_iter->time() < start + nframes) {
+               assert(_read_iter->size() > 0);
+               assert(_read_iter->buffer());
+               dst.write(_read_iter->time() + offset,
+                         _read_iter->size(), 
+                         _read_iter->buffer());
+               
+                /*cerr << this << " Sequence::read event @ " << _read_iter->time()  
+                << " type: " << hex << int(_read_iter->type()) << dec 
+                << " note: " << int(_read_iter->note()) 
+                << " velocity: " << int(_read_iter->velocity()) 
+                << endl;*/
+               
+               ++_read_iter;
+               ++read_events;
+       }
+
+       return read_events;
+}
+
+/** Write the controller event pointed to by \a iter to \a ev.
+ * The buffer of \a ev will be allocated or resized as necessary.
+ * \return true on success
+ */
+bool
+Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlIterator& iter) const
+{
+       assert(iter.list.get());
+       if (!ev) {
+               ev = boost::shared_ptr<Event>(new Event(0, 3, NULL, true));
+       }
+       
+       switch (iter.list->parameter().type()) {
+       case midi_cc_type:
+               assert(iter.list.get());
+               assert(iter.list->parameter().channel() < 16);
+               assert(iter.list->parameter().id() <= INT8_MAX);
+               assert(iter.y <= INT8_MAX);
+               
+               ev->time() = iter.x;
+               ev->realloc(3);
+               ev->buffer()[0] = MIDI_CMD_CONTROL + iter.list->parameter().channel();
+               ev->buffer()[1] = (uint8_t)iter.list->parameter().id();
+               ev->buffer()[2] = (uint8_t)iter.y;
+               break;
+
+       case midi_pc_type:
+               assert(iter.list.get());
+               assert(iter.list->parameter().channel() < 16);
+               assert(iter.list->parameter().id() == 0);
+               assert(iter.y <= INT8_MAX);
+               
+               ev->time() = iter.x;
+               ev->realloc(2);
+               ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.list->parameter().channel();
+               ev->buffer()[1] = (uint8_t)iter.y;
+               break;
+
+       case midi_pb_type:
+               assert(iter.list.get());
+               assert(iter.list->parameter().channel() < 16);
+               assert(iter.list->parameter().id() == 0);
+               assert(iter.y < (1<<14));
+               
+               ev->time() = iter.x;
+               ev->realloc(3);
+               ev->buffer()[0] = MIDI_CMD_BENDER + iter.list->parameter().channel();
+               ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB
+               ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
+               break;
+
+       case midi_ca_type:
+               assert(iter.list.get());
+               assert(iter.list->parameter().channel() < 16);
+               assert(iter.list->parameter().id() == 0);
+               assert(iter.y <= INT8_MAX);
+
+               ev->time() = iter.x;
+               ev->realloc(2);
+               ev->buffer()[0] = MIDI_CMD_CHANNEL_PRESSURE + iter.list->parameter().channel();
+               ev->buffer()[1] = (uint8_t)iter.y;
+               break;
+
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+
+/** Clear all events from the model.
+ */
+void Sequence::clear()
+{
+       _lock.writer_lock();
+       _notes.clear();
+       for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
+               li->second->list()->clear();
+       _next_read = 0;
+       _read_iter = end();
+       _lock.writer_unlock();
+}
+
+
+/** Begin a write of events to the model.
+ *
+ * If \a mode is Sustained, complete notes with duration are constructed as note
+ * on/off events are received.  Otherwise (Percussive), only note on events are
+ * stored; note off events are discarded entirely and all contained notes will
+ * have duration 0.
+ */
+void Sequence::start_write()
+{
+       //cerr << "MM " << this << " START WRITE, PERCUSSIVE = " << _percussive << endl;
+       write_lock();
+       _writing = true;
+       for (int i = 0; i < 16; ++i)
+               _write_notes[i].clear();
+       
+       _dirty_controls.clear();
+       write_unlock();
+}
+
+/** Finish a write of events to the model.
+ *
+ * If \a delete_stuck is true and the current mode is Sustained, note on events
+ * that were never resolved with a corresonding note off will be deleted.
+ * Otherwise they will remain as notes with duration 0.
+ */
+void Sequence::end_write(bool delete_stuck)
+{
+       write_lock();
+       assert(_writing);
+
+       //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
+
+       if (!_percussive && delete_stuck) {
+               for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
+                       if ((*n)->duration() == 0) {
+                               cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
+                               n = _notes.erase(n);
+                               // we have to break here because erase invalidates the iterator
+                               break;
+                       } else {
+                               ++n;
+                       }
+               }
+       }
+
+       for (int i = 0; i < 16; ++i) {
+               if (!_write_notes[i].empty()) {
+                       cerr << "WARNING: Sequence::end_write: Channel " << i << " has "
+                                       << _write_notes[i].size() << " stuck notes" << endl;
+               }
+               _write_notes[i].clear();
+       }
+
+       for (ControlLists::const_iterator i = _dirty_controls.begin(); i != _dirty_controls.end(); ++i) {
+               (*i)->mark_dirty();
+       }
+       
+       _writing = false;
+       write_unlock();
+}
+
+/** Append \a ev to model.  NOT realtime safe.
+ *
+ * Timestamps of events in \a buf are expected to be relative to
+ * the start of this model (t=0) and MUST be monotonically increasing
+ * and MUST be >= the latest event currently in the model.
+ */
+void Sequence::append(const Event& ev)
+{
+       write_lock();
+       _edited = true;
+
+       assert(_notes.empty() || ev.time() >= _notes.back()->time());
+       assert(_writing);
+
+       if (ev.is_note_on()) {
+               append_note_on_unlocked(ev.channel(), ev.time(), ev.note(),
+                               ev.velocity());
+       } else if (ev.is_note_off()) {
+               append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
+       } else if (ev.is_cc()) {
+               append_control_unlocked(
+                               Evoral::MIDI::ContinuousController(midi_cc_type, ev.cc_number(), ev.channel()),
+                               ev.time(), ev.cc_value());
+       } else if (ev.is_pgm_change()) {
+               append_control_unlocked(
+                               Evoral::MIDI::ProgramChange(midi_pc_type, ev.channel()),
+                               ev.time(), ev.pgm_number());
+       } else if (ev.is_pitch_bender()) {
+               append_control_unlocked(
+                               Evoral::MIDI::PitchBender(midi_pb_type, ev.channel()),
+                               ev.time(), double(  (0x7F & ev.pitch_bender_msb()) << 7
+                                                 | (0x7F & ev.pitch_bender_lsb()) ));
+       } else if (ev.is_channel_aftertouch()) {
+               append_control_unlocked(
+                               Evoral::MIDI::ChannelAftertouch(midi_ca_type, ev.channel()),
+                               ev.time(), ev.channel_aftertouch());
+       } else {
+               printf("WARNING: Sequence: Unknown event type %X\n", ev.type());
+       }
+
+       write_unlock();
+}
+
+void Sequence::append_note_on_unlocked(uint8_t chan, double time,
+               uint8_t note_num, uint8_t velocity)
+{
+       /*cerr << "Sequence " << this << " chan " << (int)chan <<
+        " note " << (int)note_num << " on @ " << time << endl;*/
+
+       assert(note_num <= 127);
+       assert(chan < 16);
+       assert(_writing);
+       _edited = true;
+
+       boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
+       _notes.push_back(new_note);
+       if (!_percussive) {
+               //cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
+               _write_notes[chan].push_back(_notes.size() - 1);
+       }/* else {
+        cerr << "MM Percussive: NOT appending active note on" << endl;
+        }*/
+}
+
+void Sequence::append_note_off_unlocked(uint8_t chan, double time,
+               uint8_t note_num)
+{
+       /*cerr << "Sequence " << this << " chan " << (int)chan <<
+        " note " << (int)note_num << " off @ " << time << endl;*/
+
+       assert(note_num <= 127);
+       assert(chan < 16);
+       assert(_writing);
+       _edited = true;
+
+       if (_percussive) {
+               cerr << "Sequence Ignoring note off (percussive mode)" << endl;
+               return;
+       }
+
+       /* FIXME: make _write_notes fixed size (127 noted) for speed */
+
+       /* FIXME: note off velocity for that one guy out there who actually has
+        * keys that send it */
+
+       bool resolved = false;
+
+       for (WriteNotes::iterator n = _write_notes[chan].begin(); n
+                       != _write_notes[chan].end(); ++n) {
+               Note& note = *_notes[*n].get();
+               if (note.note() == note_num) {
+                       assert(time >= note.time());
+                       note.set_duration(time - note.time());
+                       _write_notes[chan].erase(n);
+                       //cerr << "MM resolved note, duration: " << note.duration() << endl;
+                       resolved = true;
+                       break;
+               }
+       }
+
+       if (!resolved) {
+               cerr << "Sequence " << this << " spurious note off chan " << (int)chan
+                               << ", note " << (int)note_num << " @ " << time << endl;
+       }
+}
+
+void Sequence::append_control_unlocked(Parameter param, double time, double value)
+{
+       control(param, true)->list()->rt_add(time, value);
+}
+
+
+void Sequence::add_note_unlocked(const boost::shared_ptr<Note> note)
+{
+       //cerr << "Sequence " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
+       _edited = true;
+       Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note,
+                       note_time_comparator);
+       _notes.insert(i, note);
+}
+
+void Sequence::remove_note_unlocked(const boost::shared_ptr<const Note> note)
+{
+       _edited = true;
+       //cerr << "Sequence " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
+       for (Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
+               Note& _n = *(*n);
+               const Note& _note = *note;
+               // TODO: There is still the issue, that after restarting ardour
+               // persisted undo does not work, because of rounding errors in the
+               // event times after saving/restoring to/from MIDI files
+               /*cerr << "======================================= " << endl;
+               cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl;
+               cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl;
+               cerr << "Equal: " << bool(_n == _note) << endl;
+               cerr << endl << endl;*/
+               if (_n == _note) {
+                       _notes.erase(n);
+                       // we have to break here, because erase invalidates all iterators, ie. n itself
+                       break;
+               }
+       }
+}
+
+/** Slow!  for debugging only. */
+#ifndef NDEBUG
+bool Sequence::is_sorted() const {
+       bool t = 0;
+       for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
+               if ((*n)->time() < t)
+                       return false;
+               else
+                       t = (*n)->time();
+
+       return true;
+}
+#endif
+
+} // namespace Evoral
+
diff --git a/libs/evoral/test/sequence.cpp b/libs/evoral/test/sequence.cpp
new file mode 100644 (file)
index 0000000..ffe4c63
--- /dev/null
@@ -0,0 +1,12 @@
+#include <evoral/Sequence.hpp>
+
+using namespace Evoral;
+
+int
+main()
+{
+       Glib::thread_init();
+
+       Sequence s(100);
+       return 0;
+}
index 222e9e249a565e26d008aa918e920de63f41fc1f..95b64a0599f41f9e9ed74e30bca39d77db8a69d9 100644 (file)
@@ -9,6 +9,7 @@ Import('env libraries install_prefix')
 midi2 = env.Clone()
 midi2.Merge([ libraries['sigc2'], 
              libraries['xml'], 
+             libraries['evoral'], 
              libraries['glibmm2'], 
              libraries['glib2'], 
              libraries['pbd'], 
@@ -25,7 +26,6 @@ midi2.Append(DOMAIN=domain,MAJOR=2,MINOR=1,MICRO=1)
 sources = Split("""
 fd_midiport.cc
 fifomidi.cc
-event.cc
 midi.cc
 midichannel.cc
 midifactory.cc
diff --git a/libs/midi++2/event.cc b/libs/midi++2/event.cc
deleted file mode 100644 (file)
index 09e3fcf..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "midi++/event.h"
-
-namespace MIDI {
-
-#ifdef MIDI_EVENT_ALLOW_ALLOC
-Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer)
-       : _time(t)
-       , _size(s)
-       , _buffer(b)
-       , _owns_buffer(owns_buffer)
-{
-       if (owns_buffer) {
-               _buffer = (uint8_t*)malloc(_size);
-               if (b) {
-                       memcpy(_buffer, b, _size);
-               } else {
-                       memset(_buffer, 0, _size);
-               }
-       }
-}
-
-Event::Event(const XMLNode& event)
-{
-       string name = event.name();
-       
-       if (name == "ControlChange") {
-               
-       } else if (name == "ProgramChange") {
-               
-       }
-}
-
-Event::Event(const Event& copy, bool owns_buffer)
-       : _time(copy._time)
-       , _size(copy._size)
-       , _buffer(copy._buffer)
-       , _owns_buffer(owns_buffer)
-{
-       if (owns_buffer) {
-               _buffer = (uint8_t*)malloc(_size);
-               if (copy._buffer) {
-                       memcpy(_buffer, copy._buffer, _size);
-               } else {
-                       memset(_buffer, 0, _size);
-               }
-       }
-}
-
-Event::~Event() {
-       if (_owns_buffer) {
-               free(_buffer);
-       }
-}
-
-
-#endif // MIDI_EVENT_ALLOW_ALLOC
-
-std::string      
-Event::to_string() const 
-{
-       std::ostringstream result(std::ios::ate);
-       result << "MIDI::Event type:" << std::hex << "0x" << int(type()) << "   buffer: ";
-       
-       for(uint32_t i = 0; i < size(); ++i) {
-               result << " 0x" << int(_buffer[i]); 
-       }
-       return result.str();
-}
-
-boost::shared_ptr<XMLNode> 
-Event::to_xml() const
-{
-       XMLNode *result = 0;
-       
-       switch (type()) {
-               case MIDI_CMD_CONTROL:
-                       result = new XMLNode("ControlChange");
-                       result->add_property("Channel", channel());
-                       result->add_property("Control", cc_number());
-                       result->add_property("Value",   cc_value());
-                       break;
-                       
-               case MIDI_CMD_PGM_CHANGE:
-                       result = new XMLNode("ProgramChange");
-                       result->add_property("Channel", channel());
-                       result->add_property("number",  pgm_number());
-                       break;
-               
-               default:
-                       // The implementation is continued as needed
-                       break;
-       }
-       
-       return boost::shared_ptr<XMLNode>(result);
-}
-
-} // namespace MIDI
index bcaa683bf81485572516834a7194360156f521ef..fc1ba234a3b4f27c3253a5bcc5c73a135fe1f0e6 100644 (file)
@@ -93,7 +93,7 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
        if (!is_process_thread()) {
 
                Glib::Mutex::Lock lm (non_process_thread_fifo_lock);
-               RingBuffer<Event>::rw_vector vec;
+               RingBuffer<Evoral::Event>::rw_vector vec;
                
                non_process_thread_fifo.get_write_vector (&vec);
 
@@ -157,7 +157,7 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
 void
 JACK_MidiPort::flush (void* jack_port_buffer)
 {
-       RingBuffer<Event>::rw_vector vec;
+       RingBuffer<Evoral::Event>::rw_vector vec;
        size_t written;
 
        non_process_thread_fifo.get_read_vector (&vec);
@@ -167,7 +167,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
        }
 
        if (vec.len[0]) {
-               Event* evp = vec.buf[0];
+               Evoral::Event* evp = vec.buf[0];
                
                for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
                        jack_midi_event_write (jack_port_buffer,
@@ -176,7 +176,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
        }
        
        if (vec.len[1]) {
-               Event* evp = vec.buf[1];
+               Evoral::Event* evp = vec.buf[1];
 
                for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
                        jack_midi_event_write (jack_port_buffer,
index 64f99090ad344e223c0b2c470c12a934e81e165c..8973ef48bdecd423b2219e40109c6426e1a02530 100644 (file)
  * but MidiEvent will never deep copy and (depending on the scenario)
  * may not be usable in STL containers, signals, etc. 
  */
-#define MIDI_EVENT_ALLOW_ALLOC 1
+#define EVENT_ALLOW_ALLOC 1
 
-namespace MIDI {
+/** Support serialisation of MIDI events to/from XML */
+#define EVENT_WITH_XML 1
 
-
-/** Identical to jack_midi_event_t, but with double timestamp
- *
- * time is either a frame time (from/to Jack) or a beat time (internal
- * tempo time, used in MidiModel) depending on context.
- */
-struct Event {
-#ifdef MIDI_EVENT_ALLOW_ALLOC
-       Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false);
-       
-       /** Copy \a copy.
-        * 
-        * If \a owns_buffer is true, the buffer will be copied and this method
-        * is NOT REALTIME SAFE.  Otherwise both events share a buffer and
-        * memory management semantics are the caller's problem.
-        */
-       Event(const Event& copy, bool owns_buffer);
-       
-       /**
-        * see the MIDI XML specification: http://www.midi.org/dtds/MIDIEvents10.dtd
-        */
-       Event(const XMLNode &event);
-       
-       ~Event();
-
-       inline const Event& operator=(const Event& copy) {
-               _time = copy._time;
-               if (_owns_buffer) {
-                       if (copy._buffer) {
-                               if (copy._size > _size) {
-                                       _buffer = (uint8_t*)::realloc(_buffer, copy._size);
-                               }
-                               memcpy(_buffer, copy._buffer, copy._size);
-                       } else {
-                               free(_buffer);
-                               _buffer = NULL;
-                       }
-               } else {
-                       _buffer = copy._buffer;
-               }
-
-               _size = copy._size;
-               return *this;
-       }
-
-       inline void shallow_copy(const Event& copy) {
-               if (_owns_buffer) {
-                       free(_buffer);
-                       _buffer = false;
-                       _owns_buffer = false;
-               }
-
-               _time = copy._time;
-               _size = copy._size;
-               _buffer = copy._buffer;
-       }
-       
-       inline void set(uint8_t* buf, size_t size, double t) {
-               if (_owns_buffer) {
-                       if (_size < size) {
-                               _buffer = (uint8_t*) ::realloc(_buffer, size);
-                       }
-                       memcpy (_buffer, buf, size);
-               } else {
-                       _buffer = buf;
-               }
-
-               _size = size;
-               _time = t;
-       }
-
-       inline bool operator==(const Event& other) const {
-               if (_time != other._time)
-                       return false;
-
-               if (_size != other._size)
-                       return false;
-
-               if (_buffer == other._buffer)
-                       return true;
-
-               for (size_t i=0; i < _size; ++i)
-                       if (_buffer[i] != other._buffer[i])
-                               return false;
-
-               return true;
-       }
-       
-       inline bool operator!=(const Event& other) const { return ! operator==(other); }
-
-       inline bool owns_buffer() const { return _owns_buffer; }
-       
-       inline void set_buffer(size_t size, uint8_t* buf, bool own) {
-               if (_owns_buffer) {
-                       free(_buffer);
-                       _buffer = NULL;
-               }
-               _size = size;
-               _buffer = buf;
-               _owns_buffer = own;
-       }
-
-       inline void realloc(size_t size) {
-               if (_owns_buffer) {
-                       if (size > _size)
-                               _buffer = (uint8_t*) ::realloc(_buffer, size);
-               } else {
-                       _buffer = (uint8_t*) ::malloc(size);
-                       _owns_buffer = true;
-               }
-
-               _size = size;
-       }
-
-
-#else
-
-       inline void set_buffer(uint8_t* buf) { _buffer = buf; }
-
-#endif // MIDI_EVENT_ALLOW_ALLOC
-
-       inline double      time()                  const { return _time; }
-       inline double&     time()                        { return _time; }
-       inline uint32_t    size()                  const { return _size; }
-       inline uint32_t&   size()                        { return _size; }
-       inline uint8_t     type()                  const { return (_buffer[0] & 0xF0); }
-       inline void        set_type(uint8_t type)        { _buffer[0] = (0x0F & _buffer[0]) | (0xF0 & type); }
-       inline uint8_t     channel()               const { return (_buffer[0] & 0x0F); }
-       inline void        set_channel(uint8_t channel)  { _buffer[0] = (0xF0 & _buffer[0]) | (0x0F & channel); }
-       inline bool        is_note_on()            const { return (type() == MIDI_CMD_NOTE_ON); }
-       inline bool        is_note_off()           const { return (type() == MIDI_CMD_NOTE_OFF); }
-       inline bool        is_cc()                 const { return (type() == MIDI_CMD_CONTROL); }
-       inline bool        is_pitch_bender()       const { return (type() == MIDI_CMD_BENDER); }
-       inline bool        is_pgm_change()         const { return (type() == MIDI_CMD_PGM_CHANGE); }
-       inline bool        is_note()               const { return (is_note_on() || is_note_off()); }
-       inline bool        is_aftertouch()         const { return (type() == MIDI_CMD_NOTE_PRESSURE); }
-       inline bool        is_channel_aftertouch() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); }
-       inline uint8_t     note()                  const { return (_buffer[1]); }
-       inline uint8_t     velocity()              const { return (_buffer[2]); }
-       inline uint8_t     cc_number()             const { return (_buffer[1]); }
-       inline uint8_t     cc_value()              const { return (_buffer[2]); }
-       inline uint8_t     pitch_bender_lsb()      const { return (_buffer[1]); }
-       inline uint8_t     pitch_bender_msb()      const { return (_buffer[2]); }
-       inline uint16_t    pitch_bender_value()    const { return (((0x7F & _buffer[2]) << 7) | (0x7F & _buffer[1])); }
-       inline uint8_t     pgm_number()            const { return (_buffer[1]); }
-       inline void        set_pgm_number(uint8_t number){ _buffer[1] = number; }
-       inline uint8_t     aftertouch()            const { return (_buffer[1]); }
-       inline uint8_t     channel_aftertouch()    const { return (_buffer[1]); }
-       // midi channel events range from 0x80 to 0xE0
-       inline bool        is_channel_event()      const { return (0x80 <= type()) && (type() <= 0xE0); }
-       inline bool        is_smf_meta_event()     const { return _buffer[0] == 0xFF; }
-       inline bool        is_sysex()              const { return _buffer[0] == 0xF0 || _buffer[0] == 0xF7; }
-       inline const uint8_t* buffer()             const { return _buffer; }
-       inline uint8_t*&      buffer()                   { return _buffer; }
-       
-              /**
-               * mainly used for debugging purposes
-               */
-              std::string    to_string()          const;
-              
-              /**
-               * see the MIDI XML specification: http://www.midi.org/dtds/MIDIEvents10.dtd
-               */
-              boost::shared_ptr<XMLNode> to_xml() const;
-
-private:
-       double   _time;   /**< Sample index (or beat time) at which event is valid */
-       uint32_t _size;   /**< Number of uint8_ts of data in \a buffer */
-       uint8_t* _buffer; /**< Raw MIDI data */
-
-#ifdef MIDI_EVENT_ALLOW_ALLOC
-       bool _owns_buffer; /**< Whether buffer is locally allocated */
-#endif
-};
-
-
-}
+#include <evoral/Event.hpp>
 
 #endif /* __libmidipp_midi_event_h__ */
index 6d3e3341bc16f8d1ae3c6cf3d1447fbf2eb5d73a..10a121baee028adfe5b5bb1c9cf3e90816acc4ca 100644 (file)
@@ -80,7 +80,7 @@ private:
        static pthread_t _process_thread;
        static bool is_process_thread();
 
-       RingBuffer<MIDI::Event> non_process_thread_fifo;
+       RingBuffer<Evoral::Event> non_process_thread_fifo;
        Glib::Mutex non_process_thread_fifo_lock;
 };
 
index 50c51b317f6667f815e6d3a6ddb6f52576402afd..d4f8dbf46602034d096cf969f9af52b5dbb95959 100644 (file)
@@ -18,7 +18,7 @@ namespace Name
 class Patch : public PBD::Stateful
 {
 public:
-       typedef std::list<MIDI::Event> PatchMidiCommands;
+       typedef std::list<Evoral::Event> PatchMidiCommands;
 
        Patch() {};
        Patch(string a_number, string a_name) : _number(a_number), _name(a_name) {};
index f149f2884beedebc5a504833f1817dd88d3a07b9..3434db896acc8812748b46827454a7119e3ee794 100644 (file)
@@ -33,7 +33,7 @@ Patch::set_state (const XMLNode& node)
        assert(commands);
        const XMLNodeList events = commands->children();
        for (XMLNodeList::const_iterator i = events.begin(); i != events.end(); ++i) {
-               _patch_midi_commands.push_back(*(new Event(*(*i))));
+               _patch_midi_commands.push_back(*(new Evoral::Event(*(*i))));
        }
 
        return 0;
@@ -132,13 +132,16 @@ ChannelNameSet::set_state (const XMLNode& node)
 }
 
 int
-MIDINameDocument::set_state(const XMLNode & a_node)
+MIDINameDocument::set_state(const XMLNode& a_node)
 {
+       return 0;
 }
 
 XMLNode&
 MIDINameDocument::get_state(void)
 {
+       static XMLNode nothing("<nothing>");
+       return nothing;
 }
 
 
index f45349b8359f69cd058dc5917dd37edadb7f203a..a9d60c6e2555324cb7faa515fed17a7c88206ef4 100644 (file)
@@ -38,6 +38,7 @@ cp.Merge ([
     libraries['sigc2'],
     libraries['pbd'],
     libraries['midi++2'],
+    libraries['evoral'],
     libraries['xml'],
     libraries['glib2'],
     libraries['glibmm2']
index 0f05e379ccddae3fbd4cbfeacfdbde90545d9afb..b3794899b50bee621b318205f8fac5d06033fe2e 100644 (file)
@@ -36,6 +36,7 @@ tranzport.Merge ([
     libraries['sigc2'],
     libraries['pbd'],
     libraries['midi++2'],
+    libraries['evoral'],
     libraries['xml'],
     libraries['usb'],
     libraries['glib2'],
index 33cfd7074179499df109055c637b028e53f65221..e3e62e74026900dc5fa2d72f85631b183a806554 100644 (file)
@@ -37,6 +37,7 @@ genericmidi.Merge ([
        libraries['ardour_cp'],
        libraries['sndfile'],
        libraries['midi++2'],
+       libraries['evoral'],
        libraries['pbd'],
        libraries['sigc2'],
        libraries['usb'],
index e1972d3bdb6c1670164d7393a642c1750ebeeb87..143ebc985659c92077296679fc4744049b761666 100644 (file)
@@ -52,6 +52,7 @@ mackie.Merge ([
     libraries['sigc2'],
     libraries['pbd'],
     libraries['midi++2'],
+    libraries['evoral'],
     libraries['xml'],
     libraries['glib2'],
     libraries['glibmm2'],
index 26ecc511eb2420adf898a3330784742a2874d632..c1a79fe955f19d752a86c23df1c4e403711df028 100644 (file)
@@ -36,6 +36,7 @@ powermate.Merge ([
     libraries['sigc2'],
     libraries['pbd'],
     libraries['midi++2'],
+    libraries['evoral'],
     libraries['xml'],
     libraries['usb'],
     libraries['glib2'],