diff options
46 files changed, 455 insertions, 370 deletions
diff --git a/gtk2_ardour/audio_time_axis.cc b/gtk2_ardour/audio_time_axis.cc index 8c77da3443..51aa299128 100644 --- a/gtk2_ardour/audio_time_axis.cc +++ b/gtk2_ardour/audio_time_axis.cc @@ -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, diff --git a/gtk2_ardour/automation_controller.cc b/gtk2_ardour/automation_controller.cc index 775c12f668..263761dcc5 100644 --- a/gtk2_ardour/automation_controller.cc +++ b/gtk2_ardour/automation_controller.cc @@ -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)); } diff --git a/gtk2_ardour/automation_controller.h b/gtk2_ardour/automation_controller.h index 0da24b3588..767c4ced00 100644 --- a/gtk2_ardour/automation_controller.h +++ b/gtk2_ardour/automation_controller.h @@ -37,7 +37,7 @@ class AutomationController : public Gtkmm2ext::BarController { public: static boost::shared_ptr<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(); diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 27da5e5c1f..8754bbc21a 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -1107,6 +1107,13 @@ AutomationLine::change_model_range (AutomationList::iterator start, AutomationLi } void +AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list) +{ + alist = list; + queue_reset(); +} + +void AutomationLine::show_all_control_points () { points_visible = true; diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 92c012fc82..1123a7b9a4 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -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 (); diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc index c213b3feeb..b581cb9a9d 100644 --- a/gtk2_ardour/automation_region_view.cc +++ b/gtk2_ardour/automation_region_view.cc @@ -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)); diff --git a/gtk2_ardour/automation_region_view.h b/gtk2_ardour/automation_region_view.h index 33c6a6c7e6..759382b562 100644 --- a/gtk2_ardour/automation_region_view.h +++ b/gtk2_ardour/automation_region_view.h @@ -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. diff --git a/gtk2_ardour/automation_streamview.cc b/gtk2_ardour/automation_streamview.cc index 9bce718fce..024d235c7d 100644 --- a/gtk2_ardour/automation_streamview.cc +++ b/gtk2_ardour/automation_streamview.cc @@ -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; } @@ -140,6 +142,17 @@ AutomationStreamView::display_region(AutomationRegionView* region_view) } 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 () { list<RegionView *>::iterator i, tmp; diff --git a/gtk2_ardour/automation_streamview.h b/gtk2_ardour/automation_streamview.h index 5eebfaf28d..4188e7e983 100644 --- a/gtk2_ardour/automation_streamview.h +++ b/gtk2_ardour/automation_streamview.h @@ -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 (); diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index daf0a01b95..76ef196b52 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -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")); diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc index 34ab571c8c..c77f7e7d74 100644 --- a/gtk2_ardour/generic_pluginui.cc +++ b/gtk2_ardour/generic_pluginui.cc @@ -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. diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 3184bd104b..bfd23f900e 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -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(); diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 500b81cb01..b8f5da2ed9 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -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, diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 2eceeb4974..6d6188c146 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -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, diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index f8bd8a1794..b8cde44b77 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -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; diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index 99e7891ce8..837bbbc617 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -28,22 +28,28 @@ #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__ */ diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 78f4553d87..52fce1096c 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -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); } diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 6cae11a7fa..08c8a1b211 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -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) {} diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 51bddfde44..a1a0da7296 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -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); diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index e0caddd954..2cbed8f99c 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -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; diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 7eda6f904b..02313c7e6e 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -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) {} diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 47ef212d58..fb8048e7ef 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -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); } diff --git a/libs/ardour/ardour/processor.h b/libs/ardour/ardour/processor.h index 8c4ac8dfe5..4b236d159e 100644 --- a/libs/ardour/ardour/processor.h +++ b/libs/ardour/ardour/processor.h @@ -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; diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index dc81de6374..d3c8341623 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -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 (); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 532abeb123..0418074ef2 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -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); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 707f10e91a..2b6b87d061 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -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))) diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 5595ebc2cf..f77975c303 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -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)); -} diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index a16306838a..afa14c3f98 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -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()); diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 69c74f5f03..7db2493c76 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -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); diff --git a/libs/ardour/jack_slave.cc b/libs/ardour/jack_slave.cc index f65be1deea..7865f5253b 100644 --- a/libs/ardour/jack_slave.cc +++ b/libs/ardour/jack_slave.cc @@ -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; diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index da0fa364b9..c22820c83c 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -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; } diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index d258d49524..9ac1d41994 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -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); } } diff --git a/libs/ardour/midi_stretch.cc b/libs/ardour/midi_stretch.cc index c11963c9d5..975ec6d714 100644 --- a/libs/ardour/midi_stretch.cc +++ b/libs/ardour/midi_stretch.cc @@ -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 diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index b82ba28290..fde0281ed1 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -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) { diff --git a/libs/ardour/processor.cc b/libs/ardour/processor.cc index 486a75703b..82591effa4 100644 --- a/libs/ardour/processor.cc +++ b/libs/ardour/processor.cc @@ -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) diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index ccbda9711a..ff8925edd9 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -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) diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 25435024b3..8693b7df8e 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -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) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 79d2b4f9b6..7cf6c4ef36 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -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()) { diff --git a/libs/evoral/evoral/Control.hpp b/libs/evoral/evoral/Control.hpp index 15d50fcdca..01dc2eebed 100644 --- a/libs/evoral/evoral/Control.hpp +++ b/libs/evoral/evoral/Control.hpp @@ -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; }; diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 1b2b46b10b..36799300fe 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -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&); diff --git a/libs/evoral/evoral/ControlSet.hpp b/libs/evoral/evoral/ControlSet.hpp index 73fa5554e6..1b5edb92ac 100644 --- a/libs/evoral/evoral/ControlSet.hpp +++ b/libs/evoral/evoral/ControlSet.hpp @@ -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 diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index 10a61704ed..73ddaca21b 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -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 diff --git a/libs/evoral/src/Control.cpp b/libs/evoral/src/Control.cpp index d23f6c3c9a..75b038f1d4 100644 --- a/libs/evoral/src/Control.cpp +++ b/libs/evoral/src/Control.cpp @@ -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 diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index 62c49a97e6..fd0a2e52bd 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -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); diff --git a/libs/evoral/src/ControlSet.cpp b/libs/evoral/src/ControlSet.cpp index 5aacff598d..837810c727 100644 --- a/libs/evoral/src/ControlSet.cpp +++ b/libs/evoral/src/ControlSet.cpp @@ -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 diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp index fecc37be29..9e073699d9 100644 --- a/libs/evoral/src/Sequence.cpp +++ b/libs/evoral/src/Sequence.cpp @@ -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) |