Display recorded controller data (fix show all/existing automation).
authorDavid Robillard <d@drobilla.net>
Sun, 21 Sep 2008 16:17:02 +0000 (16:17 +0000)
committerDavid Robillard <d@drobilla.net>
Sun, 21 Sep 2008 16:17:02 +0000 (16:17 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@3779 d708f5d6-7413-0410-9779-e7cbd77b26cf

46 files changed:
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_region_view.h
gtk2_ardour/automation_streamview.cc
gtk2_ardour/automation_streamview.h
gtk2_ardour/automation_time_axis.cc
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/route_time_axis.cc
libs/ardour/ardour/audioregion.h
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_control.h
libs/ardour/ardour/io.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_region.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/panner.h
libs/ardour/ardour/processor.h
libs/ardour/ardour/region.h
libs/ardour/audio_track.cc
libs/ardour/audioregion.cc
libs/ardour/automatable.cc
libs/ardour/automation_control.cc
libs/ardour/io.cc
libs/ardour/jack_slave.cc
libs/ardour/midi_model.cc
libs/ardour/midi_playlist.cc
libs/ardour/midi_stretch.cc
libs/ardour/plugin_insert.cc
libs/ardour/processor.cc
libs/ardour/quantize.cc
libs/ardour/region.cc
libs/ardour/route.cc
libs/evoral/evoral/Control.hpp
libs/evoral/evoral/ControlList.hpp
libs/evoral/evoral/ControlSet.hpp
libs/evoral/evoral/Sequence.hpp
libs/evoral/src/Control.cpp
libs/evoral/src/ControlList.cpp
libs/evoral/src/ControlSet.cpp
libs/evoral/src/Sequence.cpp

index 8c77da34437049f9a2196825c59818a81a93782b..51aa2991283379a7d64f83fdd3acb1616fbaf307 100644 (file)
@@ -364,7 +364,7 @@ AudioTimeAxisView::update_pans (bool show)
                }
 
                boost::shared_ptr<AutomationTimeAxisView> pan_track(new AutomationTimeAxisView (_session,
-                                       _route, _route/*FIXME*/, pan_control, 
+                                       _route, _route, pan_control, 
                                        editor,
                                        *this,
                                        false,
index 775c12f6689499452981e86f68c862f2ca55cd47..263761dcc5a9da001a4237f8036817710ef2030b 100644 (file)
@@ -63,13 +63,15 @@ AutomationController::~AutomationController()
 boost::shared_ptr<AutomationController>
 AutomationController::create(
                boost::shared_ptr<Automatable> parent,
-               boost::shared_ptr<Evoral::ControlList> cl,
+               const Evoral::Parameter& param,
                boost::shared_ptr<AutomationControl> ac)
 {
-       Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(cl->default_value(), cl->get_min_y(), cl->get_max_y()));
+       Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(param.normal(), param.min(), param.max()));
        if (!ac) {
-               PBD::warning << "Creating AutomationController for " << cl->parameter().symbol() << endmsg;
-               ac = boost::dynamic_pointer_cast<AutomationControl>(parent->control_factory(cl));
+               PBD::warning << "Creating AutomationController for " << param.symbol() << endmsg;
+               ac = boost::dynamic_pointer_cast<AutomationControl>(parent->control_factory(param));
+       } else {
+               assert(ac->parameter() == param);
        }
        return boost::shared_ptr<AutomationController>(new AutomationController(ac, adjustment));
 }
index 0da24b3588c5cc38bacc5a43866c2b0b959ec381..767c4ced00fe68c808887c970e6fed572e6fb060 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<Evoral::ControlList> cl,
+                       const Evoral::Parameter& param,
                        boost::shared_ptr<ARDOUR::AutomationControl> ac);
 
        ~AutomationController();
index 27da5e5c1f99881cbaa92d53758c5375d6642a00..8754bbc21ab62a36c18662a7d208e30ee4c53673 100644 (file)
@@ -1106,6 +1106,13 @@ AutomationLine::change_model_range (AutomationList::iterator start, AutomationLi
        alist->move_range (start, end, xdelta, ydelta);
 }
 
+void
+AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
+{
+       alist = list;
+       queue_reset();
+}
+
 void
 AutomationLine::show_all_control_points ()
 {
index 92c012fc821d808a22e726d7a96e48897352bcd2..1123a7b9a428f439d783660e680a07c49b7f755d 100644 (file)
@@ -109,6 +109,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin
        virtual void view_to_model_y (double&);
        virtual void model_to_view_y (double&);
 
+       void set_list(boost::shared_ptr<ARDOUR::AutomationList> list);
        boost::shared_ptr<ARDOUR::AutomationList> the_list() const { return alist; }
 
        void show_all_control_points ();
index c213b3feeb3137152871fc298f8171735ae82693..b581cb9a9da2312a91eb035c76bddb48a0f0c2a2 100644 (file)
@@ -116,6 +116,12 @@ AutomationRegionView::add_automation_event (GdkEvent* event, nframes_t when, dou
 
        _line->the_list()->add (when, y);
 
+       boost::shared_ptr<ARDOUR::MidiRegion> mr = boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
+       if (mr) {
+               cout << "ADD TO LIST: " << _line->the_list().get() << " ON " << _region
+                       << " (model " << mr->midi_source(0)->model() << ")" << endl;
+       }
+
        XMLNode& after = _line->the_list()->get_state();
        view->session().commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(
                        *_line->the_list(), &before, &after));
index 33c6a6c7e65cdbdd2ffb49ef7a244b3b742ac18a..759382b562be6e8e95aefb579e31826fd9d53dea 100644 (file)
@@ -54,6 +54,7 @@ public:
        inline AutomationTimeAxisView* automation_view() const
                { return dynamic_cast<AutomationTimeAxisView*>(&trackview); }
        
+       void set_line(boost::shared_ptr<AutomationLine> line) { _line = line; }
        boost::shared_ptr<AutomationLine> line() { return _line; }
        
        // We are a ghost.  Meta ghosts?  Crazy talk.
index 9bce718fce99c8a6d336af86263b1acb2f21e488..024d235c7d85987d110dc32f17f3b2e9270d4804 100644 (file)
@@ -99,11 +99,13 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region
        for (i = region_views.begin(); i != region_views.end(); ++i) {
                if ((*i)->region() == region) {
                        
-                       /* great. we already have a MidiRegionView for this Region. use it again. */
+                       /* great. we already have an AutomationRegionView for this Region. use it again. */
+                       AutomationRegionView* arv = dynamic_cast<AutomationRegionView*>(*i);;
 
+                       arv->line()->set_list (list);
                        (*i)->set_valid (true);
                        (*i)->enable_display(wfd);
-                       display_region(dynamic_cast<AutomationRegionView*>(*i));
+                       display_region(arv);
 
                        return NULL;
                }
@@ -139,6 +141,17 @@ AutomationStreamView::display_region(AutomationRegionView* region_view)
        region_view->line().reset();
 }
 
+void
+AutomationStreamView::set_automation_state (AutoState state)
+{
+       std::list<RegionView *>::iterator i;
+       for (i = region_views.begin(); i != region_views.end(); ++i) {
+               boost::shared_ptr<AutomationLine> line = ((AutomationRegionView*)(*i))->line();
+               if (line && line->the_list())
+                       line->the_list()->set_automation_state (state);
+       }
+}
+
 void
 AutomationStreamView::redisplay_diskstream ()
 {
index 5eebfaf28d7247c0b412b23caa65d3d8125b41f4..4188e7e983fa7c293e1f8e29c8f1024100540d3a 100644 (file)
@@ -45,9 +45,7 @@ class AutomationStreamView : public StreamView
        AutomationStreamView (AutomationTimeAxisView& tv);
        ~AutomationStreamView ();
 
-       void set_selected_regionviews (RegionSelection&);
-       void get_selectables (jack_nframes_t start, jack_nframes_t end, list<Selectable* >&);
-       void get_inverted_selectables (Selection&, list<Selectable* >& results);
+       void set_automation_state (ARDOUR::AutoState state);
 
        void redisplay_diskstream ();
        
index daf0a01b95483180a260b9f49998e5d81e7c6eff..76ef196b52230ae7b16b47aa038838dab24749a4 100644 (file)
@@ -49,17 +49,21 @@ Pango::FontDescription* AutomationTimeAxisView::name_font = 0;
 bool AutomationTimeAxisView::have_name_font = false;
 const string AutomationTimeAxisView::state_node_name = "AutomationChild";
 
+/** \a a the automatable object this time axis is to display data for.
+ * For route/track automation (e.g. gain) pass the route for both \r and \a.
+ * For route child (e.g. plugin) automation, pass the child for \a.
+ * For region automation (e.g. MIDI CC), pass null for \a.
+ */
 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r,
                boost::shared_ptr<Automatable> a, boost::shared_ptr<AutomationControl> c,
                PublicEditor& e, TimeAxisView& parent, bool show_regions,
                ArdourCanvas::Canvas& canvas, const string & nom, const string & nomparent)
-
        : AxisView (s), 
          TimeAxisView (s, e, &parent, canvas),
          _route (r),
          _control (c),
          _automatable (a),
-         _controller(AutomationController::create(a, c->list(), c)),
+         _controller(AutomationController::create(a, c->parameter(), c)),
          _base_rect (0),
          _view (show_regions ? new AutomationStreamView(*this) : NULL),
          _name (nom),
@@ -238,14 +242,14 @@ AutomationTimeAxisView::auto_clicked ()
                automation_menu->set_name ("ArdourContextMenu");
                MenuList& items (automation_menu->items());
 
-               items.push_back (MenuElem (_("Manual"), 
-                                          bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
-               items.push_back (MenuElem (_("Play"),
-                                          bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
-               items.push_back (MenuElem (_("Write"),
-                                          bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
-               items.push_back (MenuElem (_("Touch"),
-                                          bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
+               items.push_back (MenuElem (_("Manual"), bind (mem_fun(*this,
+                               &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
+               items.push_back (MenuElem (_("Play"), bind (mem_fun(*this,
+                               &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
+               items.push_back (MenuElem (_("Write"), bind (mem_fun(*this,
+                               &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
+               items.push_back (MenuElem (_("Touch"), bind (mem_fun(*this,
+                               &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
        }
 
        automation_menu->popup (1, gtk_get_current_event_time());
@@ -255,15 +259,14 @@ void
 AutomationTimeAxisView::set_automation_state (AutoState state)
 {
        if (!ignore_state_request) {
-               if (_route == _automatable) { // FIXME: ew
-                       _route->set_parameter_automation_state (
-                                       _control->parameter(),
-                                       state);
+               if (_route == _automatable) { // This is a time axis for route (not region) automation
+                       _route->set_parameter_automation_state (_control->parameter(), state);
                }
 
-               _control->alist()->set_automation_state(state);
-
+               if (_control->list())
+                       _control->alist()->set_automation_state(state);
        }
+       _view->set_automation_state (state);
 }
 
 void
@@ -278,7 +281,7 @@ AutomationTimeAxisView::automation_state_changed ()
        } else {
                state = _control->alist()->automation_state ();
        }
-
+       
        switch (state & (Off|Play|Touch|Write)) {
        case Off:
                auto_button.set_label (_("Manual"));
index 34ab571c8ccde9467056c45afc0a17b3f9bbb41b..c77f7e7d740e3e6e4062b5a3a2c5a1d7df8f6230 100644 (file)
@@ -211,7 +211,7 @@ GenericPluginUI::build ()
 
                        boost::shared_ptr<ARDOUR::AutomationControl> c
                                = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
-                                       insert->control(Parameter(PluginAutomation, i)));
+                                       insert->data().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;
@@ -462,7 +462,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
 
                /* create the controller */
        
-               control_ui->controller = AutomationController::create(insert, mcontrol->list(), mcontrol);
+               control_ui->controller = AutomationController::create(insert, mcontrol->parameter(), mcontrol);
 
                /* XXX this code is not right yet, because it doesn't handle
                   the absence of bounds in any sensible fashion.
index 3184bd104bd04d346db6973d5ab209d6e295f65d..bfd23f900eec96bc3d925a3b962464e19ace1117 100644 (file)
@@ -403,7 +403,7 @@ MidiRegionView::create_note_at(double x, double y, double duration)
 
        MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
        cmd->add(new_note);
-       _model->apply_command(cmd);
+       _model->apply_command(trackview.session(), cmd);
 }
 
 
@@ -474,8 +474,8 @@ MidiRegionView::apply_command()
                _marked_for_selection.insert((*i)->note());
        }
        
-       _model->apply_command(_delta_command);
-       _delta_command = NULL;
+       _model->apply_command(trackview.session(), _delta_command);
+       _delta_command = NULL; 
        midi_view()->midi_track()->diskstream()->playlist_modified();
 
        _marked_for_selection.clear();
index 500b81cb0138f6cd7be76177348f7bc3c541eca2..b8f5da2ed9d781195f5b67895dcf31a9bb53ace9 100644 (file)
@@ -293,8 +293,9 @@ MidiTimeAxisView::show_all_automation ()
                const set<Parameter> params = midi_track()->midi_diskstream()->
                                midi_playlist()->contained_automation();
 
-               for (set<Parameter>::const_iterator i = params.begin(); i != params.end(); ++i)
+               for (set<Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
                        create_automation_child(*i, true);
+               }
        }
 
        RouteTimeAxisView::show_all_automation ();
@@ -307,8 +308,9 @@ MidiTimeAxisView::show_existing_automation ()
                const set<Parameter> params = midi_track()->midi_diskstream()->
                                midi_playlist()->contained_automation();
 
-               for (set<Parameter>::const_iterator i = params.begin(); i != params.end(); ++i)
+               for (set<Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
                        create_automation_child(*i, true);
+               }
        }
 
        RouteTimeAxisView::show_existing_automation ();
@@ -344,24 +346,23 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
                        param.type() == MidiChannelAftertouchAutomation
           ) {
        
-               /* FIXME: don't create AutomationList for track itself
-                * (not actually needed or used, since the automation is region-ey) */
-               
+               /* These controllers are region "automation", so we do not create
+                * an AutomationList/Line for the track */
+
                AutomationTracks::iterator existing = _automation_tracks.find(param);
                if (existing != _automation_tracks.end())
                        return;
 
                boost::shared_ptr<AutomationControl> c
-                       = boost::dynamic_pointer_cast<AutomationControl>(_route->control(param));
+                       = boost::dynamic_pointer_cast<AutomationControl>(_route->data().control(param));
 
                if (!c) {
-                       boost::shared_ptr<AutomationList> al(new ARDOUR::AutomationList(param));
-                       c = boost::dynamic_pointer_cast<AutomationControl>(_route->control_factory(al));
+                       c = boost::dynamic_pointer_cast<AutomationControl>(_route->control_factory(param));
                        _route->add_control(c);
                }
 
                boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
-                               _route, _route, c,
+                               _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
                                editor,
                                *this,
                                true,
index 2eceeb4974d6e438546cbf52cfbc3a7558c07505..6d6188c1463232007cc0ae79f5c4458f682674c1 100644 (file)
@@ -1619,7 +1619,7 @@ RouteTimeAxisView::show_existing_automation ()
 
        for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
                for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
-                       if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
+                       if ((*ii)->view != 0 && (*i)->processor->data().control((*ii)->what)->list()->size() > 0) {
                                (*ii)->menu_item->set_active (true);
                        }
                }
@@ -1765,7 +1765,7 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor>
        snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
 
        boost::shared_ptr<AutomationControl> control
-                       = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
+                       = boost::dynamic_pointer_cast<AutomationControl>(processor->data().control(what, true));
 
        pan->view = boost::shared_ptr<AutomationTimeAxisView>(
                        new AutomationTimeAxisView (_session, _route, processor, control,
index f8bd8a1794d16c0c4b33429e62aab81df31f3430..b8cde44b77b877ab737cda25e7bd514cd5bb26a6 100644 (file)
@@ -123,6 +123,18 @@ class AudioRegion : public Region
        void set_default_envelope ();
 
        int separate_by_channel (ARDOUR::Session&, vector<boost::shared_ptr<AudioRegion> >&) const;
+       
+       /* automation */
+       
+       boost::shared_ptr<Evoral::Control>
+       control(const Evoral::Parameter& id, bool create=false) {
+               return _automatable.data().control(id, create);
+       }
+
+       virtual boost::shared_ptr<const Evoral::Control>
+       control(const Evoral::Parameter& id) const {
+               return _automatable.data().control(id);
+       }
 
        /* export */
 
@@ -174,6 +186,8 @@ class AudioRegion : public Region
        void listen_to_my_curves ();
        void listen_to_my_sources ();
 
+       AutomatableControls _automatable;
+
        boost::shared_ptr<AutomationList> _fade_in;
        FadeShape                         _fade_in_shape;
        boost::shared_ptr<AutomationList> _fade_out;
index 99e7891ce8ea8568ecd15b7611af21d367357d9d..837bbbc61748e1f460074bf07eb5e387f87e4f77 100644 (file)
 #include <ardour/automation_control.h>
 #include <ardour/parameter.h>
 #include <evoral/ControlSet.hpp>
+#include <evoral/Sequence.hpp>
 
 namespace ARDOUR {
 
 class Session;
 class AutomationControl;
 
-class Automatable : public SessionObject, virtual public Evoral::ControlSet
+
+/** Note this class is abstract, actual objects must either be
+ * an AutomatableControls or an AutomatableSequence
+ */
+class Automatable : virtual public Evoral::ControlSet
 {
 public:
-       Automatable(Session&, const std::string& name);
+       Automatable(Session&);
+       Automatable();
 
        virtual ~Automatable() {}
 
-       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<Evoral::Control>
+       control_factory(const Evoral::Parameter& id);
+
        virtual void add_control(boost::shared_ptr<Evoral::Control>);
        
        virtual void automation_snapshot(nframes_t now, bool force);
@@ -74,8 +80,14 @@ public:
        static jack_nframes_t automation_interval() { 
                return _automation_interval;
        }
+       
+       typedef Evoral::ControlSet::Controls Controls;
+       
+       Evoral::ControlSet&       data()       { return *this; }
+       const Evoral::ControlSet& data() const { return *this; }
 
 protected:
+       Session& _a_session;
 
        void can_automate(Parameter);
 
@@ -90,10 +102,29 @@ protected:
        std::set<Parameter> _visible_controls;
        std::set<Parameter> _can_automate_list;
        
-       nframes_t _last_automation_snapshot;
+       nframes_t        _last_automation_snapshot;
        static nframes_t _automation_interval;
 };
 
+
+/** Contains notes and controllers */
+class AutomatableSequence : public Automatable, public Evoral::Sequence {
+public:
+       AutomatableSequence(Session& s, size_t size)
+               : Evoral::ControlSet()
+               , Automatable(s)
+               , Evoral::Sequence(size)
+       {}
+};
+
+
+/** Contains only controllers */
+class AutomatableControls : public Automatable {
+public:
+       AutomatableControls(Session& s) : Evoral::ControlSet(), Automatable(s) {}
+};
+
+
 } // namespace ARDOUR
 
 #endif /* __ardour_automatable_h__ */
index 78f4553d873deb764318913733ac60b42e5b2477..52fce1096cb19b84afec58871e6e114a211aea39 100644 (file)
@@ -34,14 +34,15 @@ class Session;
 class Automatable;
 
 
-/** A PBD:Controllable with associated automation data (AutomationList)
+/** A PBD::Controllable with associated automation data (AutomationList)
  */
 class AutomationControl : public PBD::Controllable, public Evoral::Control
 {
 public:
        AutomationControl(ARDOUR::Session&,
-                       boost::shared_ptr<ARDOUR::AutomationList>,
-                       std::string name="unnamed controllable");
+                       const Parameter& parameter,
+                       boost::shared_ptr<ARDOUR::AutomationList> l=boost::shared_ptr<ARDOUR::AutomationList>(),
+                       const string& name="");
        
        boost::shared_ptr<AutomationList> alist() const { return boost::dynamic_pointer_cast<AutomationList>(_list); }
 
index 6cae11a7fa81b05560c50119586256dba53b925c..08c8a1b21165c42342db3cf91c220f53b2ec25f2 100644 (file)
@@ -69,7 +69,7 @@ class BufferSet;
  * varied combinations of types (eg MIDI and audio) possible.
  */
 
-class IO : public Automatable, public Latent
+class IO : public SessionObject, public AutomatableControls, public Latent
 {
   public:
        static const string state_node_name;
@@ -229,7 +229,7 @@ class IO : public Automatable, public Latent
 
        struct GainControl : public AutomationControl {
            GainControl (std::string name, IO& i, boost::shared_ptr<AutomationList> al)
-                       : AutomationControl (i._session, al, name)
+                       : AutomationControl (i._session, al->parameter(), al, name)
                        , _io (i)
                {}
         
index 51bddfde44b7f7cb51abb21787493806a033a3f1..a1a0da7296b685e184edd827a1475d72e7f00fdb 100644 (file)
@@ -39,7 +39,7 @@ namespace ARDOUR {
 
 class Session;
 class MidiSource;
-       
+
 /** 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
@@ -47,7 +47,7 @@ class MidiSource;
  * Because of this MIDI controllers and automatable controllers/widgets/etc
  * are easily interchangeable.
  */
-class MidiModel : public Automatable, public Evoral::Sequence {
+class MidiModel : public AutomatableSequence {
 public:
        MidiModel(MidiSource* s, size_t size=0);
        
@@ -55,13 +55,13 @@ public:
        void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
 
        /** Add/Remove notes.
-        * Technically all operations can be implemented as one of these.
+        * Technically all note operations can be implemented as one of these, but
+        * a custom command can be more efficient.
         */
-       class DeltaCommand : public Command
-       {
+       class DeltaCommand : public Command {
        public:
                DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
-               DeltaCommand (boost::shared_ptr<MidiModel>,   const XMLNode& node);
+               DeltaCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
 
                const std::string& name() const { return _name; }
                
@@ -78,8 +78,8 @@ public:
                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;
+               boost::shared_ptr<MidiModel> _model;
+               const std::string            _name;
                
                typedef std::list< boost::shared_ptr<Evoral::Note> > NoteList;
                
@@ -88,7 +88,7 @@ public:
        };
 
        MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
-       void                     apply_command(Command* cmd);
+       void                     apply_command(Session& session, Command* cmd);
 
        bool write_to(boost::shared_ptr<MidiSource> source);
                
index e0caddd954958e1c0155f458c5ca5cce5dac5ce8..2cbed8f99c18e8e99bb6c9e3522d2582f5d239c2 100644 (file)
@@ -72,22 +72,28 @@ class MidiRegion : public Region
        int      set_state (const XMLNode&);
 
        int separate_by_channel (ARDOUR::Session&, vector<MidiRegion*>&) const;
-
-       UndoAction get_memento() const;
-
-       // Act as a proxy for MidiModel automation stuff (for CC)
-       // Yep, this is pretty ugly...
-       Controls&       controls()       { return midi_source()->model()->controls(); }
-       const Controls& controls() const { return midi_source()->model()->controls(); }
        
-       boost::shared_ptr<Evoral::Control> control(const Evoral::Parameter& id, bool create=false)
-                       { return midi_source()->model()->control(id, create); }
+       /* automation */
+       
+       boost::shared_ptr<Evoral::Control>
+       control(const Evoral::Parameter& id, bool create=false) {
+               return model()->data().control(id, create);
+       }
 
-       boost::shared_ptr<const Evoral::Control> control(const Evoral::Parameter& id) const
-                       { return midi_source()->model()->control(id); }
+       virtual boost::shared_ptr<const Evoral::Control>
+       control(const Evoral::Parameter& id) const {
+               return model()->data().control(id);
+       }
+
+       /* export */
        
        int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&);
 
+       UndoAction get_memento() const;
+
+       boost::shared_ptr<MidiModel> model()             { return midi_source()->model(); }
+       boost::shared_ptr<const MidiModel> model() const { return midi_source()->model(); }
+
   private:
        friend class RegionFactory;
 
index 7eda6f904babe16357330f36a1cfbc8ccb7356d0..02313c7e6eec4edb22780a992a137215bc7545a0 100644 (file)
@@ -73,9 +73,11 @@ public:
        void midi_panic(void);
        bool write_immediate_event(size_t size, const uint8_t* buf);
        
+       /** A control that will send "immediate" events to a MIDI track when twiddled */
        struct MidiControl : public AutomationControl {
-           MidiControl(MidiTrack* route, boost::shared_ptr<AutomationList> al)
-                       : AutomationControl (route->session(), al, al->parameter().symbol())
+           MidiControl(MidiTrack* route, const Parameter& param,
+                               boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>())
+                       : AutomationControl (route->session(), param, al)
                        , _route (route)
                {}
         
index 47ef212d584a6e0bb77a2d5f18423e751f08aefe..fb8048e7ef9f8b0731338d566dc5f593bdd65388 100644 (file)
@@ -104,7 +104,7 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
 
        struct PanControllable : public AutomationControl {
            PanControllable (Session& s, std::string name, StreamPanner& p, Parameter param)
-                       : AutomationControl (s,
+                       : AutomationControl (s, param,
                                        boost::shared_ptr<AutomationList>(new AutomationList(param)), name)
                        , panner (p)
                { assert(param.type() != NullAutomation); }
index 8c4ac8dfe52751c0bc78838a8f7b7876c28e9a61..4b236d159e1ee0f552bdb1bf59f0f5840d4fbbd2 100644 (file)
@@ -42,7 +42,7 @@ class Session;
 
 /* A mixer strip element - plugin, send, meter, etc.
  */
-class Processor : public Automatable, public Latent
+class Processor : public SessionObject, public AutomatableControls, public Latent
 {
   public:
        static const string state_node_name;
index dc81de6374884b08580200347d6a22b13eb1bb94..d3c8341623ce2c399c6a16adb7ce85c06587dc25 100644 (file)
@@ -45,7 +45,10 @@ enum RegionEditState {
        EditChangesID      = 2
 };
 
-class Region : public Automatable, public boost::enable_shared_from_this<Region>, public Readable
+class Region
+               : public SessionObject
+               , public boost::enable_shared_from_this<Region>
+               , public Readable
 {
   public:
        typedef std::vector<boost::shared_ptr<Source> > SourceList;
@@ -220,6 +223,14 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        std::vector<string> master_source_names();
        void set_master_sources (const SourceList&);
        
+       /* automation */
+       
+       virtual boost::shared_ptr<Evoral::Control>
+       control(const Evoral::Parameter& id, bool create=false) = 0;
+
+       virtual boost::shared_ptr<const Evoral::Control>
+       control(const Evoral::Parameter& id) const = 0;
+       
        /* serialization */
        
        XMLNode&         get_state ();
index 532abeb123474fd8699abba86b84aecff00a02a7..0418074ef20eab4163d87c3cd2b9a780db9c2dd0 100644 (file)
@@ -604,7 +604,7 @@ 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 (_control_lock, Glib::TRY_LOCK);
+                       Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK);
                        
                        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);
index 707f10e91a8669d8e264068fb39833de678166a4..2b6b87d06188f72e9f5e7a01804ec2da35c5d78e 100644 (file)
@@ -77,6 +77,7 @@ 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)
+       , _automatable(s)
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -87,6 +88,7 @@ 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))
+       , _automatable(src->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -102,6 +104,7 @@ 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)
+       , _automatable(src->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -117,6 +120,7 @@ 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)
+       , _automatable(srcs[0]->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -128,6 +132,7 @@ 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)
+       , _automatable(other->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -180,6 +185,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
 
 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
        : Region (other)
+       , _automatable(other->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -196,6 +202,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
 
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
        : Region (src, node)
+       , _automatable(src->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
@@ -217,6 +224,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
 
 AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
        : Region (srcs, node)
+       , _automatable(srcs[0]->session())
        , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
index 5595ebc2cfc5a22966e99e36cfa9930e7dec7d9d..f77975c303759def5a1ea5b83fed7b851898f194 100644 (file)
@@ -37,10 +37,11 @@ using namespace PBD;
 
 nframes_t Automatable::_automation_interval = 0;
 
-Automatable::Automatable(Session& _session, const string& name)
-       : SessionObject(_session, name)
+Automatable::Automatable(Session& session)
+       : _a_session(session)
        , _last_automation_snapshot(0)
-{}
+{
+}
 
 int
 Automatable::old_set_automation_state (const XMLNode& node)
@@ -50,7 +51,7 @@ Automatable::old_set_automation_state (const XMLNode& node)
        if ((prop = node.property ("path")) != 0) {
                load_automation (prop->value());
        } else {
-               warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg;
+               warning << _("Automation node has no path property") << endmsg;
        }
        
        if ((prop = node.property ("visible")) != 0) {
@@ -82,19 +83,20 @@ Automatable::load_automation (const string& path)
        if (path[0] == '/') { // legacy
                fullpath = path;
        } else {
-               fullpath = _session.automation_dir();
+               fullpath = _a_session.automation_dir();
                fullpath += path;
        }
        ifstream in (fullpath.c_str());
 
        if (!in) {
-               warning << string_compose(_("%1: cannot open %2 to load automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg;
+               warning << string_compose(_("cannot open %2 to load automation data (%3)")
+                               , fullpath, strerror (errno)) << endmsg;
                return 1;
        }
 
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
        set<Parameter> tosave;
-       _controls.clear ();
+       controls().clear ();
        
        _last_automation_snapshot = 0;
 
@@ -116,8 +118,8 @@ Automatable::load_automation (const string& path)
        return 0;
 
   bad:
-       error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg;
-       _controls.clear ();
+       error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
+       controls().clear ();
        return -1;
 }
 
@@ -125,17 +127,16 @@ void
 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
 {
        Parameter param = ac->parameter();
-
+       
+       ControlSet::add_control(ac);
        _can_automate_list.insert(param);
-
-       // Sync everything (derived classes) up to initial values
-       auto_state_changed(param);
+       auto_state_changed(param); // sync everything up
 }
 
 void
-Automatable::what_has_visible_data (set<Parameter>& s) const
+Automatable::what_has_visible_data(set<Parameter>& s) const
 {
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
        set<Parameter>::const_iterator li;
        
        for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
@@ -194,7 +195,7 @@ Automatable::mark_automation_visible (Parameter what, bool yn)
 int
 Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
 {      
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
 
        /* Don't clear controls, since some may be special derived Controllable classes */
 
@@ -215,20 +216,23 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
                        const XMLProperty* id_prop = (*niter)->property("automation-id");
 
                        Parameter param = (id_prop ? Parameter(id_prop->value()) : legacy_param);
+                       if (param.type() == NullAutomation) {
+                               warning << "Automation has null type" << endl;
+                               continue;
+                       }
                        
                        boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
                        
                        if (!id_prop) {
                                warning << "AutomationList node without automation-id property, "
                                        << "using default: " << legacy_param.symbol() << endmsg;
-                               al->set_parameter(legacy_param);
                        }
 
                        boost::shared_ptr<Evoral::Control> existing = control(param);
                        if (existing)
                                existing->set_list(al);
                        else
-                               add_control(control_factory(al));
+                               add_control(control_factory(param));
 
                } else {
                        error << "Expected AutomationList node, got '" << (*niter)->name() << endmsg;
@@ -243,14 +247,14 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
 XMLNode&
 Automatable::get_automation_state ()
 {
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
        XMLNode* node = new XMLNode (X_("Automation"));
        
-       if (_controls.empty()) {
+       if (controls().empty()) {
                return *node;
        }
 
-       for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
+       for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
                boost::shared_ptr<AutomationList> l
                                = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
                node->add_child_nocopy (l->get_state ());
@@ -262,14 +266,14 @@ Automatable::get_automation_state ()
 void
 Automatable::set_parameter_automation_state (Parameter param, AutoState s)
 {
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
        
        boost::shared_ptr<Evoral::Control> c = control (param, true);
        boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
        if (s != l->automation_state()) {
                l->set_automation_state (s);
-               _session.set_dirty ();
+               _a_session.set_dirty ();
        }
 }
 
@@ -279,7 +283,7 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock)
        AutoState result = Off;
 
        if (lock)
-               _control_lock.lock();
+               control_lock().lock();
 
        boost::shared_ptr<Evoral::Control> c = control(param);
        boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
@@ -288,7 +292,7 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock)
                result = l->automation_state();
        
        if (lock)
-               _control_lock.unlock();
+               control_lock().unlock();
 
        return result;
 }
@@ -296,21 +300,21 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock)
 void
 Automatable::set_parameter_automation_style (Parameter param, AutoStyle s)
 {
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
        
        boost::shared_ptr<Evoral::Control> c = control(param, true);
        boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
        if (s != l->automation_style()) {
                l->set_automation_style (s);
-               _session.set_dirty ();
+               _a_session.set_dirty ();
        }
 }
 
 AutoStyle
 Automatable::get_parameter_automation_style (Parameter param)
 {
-       Glib::Mutex::Lock lm (_control_lock);
+       Glib::Mutex::Lock lm (control_lock());
 
        boost::shared_ptr<Evoral::Control> c = control(param);
        boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
@@ -328,7 +332,7 @@ Automatable::protect_automation ()
        typedef set<Evoral::Parameter> ParameterSet;
        ParameterSet automated_params;
 
-       what_has_data (automated_params);
+       what_has_data(automated_params);
 
        for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
 
@@ -353,7 +357,7 @@ 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) {
+               for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
                        boost::shared_ptr<AutomationControl> c
                                        = boost::dynamic_pointer_cast<AutomationControl>(i->second);
                        if (c->automation_write()) {
@@ -368,7 +372,7 @@ Automatable::automation_snapshot (nframes_t now, bool force)
 void
 Automatable::transport_stopped (nframes_t now)
 {
-       for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
+       for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
                
                boost::shared_ptr<AutomationControl> c
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
@@ -384,20 +388,16 @@ Automatable::transport_stopped (nframes_t now)
 }
 
 boost::shared_ptr<Evoral::Control>
-Automatable::control_factory(boost::shared_ptr<Evoral::ControlList> list) const
+Automatable::control_factory(const Evoral::Parameter& param)
 {
-       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));
+       boost::shared_ptr<AutomationList> list(new AutomationList(param));
+       Evoral::Control* control = NULL;
+       if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelAftertouchAutomation) {
+               control = new MidiTrack::MidiControl((MidiTrack*)this, param);
        } else {
-               return boost::shared_ptr<Evoral::Control>(new AutomationControl(_session, l));
+               control = new AutomationControl(_a_session, param);
        }
+       control->set_list(list);
+       return boost::shared_ptr<Evoral::Control>(control);
 }
 
-boost::shared_ptr<Evoral::ControlList>
-Automatable::control_list_factory(const Evoral::Parameter& param) const
-{
-       return boost::shared_ptr<Evoral::ControlList>(new AutomationList(param));
-}
index a16306838a6d5f165c9c7db62bd3fd2f5bbe6e4c..afa14c3f98ec937237dfef08832f4d5684bad343 100644 (file)
@@ -29,9 +29,13 @@ using namespace ARDOUR;
 using namespace PBD;
 
 
-AutomationControl::AutomationControl(Session& session, boost::shared_ptr<AutomationList> list, string name)
-       : Controllable((name == "unnamed controllable") ? list->parameter().symbol() : name)
-       , Evoral::Control(list)
+AutomationControl::AutomationControl(
+               ARDOUR::Session& session,
+               const Parameter& parameter,
+               boost::shared_ptr<ARDOUR::AutomationList> list,
+               const string& name)
+       : Controllable((name != "") ? name : parameter.symbol())
+       , Evoral::Control(parameter, list)
        , _session(session)
 {
 }
@@ -42,7 +46,7 @@ AutomationControl::AutomationControl(Session& session, boost::shared_ptr<Automat
 float
 AutomationControl::get_value() const
 {
-       bool from_list = ((AutomationList*)_list.get())->automation_playback();
+       bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
        return Control::get_value(from_list, _session.transport_frame());
 }
 
@@ -50,7 +54,7 @@ AutomationControl::get_value() const
 void
 AutomationControl::set_value(float value)
 {
-       bool to_list = _session.transport_stopped()
+       bool to_list = _list && _session.transport_stopped()
                && ((AutomationList*)_list.get())->automation_playback();
        
        Control::set_value(value, to_list, _session.transport_frame());
index 69c74f5f03cb3be933967471858c4e56c4ecbdda..7db2493c76af5a57bf583fd17adfb3649c9c5485 100644 (file)
@@ -103,7 +103,8 @@ static double direct_gain_to_control (gain_t gain) {
 IO::IO (Session& s, const string& name,
        int input_min, int input_max, int output_min, int output_max,
        DataType default_type, bool public_ports)
-       : Automatable (s, name),
+       : SessionObject(s, name),
+         AutomatableControls (s),
          _output_buffers (new BufferSet()),
          _active(true),
          _default_type (default_type),
@@ -162,8 +163,9 @@ IO::IO (Session& s, const string& name,
 }
 
 IO::IO (Session& s, const XMLNode& node, DataType dt)
-       : Automatable (s, "unnamed io"),
-      _output_buffers (new BufferSet()),
+       : SessionObject(s, "unnamed io"),
+         AutomatableControls (s),
+         _output_buffers (new BufferSet()),
          _active(true),
          _default_type (dt)
 {
@@ -2266,7 +2268,7 @@ IO::meter ()
 void
 IO::clear_automation ()
 {
-       Automatable::clear (); // clears gain automation
+       data().clear (); // clears gain automation
        _panner->clear_automation ();
 }
 
@@ -2280,7 +2282,7 @@ IO::set_parameter_automation_state (Parameter param, AutoState state)
                bool changed = false;
 
                { 
-                       Glib::Mutex::Lock lm (_control_lock);
+                       Glib::Mutex::Lock lm (control_lock());
 
                        boost::shared_ptr<AutomationList> gain_auto
                                = boost::dynamic_pointer_cast<AutomationList>(_gain_control->list());
@@ -2302,7 +2304,7 @@ IO::set_parameter_automation_state (Parameter param, AutoState state)
                }
 
        } else {
-               Automatable::set_parameter_automation_state(param, state);
+               AutomatableControls::set_parameter_automation_state(param, state);
        }
 }
 
@@ -2366,7 +2368,7 @@ IO::end_pan_touch (uint32_t which)
 void
 IO::automation_snapshot (nframes_t now, bool force)
 {
-       Automatable::automation_snapshot (now, force);
+       AutomatableControls::automation_snapshot (now, force);
 
        if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
                _panner->snapshot (now);
index f65be1deead78051fff8ee0697643a7f76058805..7865f5253ba02d4f94278e5737c523360d5cb628 100644 (file)
@@ -87,6 +87,8 @@ JACK_Slave::speed_and_position (float& sp, nframes_t& position)
                _starting = true;
                // don't adjust speed here, just leave it as it was
                break;
+       default:
+               cerr << "WARNING: Unknown JACK transport state: " << state << endl;
        }
 
        sp = speed;
index da0fa364b980cd7a97a14938acdb7a10b64aaa16..c22820c83c090bfeb572e8359b89d1f877b324e9 100644 (file)
@@ -37,11 +37,10 @@ using namespace ARDOUR;
 
 
 MidiModel::MidiModel(MidiSource *s, size_t size)
-       : ControlSet()
-       , Automatable(s->session(), "midi model")
-       , Sequence(size)
+       : AutomatableSequence(s->session(), size)
        , _midi_source(s)
 {
+       cerr << "MidiModel \"" << s->name() << "\" constructed: " << this << endl;
 }
 
 /** Start a new command.
@@ -62,13 +61,13 @@ MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name)
  * The command will constitute one item on the undo stack.
  */
 void
-MidiModel::apply_command(Command* cmd)
+MidiModel::apply_command(Session& session, Command* cmd)
 {
-       _session.begin_reversible_command(cmd->name());
+       session.begin_reversible_command(cmd->name());
        (*cmd)();
        assert(is_sorted());
-       _session.commit_reversible_command(cmd);
-       _edited = true;
+       session.commit_reversible_command(cmd);
+       set_edited(true);
 }
 
 
@@ -110,35 +109,22 @@ 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
-
-       // Need to reset iterator to drop the read lock it holds, or we'll deadlock
-       const bool reset_iter = (_model->_read_iter.locked());
-       double iter_time = -1.0;
-
-       if (reset_iter) {
-               if (_model->_read_iter.get_event_pointer().get()) {
-                       iter_time = _model->_read_iter->time();
-               } else {
-                       cerr << "MidiModel::DeltaCommand::operator(): WARNING: _read_iter points to no event" << endl;
-               }
-               _model->_read_iter = _model->end(); // drop read lock
-       }
-
-       assert( ! _model->_read_iter.locked());
-
+       
        _model->write_lock();
 
-       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
+       // Store the current seek position so we can restore the read iterator
+       // after modifying the contents of the model
+       const double read_time = _model->read_time();
+
+       for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
                _model->add_note_unlocked(*i);
 
-       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
+       for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
                _model->remove_note_unlocked(*i);
 
        _model->write_unlock();
-
-       if (reset_iter && iter_time != -1.0) {
-               _model->_read_iter = const_iterator(*_model.get(), iter_time);
-       }
+       // FIXME: race?
+       _model->read_seek(read_time); // restore read position
 
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
@@ -148,37 +134,22 @@ 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
-
-       // Need to reset iterator to drop the read lock it holds, or we'll deadlock
-       const bool reset_iter = (_model->_read_iter.locked());
-       double iter_time = -1.0;
-
-       if (reset_iter) {
-               if (_model->_read_iter.get_event_pointer().get()) {
-                       iter_time = _model->_read_iter->time();
-               } else {
-                       cerr << "MidiModel::DeltaCommand::undo(): WARNING: _read_iter points to no event" << endl;
-               }
-               _model->_read_iter = _model->end(); // drop read lock
-       }
-
-       assert( ! _model->_read_iter.locked());
-
+       
        _model->write_lock();
 
-       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _added_notes.begin(); i
-                       != _added_notes.end(); ++i)
+       // Store the current seek position so we can restore the read iterator
+       // after modifying the contents of the model
+       const double read_time = _model->read_time();
+
+       for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
                _model->remove_note_unlocked(*i);
 
-       for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i =
-                       _removed_notes.begin(); i != _removed_notes.end(); ++i)
+       for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
                _model->add_note_unlocked(*i);
 
        _model->write_unlock();
-
-       if (reset_iter && iter_time != -1.0) {
-               _model->_read_iter = const_iterator(*_model.get(), iter_time);
-       }
+       // FIXME: race?
+       _model->read_seek(read_time); // restore read position
 
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
@@ -300,14 +271,14 @@ bool MidiModel::write_to(boost::shared_ptr<MidiSource> source)
        const bool old_percussive = percussive();
        set_percussive(false);
        
-       for (const_iterator i = begin(); i != end(); ++i) {
+       for (Evoral::Sequence::const_iterator i = begin(); i != end(); ++i) {
                source->append_event_unlocked(Frames, *i);
        }
                
        set_percussive(old_percussive);
        
        read_unlock();
-       _edited = false;
+       set_edited(false);
 
        return true;
 }
index d258d495247db4b54aba0418ee9399b69976b1dd..9ac1d4199499bdbd825691a46ed41c3f5d7eb6f6 100644 (file)
@@ -279,8 +279,8 @@ MidiPlaylist::contained_automation()
        for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
                boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
 
-               for (Automatable::Controls::iterator c = mr->controls().begin();
-                               c != mr->controls().end(); ++c) {
+               for (Automatable::Controls::iterator c = mr->model()->controls().begin();
+                               c != mr->model()->controls().end(); ++c) {
                        ret.insert(c->first);
                }
        }
index c11963c9d50846832d38fd01ae7572cd7d2ec0d9..975ec6d714564785a020080f4c49be8f8fc9302b 100644 (file)
@@ -87,7 +87,7 @@ MidiStretch::run (boost::shared_ptr<Region> r)
        boost::shared_ptr<MidiModel> new_model = new_src->model();
        new_model->start_write();
        
-       for (MidiModel::const_iterator i = old_model->begin(); i != old_model->end(); ++i) {
+       for (Evoral::Sequence::const_iterator i = old_model->begin(); i != old_model->end(); ++i) {
                const double new_time = i->time() * _request.time_fraction;
                
                // FIXME: double copy
index b82ba28290c985de0e686a3f1837492bf3071465..fde0281ed13fbe3df52fb1072c735fbb798b38ec 100644 (file)
@@ -153,7 +153,7 @@ PluginInsert::auto_state_changed (Parameter which)
                return;
 
        boost::shared_ptr<AutomationControl> c
-                       = boost::dynamic_pointer_cast<AutomationControl>(control (which));
+                       = boost::dynamic_pointer_cast<AutomationControl>(data().control (which));
 
        if (c && ((AutomationList*)c->list().get())->automation_state() != Off) {
                _plugins[0]->set_parameter (which.id(), c->list()->eval (_session.transport_frame()));
@@ -290,7 +290,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off
 
                uint32_t n = 0;
                
-               for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li, ++n) {
+               for (Controls::iterator li = data().controls().begin(); li != data().controls().end(); ++li, ++n) {
                        
                        boost::shared_ptr<AutomationControl> c
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
@@ -368,7 +368,7 @@ PluginInsert::set_parameter (Parameter param, float val)
 
        _plugins[0]->set_parameter (param.id(), val);
        
-       boost::shared_ptr<Evoral::Control> c = control (param);
+       boost::shared_ptr<Evoral::Control> c = data().control (param);
        if (c)
                c->set_value(val);
 
@@ -392,14 +392,14 @@ 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 (_control_lock, Glib::TRY_LOCK);
+       Glib::Mutex::Lock lm (data().control_lock(), Glib::TRY_LOCK);
 
        if (!lm.locked()) {
                connect_and_run (bufs, nframes, offset, false);
                return;
        }
        
-       if (!find_next_event (now, end, next_event)) {
+       if (!data().find_next_event (now, end, next_event)) {
                
                /* no events have a time within the relevant range */
                
@@ -417,7 +417,7 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs
                offset += cnt;
                now += cnt;
 
-               if (!find_next_event (now, end, next_event)) {
+               if (!data().find_next_event (now, end, next_event)) {
                        break;
                }
        }
@@ -636,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 (((AutomationList*)control(*x)->list().get())->state (full));
+               autonode->add_child_nocopy (((AutomationList*)data().control(*x)->list().get())->state (full));
        }
 
        node.add_child_nocopy (*autonode);
@@ -760,7 +760,7 @@ PluginInsert::set_state(const XMLNode& node)
                        }
 
                        boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(
-                                       control(Parameter(PluginAutomation, port_id), true));
+                                       data().control(Parameter(PluginAutomation, port_id), true));
 
                        if (!child->children().empty()) {
                                c->alist()->set_state (*child->children().front());
@@ -847,7 +847,7 @@ PluginInsert::type ()
 }
 
 PluginInsert::PluginControl::PluginControl (PluginInsert& p, boost::shared_ptr<AutomationList> list)
-       : AutomationControl (p.session(), list, p.describe_parameter(list->parameter()))
+       : AutomationControl (p.session(), list->parameter(), list, p.describe_parameter(list->parameter()))
        , _plugin (p)
        , _list (list)
 {
index 486a75703bb59758ca7f54be175de0ca33fb67be..82591effa4cf6f80cf63dfcc16e1fcde16eba30b 100644 (file)
@@ -59,7 +59,8 @@ sigc::signal<void,Processor*> Processor::ProcessorCreated;
 const string Processor::state_node_name = "Processor";
 
 Processor::Processor(Session& session, const string& name, Placement p)
-       : Automatable(session, name)
+       : SessionObject(session, name)
+       , AutomatableControls(session)
        , _active(false)
        , _next_ab_is_active(false)
        , _configured(false)
index ccbda9711a5f5f02b73bf854fd065c1936e6857f..ff8925edd95039d440f45a79d7398a1956c1e679 100644 (file)
@@ -69,7 +69,8 @@ Quantize::run (boost::shared_ptr<Region> r)
 
        double q_frames = _q * (m.frames_per_bar(t, session.frame_rate()) / (double)m.beats_per_bar());
 
-       for (MidiModel::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) {
+       for (Evoral::Sequence::Notes::iterator i = model->notes().begin();
+                       i != model->notes().end(); ++i) {
                const double new_time = lrint((*i)->time() / q_frames) * q_frames;
                double new_dur = lrint((*i)->duration() / q_frames) * q_frames;
                if (new_dur == 0.0)
index 25435024b331cb5aa5c368a0c25301db9e64437f..8693b7df8e50fa4a026715ef2efb1f91cc297a81 100644 (file)
@@ -57,7 +57,7 @@ sigc::signal<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChan
 
 /* derived-from-derived constructor (no sources in constructor) */
 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
-       : Automatable(s, name)
+       : SessionObject(s, name)
        , _type(type)
        , _flags(flags)
        , _start(start) 
@@ -79,7 +79,7 @@ Region::Region (Session& s, nframes_t start, nframes_t length, const string& nam
 
 /** Basic Region constructor (single source) */
 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
-       : Automatable(src->session(), name)
+       : SessionObject(src->session(), name)
        , _type(type)
        , _flags(flags)
        , _start(start) 
@@ -112,7 +112,7 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
 
 /** Basic Region constructor (many sources) */
 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
-       : Automatable(srcs.front()->session(), name)
+       : SessionObject(srcs.front()->session(), name)
        , _type(type)
        , _flags(flags)
        , _start(start) 
@@ -150,7 +150,7 @@ Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const
 
 /** Create a new Region from part of an existing one */
 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
-       : Automatable(other->session(), name)
+       : SessionObject(other->session(), name)
        , _type(other->data_type())
        , _flags(Flag(flags & ~(Locked|PositionLocked|WholeFile|Hidden)))
        , _start(other->_start + offset) 
@@ -199,7 +199,7 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
 
 /** Pure copy constructor */
 Region::Region (boost::shared_ptr<const Region> other)
-       : Automatable(other->session(), other->name())
+       : SessionObject(other->session(), other->name())
        , _type(other->data_type())
        , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
        , _start(other->_start) 
@@ -247,7 +247,7 @@ Region::Region (boost::shared_ptr<const Region> other)
 }
 
 Region::Region (const SourceList& srcs, const XMLNode& node)
-       : Automatable(srcs.front()->session(), X_("error: XML did not reset this"))
+       : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
        , _type(DataType::NIL) // to be loaded from XML
        , _flags(Flag(0))
        , _start(0) 
@@ -288,7 +288,7 @@ Region::Region (const SourceList& srcs, const XMLNode& node)
 }
 
 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
-       : Automatable(src->session(), X_("error: XML did not reset this"))
+       : SessionObject(src->session(), X_("error: XML did not reset this"))
        , _type(DataType::NIL)
        , _flags(Flag(0))
        , _start(0) 
index 79d2b4f9b6da17d479a7f3d2451443c88620cd28..7cf6c4ef36487e3efff13fe334a01f57b0ffb8bb 100644 (file)
@@ -2528,7 +2528,7 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra
        apply_gain_automation = false;
 
        { 
-               Glib::Mutex::Lock am (_control_lock, Glib::TRY_LOCK);
+               Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK);
                
                if (am.locked() && _session.transport_rolling()) {
                        
index 15d50fcdcab430a3e3f03516fc6bdff38a1d2948..01dc2eebed2aaba3b39eebc4b76e12c77d6decb3 100644 (file)
@@ -34,7 +34,7 @@ class Transport;
 class Control
 {
 public:
-       Control(boost::shared_ptr<ControlList>);
+       Control(const Parameter& parameter, boost::shared_ptr<ControlList>);
        virtual ~Control() {}
 
        void  set_value(float val, bool to_list=false, nframes_t frame=0);
@@ -46,9 +46,10 @@ public:
        boost::shared_ptr<ControlList>       list()       { return _list; }
        boost::shared_ptr<const ControlList> list() const { return _list; }
 
-       const Parameter& parameter() const;
+       inline const Parameter& parameter() const { return _parameter; }
 
 protected:
+       Parameter                      _parameter;
        boost::shared_ptr<ControlList> _list;
        float                          _user_value;
 };
index 1b2b46b10bfb959dcbd2e6b24ec6a4865470fe0d..36799300feed4a48dd3d7a365a4e76dbe141edd3 100644 (file)
@@ -84,13 +84,12 @@ public:
        typedef EventList::const_iterator const_iterator;
 
        ControlList (const Parameter& id);
-       //ControlList (const XMLNode&, Parameter id);
+       ControlList (const ControlList&);
+       ControlList (const ControlList&, double start, double end);
        virtual ~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&);
        
index 73fa5554e6415c46ba114cd249d6367d6ab6e373..1b5edb92ace1ec76f18d2fca631f33c2bc889a8f 100644 (file)
@@ -22,6 +22,7 @@
 #include <set>
 #include <map>
 #include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
 #include <glibmm/thread.h>
 #include <evoral/types.hpp>
 #include <evoral/Parameter.hpp>
@@ -32,27 +33,32 @@ class Control;
 class ControlList;
 class ControlEvent;
 
-class ControlSet {
+class ControlSet : public boost::noncopyable {
 public:
        ControlSet();
        virtual ~ControlSet() {}
-
-       virtual boost::shared_ptr<Control> control(const Parameter& id, bool create_if_missing=false);
-       virtual boost::shared_ptr<const Control> control(const 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;
+       virtual boost::shared_ptr<Evoral::Control>
+       control_factory(const Evoral::Parameter& id) = 0;
        
+       boost::shared_ptr<Control>
+       control (const Parameter& id, bool create_if_missing=false);
+
+       inline boost::shared_ptr<const Control>
+       control (const Parameter& id) const {
+               const Controls::const_iterator i = _controls.find(id);
+               return (i != _controls.end() ? i->second : boost::shared_ptr<Control>());
+       }
+
        typedef std::map< Parameter, boost::shared_ptr<Control> > Controls;
-       Controls&       controls()       { return _controls; }
-       const Controls& controls() const { return _controls; }
+       inline Controls&       controls()       { return _controls; }
+       inline 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;
+       bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
        
-       virtual float default_parameter_value(const Parameter& param) { return 1.0f; }
-
+       virtual bool empty() const { return _controls.size() == 0; }
        virtual void clear();
 
        void what_has_data(std::set<Parameter>&) const;
@@ -64,6 +70,7 @@ protected:
        Controls            _controls;
 };
 
+
 } // namespace Evoral
 
 #endif // EVORAL_CONTROLLABLE_HPP
index 10a61704ed1a835f282e8e26e0a97797e7cefea1..73ddaca21b4c84cf7baad3c550ac3d1b6e808acc 100644 (file)
@@ -25,7 +25,6 @@
 #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>
@@ -39,6 +38,7 @@ class Note;
 class Event;
 class ControlList;
 
+
 /** This class keeps track of the current x and y for a control
  */
 class ControlIterator {
@@ -59,12 +59,13 @@ public:
 
 /** 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 {
+ * Controller data is represented as a list of time-stamped float values. */
+class Sequence : virtual public ControlSet {
 public:
-       Sequence(size_t size);
+       Sequence(size_t size=0);
        
+       bool read_locked() { return _read_iter.locked(); }
+
        void write_lock();
        void write_unlock();
 
@@ -92,10 +93,10 @@ public:
        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 bool   empty()   const { return _notes.size() == 0 && ControlSet::empty(); }
 
-       inline static bool note_time_comparator(const boost::shared_ptr<const Note> a,
-                                               const boost::shared_ptr<const Note> b) { 
+       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();
        }
 
@@ -117,6 +118,7 @@ public:
                const_iterator(const Sequence& seq, double t);
                ~const_iterator();
 
+               inline bool valid() const { return !_is_end && _event; }
                inline bool locked() const { return _locked; }
 
                const Event& operator*()  const { return *_event;  }
@@ -149,28 +151,28 @@ public:
                std::vector<ControlIterator>::iterator _control_iter;
        };
        
-       const_iterator        begin() const { return const_iterator(*this, 0); }
-       const const_iterator& end()   const { return _end_iter; }
+       const_iterator        begin(double t=0) const { return const_iterator(*this, t); }
+       const const_iterator& end()             const { return _end_iter; }
        
+       void   read_seek(double t) { _read_iter = begin(t); }
+       double read_time() const   { return _read_iter.valid() ? _read_iter->time() : 0.0; }
+
        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; }
+
+#ifndef NDEBUG
+       bool is_sorted() const;
+#endif
        
-protected:
        void add_note_unlocked(const boost::shared_ptr<Note> note);
        void remove_note_unlocked(const boost::shared_ptr<const Note> note);
        
+protected:
        mutable const_iterator _read_iter;
        bool                   _edited;
-#ifndef NDEBUG
-       bool is_sorted() const;
-#endif
 
 private:
        friend class const_iterator;
@@ -182,7 +184,6 @@ private:
        mutable Glib::RWLock _lock;
 
        Notes    _notes;
-       Controls _controls;
        
        typedef std::vector<size_t> WriteNotes;
        WriteNotes _write_notes[16];
@@ -197,7 +198,7 @@ private:
 
        /** FIXME: Make fully dynamic, map to URIs */
        enum EventTypes {
-               midi_cc_type=1,
+               midi_cc_type=0x20, // FIXME FIXME FIXME eeww
                midi_pc_type,
                midi_pb_type,
                midi_ca_type
@@ -209,6 +210,7 @@ private:
                ActiveNotes;
 };
 
+
 } // namespace Evoral
 
 #endif // EVORAL_SEQUENCE_HPP
index d23f6c3c9a931daa584ab02133a9ecac64519e39..75b038f1d41325bcf04dc759c641891422aef851 100644 (file)
@@ -24,9 +24,10 @@ namespace Evoral {
 
 Parameter::TypeMetadata Parameter::_type_metadata;
 
-Control::Control(boost::shared_ptr<ControlList> list)
-       : _list(list)
-       , _user_value(list->default_value())
+Control::Control(const Parameter& parameter, boost::shared_ptr<ControlList> list)
+       : _parameter(parameter)
+       , _list(list)
+       , _user_value(list ? list->default_value() : parameter.normal())
 {
 }
 
@@ -70,14 +71,6 @@ void
 Control::set_list(boost::shared_ptr<ControlList> list)
 {
        _list = list;
-       _user_value = list->default_value();
-}
-
-       
-const Parameter&
-Control::parameter() const
-{
-       return _list->parameter();
 }
 
 } // namespace Evoral
index 62c49a97e6f2175460391a171afb6892d07100f9..fd0a2e52bde08f57a1b94d9cc282ac44a054c3ea 100644 (file)
@@ -109,7 +109,6 @@ ControlList::~ControlList()
                delete (*x);
        }
 }
-       
 
 boost::shared_ptr<ControlList>
 ControlList::create(Parameter id)
@@ -117,7 +116,6 @@ ControlList::create(Parameter id)
        return boost::shared_ptr<ControlList>(new ControlList(id));
 }
 
-
 bool
 ControlList::operator== (const ControlList& other)
 {
@@ -205,7 +203,7 @@ ControlList::reposition_for_rt_add (double when)
 void
 ControlList::rt_add (double when, double value)
 {
-       // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
+       cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
 
        {
                Glib::Mutex::Lock lm (_lock);
index 5aacff598d6792ab7aa2d029bf9ce2b6b3d3966b..837810c72764ae4a2f1e464e3593b9240310843b 100644 (file)
@@ -26,6 +26,7 @@ using namespace std;
 
 namespace Evoral {
 
+
 ControlSet::ControlSet()
 {
 }
@@ -40,17 +41,14 @@ 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);
+       for (Controls::const_iterator 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.
+/** If a control for the given parameter does not exist and \a create_if_missing is true,
+ * a control will be created, added to this set, and returned.
+ * If \a create_if_missing is false this function may return null.
  */
 boost::shared_ptr<Control>
 ControlSet::control (const Parameter& parameter, bool create_if_missing)
@@ -61,8 +59,7 @@ ControlSet::control (const Parameter& parameter, bool create_if_missing)
                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));
+               boost::shared_ptr<Control> ac(control_factory(parameter));
                add_control(ac);
                return ac;
 
@@ -72,19 +69,6 @@ ControlSet::control (const Parameter& parameter, bool create_if_missing)
        }
 }
 
-boost::shared_ptr<const Control>
-ControlSet::control (const 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
 {
@@ -122,18 +106,6 @@ ControlSet::clear ()
        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
index fecc37be29d2e56fabc306eb963f527e7103f4e0..9e073699d944fdb784562f1785f124a023f2b2e5 100644 (file)
@@ -52,6 +52,16 @@ void Sequence::read_unlock() const {
        _lock.reader_unlock();
 }
 
+struct null_ostream : public std::ostream {
+       null_ostream(): std::ios(0), std::ostream(0) {}
+};
+static null_ostream nullout;
+
+//static ostream& debugout = cout;
+//static ostream& errorout = cerr;
+static ostream& debugout = nullout;
+static ostream& errorout = nullout;
+
 // Read iterator (const_iterator)
 
 Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
@@ -59,7 +69,7 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
        , _is_end( (t == DBL_MAX) || seq.empty() )
        , _locked( !_is_end )
 {
-       //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
+       debugout << "Created Iterator @ " << t << " (is end: " << _is_end << ")" << endl;
 
        if (_is_end) {
                return;
@@ -78,30 +88,31 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
 
        ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
 
-       _control_iters.reserve(seq.controls().size());
+       _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) {
+       for (Controls::const_iterator i = seq._controls.begin(); i != seq._controls.end(); ++i) {
+               debugout << "Iterator: control: " << i->first.symbol() << endl;
                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;
+                       debugout << "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()
+               /*if (y < i->first.min() || y > i->first.max()) {
+                       errorout << "ERROR: Controller " << i->first.symbol() << " 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;
+               debugout << "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
@@ -137,7 +148,7 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
        }
 
        if ( (! _event.get()) || _event->size() == 0) {
-               //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
+               debugout << "New iterator @ " << t << " is at end." << endl;
                _is_end = true;
 
                // eliminate possible race condition here (ugly)
@@ -148,7 +159,8 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
                        _locked = false;
                }
        } else {
-               //printf("New MIDI Iterator = %X @ %lf\n", _event->type(), _event->time());
+               debugout << "New Iterator = " << hex << _event->type();
+               debugout << " @ " <<  _event->time() << endl;
        }
 
        assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0'));
@@ -161,7 +173,8 @@ Sequence::const_iterator::~const_iterator()
        }
 }
 
-const Sequence::const_iterator& Sequence::const_iterator::operator++()
+const
+Sequence::const_iterator& Sequence::const_iterator::operator++()
 {
        if (_is_end) {
                throw std::logic_error("Attempt to iterate past end of Sequence");
@@ -169,10 +182,12 @@ const Sequence::const_iterator& Sequence::const_iterator::operator++()
        
        assert(_event->buffer() && _event->buffer()[0] != '\0');
 
-       /*cerr << "const_iterator::operator++: " << _event->to_string() << endl;*/
+       //debugout << "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;
+       if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change()
+                               || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
+               errorout << "Unknown event type: " << 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()));
 
@@ -202,7 +217,7 @@ const Sequence::const_iterator& Sequence::const_iterator::operator++()
                }
        }
 
-       enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
+       enum Type {NIL, NOTE_ON, NOTE_OFF, CONTROL};
 
        Type type = NIL;
        double t = 0;
@@ -222,26 +237,27 @@ const Sequence::const_iterator& Sequence::const_iterator::operator++()
        }
 
        // 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 (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX
+                       && _control_iter != old_control_iter) {
                if (type == NIL || _control_iter->x < t) {
-                       type = AUTOMATION;
+                       type = CONTROL;
                }
        }
 
        if (type == NOTE_ON) {
-               //cerr << "********** MIDI Iterator = note on" << endl;
+               debugout << "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;
+               debugout << "Iterator = note off" << endl;
                *_event = _active_notes.top()->off_event();
                _active_notes.pop();
-       } else if (type == AUTOMATION) {
-               //cerr << "********** MIDI Iterator = Automation" << endl;
+       } else if (type == CONTROL) {
+               debugout << "Iterator = control" << endl;
                _seq->control_to_midi_event(_event, *_control_iter);
        } else {
-               //cerr << "********** MIDI Iterator = End" << endl;
+               debugout << "Iterator = End" << endl;
                _is_end = true;
        }
 
@@ -250,7 +266,8 @@ const Sequence::const_iterator& Sequence::const_iterator::operator++()
        return *this;
 }
 
-bool Sequence::const_iterator::operator==(const const_iterator& other) const
+bool
+Sequence::const_iterator::operator==(const const_iterator& other) const
 {
        if (_is_end || other._is_end) {
                return (_is_end == other._is_end);
@@ -259,7 +276,8 @@ bool Sequence::const_iterator::operator==(const const_iterator& other) const
        }
 }
 
-Sequence::const_iterator& Sequence::const_iterator::operator=(const const_iterator& other)
+Sequence::const_iterator&
+Sequence::const_iterator::operator=(const const_iterator& other)
 {
        if (_locked && _seq != other._seq) {
                _seq->read_unlock();
@@ -292,6 +310,7 @@ Sequence::Sequence(size_t size)
        , _next_read(UINT32_MAX)
        , _percussive(false)
 {
+       debugout << "Sequence (size " << size << ") constructed: " << this << endl;
        assert(_end_iter._is_end);
        assert( ! _end_iter._locked);
 }
@@ -300,18 +319,21 @@ Sequence::Sequence(size_t size)
  * 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
+size_t
+Sequence::read(EventSink& dst, timestamp_t start, timedur_t nframes, timestamp_t offset) const
 {
-       //cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
-       //cerr << this << " MM # notes: " << n_notes() << endl;
+       debugout << this << " read ev @ " << start << " * " << nframes << " + " << offset << endl;
+       debugout << this << " # notes: " << n_notes() << endl;
+       debugout << this << " controls: " << &_controls << endl;
+       debugout << this << " # controls: " << _controls.size() << 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;
+               debugout << "Repositioning iterator from " << _next_read << " to " << start << endl;
        } else {
-               //cerr << "Using cached iterator at " << _next_read << endl;
+               debugout << "Using cached iterator at " << _next_read << endl;
        }
 
        _next_read = (nframes_t) floor (start + nframes);
@@ -323,11 +345,11 @@ size_t Sequence::read(EventSink& dst, timestamp_t start, timestamp_t nframes, ti
                          _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;*/
+                debugout << this << " 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;
@@ -406,10 +428,10 @@ Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlItera
        return true;
 }
 
-
 /** Clear all events from the model.
  */
-void Sequence::clear()
+void
+Sequence::clear()
 {
        _lock.writer_lock();
        _notes.clear();
@@ -420,7 +442,6 @@ void Sequence::clear()
        _lock.writer_unlock();
 }
 
-
 /** Begin a write of events to the model.
  *
  * If \a mode is Sustained, complete notes with duration are constructed as note
@@ -428,9 +449,10 @@ void Sequence::clear()
  * stored; note off events are discarded entirely and all contained notes will
  * have duration 0.
  */
-void Sequence::start_write()
+void
+Sequence::start_write()
 {
-       //cerr << "MM " << this << " START WRITE, PERCUSSIVE = " << _percussive << endl;
+       debugout << this << " START WRITE, PERCUSSIVE = " << _percussive << endl;
        write_lock();
        _writing = true;
        for (int i = 0; i < 16; ++i)
@@ -446,17 +468,18 @@ void Sequence::start_write()
  * 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)
+void
+Sequence::end_write(bool delete_stuck)
 {
        write_lock();
        assert(_writing);
 
-       //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
+       debugout << 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;
+                               errorout << "WARNING: Stuck note lost: " << (*n)->note() << endl;
                                n = _notes.erase(n);
                                // we have to break here because erase invalidates the iterator
                                break;
@@ -468,7 +491,7 @@ void Sequence::end_write(bool delete_stuck)
 
        for (int i = 0; i < 16; ++i) {
                if (!_write_notes[i].empty()) {
-                       cerr << "WARNING: Sequence::end_write: Channel " << i << " has "
+                       errorout << "WARNING: Sequence::end_write: Channel " << i << " has "
                                        << _write_notes[i].size() << " stuck notes" << endl;
                }
                _write_notes[i].clear();
@@ -488,7 +511,8 @@ void Sequence::end_write(bool delete_stuck)
  * 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)
+void
+Sequence::append(const Event& ev)
 {
        write_lock();
        _edited = true;
@@ -503,7 +527,7 @@ void Sequence::append(const Event& ev)
                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()),
+                               Evoral::MIDI::ContinuousController(midi_cc_type, ev.channel(), ev.cc_number()),
                                ev.time(), ev.cc_value());
        } else if (ev.is_pgm_change()) {
                append_control_unlocked(
@@ -525,12 +549,10 @@ void Sequence::append(const Event& ev)
        write_unlock();
 }
 
-void Sequence::append_note_on_unlocked(uint8_t chan, double time,
-               uint8_t note_num, uint8_t velocity)
+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;*/
-
+       debugout << this << " c" << (int)chan << " note " << (int)note_num << " off @ " << time << endl;
        assert(note_num <= 127);
        assert(chan < 16);
        assert(_writing);
@@ -539,26 +561,24 @@ void Sequence::append_note_on_unlocked(uint8_t chan, double time,
        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;
+               debugout << "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;
-        }*/
+       } else {
+               debugout << "Percussive: NOT appending active note on" << endl;
+        }
 }
 
-void Sequence::append_note_off_unlocked(uint8_t chan, double time,
-               uint8_t note_num)
+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;*/
-
+       debugout << this << " c" << (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;
+               debugout << "Sequence Ignoring note off (percussive mode)" << endl;
                return;
        }
 
@@ -576,37 +596,43 @@ void Sequence::append_note_off_unlocked(uint8_t chan, double time,
                        assert(time >= note.time());
                        note.set_duration(time - note.time());
                        _write_notes[chan].erase(n);
-                       //cerr << "MM resolved note, duration: " << note.duration() << endl;
+                       debugout << "resolved note, duration: " << note.duration() << endl;
                        resolved = true;
                        break;
                }
        }
 
        if (!resolved) {
-               cerr << "Sequence " << this << " spurious note off chan " << (int)chan
+               errorout << this << " spurious note off chan " << (int)chan
                                << ", note " << (int)note_num << " @ " << time << endl;
        }
 }
 
-void Sequence::append_control_unlocked(const Parameter& param, double time, double value)
+void
+Sequence::append_control_unlocked(const Parameter& param, double time, double value)
 {
+       debugout << this << " " << param.symbol() << " @ " << time << " = " << value
+                       << " controls: " << &_controls
+                       << " # controls: " << _controls.size() << endl;
        control(param, true)->list()->rt_add(time, value);
 }
 
 
-void Sequence::add_note_unlocked(const boost::shared_ptr<Note> note)
+void
+Sequence::add_note_unlocked(const boost::shared_ptr<Note> note)
 {
-       //cerr << "Sequence " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
+       debugout << 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)
+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;
+       debugout << 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;
@@ -628,7 +654,8 @@ void Sequence::remove_note_unlocked(const boost::shared_ptr<const Note> note)
 
 /** Slow!  for debugging only. */
 #ifndef NDEBUG
-bool Sequence::is_sorted() const {
+bool
+Sequence::is_sorted() const {
        bool t = 0;
        for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
                if ((*n)->time() < t)