mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 14:54:56 +01:00
Display recorded controller data (fix show all/existing automation).
git-svn-id: svn://localhost/ardour2/branches/3.0@3779 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
eec19ca7af
commit
e14187aadd
46 changed files with 460 additions and 375 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
if (_control->list())
|
||||
_control->alist()->set_automation_state(state);
|
||||
|
||||
}
|
||||
_view->set_automation_state (state);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,7 +474,7 @@ MidiRegionView::apply_command()
|
|||
_marked_for_selection.insert((*i)->note());
|
||||
}
|
||||
|
||||
_model->apply_command(_delta_command);
|
||||
_model->apply_command(trackview.session(), _delta_command);
|
||||
_delta_command = NULL;
|
||||
midi_view()->midi_track()->diskstream()->playlist_modified();
|
||||
|
||||
|
|
|
|||
|
|
@ -293,9 +293,10 @@ 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,9 +308,10 @@ 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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -124,6 +124,18 @@ class AudioRegion : public Region
|
|||
|
||||
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 */
|
||||
|
||||
int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&);
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,21 +28,27 @@
|
|||
#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>);
|
||||
|
||||
|
|
@ -75,7 +81,13 @@ public:
|
|||
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);
|
||||
|
||||
|
|
@ -94,6 +106,25 @@ protected:
|
|||
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__ */
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,21 +73,27 @@ class MidiRegion : public Region
|
|||
|
||||
int separate_by_channel (ARDOUR::Session&, vector<MidiRegion*>&) const;
|
||||
|
||||
UndoAction get_memento() const;
|
||||
/* automation */
|
||||
|
||||
// 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 model()->data().control(id, create);
|
||||
}
|
||||
|
||||
boost::shared_ptr<Evoral::Control> control(const Evoral::Parameter& id, bool create=false)
|
||||
{ return midi_source()->model()->control(id, create); }
|
||||
virtual boost::shared_ptr<const Evoral::Control>
|
||||
control(const Evoral::Parameter& id) const {
|
||||
return model()->data().control(id);
|
||||
}
|
||||
|
||||
boost::shared_ptr<const Evoral::Control> control(const Evoral::Parameter& id) const
|
||||
{ return midi_source()->model()->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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -126,16 +128,15 @@ 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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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,7 +163,8 @@ IO::IO (Session& s, const string& name,
|
|||
}
|
||||
|
||||
IO::IO (Session& s, const XMLNode& node, DataType dt)
|
||||
: Automatable (s, "unnamed io"),
|
||||
: 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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -111,34 +110,21 @@ 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 */
|
||||
}
|
||||
|
|
@ -149,36 +135,21 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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&);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Evoral::Control>
|
||||
control_factory(const Evoral::Parameter& id) = 0;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
virtual float default_parameter_value(const Parameter& param) { return 1.0f; }
|
||||
bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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,11 +59,12 @@ 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,29 +151,29 @@ public:
|
|||
std::vector<ControlIterator>::iterator _control_iter;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(*this, 0); }
|
||||
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; }
|
||||
|
||||
protected:
|
||||
void add_note_unlocked(const boost::shared_ptr<Note> note);
|
||||
void remove_note_unlocked(const boost::shared_ptr<const Note> note);
|
||||
|
||||
mutable const_iterator _read_iter;
|
||||
bool _edited;
|
||||
#ifndef NDEBUG
|
||||
bool is_sorted() const;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -110,14 +110,12 @@ ControlList::~ControlList()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<ControlList>
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -123,17 +107,5 @@ ControlSet::clear ()
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
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;*/
|
||||
<< 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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue