mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 23:35:03 +01:00
Factor out sequencing related things into an independant new library: "evoral".
Anything related to the storage of events/values over a range of time lives in evoral. This includes MidiModel (Evoral::Sequence) and automation data (AutomationList (Evoral::ControlList), Automatable (Evoral::ControlSet), etc). libs/evoral synced with http://svn.drobilla.net/lad/trunk/evoral r1511. git-svn-id: svn://localhost/ardour2/branches/3.0@3754 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
3d976c5b72
commit
d357eca668
102 changed files with 4640 additions and 3217 deletions
|
|
@ -628,6 +628,7 @@ libraries['ardour_cp'] = LibraryInfo (LIBS='ardour_cp', LIBPATH='#libs/surfaces/
|
|||
|
||||
libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
|
||||
libraries['midi++2'] = LibraryInfo (LIBS='midi++', LIBPATH='#libs/midi++2', CPPPATH='#libs/midi++2')
|
||||
libraries['evoral'] = LibraryInfo (LIBS='evoral', LIBPATH='#libs/evoral', CPPPATH='#libs/evoral')
|
||||
libraries['pbd'] = LibraryInfo (LIBS='pbd', LIBPATH='#libs/pbd', CPPPATH='#libs/pbd')
|
||||
libraries['gtkmm2ext'] = LibraryInfo (LIBS='gtkmm2ext', LIBPATH='#libs/gtkmm2ext', CPPPATH='#libs/gtkmm2ext')
|
||||
|
||||
|
|
@ -1083,6 +1084,7 @@ if env['SYSLIBS']:
|
|||
subdirs = [
|
||||
'libs/pbd',
|
||||
'libs/midi++2',
|
||||
'libs/evoral',
|
||||
'libs/ardour',
|
||||
'libs/vamp-sdk',
|
||||
'libs/vamp-plugins/',
|
||||
|
|
@ -1159,6 +1161,7 @@ else:
|
|||
'libs/taglib',
|
||||
'libs/pbd',
|
||||
'libs/midi++2',
|
||||
'libs/evoral',
|
||||
'libs/ardour',
|
||||
'libs/vamp-sdk',
|
||||
'libs/vamp-plugins/',
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ gtkardour.Merge ([
|
|||
libraries['libgnomecanvasmm'],
|
||||
libraries['lrdf'],
|
||||
libraries['midi++2'],
|
||||
libraries['evoral'],
|
||||
libraries['pangomm'],
|
||||
libraries['pbd'],
|
||||
libraries['samplerate'],
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ AudioTimeAxisView::create_automation_child (Parameter param, bool show)
|
|||
update_pans (show);
|
||||
|
||||
} else {
|
||||
error << "AudioTimeAxisView: unknown automation child " << param.to_string() << endmsg;
|
||||
error << "AudioTimeAxisView: unknown automation child " << param.symbol() << endmsg;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,12 +61,15 @@ AutomationController::~AutomationController()
|
|||
}
|
||||
|
||||
boost::shared_ptr<AutomationController>
|
||||
AutomationController::create(boost::shared_ptr<Automatable> parent, boost::shared_ptr<AutomationList> al, boost::shared_ptr<AutomationControl> ac)
|
||||
AutomationController::create(
|
||||
boost::shared_ptr<Automatable> parent,
|
||||
boost::shared_ptr<Evoral::ControlList> cl,
|
||||
boost::shared_ptr<AutomationControl> ac)
|
||||
{
|
||||
Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(al->default_value(), al->get_min_y(), al->get_max_y()));
|
||||
Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(cl->default_value(), cl->get_min_y(), cl->get_max_y()));
|
||||
if (!ac) {
|
||||
PBD::warning << "Creating AutomationController for " << al->parameter().to_string() << endmsg;
|
||||
ac = parent->control_factory(al);
|
||||
PBD::warning << "Creating AutomationController for " << cl->parameter().symbol() << endmsg;
|
||||
ac = boost::dynamic_pointer_cast<AutomationControl>(parent->control_factory(cl));
|
||||
}
|
||||
return boost::shared_ptr<AutomationController>(new AutomationController(ac, adjustment));
|
||||
}
|
||||
|
|
@ -109,13 +112,13 @@ AutomationController::value_adjusted()
|
|||
void
|
||||
AutomationController::start_touch()
|
||||
{
|
||||
_controllable->list()->start_touch();
|
||||
_controllable->start_touch();
|
||||
}
|
||||
|
||||
void
|
||||
AutomationController::end_touch()
|
||||
{
|
||||
_controllable->list()->stop_touch();
|
||||
_controllable->stop_touch();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -123,7 +126,7 @@ AutomationController::automation_state_changed ()
|
|||
{
|
||||
ENSURE_GUI_THREAD(mem_fun(*this, &AutomationController::automation_state_changed));
|
||||
|
||||
bool x = (_controllable->list()->automation_state() != Off);
|
||||
bool x = (_controllable->automation_state() != Off);
|
||||
|
||||
/* start watching automation so that things move */
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class AutomationController : public Gtkmm2ext::BarController {
|
|||
public:
|
||||
static boost::shared_ptr<AutomationController> create(
|
||||
boost::shared_ptr<ARDOUR::Automatable> parent,
|
||||
boost::shared_ptr<ARDOUR::AutomationList> al,
|
||||
boost::shared_ptr<Evoral::ControlList> cl,
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> ac);
|
||||
|
||||
~AutomationController();
|
||||
|
|
|
|||
|
|
@ -1048,7 +1048,7 @@ AutomationLine::list_changed ()
|
|||
}
|
||||
|
||||
void
|
||||
AutomationLine::reset_callback (const AutomationList& events)
|
||||
AutomationLine::reset_callback (const Evoral::ControlList& events)
|
||||
{
|
||||
ALPoints tmp_points;
|
||||
uint32_t npoints = events.size();
|
||||
|
|
@ -1064,7 +1064,7 @@ AutomationLine::reset_callback (const AutomationList& events)
|
|||
|
||||
AutomationList::const_iterator ai;
|
||||
|
||||
for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
|
||||
for (ai = events.begin(); ai != events.end(); ++ai) {
|
||||
|
||||
double translated_y = (*ai)->value;
|
||||
model_to_view_y (translated_y);
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin
|
|||
virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y);
|
||||
virtual void change_model_range (ARDOUR::AutomationList::iterator,ARDOUR::AutomationList::iterator, double delta, float ydelta);
|
||||
|
||||
void reset_callback (const ARDOUR::AutomationList&);
|
||||
void reset_callback (const Evoral::ControlList&);
|
||||
void list_changed ();
|
||||
|
||||
virtual bool event_handler (GdkEvent*);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ AutomationRegionView::AutomationRegionView(ArdourCanvas::Group*
|
|||
{
|
||||
if (list) {
|
||||
_line = boost::shared_ptr<AutomationLine>(new AutomationLine(
|
||||
list->parameter().to_string(), time_axis, *group, list));
|
||||
list->parameter().symbol(), time_axis, *group, list));
|
||||
_line->set_colors();
|
||||
_line->show();
|
||||
_line->show_all_control_points();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,6 @@ namespace ARDOUR {
|
|||
class AutomationList;
|
||||
}
|
||||
|
||||
struct AutomationSelection : list<ARDOUR::AutomationList*> {};
|
||||
struct AutomationSelection : list< boost::shared_ptr<ARDOUR::AutomationList> > {};
|
||||
|
||||
#endif /* __ardour_gtk_automation_selection_h__ */
|
||||
|
|
|
|||
|
|
@ -83,11 +83,13 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region
|
|||
mr->midi_source()->load_model();
|
||||
}
|
||||
|
||||
const boost::shared_ptr<AutomationControl> control = region->control(_controller->controllable()->parameter());
|
||||
const boost::shared_ptr<AutomationControl> control
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(
|
||||
region->control(_controller->controllable()->parameter()));
|
||||
|
||||
boost::shared_ptr<AutomationList> list;
|
||||
if (control)
|
||||
list = control->list();
|
||||
list = boost::dynamic_pointer_cast<AutomationList>(control->list());
|
||||
|
||||
AutomationRegionView *region_view;
|
||||
std::list<RegionView *>::iterator i;
|
||||
|
|
|
|||
|
|
@ -208,10 +208,10 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Ro
|
|||
} else {
|
||||
|
||||
boost::shared_ptr<AutomationLine> line(new AutomationLine (
|
||||
_control->parameter().to_string(),
|
||||
_control->parameter().symbol(),
|
||||
*this,
|
||||
*canvas_display,
|
||||
_control->list()));
|
||||
_control->alist()));
|
||||
|
||||
line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
|
||||
line->queue_reset ();
|
||||
|
|
@ -261,7 +261,7 @@ AutomationTimeAxisView::set_automation_state (AutoState state)
|
|||
state);
|
||||
}
|
||||
|
||||
_control->list()->set_automation_state(state);
|
||||
_control->alist()->set_automation_state(state);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -276,7 +276,7 @@ AutomationTimeAxisView::automation_state_changed ()
|
|||
if (!_line) {
|
||||
state = Off;
|
||||
} else {
|
||||
state = _control->list()->automation_state ();
|
||||
state = _control->alist()->automation_state ();
|
||||
}
|
||||
|
||||
switch (state & (Off|Play|Touch|Write)) {
|
||||
|
|
@ -578,12 +578,12 @@ AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEvent
|
|||
_line->view_to_model_y (y);
|
||||
|
||||
_session.begin_reversible_command (_("add automation event"));
|
||||
XMLNode& before = _control->list()->get_state();
|
||||
XMLNode& before = _control->alist()->get_state();
|
||||
|
||||
_control->list()->add (when, y);
|
||||
_control->alist()->add (when, y);
|
||||
|
||||
XMLNode& after = _control->list()->get_state();
|
||||
_session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->list().get(), &before, &after));
|
||||
XMLNode& after = _control->alist()->get_state();
|
||||
_session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
|
||||
|
||||
_session.set_dirty ();
|
||||
}
|
||||
|
|
@ -598,7 +598,7 @@ AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
|
|||
bool
|
||||
AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
|
||||
{
|
||||
AutomationList* what_we_got = 0;
|
||||
boost::shared_ptr<Evoral::ControlList> what_we_got;
|
||||
boost::shared_ptr<AutomationList> alist (line.the_list());
|
||||
bool ret = false;
|
||||
|
||||
|
|
@ -621,8 +621,6 @@ AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& sel
|
|||
case Clear:
|
||||
if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
|
||||
_session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
|
||||
delete what_we_got;
|
||||
what_we_got = 0;
|
||||
ret = true;
|
||||
}
|
||||
break;
|
||||
|
|
@ -671,7 +669,7 @@ AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCo
|
|||
bool
|
||||
AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
|
||||
{
|
||||
AutomationList* what_we_got = 0;
|
||||
boost::shared_ptr<Evoral::ControlList> what_we_got;
|
||||
boost::shared_ptr<AutomationList> alist(line.the_list());
|
||||
bool ret = false;
|
||||
|
||||
|
|
@ -700,8 +698,6 @@ AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointS
|
|||
case Clear:
|
||||
if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
|
||||
_session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
|
||||
delete what_we_got;
|
||||
what_we_got = 0;
|
||||
ret = true;
|
||||
}
|
||||
break;
|
||||
|
|
@ -822,7 +818,7 @@ AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
|
|||
assert(!_line);
|
||||
assert(line->the_list() == _control->list());
|
||||
|
||||
automation_connection = _control->list()->automation_state_changed.connect
|
||||
automation_connection = _control->alist()->automation_state_changed.connect
|
||||
(mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
|
||||
|
||||
_line = line;
|
||||
|
|
@ -884,7 +880,7 @@ AutomationTimeAxisView::set_state (const XMLNode& node)
|
|||
if ((*iter)->name() == state_node_name) {
|
||||
XMLProperty* type = (*iter)->property("automation-id");
|
||||
|
||||
if (type && type->value() == _control->parameter().to_string()) {
|
||||
if (type && type->value() == _control->parameter().symbol()) {
|
||||
XMLProperty *shown = (*iter)->property("shown_editor");
|
||||
|
||||
if (shown && shown->value() == "yes") {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public:
|
|||
MidiRegionView& region,
|
||||
Group& group,
|
||||
double size,
|
||||
const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>())
|
||||
const boost::shared_ptr<Evoral::Note> note = boost::shared_ptr<Evoral::Note>())
|
||||
|
||||
: Diamond(group, size), CanvasNoteEvent(region, this, note)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Canvas {
|
|||
|
||||
|
||||
CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item,
|
||||
const boost::shared_ptr<ARDOUR::Note> note)
|
||||
const boost::shared_ptr<Evoral::Note> note)
|
||||
: _region(region)
|
||||
, _item(item)
|
||||
, _text(0)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public:
|
|||
CanvasNoteEvent(
|
||||
MidiRegionView& region,
|
||||
Item* item,
|
||||
const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
|
||||
const boost::shared_ptr<Evoral::Note> note = boost::shared_ptr<Evoral::Note>());
|
||||
|
||||
virtual ~CanvasNoteEvent();
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ public:
|
|||
virtual double x2() = 0;
|
||||
virtual double y2() = 0;
|
||||
|
||||
const boost::shared_ptr<ARDOUR::Note> note() const { return _note; }
|
||||
const boost::shared_ptr<Evoral::Note> note() const { return _note; }
|
||||
|
||||
protected:
|
||||
enum State { None, Pressed, Dragging };
|
||||
|
|
@ -91,7 +91,7 @@ protected:
|
|||
Text* _text;
|
||||
Widget* _channel_selector_widget;
|
||||
State _state;
|
||||
const boost::shared_ptr<ARDOUR::Note> _note;
|
||||
const boost::shared_ptr<Evoral::Note> _note;
|
||||
bool _own_note;
|
||||
bool _selected;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "canvas-note.h"
|
||||
#include "midi_region_view.h"
|
||||
#include "public_editor.h"
|
||||
#include "ardour/note.h"
|
||||
#include "evoral/Note.hpp"
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public:
|
|||
CanvasNote(
|
||||
MidiRegionView& region,
|
||||
Group& group,
|
||||
const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>())
|
||||
const boost::shared_ptr<Evoral::Note> note = boost::shared_ptr<Evoral::Note>())
|
||||
|
||||
: SimpleRect(group), CanvasNoteEvent(region, this, note), _note_state(None)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using namespace std;
|
|||
CanvasProgramChange::CanvasProgramChange(
|
||||
MidiRegionView& region,
|
||||
Group& parent,
|
||||
boost::shared_ptr<MIDI::Event> event,
|
||||
boost::shared_ptr<Evoral::Event> event,
|
||||
double height,
|
||||
double x,
|
||||
double y)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public:
|
|||
CanvasProgramChange(
|
||||
MidiRegionView& region,
|
||||
Group& parent,
|
||||
boost::shared_ptr<MIDI::Event> event,
|
||||
boost::shared_ptr<Evoral::Event> event,
|
||||
double height,
|
||||
double x = 0.0,
|
||||
double y = 0.0
|
||||
|
|
@ -29,7 +29,7 @@ public:
|
|||
|
||||
private:
|
||||
MidiRegionView& _region;
|
||||
boost::shared_ptr<MIDI::Event> _event;
|
||||
boost::shared_ptr<Evoral::Event> _event;
|
||||
Text* _text;
|
||||
SimpleLine* _line;
|
||||
SimpleRect* _rect;
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0;
|
|||
|
||||
CrossfadeEditor::Half::Half ()
|
||||
: line (0),
|
||||
normative_curve (Parameter(GainAutomation), 0.0, 1.0, 1.0), // FIXME: GainAutomation?
|
||||
gain_curve (Parameter(GainAutomation), 0.0, 2.0, 1.0)
|
||||
normative_curve (Parameter(GainAutomation, 0.0, 1.0, 1.0)), // FIXME: GainAutomation?
|
||||
gain_curve (Parameter(GainAutomation))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -343,13 +343,13 @@ CrossfadeEditor::set (const ARDOUR::AutomationList& curve, WhichFade which)
|
|||
goto out;
|
||||
}
|
||||
|
||||
the_end = curve.const_end();
|
||||
the_end = curve.end();
|
||||
--the_end;
|
||||
|
||||
firstx = (*curve.const_begin())->when;
|
||||
firstx = (*curve.begin())->when;
|
||||
endx = (*the_end)->when;
|
||||
|
||||
for (ARDOUR::AutomationList::const_iterator i = curve.const_begin(); i != curve.const_end(); ++i) {
|
||||
for (ARDOUR::AutomationList::const_iterator i = curve.begin(); i != curve.end(); ++i) {
|
||||
|
||||
double xfract = ((*i)->when - firstx) / (endx - firstx);
|
||||
double yfract = ((*i)->value - miny) / (maxy - miny);
|
||||
|
|
@ -771,7 +771,7 @@ CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
|
|||
/* IN */
|
||||
|
||||
|
||||
ARDOUR::AutomationList::const_iterator the_end = in.const_end();
|
||||
ARDOUR::AutomationList::const_iterator the_end = in.end();
|
||||
--the_end;
|
||||
|
||||
double firstx = (*in.begin())->when;
|
||||
|
|
@ -791,7 +791,7 @@ CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
|
|||
|
||||
/* OUT */
|
||||
|
||||
the_end = out.const_end();
|
||||
the_end = out.end();
|
||||
--the_end;
|
||||
|
||||
firstx = (*out.begin())->when;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ curvetest (string filename)
|
|||
{
|
||||
ifstream in (filename.c_str());
|
||||
stringstream line;
|
||||
AutomationList al (Parameter(), -1.0, +1.0, 0);
|
||||
Parameter param(GainAutomation, -1.0, +1.0, 0.0);
|
||||
AutomationList al (param);
|
||||
double minx = DBL_MAX;
|
||||
double maxx = DBL_MIN;
|
||||
|
||||
|
|
|
|||
|
|
@ -168,8 +168,8 @@ GainMeterBase::GainMeterBase (boost::shared_ptr<IO> io, Session& s,
|
|||
gain_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_style_button_event), false);
|
||||
gain_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &GainMeterBase::gain_automation_state_button_event), false);
|
||||
|
||||
r->gain_control()->list()->automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed));
|
||||
r->gain_control()->list()->automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed));
|
||||
r->gain_control()->alist()->automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed));
|
||||
r->gain_control()->alist()->automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed));
|
||||
|
||||
gain_automation_state_changed ();
|
||||
}
|
||||
|
|
@ -408,7 +408,8 @@ GainMeterBase::set_fader_name (const char * name)
|
|||
void
|
||||
GainMeterBase::update_gain_sensitive ()
|
||||
{
|
||||
static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (!(_io->gain_control()->list()->automation_state() & Play));
|
||||
static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (
|
||||
!(_io->gain_control()->alist()->automation_state() & Play));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -556,14 +557,14 @@ GainMeterBase::meter_point_clicked ()
|
|||
gint
|
||||
GainMeterBase::start_gain_touch (GdkEventButton* ev)
|
||||
{
|
||||
_io->gain_control()->list()->start_touch ();
|
||||
_io->gain_control()->alist()->start_touch ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gint
|
||||
GainMeterBase::end_gain_touch (GdkEventButton* ev)
|
||||
{
|
||||
_io->gain_control()->list()->stop_touch ();
|
||||
_io->gain_control()->alist()->stop_touch ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -666,10 +667,10 @@ GainMeterBase::gain_automation_style_changed ()
|
|||
{
|
||||
switch (_width) {
|
||||
case Wide:
|
||||
gain_automation_style_button.set_label (astyle_string(_io->gain_control()->list()->automation_style()));
|
||||
gain_automation_style_button.set_label (astyle_string(_io->gain_control()->alist()->automation_style()));
|
||||
break;
|
||||
case Narrow:
|
||||
gain_automation_style_button.set_label (short_astyle_string(_io->gain_control()->list()->automation_style()));
|
||||
gain_automation_style_button.set_label (short_astyle_string(_io->gain_control()->alist()->automation_style()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -683,14 +684,14 @@ GainMeterBase::gain_automation_state_changed ()
|
|||
|
||||
switch (_width) {
|
||||
case Wide:
|
||||
gain_automation_state_button.set_label (astate_string(_io->gain_control()->list()->automation_state()));
|
||||
gain_automation_state_button.set_label (astate_string(_io->gain_control()->alist()->automation_state()));
|
||||
break;
|
||||
case Narrow:
|
||||
gain_automation_state_button.set_label (short_astate_string(_io->gain_control()->list()->automation_state()));
|
||||
gain_automation_state_button.set_label (short_astate_string(_io->gain_control()->alist()->automation_state()));
|
||||
break;
|
||||
}
|
||||
|
||||
x = (_io->gain_control()->list()->automation_state() != Off);
|
||||
x = (_io->gain_control()->alist()->automation_state() != Off);
|
||||
|
||||
if (gain_automation_state_button.get_active() != x) {
|
||||
ignore_toggle = true;
|
||||
|
|
|
|||
|
|
@ -209,7 +209,11 @@ GenericPluginUI::build ()
|
|||
}
|
||||
}
|
||||
|
||||
if ((cui = build_control_ui (i, insert->control(Parameter(PluginAutomation, i)))) == 0) {
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> c
|
||||
= boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
|
||||
insert->control(Parameter(PluginAutomation, i)));
|
||||
|
||||
if ((cui = build_control_ui (i, c)) == 0) {
|
||||
error << string_compose(_("Plugin Editor: could not build control element for port %1"), i) << endmsg;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -526,7 +530,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
|
|||
automation_state_changed (control_ui);
|
||||
|
||||
mcontrol->Changed.connect (bind (mem_fun (*this, &GenericPluginUI::parameter_changed), control_ui));
|
||||
mcontrol->list()->automation_state_changed.connect
|
||||
mcontrol->alist()->automation_state_changed.connect
|
||||
(bind (mem_fun(*this, &GenericPluginUI::automation_state_changed), control_ui));
|
||||
|
||||
} else if (plugin->parameter_is_output (port_index)) {
|
||||
|
|
@ -584,13 +588,13 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
|
|||
void
|
||||
GenericPluginUI::start_touch (GenericPluginUI::ControlUI* cui)
|
||||
{
|
||||
cui->control->list()->start_touch ();
|
||||
cui->control->start_touch ();
|
||||
}
|
||||
|
||||
void
|
||||
GenericPluginUI::stop_touch (GenericPluginUI::ControlUI* cui)
|
||||
{
|
||||
cui->control->list()->stop_touch ();
|
||||
cui->control->stop_touch ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -397,7 +397,8 @@ MidiRegionView::create_note_at(double x, double y, double duration)
|
|||
new_note_duration = snap_to_frame(new_note_time_position_relative + new_note_duration) + _region->start()
|
||||
- new_note_time;
|
||||
|
||||
const boost::shared_ptr<Note> new_note(new Note(0, new_note_time, new_note_duration, (uint8_t)note, 0x40));
|
||||
const boost::shared_ptr<Evoral::Note> new_note(new Evoral::Note(
|
||||
0, new_note_time, new_note_duration, (uint8_t)note, 0x40));
|
||||
view->update_bounds(new_note->note());
|
||||
|
||||
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
|
||||
|
|
@ -444,7 +445,7 @@ MidiRegionView::start_delta_command(string name)
|
|||
}
|
||||
|
||||
void
|
||||
MidiRegionView::command_add_note(const boost::shared_ptr<ARDOUR::Note> note, bool selected)
|
||||
MidiRegionView::command_add_note(const boost::shared_ptr<Evoral::Note> note, bool selected)
|
||||
{
|
||||
if (_delta_command)
|
||||
_delta_command->add(note);
|
||||
|
|
@ -524,8 +525,8 @@ MidiRegionView::redisplay_model()
|
|||
|
||||
for (AutomationList::const_iterator event = control->second->list()->begin();
|
||||
event != control->second->list()->end(); ++event) {
|
||||
MidiControlIterator iter(control->second->list(), (*event)->when, (*event)->value);
|
||||
boost::shared_ptr<MIDI::Event> event(new MIDI::Event());
|
||||
Evoral::ControlIterator iter(control->second->list(), (*event)->when, (*event)->value);
|
||||
boost::shared_ptr<Evoral::Event> event(new Evoral::Event());
|
||||
_model->control_to_midi_event(event, iter);
|
||||
add_pgm_change(event);
|
||||
}
|
||||
|
|
@ -774,7 +775,7 @@ MidiRegionView::extend_active_notes()
|
|||
* event arrives, to properly display the note.
|
||||
*/
|
||||
void
|
||||
MidiRegionView::add_note(const boost::shared_ptr<Note> note)
|
||||
MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
|
||||
{
|
||||
assert(note->time() >= 0);
|
||||
assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
|
||||
|
|
@ -816,7 +817,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Note> note)
|
|||
// finish the old note rectangle
|
||||
if (_active_notes[note->note()]) {
|
||||
CanvasNote* const old_rect = _active_notes[note->note()];
|
||||
boost::shared_ptr<ARDOUR::Note> old_note = old_rect->note();
|
||||
boost::shared_ptr<Evoral::Note> old_note = old_rect->note();
|
||||
cerr << "MidiModel: WARNING: Note has duration 0: chan " << old_note->channel()
|
||||
<< "note " << (int)old_note->note() << " @ " << old_note->time() << endl;
|
||||
/* FIXME: How large to make it? Make it a diamond? */
|
||||
|
|
@ -870,7 +871,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Note> note)
|
|||
}
|
||||
|
||||
void
|
||||
MidiRegionView::add_pgm_change(boost::shared_ptr<MIDI::Event> event)
|
||||
MidiRegionView::add_pgm_change(boost::shared_ptr<Evoral::Event> event)
|
||||
{
|
||||
assert(event->time() >= 0);
|
||||
|
||||
|
|
@ -1061,7 +1062,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
|
|||
Selection::iterator next = i;
|
||||
++next;
|
||||
|
||||
const boost::shared_ptr<Note> copy(new Note(*(*i)->note().get()));
|
||||
const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(*i)->note().get()));
|
||||
|
||||
// we need to snap here again in nframes64_t in order to be sample accurate
|
||||
double new_note_time = (*i)->note()->time();
|
||||
|
|
@ -1253,7 +1254,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo
|
|||
// transform to region start relative
|
||||
current_frame += _region->start();
|
||||
|
||||
const boost::shared_ptr<Note> copy(new Note(*(canvas_note->note().get())));
|
||||
const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(canvas_note->note().get())));
|
||||
|
||||
// resize beginning of note
|
||||
if (note_end == CanvasNote::NOTE_ON && current_frame < copy->end_time()) {
|
||||
|
|
@ -1286,7 +1287,7 @@ MidiRegionView::change_velocity(uint8_t velocity, bool relative)
|
|||
++next;
|
||||
|
||||
CanvasNoteEvent *event = *i;
|
||||
const boost::shared_ptr<Note> copy(new Note(*(event->note().get())));
|
||||
const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
|
||||
|
||||
if (relative) {
|
||||
uint8_t new_velocity = copy->velocity() + velocity;
|
||||
|
|
@ -1315,7 +1316,7 @@ MidiRegionView::change_channel(uint8_t channel)
|
|||
++next;
|
||||
|
||||
CanvasNoteEvent *event = *i;
|
||||
const boost::shared_ptr<Note> copy(new Note(*(event->note().get())));
|
||||
const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
|
||||
|
||||
copy->set_channel(channel);
|
||||
|
||||
|
|
|
|||
|
|
@ -79,10 +79,10 @@ class MidiRegionView : public RegionView
|
|||
|
||||
GhostRegion* add_ghost (TimeAxisView&);
|
||||
|
||||
void add_note(const boost::shared_ptr<ARDOUR::Note> note);
|
||||
void add_note(const boost::shared_ptr<Evoral::Note> note);
|
||||
void resolve_note(uint8_t note_num, double end_time);
|
||||
|
||||
void add_pgm_change(boost::shared_ptr<MIDI::Event> event);
|
||||
void add_pgm_change(boost::shared_ptr<Evoral::Event> event);
|
||||
|
||||
void begin_write();
|
||||
void end_write();
|
||||
|
|
@ -93,7 +93,7 @@ class MidiRegionView : public RegionView
|
|||
void display_model(boost::shared_ptr<ARDOUR::MidiModel> model);
|
||||
|
||||
void start_delta_command(string name = "midi edit");
|
||||
void command_add_note(const boost::shared_ptr<ARDOUR::Note> note, bool selected);
|
||||
void command_add_note(const boost::shared_ptr<Evoral::Note> note, bool selected);
|
||||
void command_remove_note(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
|
||||
void apply_command();
|
||||
|
|
@ -240,7 +240,7 @@ class MidiRegionView : public RegionView
|
|||
|
||||
/** New notes (created in the current command) which should be selected
|
||||
* when they appear after the command is applied. */
|
||||
std::set< boost::shared_ptr<ARDOUR::Note> > _marked_for_selection;
|
||||
std::set< boost::shared_ptr<Evoral::Note> > _marked_for_selection;
|
||||
|
||||
std::vector<NoteResizeData *> _resize_data;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -554,7 +554,7 @@ MidiStreamView::update_rec_regions (boost::shared_ptr<MidiModel> data, nframes_t
|
|||
|
||||
// FIXME: slooooooooow!
|
||||
|
||||
const boost::shared_ptr<Note> note = data->note_at(i);
|
||||
const boost::shared_ptr<Evoral::Note> note = data->note_at(i);
|
||||
|
||||
if (note->duration() > 0 && note->end_time() + region->position() > start)
|
||||
mrv->resolve_note(note->note(), note->end_time());
|
||||
|
|
|
|||
|
|
@ -334,12 +334,12 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
|
|||
/* FIXME: don't create AutomationList for track itself
|
||||
* (not actually needed or used, since the automation is region-ey) */
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = _route->control(param);
|
||||
boost::shared_ptr<AutomationControl> c
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(_route->control(param));
|
||||
|
||||
if (!c) {
|
||||
boost::shared_ptr<AutomationList> al(new ARDOUR::AutomationList(param,
|
||||
param.min(), param.max(), (param.max() - param.min() / 2)));
|
||||
c = boost::shared_ptr<AutomationControl>(_route->control_factory(al));
|
||||
boost::shared_ptr<AutomationList> al(new ARDOUR::AutomationList(param));
|
||||
c = boost::dynamic_pointer_cast<AutomationControl>(_route->control_factory(al));
|
||||
_route->add_control(c);
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +358,7 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
|
|||
add_automation_child(param, track, show);
|
||||
|
||||
} else {
|
||||
error << "MidiTimeAxisView: unknown automation child " << param.to_string() << endmsg;
|
||||
error << "MidiTimeAxisView: unknown automation child " << param.symbol() << endmsg;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ MixerStrip::set_width (Width w, void* owner)
|
|||
pre_processor_box.set_width (w);
|
||||
post_processor_box.set_width (w);
|
||||
|
||||
boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->list();
|
||||
boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
|
||||
|
||||
_width_owner = owner;
|
||||
|
||||
|
|
@ -748,8 +748,8 @@ MixerStrip::connect_to_pan ()
|
|||
if (!_route->panner().empty()) {
|
||||
StreamPanner* sp = _route->panner().front();
|
||||
|
||||
panstate_connection = sp->pan_control()->list()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed));
|
||||
panstyle_connection = sp->pan_control()->list()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed));
|
||||
panstate_connection = sp->pan_control()->alist()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed));
|
||||
panstyle_connection = sp->pan_control()->alist()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed));
|
||||
}
|
||||
|
||||
panners.pan_changed (this);
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ PannerUI::update_pan_bars (bool only_if_aplay)
|
|||
float xpos, val;
|
||||
|
||||
if (only_if_aplay) {
|
||||
boost::shared_ptr<AutomationList> alist (_io->panner()[n]->pan_control()->list());
|
||||
boost::shared_ptr<AutomationList> alist (_io->panner()[n]->pan_control()->alist());
|
||||
|
||||
if (!alist->automation_playback()) {
|
||||
continue;
|
||||
|
|
@ -727,7 +727,7 @@ PannerUI::pan_automation_state_changed ()
|
|||
return;
|
||||
}
|
||||
|
||||
x = (_io->panner().front()->pan_control()->list()->automation_state() != Off);
|
||||
x = (_io->panner().front()->pan_control()->alist()->automation_state() != Off);
|
||||
|
||||
if (pan_automation_state_button.get_active() != x) {
|
||||
ignore_toggle = true;
|
||||
|
|
|
|||
|
|
@ -1741,7 +1741,8 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor>
|
|||
char state_name[256];
|
||||
snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
|
||||
|
||||
boost::shared_ptr<AutomationControl> control = processor->control(what, true);
|
||||
boost::shared_ptr<AutomationControl> control
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
|
||||
|
||||
pan->view = boost::shared_ptr<AutomationTimeAxisView>(
|
||||
new AutomationTimeAxisView (_session, _route, processor, control,
|
||||
|
|
@ -1782,7 +1783,7 @@ RouteTimeAxisView::add_existing_processor_automation_curves (boost::shared_ptr<P
|
|||
set<Parameter> s;
|
||||
boost::shared_ptr<AutomationLine> al;
|
||||
|
||||
processor->what_has_visible_automation (s);
|
||||
processor->what_has_visible_data (s);
|
||||
|
||||
for (set<Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
|
||||
|
||||
|
|
@ -1843,7 +1844,7 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::shared_ptr<Processor>
|
|||
const std::set<Parameter>& automatable = processor->what_can_be_automated ();
|
||||
std::set<Parameter> has_visible_automation;
|
||||
|
||||
processor->what_has_visible_automation(has_visible_automation);
|
||||
processor->what_has_visible_data(has_visible_automation);
|
||||
|
||||
if (automatable.empty()) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -776,14 +776,14 @@ RouteUI::get_automation_child_xml_node (Parameter param)
|
|||
for (iter = kids.begin(); iter != kids.end(); ++iter) {
|
||||
if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
|
||||
XMLProperty* type = (*iter)->property("automation-id");
|
||||
if (type && type->value() == param.to_string())
|
||||
if (type && type->value() == param.symbol())
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find it, make a new one
|
||||
XMLNode* child = new XMLNode (AutomationTimeAxisView::state_node_name);
|
||||
child->add_property("automation-id", param.to_string());
|
||||
child->add_property("automation-id", param.symbol());
|
||||
xml_node->add_child_nocopy (*child);
|
||||
|
||||
return child;
|
||||
|
|
|
|||
|
|
@ -389,10 +389,17 @@ Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
|
|||
}
|
||||
|
||||
void
|
||||
Selection::add (AutomationList* ac)
|
||||
Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
|
||||
{
|
||||
if (find (lines.begin(), lines.end(), ac) == lines.end()) {
|
||||
lines.push_back (ac);
|
||||
boost::shared_ptr<ARDOUR::AutomationList> al
|
||||
= boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
|
||||
if (!al) {
|
||||
warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
if (find (lines.begin(), lines.end(), al) == lines.end()) {
|
||||
lines.push_back (al);
|
||||
LinesChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -493,9 +500,9 @@ Selection::remove (nframes_t start, nframes_t end)
|
|||
}
|
||||
|
||||
void
|
||||
Selection::remove (AutomationList *ac)
|
||||
Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
|
||||
{
|
||||
list<AutomationList*>::iterator i;
|
||||
AutomationSelection::iterator i;
|
||||
if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
|
||||
lines.erase (i);
|
||||
LinesChanged();
|
||||
|
|
@ -595,7 +602,7 @@ Selection::set (TimeAxisView* track, nframes_t start, nframes_t end)
|
|||
}
|
||||
|
||||
void
|
||||
Selection::set (AutomationList *ac)
|
||||
Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
|
||||
{
|
||||
lines.clear();
|
||||
add (ac);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ namespace ARDOUR {
|
|||
class AutomationList;
|
||||
}
|
||||
|
||||
namespace Evoral {
|
||||
class ControlList;
|
||||
}
|
||||
|
||||
/// Lists of selected things
|
||||
|
||||
/** The Selection class holds lists of selected items (tracks, regions, etc. etc.). */
|
||||
|
|
@ -105,7 +109,7 @@ class Selection : public sigc::trackable
|
|||
void set (RegionView*, bool also_clear_tracks = true);
|
||||
void set (std::vector<RegionView*>&);
|
||||
long set (TimeAxisView*, nframes_t, nframes_t);
|
||||
void set (ARDOUR::AutomationList*);
|
||||
void set (boost::shared_ptr<Evoral::ControlList>);
|
||||
void set (boost::shared_ptr<ARDOUR::Playlist>);
|
||||
void set (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
|
||||
void set (AutomationSelectable*);
|
||||
|
|
@ -128,7 +132,7 @@ class Selection : public sigc::trackable
|
|||
void add (RegionView*);
|
||||
void add (std::vector<RegionView*>&);
|
||||
long add (nframes_t, nframes_t);
|
||||
void add (ARDOUR::AutomationList*);
|
||||
void add (boost::shared_ptr<Evoral::ControlList>);
|
||||
void add (boost::shared_ptr<ARDOUR::Playlist>);
|
||||
void add (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
|
||||
void add (Marker*);
|
||||
|
|
@ -139,7 +143,7 @@ class Selection : public sigc::trackable
|
|||
void remove (RegionView*);
|
||||
void remove (uint32_t selection_id);
|
||||
void remove (nframes_t, nframes_t);
|
||||
void remove (ARDOUR::AutomationList*);
|
||||
void remove (boost::shared_ptr<ARDOUR::AutomationList>);
|
||||
void remove (boost::shared_ptr<ARDOUR::Playlist>);
|
||||
void remove (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
|
||||
void remove (const list<Selectable*>&);
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ mix.cc
|
|||
mtc_slave.cc
|
||||
midi_clock_slave.cc
|
||||
named_selection.cc
|
||||
note.cc
|
||||
onset_detector.cc
|
||||
panner.cc
|
||||
parameter.cc
|
||||
|
|
@ -330,6 +329,7 @@ ardour.Merge ([
|
|||
libraries['glibmm2'],
|
||||
libraries['lrdf'],
|
||||
libraries['midi++2'],
|
||||
libraries['evoral'],
|
||||
libraries['pbd'],
|
||||
libraries['raptor'],
|
||||
libraries['samplerate'],
|
||||
|
|
|
|||
|
|
@ -27,35 +27,24 @@
|
|||
#include <ardour/automation_event.h>
|
||||
#include <ardour/automation_control.h>
|
||||
#include <ardour/parameter.h>
|
||||
#include <evoral/ControlSet.hpp>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Session;
|
||||
class AutomationControl;
|
||||
|
||||
class Automatable : public SessionObject
|
||||
class Automatable : public SessionObject, virtual public Evoral::ControlSet
|
||||
{
|
||||
public:
|
||||
Automatable(Session&, const std::string& name);
|
||||
|
||||
virtual ~Automatable() {}
|
||||
|
||||
// shorthand for gain, pan, etc
|
||||
inline boost::shared_ptr<AutomationControl>
|
||||
control(AutomationType type, bool create_if_missing=false) {
|
||||
return control(Parameter(type), create_if_missing);
|
||||
}
|
||||
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;
|
||||
|
||||
virtual boost::shared_ptr<AutomationControl> control(Parameter id, bool create_if_missing=false);
|
||||
virtual boost::shared_ptr<const AutomationControl> control(Parameter id) const;
|
||||
|
||||
boost::shared_ptr<AutomationControl> control_factory(boost::shared_ptr<AutomationList> list);
|
||||
|
||||
typedef std::map<Parameter,boost::shared_ptr<AutomationControl> > Controls;
|
||||
Controls& controls() { return _controls; }
|
||||
const Controls& controls() const { return _controls; }
|
||||
|
||||
virtual void add_control(boost::shared_ptr<AutomationControl>);
|
||||
virtual void add_control(boost::shared_ptr<Evoral::Control>);
|
||||
|
||||
virtual void automation_snapshot(nframes_t now, bool force);
|
||||
bool should_snapshot (nframes_t now) {
|
||||
|
|
@ -63,12 +52,7 @@ public:
|
|||
}
|
||||
virtual void transport_stopped(nframes_t now);
|
||||
|
||||
virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
|
||||
|
||||
virtual string describe_parameter(Parameter param);
|
||||
virtual float default_parameter_value(Parameter param) { return 1.0f; }
|
||||
|
||||
virtual void clear_automation();
|
||||
|
||||
AutoState get_parameter_automation_state (Parameter param, bool lock = true);
|
||||
virtual void set_parameter_automation_state (Parameter param, AutoState);
|
||||
|
|
@ -78,14 +62,11 @@ public:
|
|||
|
||||
void protect_automation ();
|
||||
|
||||
void what_has_automation(std::set<Parameter>&) const;
|
||||
void what_has_visible_automation(std::set<Parameter>&) const;
|
||||
void what_has_visible_data(std::set<Parameter>&) const;
|
||||
const std::set<Parameter>& what_can_be_automated() const { return _can_automate_list; }
|
||||
|
||||
void mark_automation_visible(Parameter, bool);
|
||||
|
||||
Glib::Mutex& automation_lock() const { return _automation_lock; }
|
||||
|
||||
static void set_automation_interval (jack_nframes_t frames) {
|
||||
_automation_interval = frames;
|
||||
}
|
||||
|
|
@ -106,9 +87,6 @@ protected:
|
|||
int load_automation (const std::string& path);
|
||||
int old_set_automation_state(const XMLNode&);
|
||||
|
||||
mutable Glib::Mutex _automation_lock;
|
||||
|
||||
Controls _controls;
|
||||
std::set<Parameter> _visible_controls;
|
||||
std::set<Parameter> _can_automate_list;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
#include <boost/shared_ptr.hpp>
|
||||
#include <pbd/controllable.h>
|
||||
#include <ardour/parameter.h>
|
||||
#include <evoral/Control.hpp>
|
||||
#include <ardour/automation_event.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
|
@ -34,28 +36,42 @@ class Automatable;
|
|||
|
||||
/** A PBD:Controllable with associated automation data (AutomationList)
|
||||
*/
|
||||
class AutomationControl : public PBD::Controllable
|
||||
class AutomationControl : public PBD::Controllable, public Evoral::Control
|
||||
{
|
||||
public:
|
||||
AutomationControl(ARDOUR::Session&,
|
||||
boost::shared_ptr<ARDOUR::AutomationList>,
|
||||
std::string name="unnamed controllable");
|
||||
|
||||
boost::shared_ptr<AutomationList> alist() { return boost::dynamic_pointer_cast<AutomationList>(_list); }
|
||||
|
||||
void set_list(boost::shared_ptr<Evoral::ControlList>);
|
||||
|
||||
inline bool automation_playback() const {
|
||||
return ((ARDOUR::AutomationList*)_list.get())->automation_playback();
|
||||
}
|
||||
|
||||
inline bool automation_write() const {
|
||||
return ((ARDOUR::AutomationList*)_list.get())->automation_write();
|
||||
}
|
||||
|
||||
inline AutoState automation_state() {
|
||||
return ((ARDOUR::AutomationList*)_list.get())->automation_state();
|
||||
}
|
||||
|
||||
inline void start_touch() {
|
||||
return ((ARDOUR::AutomationList*)_list.get())->start_touch();
|
||||
}
|
||||
|
||||
inline void stop_touch() {
|
||||
return ((ARDOUR::AutomationList*)_list.get())->stop_touch();
|
||||
}
|
||||
|
||||
void set_value(float val);
|
||||
float get_value() const;
|
||||
float user_value() const;
|
||||
|
||||
void set_list(boost::shared_ptr<ARDOUR::AutomationList>);
|
||||
|
||||
boost::shared_ptr<ARDOUR::AutomationList> list() { return _list; }
|
||||
boost::shared_ptr<const ARDOUR::AutomationList> list() const { return _list; }
|
||||
|
||||
Parameter parameter() const;
|
||||
|
||||
protected:
|
||||
ARDOUR::Session& _session;
|
||||
boost::shared_ptr<ARDOUR::AutomationList> _list;
|
||||
float _user_value;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,105 +37,29 @@
|
|||
#include <ardour/ardour.h>
|
||||
#include <ardour/parameter.h>
|
||||
|
||||
#include <evoral/ControlList.hpp>
|
||||
|
||||
using Evoral::ControlEvent;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Curve;
|
||||
|
||||
struct ControlEvent {
|
||||
|
||||
ControlEvent (double w, double v)
|
||||
: when (w), value (v), coeff (0) {
|
||||
}
|
||||
|
||||
ControlEvent (const ControlEvent& other)
|
||||
: when (other.when), value (other.value), coeff (0) {
|
||||
if (other.coeff) {
|
||||
create_coeffs();
|
||||
for (size_t i=0; i < 4; ++i)
|
||||
coeff[i] = other.coeff[i];
|
||||
}
|
||||
}
|
||||
|
||||
~ControlEvent() { if (coeff) delete[] coeff; }
|
||||
|
||||
void create_coeffs() {
|
||||
if (!coeff)
|
||||
coeff = new double[4];
|
||||
|
||||
coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
|
||||
}
|
||||
|
||||
double when;
|
||||
double value;
|
||||
double* coeff; ///< double[4] allocated by Curve as needed
|
||||
};
|
||||
|
||||
/* automation lists use a pool allocator that does not use a lock and
|
||||
allocates 8k of new pointers at a time
|
||||
*/
|
||||
|
||||
typedef boost::fast_pool_allocator<ControlEvent*,
|
||||
boost::default_user_allocator_new_delete,
|
||||
boost::details::pool::null_mutex,
|
||||
8192> ControlEventAllocator;
|
||||
|
||||
class AutomationList : public PBD::StatefulDestructible
|
||||
class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlList
|
||||
{
|
||||
public:
|
||||
typedef std::list<ControlEvent*,ControlEventAllocator> EventList;
|
||||
typedef EventList::iterator iterator;
|
||||
typedef EventList::reverse_iterator reverse_iterator;
|
||||
typedef EventList::const_iterator const_iterator;
|
||||
|
||||
AutomationList (Parameter id, double min_val, double max_val, double default_val);
|
||||
AutomationList (Parameter id);
|
||||
AutomationList (const XMLNode&, Parameter id);
|
||||
~AutomationList();
|
||||
|
||||
virtual boost::shared_ptr<Evoral::ControlList> create(Evoral::Parameter id);
|
||||
|
||||
AutomationList (const AutomationList&);
|
||||
AutomationList (const AutomationList&, double start, double end);
|
||||
AutomationList& operator= (const AutomationList&);
|
||||
bool operator== (const AutomationList&);
|
||||
|
||||
const Parameter& parameter() const { return _parameter; }
|
||||
void set_parameter(Parameter p) { _parameter = p; }
|
||||
|
||||
void freeze();
|
||||
void thaw ();
|
||||
|
||||
EventList::size_type size() const { return _events.size(); }
|
||||
bool empty() const { return _events.empty(); }
|
||||
|
||||
void reset_default (double val) {
|
||||
_default_value = val;
|
||||
}
|
||||
|
||||
void clear ();
|
||||
void x_scale (double factor);
|
||||
bool extend_to (double);
|
||||
void slide (iterator before, double distance);
|
||||
|
||||
void reposition_for_rt_add (double when);
|
||||
void rt_add (double when, double value);
|
||||
void add (double when, double value);
|
||||
/* this should be private but old-school automation loading needs it in IO/IOProcessor */
|
||||
void fast_simple_add (double when, double value);
|
||||
|
||||
void reset_range (double start, double end);
|
||||
void erase_range (double start, double end);
|
||||
void erase (iterator);
|
||||
void erase (iterator, iterator);
|
||||
void move_range (iterator start, iterator end, double, double);
|
||||
void modify (iterator, double, double);
|
||||
|
||||
AutomationList* cut (double, double);
|
||||
AutomationList* copy (double, double);
|
||||
void clear (double, double);
|
||||
|
||||
AutomationList* cut (iterator, iterator);
|
||||
AutomationList* copy (iterator, iterator);
|
||||
void clear (iterator, iterator);
|
||||
|
||||
bool paste (AutomationList&, double position, float times);
|
||||
void mark_dirty () const;
|
||||
|
||||
void set_automation_state (AutoState);
|
||||
AutoState automation_state() const { return _state; }
|
||||
|
|
@ -152,156 +76,28 @@ class AutomationList : public PBD::StatefulDestructible
|
|||
return (_state & Write) || ((_state & Touch) && _touching);
|
||||
}
|
||||
|
||||
sigc::signal<void> StateChanged;
|
||||
|
||||
static sigc::signal<void, AutomationList*> AutomationListCreated;
|
||||
mutable sigc::signal<void> Dirty;
|
||||
|
||||
void start_touch ();
|
||||
void stop_touch ();
|
||||
bool touching() const { return _touching; }
|
||||
|
||||
void set_yrange (double min, double max) {
|
||||
_min_yval = min;
|
||||
_max_yval = max;
|
||||
}
|
||||
|
||||
double get_max_y() const { return _max_yval; }
|
||||
double get_min_y() const { return _min_yval; }
|
||||
|
||||
void truncate_end (double length);
|
||||
void truncate_start (double length);
|
||||
|
||||
iterator begin() { return _events.begin(); }
|
||||
iterator end() { return _events.end(); }
|
||||
|
||||
ControlEvent* back() { return _events.back(); }
|
||||
ControlEvent* front() { return _events.front(); }
|
||||
|
||||
const_iterator const_begin() const { return _events.begin(); }
|
||||
const_iterator const_end() const { return _events.end(); }
|
||||
|
||||
std::pair<AutomationList::iterator,AutomationList::iterator> control_points_adjacent (double when);
|
||||
|
||||
template<class T> void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) {
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
(obj.*method)(*this);
|
||||
}
|
||||
|
||||
sigc::signal<void> StateChanged;
|
||||
|
||||
XMLNode& get_state(void);
|
||||
int set_state (const XMLNode &s);
|
||||
XMLNode& state (bool full);
|
||||
XMLNode& serialize_events ();
|
||||
|
||||
void set_max_xval (double);
|
||||
double get_max_xval() const { return _max_xval; }
|
||||
|
||||
double eval (double where) {
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
return unlocked_eval (where);
|
||||
}
|
||||
|
||||
double rt_safe_eval (double where, bool& ok) {
|
||||
|
||||
Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
|
||||
|
||||
if ((ok = lm.locked())) {
|
||||
return unlocked_eval (where);
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool time_comparator (const ControlEvent* a, const ControlEvent* b) {
|
||||
return a->when < b->when;
|
||||
}
|
||||
|
||||
/** Lookup cache for eval functions, range contains equivalent values */
|
||||
struct LookupCache {
|
||||
LookupCache() : left(-1) {}
|
||||
double left; /* leftmost x coordinate used when finding "range" */
|
||||
std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
|
||||
};
|
||||
|
||||
/** Lookup cache for point finding, range contains points between left and right */
|
||||
struct SearchCache {
|
||||
SearchCache() : left(-1), right(-1) {}
|
||||
double left; /* leftmost x coordinate used when finding "range" */
|
||||
double right; /* rightmost x coordinate used when finding "range" */
|
||||
std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
|
||||
};
|
||||
|
||||
static sigc::signal<void, AutomationList*> AutomationListCreated;
|
||||
|
||||
const EventList& events() const { return _events; }
|
||||
double default_value() const { return _default_value; }
|
||||
|
||||
// teeny const violations for Curve
|
||||
mutable sigc::signal<void> Dirty;
|
||||
Glib::Mutex& lock() const { return _lock; }
|
||||
LookupCache& lookup_cache() const { return _lookup_cache; }
|
||||
SearchCache& search_cache() const { return _search_cache; }
|
||||
|
||||
/** Called by locked entry point and various private
|
||||
* locations where we already hold the lock.
|
||||
*
|
||||
* FIXME: Should this be private? Curve needs it..
|
||||
*/
|
||||
double unlocked_eval (double x) const;
|
||||
|
||||
bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
|
||||
bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
|
||||
|
||||
Curve& curve() { return *_curve; }
|
||||
const Curve& curve() const { return *_curve; }
|
||||
|
||||
enum InterpolationStyle {
|
||||
Discrete,
|
||||
Linear,
|
||||
Curved
|
||||
};
|
||||
|
||||
InterpolationStyle interpolation() const { return _interpolation; }
|
||||
void set_interpolation(InterpolationStyle style) { _interpolation = style; }
|
||||
|
||||
private:
|
||||
|
||||
/** Called by unlocked_eval() to handle cases of 3 or more control points.
|
||||
*/
|
||||
double multipoint_eval (double x) const;
|
||||
|
||||
void build_search_cache_if_necessary(double start, double end) const;
|
||||
|
||||
bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
|
||||
bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
|
||||
|
||||
AutomationList* cut_copy_clear (double, double, int op);
|
||||
|
||||
int deserialize_events (const XMLNode&);
|
||||
|
||||
void maybe_signal_changed ();
|
||||
void mark_dirty ();
|
||||
void _x_scale (double factor);
|
||||
|
||||
mutable LookupCache _lookup_cache;
|
||||
mutable SearchCache _search_cache;
|
||||
|
||||
Parameter _parameter;
|
||||
InterpolationStyle _interpolation;
|
||||
EventList _events;
|
||||
mutable Glib::Mutex _lock;
|
||||
int8_t _frozen;
|
||||
bool _changed_when_thawed;
|
||||
AutoState _state;
|
||||
AutoStyle _style;
|
||||
bool _touching;
|
||||
bool _new_touch;
|
||||
double _max_xval;
|
||||
double _min_yval;
|
||||
double _max_yval;
|
||||
double _default_value;
|
||||
bool _sort_pending;
|
||||
iterator _rt_insertion_point;
|
||||
double _rt_pos;
|
||||
|
||||
Curve* _curve;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public:
|
|||
|
||||
void copy(const MidiBuffer& copy);
|
||||
|
||||
bool push_back(const MIDI::Event& event);
|
||||
bool push_back(const Evoral::Event& event);
|
||||
bool push_back(const jack_midi_event_t& event);
|
||||
uint8_t* reserve(double time, size_t size);
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ public:
|
|||
struct iterator {
|
||||
iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {}
|
||||
|
||||
inline MIDI::Event& operator*() const { return buffer[index]; }
|
||||
inline Evoral::Event& operator*() const { return buffer[index]; }
|
||||
inline iterator& operator++() { ++index; return *this; } // prefix
|
||||
inline bool operator!=(const iterator& other) const { return index != other.index; }
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ public:
|
|||
struct const_iterator {
|
||||
const_iterator(const MidiBuffer& b, size_t i) : buffer(b), index(i) {}
|
||||
|
||||
inline const MIDI::Event& operator*() const { return buffer[index]; }
|
||||
inline const Evoral::Event& operator*() const { return buffer[index]; }
|
||||
inline const_iterator& operator++() { ++index; return *this; } // prefix
|
||||
inline bool operator!=(const const_iterator& other) const { return index != other.index; }
|
||||
|
||||
|
|
@ -80,8 +80,8 @@ private:
|
|||
friend class iterator;
|
||||
friend class const_iterator;
|
||||
|
||||
const MIDI::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; }
|
||||
MIDI::Event& operator[](size_t i) { assert(i < _size); return _events[i]; }
|
||||
const Evoral::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; }
|
||||
Evoral::Event& operator[](size_t i) { assert(i < _size); return _events[i]; }
|
||||
|
||||
// FIXME: Eliminate this
|
||||
static const size_t MAX_EVENT_SIZE = 4; // bytes
|
||||
|
|
@ -92,7 +92,7 @@ private:
|
|||
|
||||
/* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */
|
||||
|
||||
MIDI::Event* _events; ///< Event structs that point to offsets in _data
|
||||
Evoral::Event* _events; ///< Event structs that point to offsets in _data
|
||||
uint8_t* _data; ///< MIDI, straight up. No time stamps.
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,84 +31,28 @@
|
|||
#include <ardour/midi_buffer.h>
|
||||
#include <ardour/midi_ring_buffer.h>
|
||||
#include <ardour/automatable.h>
|
||||
#include <ardour/note.h>
|
||||
#include <ardour/types.h>
|
||||
#include <evoral/Note.hpp>
|
||||
#include <evoral/Sequence.hpp>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Session;
|
||||
class MidiSource;
|
||||
|
||||
/**
|
||||
* This class keeps track of the current x and y for a control
|
||||
*/
|
||||
class MidiControlIterator {
|
||||
public:
|
||||
boost::shared_ptr<const AutomationList> automation_list;
|
||||
double x;
|
||||
double y;
|
||||
|
||||
MidiControlIterator(boost::shared_ptr<const AutomationList> a_list,
|
||||
double a_x,
|
||||
double a_y)
|
||||
: automation_list(a_list)
|
||||
, x(a_x)
|
||||
, y(a_y)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/** This is a higher level (than MidiBuffer) model of MIDI data, with separate
|
||||
* representations for notes (instead of just unassociated note on/off events)
|
||||
* and controller data. Controller data is represented as part of the
|
||||
* Automatable base (i.e. in a map of AutomationList, keyed by Parameter).
|
||||
* Because of this MIDI controllers and automatable controllers/widgets/etc
|
||||
* are easily interchangeable.
|
||||
*/
|
||||
class MidiModel : public boost::noncopyable, public Automatable {
|
||||
class MidiModel : public Automatable, public Evoral::Sequence {
|
||||
public:
|
||||
MidiModel(MidiSource* s, size_t size=0);
|
||||
|
||||
void write_lock();
|
||||
void write_unlock();
|
||||
|
||||
void read_lock() const;
|
||||
void read_unlock() const;
|
||||
|
||||
void clear();
|
||||
|
||||
NoteMode note_mode() const { return _note_mode; }
|
||||
void set_note_mode(NoteMode mode) { _note_mode = mode; }
|
||||
|
||||
void start_write();
|
||||
bool writing() const { return _writing; }
|
||||
void end_write(bool delete_stuck=false);
|
||||
|
||||
size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
|
||||
|
||||
/** Resizes vector if necessary (NOT realtime safe) */
|
||||
void append(const MIDI::Event& ev);
|
||||
|
||||
inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
|
||||
inline const boost::shared_ptr<Note> note_at(unsigned i) { return _notes[i]; }
|
||||
|
||||
inline size_t n_notes() const { return _notes.size(); }
|
||||
inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; }
|
||||
|
||||
inline static bool note_time_comparator (const boost::shared_ptr<const Note> a,
|
||||
const boost::shared_ptr<const Note> b) {
|
||||
return a->time() < b->time();
|
||||
}
|
||||
|
||||
struct LaterNoteEndComparator {
|
||||
typedef const Note* value_type;
|
||||
inline bool operator()(const boost::shared_ptr<const Note> a,
|
||||
const boost::shared_ptr<const Note> b) const {
|
||||
return a->end_time() > b->end_time();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector< boost::shared_ptr<Note> > Notes;
|
||||
inline Notes& notes() { return _notes; }
|
||||
inline const Notes& notes() const { return _notes; }
|
||||
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
|
||||
void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
|
||||
|
||||
/** Add/Remove notes.
|
||||
* Technically all operations can be implemented as one of these.
|
||||
|
|
@ -127,17 +71,17 @@ public:
|
|||
int set_state (const XMLNode&);
|
||||
XMLNode& get_state ();
|
||||
|
||||
void add(const boost::shared_ptr<Note> note);
|
||||
void remove(const boost::shared_ptr<Note> note);
|
||||
void add(const boost::shared_ptr<Evoral::Note> note);
|
||||
void remove(const boost::shared_ptr<Evoral::Note> note);
|
||||
|
||||
private:
|
||||
XMLNode &marshal_note(const boost::shared_ptr<Note> note);
|
||||
boost::shared_ptr<Note> unmarshal_note(XMLNode *xml_note);
|
||||
XMLNode &marshal_note(const boost::shared_ptr<Evoral::Note> note);
|
||||
boost::shared_ptr<Evoral::Note> unmarshal_note(XMLNode *xml_note);
|
||||
|
||||
boost::shared_ptr<MidiModel> _model;
|
||||
const std::string _name;
|
||||
|
||||
typedef std::list< boost::shared_ptr<Note> > NoteList;
|
||||
typedef std::list< boost::shared_ptr<Evoral::Note> > NoteList;
|
||||
|
||||
NoteList _added_notes;
|
||||
NoteList _removed_notes;
|
||||
|
|
@ -146,8 +90,6 @@ public:
|
|||
MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
|
||||
void apply_command(Command* cmd);
|
||||
|
||||
bool edited() const { return _edited; }
|
||||
void set_edited(bool yn) { _edited = yn; }
|
||||
bool write_to(boost::shared_ptr<MidiSource> source);
|
||||
|
||||
// MidiModel doesn't use the normal AutomationList serialisation code
|
||||
|
|
@ -157,90 +99,11 @@ public:
|
|||
|
||||
sigc::signal<void> ContentsChanged;
|
||||
|
||||
/** Read iterator */
|
||||
class const_iterator {
|
||||
public:
|
||||
const_iterator(const MidiModel& model, double t);
|
||||
~const_iterator();
|
||||
|
||||
inline bool locked() const { return _locked; }
|
||||
|
||||
const MIDI::Event& operator*() const { return *_event; }
|
||||
const boost::shared_ptr<MIDI::Event> operator->() const { return _event; }
|
||||
const boost::shared_ptr<MIDI::Event> get_event_pointer() { return _event; }
|
||||
|
||||
const const_iterator& operator++(); // prefix only
|
||||
bool operator==(const const_iterator& other) const;
|
||||
bool operator!=(const const_iterator& other) const { return ! operator==(other); }
|
||||
|
||||
const_iterator& operator=(const const_iterator& other);
|
||||
|
||||
private:
|
||||
friend class MidiModel;
|
||||
|
||||
const MidiModel* _model;
|
||||
boost::shared_ptr<MIDI::Event> _event;
|
||||
|
||||
typedef std::priority_queue<
|
||||
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
|
||||
LaterNoteEndComparator>
|
||||
ActiveNotes;
|
||||
|
||||
mutable ActiveNotes _active_notes;
|
||||
|
||||
bool _is_end;
|
||||
bool _locked;
|
||||
Notes::const_iterator _note_iter;
|
||||
std::vector<MidiControlIterator> _control_iters;
|
||||
std::vector<MidiControlIterator>::iterator _control_iter;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(*this, 0); }
|
||||
const const_iterator& end() const { return _end_iter; }
|
||||
|
||||
const MidiSource* midi_source() const { return _midi_source; }
|
||||
void set_midi_source(MidiSource* source) { _midi_source = source; }
|
||||
bool control_to_midi_event(boost::shared_ptr<MIDI::Event>& ev, const MidiControlIterator& iter) const;
|
||||
|
||||
private:
|
||||
friend class DeltaCommand;
|
||||
void add_note_unlocked(const boost::shared_ptr<Note> note);
|
||||
void remove_note_unlocked(const boost::shared_ptr<const Note> note);
|
||||
|
||||
friend class const_iterator;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool is_sorted() const;
|
||||
#endif
|
||||
|
||||
void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
|
||||
void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
|
||||
void append_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte);
|
||||
void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number);
|
||||
|
||||
mutable Glib::RWLock _lock;
|
||||
|
||||
Notes _notes;
|
||||
|
||||
NoteMode _note_mode;
|
||||
|
||||
typedef std::vector<size_t> WriteNotes;
|
||||
WriteNotes _write_notes[16];
|
||||
bool _writing;
|
||||
bool _edited;
|
||||
|
||||
typedef std::vector< boost::shared_ptr<const ARDOUR::AutomationList> > AutomationLists;
|
||||
AutomationLists _dirty_automations;
|
||||
|
||||
const const_iterator _end_iter;
|
||||
|
||||
mutable nframes_t _next_read;
|
||||
mutable const_iterator _read_iter;
|
||||
|
||||
typedef std::priority_queue<
|
||||
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
|
||||
LaterNoteEndComparator>
|
||||
ActiveNotes;
|
||||
|
||||
// We cannot use a boost::shared_ptr here to avoid a retain cycle
|
||||
MidiSource* _midi_source;
|
||||
|
|
|
|||
|
|
@ -80,10 +80,10 @@ class MidiRegion : public Region
|
|||
Controls& controls() { return midi_source()->model()->controls(); }
|
||||
const Controls& controls() const { return midi_source()->model()->controls(); }
|
||||
|
||||
boost::shared_ptr<AutomationControl> control(Parameter id, bool create_if_missing=false)
|
||||
boost::shared_ptr<Evoral::Control> control(Evoral::Parameter id, bool create_if_missing=false)
|
||||
{ return midi_source()->model()->control(id, create_if_missing); }
|
||||
|
||||
boost::shared_ptr<const AutomationControl> control(Parameter id) const
|
||||
boost::shared_ptr<const Evoral::Control> control(Evoral::Parameter id) const
|
||||
{ return midi_source()->model()->control(id); }
|
||||
|
||||
int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <algorithm>
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/buffer.h>
|
||||
#include <evoral/EventSink.hpp>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
|
@ -243,7 +244,7 @@ MidiRingBufferBase<T>::write(size_t size, const T* src)
|
|||
*
|
||||
* [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..]
|
||||
*/
|
||||
class MidiRingBuffer : public MidiRingBufferBase<uint8_t> {
|
||||
class MidiRingBuffer : public MidiRingBufferBase<uint8_t>, public Evoral::EventSink {
|
||||
public:
|
||||
/** @param size Size in bytes.
|
||||
*/
|
||||
|
|
@ -251,8 +252,8 @@ public:
|
|||
: MidiRingBufferBase<uint8_t>(size), _channel_mask(0x0000FFFF)
|
||||
{}
|
||||
|
||||
size_t write(double time, size_t size, const uint8_t* buf);
|
||||
bool read(double* time, size_t* size, uint8_t* buf);
|
||||
size_t write(double time, uint32_t size, const uint8_t* buf);
|
||||
bool read(double* time, uint32_t* size, uint8_t* buf);
|
||||
|
||||
bool read_prefix(double* time, size_t* size);
|
||||
bool read_contents(size_t size, uint8_t* buf);
|
||||
|
|
@ -292,7 +293,7 @@ private:
|
|||
|
||||
|
||||
inline bool
|
||||
MidiRingBuffer::read(double* time, size_t* size, uint8_t* buf)
|
||||
MidiRingBuffer::read(double* time, uint32_t* size, uint8_t* buf)
|
||||
{
|
||||
bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
|
||||
|
||||
|
|
@ -333,7 +334,7 @@ MidiRingBuffer::read_contents(size_t size, uint8_t* buf)
|
|||
|
||||
|
||||
inline size_t
|
||||
MidiRingBuffer::write(double time, size_t size, const uint8_t* buf)
|
||||
MidiRingBuffer::write(double time, uint32_t size, const uint8_t* buf)
|
||||
{
|
||||
/*fprintf(stderr, "MRB %p write (t = %f) ", this, time);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class MidiSource : public Source
|
|||
virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
|
||||
virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt);
|
||||
|
||||
virtual void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev) = 0;
|
||||
virtual void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev) = 0;
|
||||
|
||||
virtual void mark_for_remove() = 0;
|
||||
virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public:
|
|||
|
||||
struct MidiControl : public AutomationControl {
|
||||
MidiControl(MidiTrack* route, boost::shared_ptr<AutomationList> al)
|
||||
: AutomationControl (route->session(), al, al->parameter().to_string())
|
||||
: AutomationControl (route->session(), al, al->parameter().symbol())
|
||||
, _route (route)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2007 Paul Davis
|
||||
Author: Dave Robillard
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ardour_note_h__
|
||||
#define __ardour_note_h__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <midi++/event.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
||||
/** A MIDI Note.
|
||||
*
|
||||
* A note is (unfortunately) special and not just another MIDI::Event as it
|
||||
* has a duration and two separate MIDI events (on and off).
|
||||
*/
|
||||
class Note {
|
||||
public:
|
||||
Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
|
||||
Note(const Note& copy);
|
||||
~Note();
|
||||
|
||||
const Note& operator=(const Note& copy);
|
||||
|
||||
inline bool operator==(const Note& other)
|
||||
{ return time() == other.time() &&
|
||||
note() == other.note() &&
|
||||
duration() == other.duration() &&
|
||||
velocity() == other.velocity() &&
|
||||
channel() == other.channel();
|
||||
}
|
||||
|
||||
inline double time() const { return _on_event.time(); }
|
||||
inline double end_time() const { return _off_event.time(); }
|
||||
inline uint8_t note() const { return _on_event.note(); }
|
||||
inline uint8_t velocity() const { return _on_event.velocity(); }
|
||||
inline double duration() const { return _off_event.time() - _on_event.time(); }
|
||||
inline uint8_t channel() const {
|
||||
assert(_on_event.channel() == _off_event.channel());
|
||||
return _on_event.channel();
|
||||
}
|
||||
|
||||
inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; }
|
||||
inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; }
|
||||
inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; }
|
||||
inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; }
|
||||
inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); }
|
||||
|
||||
inline MIDI::Event& on_event() { return _on_event; }
|
||||
inline MIDI::Event& off_event() { return _off_event; }
|
||||
|
||||
inline const MIDI::Event& on_event() const { return _on_event; }
|
||||
inline const MIDI::Event& off_event() const { return _off_event; }
|
||||
|
||||
private:
|
||||
// Event buffers are self-contained
|
||||
MIDI::Event _on_event;
|
||||
MIDI::Event _off_event;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_note_h__ */
|
||||
|
|
@ -105,10 +105,12 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
|
|||
|
||||
struct PanControllable : public AutomationControl {
|
||||
PanControllable (Session& s, std::string name, StreamPanner& p, Parameter param)
|
||||
: AutomationControl (s, boost::shared_ptr<AutomationList>(new AutomationList(
|
||||
param, 0.0, 1.0, 0.5)), name)
|
||||
, panner (p) { assert(param.type() != NullAutomation); }
|
||||
: AutomationControl (s,
|
||||
boost::shared_ptr<AutomationList>(new AutomationList(param)), name)
|
||||
, panner (p)
|
||||
{ assert(param.type() != NullAutomation); }
|
||||
|
||||
AutomationList* alist() { return (AutomationList*)_list.get(); }
|
||||
StreamPanner& panner;
|
||||
|
||||
void set_value (float);
|
||||
|
|
|
|||
|
|
@ -24,140 +24,86 @@
|
|||
#include <pbd/compose.h>
|
||||
#include <pbd/error.h>
|
||||
#include <ardour/types.h>
|
||||
#include <evoral/Parameter.hpp>
|
||||
#include <evoral/MIDIParameters.hpp>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
||||
/** ID of an automatable parameter.
|
||||
*
|
||||
* A given automatable object has a number of automatable parameters. This is
|
||||
* the unique ID for those parameters. Anything automatable (AutomationList,
|
||||
* Curve) must have an ID unique with respect to it's Automatable parent.
|
||||
* Curve) must have unique Parameter ID with respect to it's Automatable parent.
|
||||
*
|
||||
* A parameter ID has two parts, a type and an int (only used by some types).
|
||||
* These are fast to compare, but passing a (const) reference around is
|
||||
* probably more efficient than copying because the Parameter contains
|
||||
* metadata not used for comparison.
|
||||
*
|
||||
* This is a bit more ugly than it could be, due to using the existing/legacy
|
||||
* ARDOUR::AutomationType: GainAutomation, PanAutomation, SoloAutomation,
|
||||
* and MuteAutomation use only the type(), but PluginAutomation and
|
||||
* MidiCCAutomation use the id() as port number and CC number, respectively.
|
||||
*
|
||||
* Future types may use a string or URI or whatever, as long as these are
|
||||
* comparable anything may be added. ints are best as these should be fast to
|
||||
* copy and compare with one another.
|
||||
* See evoral/Parameter.hpp for precise definition.
|
||||
*/
|
||||
class Parameter
|
||||
class Parameter : public Evoral::Parameter
|
||||
{
|
||||
public:
|
||||
Parameter(AutomationType type = NullAutomation, uint32_t id=0, uint8_t channel=0)
|
||||
: _type(type), _id(id), _channel(channel)
|
||||
: Evoral::Parameter((uint32_t)type, id, channel)
|
||||
{
|
||||
init(type);
|
||||
}
|
||||
|
||||
Parameter(AutomationType type, double min, double max, double normal)
|
||||
: Evoral::Parameter((uint32_t)type, 0, 0, min, max, normal)
|
||||
{}
|
||||
|
||||
Parameter(const Evoral::Parameter& copy)
|
||||
: Evoral::Parameter(copy)
|
||||
{
|
||||
init((AutomationType)_type);
|
||||
}
|
||||
|
||||
void init(AutomationType type) {
|
||||
_normal = 0.0f;
|
||||
switch(type) {
|
||||
case NullAutomation:
|
||||
case GainAutomation:
|
||||
_min = 0.0f;
|
||||
_max = 2.0f;
|
||||
_normal = 1.0f;
|
||||
break;
|
||||
case PanAutomation:
|
||||
_min = 0.0f;
|
||||
_max = 1.0f;
|
||||
_normal = 0.5f;
|
||||
case PluginAutomation:
|
||||
case SoloAutomation:
|
||||
case MuteAutomation:
|
||||
case FadeInAutomation:
|
||||
case FadeOutAutomation:
|
||||
case EnvelopeAutomation:
|
||||
_min = 0.0f;
|
||||
_max = 2.0f;
|
||||
_normal = 1.0f;
|
||||
case MidiCCAutomation:
|
||||
Evoral::MIDI::ContinuousController::set_range(*this); break;
|
||||
case MidiPgmChangeAutomation:
|
||||
Evoral::MIDI::ProgramChange::set_range(*this); break;
|
||||
case MidiPitchBenderAutomation:
|
||||
Evoral::MIDI::PitchBender::set_range(*this); break;
|
||||
case MidiChannelAftertouchAutomation:
|
||||
Evoral::MIDI::ChannelAftertouch::set_range(*this); break;
|
||||
}
|
||||
}
|
||||
|
||||
Parameter(const std::string& str);
|
||||
|
||||
inline AutomationType type() const { return _type; }
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline uint8_t channel() const { return _channel; }
|
||||
inline AutomationType type() const { return (AutomationType)_type; }
|
||||
|
||||
/**
|
||||
* Equivalence operator
|
||||
* It is obvious from the definition that this operator
|
||||
* is transitive, as required by stict weak ordering
|
||||
* (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html)
|
||||
*/
|
||||
inline bool operator==(const Parameter& id) const {
|
||||
return (_type == id._type && _id == id._id && _channel == id._channel);
|
||||
}
|
||||
|
||||
/** Strict weak ordering
|
||||
* (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html)
|
||||
* This is necessary so that std::set works):
|
||||
* Sort Parameters first according to type then to id and lastly to channel.
|
||||
*
|
||||
* Proof:
|
||||
* <ol>
|
||||
* <li>Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.</li>
|
||||
*
|
||||
* <li>Antisymmetry: given x != y, f(x, y) implies !f(y, x) because of the same
|
||||
* property of \c < in each branch and the symmetry of operator==. </li>
|
||||
*
|
||||
* <li>Transitivity: let f(x, y) and f(y, z) be true. We prove by assuming the contrary,
|
||||
* that f(x, z) does not hold.
|
||||
* That would imply exactly one of the following:
|
||||
* <ol>
|
||||
* <li> x == z which contradicts the assumption f(x, y) and f(y, x)
|
||||
* because of antisymmetry.
|
||||
* </li>
|
||||
* <li> f(z, x) is true. That would imply that one of the ivars (we call it i)
|
||||
* of x is greater than the same ivar in z while all "previous" ivars
|
||||
* are equal. That would imply that also in y all those "previous"
|
||||
* ivars are equal and because if x.i > z.i it is impossible
|
||||
* that there is an y that satisfies x.i < y.i < z.i at the same
|
||||
* time which contradicts the assumption.
|
||||
* </li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* </ol>
|
||||
*/
|
||||
inline bool operator<(const Parameter& id) const {
|
||||
#ifndef NDEBUG
|
||||
if (_type == NullAutomation)
|
||||
PBD::warning << "Uninitialized Parameter compared." << endmsg;
|
||||
#endif
|
||||
if (_type < id._type) {
|
||||
return true;
|
||||
} else if (_type == id._type && _id < id._id) {
|
||||
return true;
|
||||
} else if (_id == id._id && _channel < id._channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline operator bool() const { return (_type != 0); }
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
/* The below properties are only used for CC right now, but unchanging properties
|
||||
* of parameters (rather than changing parameters of automation lists themselves)
|
||||
* should be moved here */
|
||||
|
||||
inline double min() const {
|
||||
switch(_type) {
|
||||
case MidiCCAutomation:
|
||||
case MidiPgmChangeAutomation:
|
||||
case MidiPitchBenderAutomation:
|
||||
case MidiChannelAftertouchAutomation:
|
||||
return 0.0;
|
||||
|
||||
default:
|
||||
return DBL_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
inline double max() const {
|
||||
switch(_type) {
|
||||
case MidiCCAutomation:
|
||||
case MidiPgmChangeAutomation:
|
||||
case MidiChannelAftertouchAutomation:
|
||||
return 127.0;
|
||||
case MidiPitchBenderAutomation:
|
||||
return 16383.0;
|
||||
|
||||
default:
|
||||
return DBL_MAX;
|
||||
}
|
||||
}
|
||||
std::string symbol() const;
|
||||
|
||||
inline bool is_integer() const {
|
||||
return (_type >= MidiCCAutomation && _type <= MidiChannelAftertouchAutomation);
|
||||
}
|
||||
|
||||
private:
|
||||
// default copy constructor is ok
|
||||
AutomationType _type;
|
||||
uint32_t _id;
|
||||
uint8_t _channel;
|
||||
inline operator Parameter() { return (Parameter)*this; }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class PluginInsert : public Processor
|
|||
void set_parameter (Parameter param, float val);
|
||||
float get_parameter (Parameter param);
|
||||
|
||||
float default_parameter_value (Parameter param);
|
||||
float default_parameter_value (Evoral::Parameter param);
|
||||
|
||||
struct PluginControl : public AutomationControl
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include <ardour/midi_source.h>
|
||||
|
||||
namespace Evoral { class Event; }
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class MidiRingBuffer;
|
||||
|
|
@ -71,7 +73,7 @@ class SMFSource : public MidiSource {
|
|||
void set_allow_remove_if_empty (bool yn);
|
||||
void mark_for_remove();
|
||||
|
||||
void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev);
|
||||
void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev);
|
||||
|
||||
int flush_header ();
|
||||
int flush_footer ();
|
||||
|
|
|
|||
|
|
@ -604,9 +604,9 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
|||
/* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
|
||||
|
||||
if (!diskstream->record_enabled() && _session.transport_rolling()) {
|
||||
Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
|
||||
Glib::Mutex::Lock am (_control_lock, Glib::TRY_LOCK);
|
||||
|
||||
if (am.locked() && gain_control()->list()->automation_playback()) {
|
||||
if (am.locked() && gain_control()->automation_playback()) {
|
||||
apply_gain_automation = gain_control()->list()->curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
|
||||
}
|
||||
}
|
||||
|
|
@ -702,7 +702,7 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes
|
|||
}
|
||||
}
|
||||
|
||||
if (gain_control()->list()->automation_state() == Play) {
|
||||
if (gain_control()->automation_state() == Play) {
|
||||
|
||||
gain_control()->list()->curve().get_vector (start, start + nframes, gain_automation, nframes);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ AudioRegion::init ()
|
|||
/* constructor for use by derived types only */
|
||||
AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name)
|
||||
: Region (s, start, length, name, DataType::AUDIO)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
|
@ -87,9 +87,9 @@ AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string
|
|||
/** Basic AudioRegion constructor (one channel) */
|
||||
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
|
||||
: Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0, Region::Flag(Region::DefaultFlags|Region::External))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
|
||||
if (afs) {
|
||||
|
|
@ -102,9 +102,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
|
|||
/* Basic AudioRegion constructor (one channel) */
|
||||
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
|
||||
: Region (src, start, length, name, DataType::AUDIO, layer, flags)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
|
||||
if (afs) {
|
||||
|
|
@ -117,9 +117,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
|
|||
/* Basic AudioRegion constructor (many channels) */
|
||||
AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
|
||||
: Region (srcs, start, length, name, DataType::AUDIO, layer, flags)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
init ();
|
||||
listen_to_my_sources ();
|
||||
|
|
@ -128,9 +128,9 @@ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t len
|
|||
/** Create a new AudioRegion, that is part of an existing one */
|
||||
AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
|
||||
: Region (other, offset, length, name, layer, flags)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
set<boost::shared_ptr<Source> > unique_srcs;
|
||||
|
||||
|
|
@ -180,9 +180,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
|
|||
|
||||
AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
|
||||
: Region (other)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
assert(_type == DataType::AUDIO);
|
||||
_scale_amplitude = other->_scale_amplitude;
|
||||
|
|
@ -196,9 +196,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
|
|||
|
||||
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
|
||||
: Region (src, node)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
|
||||
if (afs) {
|
||||
|
|
@ -217,9 +217,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
|
|||
|
||||
AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
|
||||
: Region (srcs, node)
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
|
||||
, _fade_in (new AutomationList(Parameter(FadeInAutomation)))
|
||||
, _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
|
||||
, _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
|
||||
{
|
||||
init ();
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ Automatable::load_automation (const string& path)
|
|||
return 1;
|
||||
}
|
||||
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
set<Parameter> tosave;
|
||||
_controls.clear ();
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ Automatable::load_automation (const string& path)
|
|||
in >> value; if (!in) goto bad;
|
||||
|
||||
/* FIXME: this is legacy and only used for plugin inserts? I think? */
|
||||
boost::shared_ptr<AutomationControl> c = control (Parameter(PluginAutomation, port), true);
|
||||
boost::shared_ptr<Evoral::Control> c = control (Parameter(PluginAutomation, port), true);
|
||||
c->list()->add (when, value);
|
||||
tosave.insert (Parameter(PluginAutomation, port));
|
||||
}
|
||||
|
|
@ -122,12 +122,10 @@ Automatable::load_automation (const string& path)
|
|||
}
|
||||
|
||||
void
|
||||
Automatable::add_control(boost::shared_ptr<AutomationControl> ac)
|
||||
Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
|
||||
{
|
||||
Parameter param = ac->parameter();
|
||||
|
||||
_controls[param] = ac;
|
||||
|
||||
_can_automate_list.insert(param);
|
||||
|
||||
// Sync everything (derived classes) up to initial values
|
||||
|
|
@ -135,21 +133,9 @@ Automatable::add_control(boost::shared_ptr<AutomationControl> ac)
|
|||
}
|
||||
|
||||
void
|
||||
Automatable::what_has_automation (set<Parameter>& s) const
|
||||
Automatable::what_has_visible_data (set<Parameter>& s) const
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Controls::const_iterator li;
|
||||
|
||||
// FIXME: correct semantics?
|
||||
for (li = _controls.begin(); li != _controls.end(); ++li) {
|
||||
s.insert ((*li).first);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Automatable::what_has_visible_automation (set<Parameter>& s) const
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
set<Parameter>::const_iterator li;
|
||||
|
||||
for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
|
||||
|
|
@ -157,42 +143,6 @@ Automatable::what_has_visible_automation (set<Parameter>& s) const
|
|||
}
|
||||
}
|
||||
|
||||
/** Returns NULL if we don't have an AutomationList for \a parameter.
|
||||
*/
|
||||
boost::shared_ptr<AutomationControl>
|
||||
Automatable::control (Parameter parameter, bool create_if_missing)
|
||||
{
|
||||
Controls::iterator i = _controls.find(parameter);
|
||||
|
||||
if (i != _controls.end()) {
|
||||
return i->second;
|
||||
|
||||
} else if (create_if_missing) {
|
||||
boost::shared_ptr<AutomationList> al (new AutomationList (
|
||||
parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter)));
|
||||
boost::shared_ptr<AutomationControl> ac(control_factory(al));
|
||||
add_control(ac);
|
||||
return ac;
|
||||
|
||||
} else {
|
||||
//warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
|
||||
return boost::shared_ptr<AutomationControl>();
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<const AutomationControl>
|
||||
Automatable::control (Parameter parameter) const
|
||||
{
|
||||
Controls::const_iterator i = _controls.find(parameter);
|
||||
|
||||
if (i != _controls.end()) {
|
||||
return i->second;
|
||||
} else {
|
||||
//warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
|
||||
return boost::shared_ptr<AutomationControl>();
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
Automatable::describe_parameter (Parameter param)
|
||||
{
|
||||
|
|
@ -213,7 +163,7 @@ Automatable::describe_parameter (Parameter param)
|
|||
} else if (param.type() == MidiChannelAftertouchAutomation) {
|
||||
return string_compose("Aftertouch [%1]", int(param.channel()) + 1);
|
||||
} else {
|
||||
return param.to_string();
|
||||
return param.symbol();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,37 +187,6 @@ Automatable::mark_automation_visible (Parameter what, bool yn)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
|
||||
{
|
||||
Controls::const_iterator li;
|
||||
|
||||
next_event.when = max_frames;
|
||||
|
||||
for (li = _controls.begin(); li != _controls.end(); ++li) {
|
||||
|
||||
AutomationList::const_iterator i;
|
||||
boost::shared_ptr<const AutomationList> alist (li->second->list());
|
||||
ControlEvent cp (now, 0.0f);
|
||||
|
||||
for (i = lower_bound (alist->const_begin(), alist->const_end(), &cp, AutomationList::time_comparator);
|
||||
i != alist->const_end() && (*i)->when < end; ++i) {
|
||||
if ((*i)->when > now) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != alist->const_end() && (*i)->when < end) {
|
||||
|
||||
if ((*i)->when < next_event.when) {
|
||||
next_event.when = (*i)->when;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next_event.when != max_frames;
|
||||
}
|
||||
|
||||
/** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
|
||||
* had a single automation parameter, with it's type implicit. Derived objects should
|
||||
* pass that type and it will be used for the untyped AutomationList found.
|
||||
|
|
@ -275,7 +194,7 @@ Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_e
|
|||
int
|
||||
Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
|
||||
/* Don't clear controls, since some may be special derived Controllable classes */
|
||||
|
||||
|
|
@ -301,11 +220,11 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
|
|||
|
||||
if (!id_prop) {
|
||||
warning << "AutomationList node without automation-id property, "
|
||||
<< "using default: " << legacy_param.to_string() << endmsg;
|
||||
<< "using default: " << legacy_param.symbol() << endmsg;
|
||||
al->set_parameter(legacy_param);
|
||||
}
|
||||
|
||||
boost::shared_ptr<AutomationControl> existing = control(param);
|
||||
boost::shared_ptr<Evoral::Control> existing = control(param);
|
||||
if (existing)
|
||||
existing->set_list(al);
|
||||
else
|
||||
|
|
@ -324,7 +243,7 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
|
|||
XMLNode&
|
||||
Automatable::get_automation_state ()
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
XMLNode* node = new XMLNode (X_("Automation"));
|
||||
|
||||
if (_controls.empty()) {
|
||||
|
|
@ -332,30 +251,24 @@ Automatable::get_automation_state ()
|
|||
}
|
||||
|
||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
|
||||
node->add_child_nocopy (li->second->list()->get_state ());
|
||||
boost::shared_ptr<AutomationList> l
|
||||
= boost::dynamic_pointer_cast<AutomationList>(li->second->list());
|
||||
node->add_child_nocopy (l->get_state ());
|
||||
}
|
||||
|
||||
return *node;
|
||||
}
|
||||
|
||||
void
|
||||
Automatable::clear_automation ()
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
|
||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
|
||||
li->second->list()->clear();
|
||||
}
|
||||
|
||||
void
|
||||
Automatable::set_parameter_automation_state (Parameter param, AutoState s)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control (param, true);
|
||||
boost::shared_ptr<Evoral::Control> c = control (param, true);
|
||||
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
|
||||
|
||||
if (s != c->list()->automation_state()) {
|
||||
c->list()->set_automation_state (s);
|
||||
if (s != l->automation_state()) {
|
||||
l->set_automation_state (s);
|
||||
_session.set_dirty ();
|
||||
}
|
||||
}
|
||||
|
|
@ -366,15 +279,16 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock)
|
|||
AutoState result = Off;
|
||||
|
||||
if (lock)
|
||||
_automation_lock.lock();
|
||||
_control_lock.lock();
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control(param);
|
||||
boost::shared_ptr<Evoral::Control> c = control(param);
|
||||
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
|
||||
|
||||
if (c)
|
||||
result = c->list()->automation_state();
|
||||
result = l->automation_state();
|
||||
|
||||
if (lock)
|
||||
_automation_lock.unlock();
|
||||
_control_lock.unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -382,12 +296,13 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock)
|
|||
void
|
||||
Automatable::set_parameter_automation_style (Parameter param, AutoStyle s)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control(param, true);
|
||||
boost::shared_ptr<Evoral::Control> c = control(param, true);
|
||||
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
|
||||
|
||||
if (s != c->list()->automation_style()) {
|
||||
c->list()->set_automation_style (s);
|
||||
if (s != l->automation_style()) {
|
||||
l->set_automation_style (s);
|
||||
_session.set_dirty ();
|
||||
}
|
||||
}
|
||||
|
|
@ -395,12 +310,13 @@ Automatable::set_parameter_automation_style (Parameter param, AutoStyle s)
|
|||
AutoStyle
|
||||
Automatable::get_parameter_automation_style (Parameter param)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control(param);
|
||||
boost::shared_ptr<Evoral::Control> c = control(param);
|
||||
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
|
||||
|
||||
if (c) {
|
||||
return c->list()->automation_style();
|
||||
return l->automation_style();
|
||||
} else {
|
||||
return Absolute; // whatever
|
||||
}
|
||||
|
|
@ -409,20 +325,22 @@ Automatable::get_parameter_automation_style (Parameter param)
|
|||
void
|
||||
Automatable::protect_automation ()
|
||||
{
|
||||
set<Parameter> automated_params;
|
||||
typedef set<Evoral::Parameter> ParameterSet;
|
||||
ParameterSet automated_params;
|
||||
|
||||
what_has_automation (automated_params);
|
||||
what_has_data (automated_params);
|
||||
|
||||
for (set<Parameter>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
|
||||
for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control(*i);
|
||||
boost::shared_ptr<Evoral::Control> c = control(*i);
|
||||
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
|
||||
|
||||
switch (c->list()->automation_state()) {
|
||||
switch (l->automation_state()) {
|
||||
case Write:
|
||||
c->list()->set_automation_state (Off);
|
||||
l->set_automation_state (Off);
|
||||
break;
|
||||
case Touch:
|
||||
c->list()->set_automation_state (Play);
|
||||
l->set_automation_state (Play);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -436,8 +354,10 @@ Automatable::automation_snapshot (nframes_t now, bool force)
|
|||
if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
|
||||
|
||||
for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) {
|
||||
if (i->second->list()->automation_write()) {
|
||||
i->second->list()->rt_add (now, i->second->user_value());
|
||||
boost::shared_ptr<AutomationControl> c
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(i->second);
|
||||
if (c->automation_write()) {
|
||||
c->list()->rt_add (now, i->second->user_value());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -450,29 +370,34 @@ Automatable::transport_stopped (nframes_t now)
|
|||
{
|
||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = li->second;
|
||||
boost::shared_ptr<AutomationControl> c
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(li->second);
|
||||
boost::shared_ptr<AutomationList> l
|
||||
= boost::dynamic_pointer_cast<AutomationList>(c->list());
|
||||
|
||||
c->list()->reposition_for_rt_add (now);
|
||||
|
||||
if (c->list()->automation_state() != Off) {
|
||||
if (c->automation_state() != Off) {
|
||||
c->set_value(c->list()->eval(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: this probably doesn't belong here */
|
||||
boost::shared_ptr<AutomationControl>
|
||||
Automatable::control_factory(boost::shared_ptr<AutomationList> list)
|
||||
boost::shared_ptr<Evoral::Control>
|
||||
Automatable::control_factory(boost::shared_ptr<Evoral::ControlList> list) const
|
||||
{
|
||||
if (
|
||||
list->parameter().type() == MidiCCAutomation ||
|
||||
list->parameter().type() == MidiPgmChangeAutomation ||
|
||||
list->parameter().type() == MidiChannelAftertouchAutomation
|
||||
) {
|
||||
// FIXME: this will die horribly if this is not a MidiTrack
|
||||
return boost::shared_ptr<AutomationControl>(new MidiTrack::MidiControl((MidiTrack*)this, list));
|
||||
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(list);
|
||||
assert(l);
|
||||
if (l->parameter().type() >= MidiCCAutomation
|
||||
&& l->parameter().type() <= MidiChannelAftertouchAutomation) {
|
||||
return boost::shared_ptr<Evoral::Control>(new MidiTrack::MidiControl((MidiTrack*)this, l));
|
||||
} else {
|
||||
return boost::shared_ptr<AutomationControl>(new AutomationControl(_session, list));
|
||||
return boost::shared_ptr<Evoral::Control>(new AutomationControl(_session, l));
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<Evoral::ControlList>
|
||||
Automatable::control_list_factory(const Evoral::Parameter& param) const
|
||||
{
|
||||
return boost::shared_ptr<Evoral::ControlList>(new AutomationList(param));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,10 +30,9 @@ using namespace PBD;
|
|||
|
||||
|
||||
AutomationControl::AutomationControl(Session& session, boost::shared_ptr<AutomationList> list, string name)
|
||||
: Controllable((name == "unnamed controllable") ? list->parameter().to_string() : name)
|
||||
: Controllable((name == "unnamed controllable") ? list->parameter().symbol() : name)
|
||||
, Evoral::Control(list)
|
||||
, _session(session)
|
||||
, _list(list)
|
||||
, _user_value(list->default_value())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -43,49 +42,27 @@ AutomationControl::AutomationControl(Session& session, boost::shared_ptr<Automat
|
|||
float
|
||||
AutomationControl::get_value() const
|
||||
{
|
||||
if (_list->automation_playback())
|
||||
return _list->eval(_session.transport_frame());
|
||||
else
|
||||
return _user_value;
|
||||
bool from_list = ((AutomationList*)_list.get())->automation_playback();
|
||||
return Control::get_value(from_list, _session.transport_frame());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AutomationControl::set_value(float value)
|
||||
{
|
||||
_user_value = value;
|
||||
bool to_list = _session.transport_stopped()
|
||||
&& ((AutomationList*)_list.get())->automation_playback();
|
||||
|
||||
if (_session.transport_stopped() && _list->automation_write())
|
||||
_list->add(_session.transport_frame(), value);
|
||||
Control::set_value(value, to_list, _session.transport_frame());
|
||||
|
||||
Changed(); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
|
||||
/** Get the latest user-set value, which may not equal get_value() when automation
|
||||
* is playing back, etc.
|
||||
*
|
||||
* Automation write/touch works by periodically sampling this value and adding it
|
||||
* to the AutomationList.
|
||||
*/
|
||||
float
|
||||
AutomationControl::user_value() const
|
||||
{
|
||||
return _user_value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AutomationControl::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
|
||||
AutomationControl::set_list(boost::shared_ptr<Evoral::ControlList> list)
|
||||
{
|
||||
_list = list;
|
||||
_user_value = list->default_value();
|
||||
Control::set_list(list);
|
||||
Changed(); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
|
||||
Parameter
|
||||
AutomationControl::parameter() const
|
||||
{
|
||||
return _list->parameter();
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -78,8 +78,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
|
|||
nframes_t position,
|
||||
AnchorPoint ap)
|
||||
: AudioRegion (in->session(), position, length, "foobar"),
|
||||
_fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_in (Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_out (Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
|
||||
|
||||
{
|
||||
_in = in;
|
||||
|
|
@ -95,8 +95,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
|
|||
|
||||
Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act)
|
||||
: AudioRegion (a->session(), 0, 0, "foobar"),
|
||||
_fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_in (Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_out (Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
|
||||
{
|
||||
_in_update = false;
|
||||
_fixed = false;
|
||||
|
|
@ -114,8 +114,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioR
|
|||
|
||||
Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
|
||||
: AudioRegion (playlist.session(), 0, 0, "foobar"),
|
||||
_fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_in (Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB
|
||||
_fade_out (Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
|
||||
|
||||
{
|
||||
boost::shared_ptr<Region> r;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
using namespace ARDOUR;
|
||||
|
||||
Gain::Gain ()
|
||||
: AutomationList (Parameter(GainAutomation), 0.0, 2.0, 1.0f) /* XXX yuck; clamps gain to -inf .. +6db */
|
||||
: AutomationList (Parameter(GainAutomation)) /* XXX yuck; clamps gain to -inf .. +6db */
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ static void
|
|||
write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
|
||||
vector<boost::shared_ptr<Source> >& newfiles)
|
||||
{
|
||||
MIDI::Event ev(0.0, 4, NULL, true);
|
||||
Evoral::Event ev(0.0, 4, NULL, true);
|
||||
|
||||
status.progress = 0.0f;
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ IO::IO (Session& s, const string& name,
|
|||
deferred_state = 0;
|
||||
|
||||
boost::shared_ptr<AutomationList> gl(
|
||||
new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
|
||||
new AutomationList(Parameter(GainAutomation)));
|
||||
|
||||
_gain_control = boost::shared_ptr<GainControl>(
|
||||
new GainControl(X_("gaincontrol"), *this, gl));
|
||||
|
|
@ -178,7 +178,7 @@ IO::IO (Session& s, const XMLNode& node, DataType dt)
|
|||
apply_gain_automation = false;
|
||||
|
||||
boost::shared_ptr<AutomationList> gl(
|
||||
new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
|
||||
new AutomationList(Parameter(GainAutomation)));
|
||||
|
||||
_gain_control = boost::shared_ptr<GainControl>(
|
||||
new GainControl(X_("gaincontrol"), *this, gl));
|
||||
|
|
@ -1153,7 +1153,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
|
|||
gain_t
|
||||
IO::effective_gain () const
|
||||
{
|
||||
if (_gain_control->list()->automation_playback()) {
|
||||
if (_gain_control->automation_playback()) {
|
||||
return _gain_control->get_value();
|
||||
} else {
|
||||
return _desired_gain;
|
||||
|
|
@ -2266,7 +2266,7 @@ IO::meter ()
|
|||
void
|
||||
IO::clear_automation ()
|
||||
{
|
||||
Automatable::clear_automation (); // clears gain automation
|
||||
Automatable::clear (); // clears gain automation
|
||||
_panner->clear_automation ();
|
||||
}
|
||||
|
||||
|
|
@ -2280,9 +2280,10 @@ IO::set_parameter_automation_state (Parameter param, AutoState state)
|
|||
bool changed = false;
|
||||
|
||||
{
|
||||
Glib::Mutex::Lock lm (_automation_lock);
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
|
||||
boost::shared_ptr<AutomationList> gain_auto = _gain_control->list();
|
||||
boost::shared_ptr<AutomationList> gain_auto
|
||||
= boost::dynamic_pointer_cast<AutomationList>(_gain_control->list());
|
||||
|
||||
if (state != gain_auto->automation_state()) {
|
||||
changed = true;
|
||||
|
|
@ -2337,7 +2338,7 @@ IO::set_gain (gain_t val, void *src)
|
|||
_gain = val;
|
||||
}
|
||||
|
||||
if (_session.transport_stopped() && src != 0 && src != this && _gain_control->list()->automation_write()) {
|
||||
if (_session.transport_stopped() && src != 0 && src != this && _gain_control->automation_write()) {
|
||||
_gain_control->list()->add (_session.transport_frame(), val);
|
||||
|
||||
}
|
||||
|
|
@ -2349,7 +2350,7 @@ void
|
|||
IO::start_pan_touch (uint32_t which)
|
||||
{
|
||||
if (which < _panner->size()) {
|
||||
(*_panner)[which]->pan_control()->list()->start_touch();
|
||||
(*_panner)[which]->pan_control()->start_touch();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2357,7 +2358,7 @@ void
|
|||
IO::end_pan_touch (uint32_t which)
|
||||
{
|
||||
if (which < _panner->size()) {
|
||||
(*_panner)[which]->pan_control()->list()->stop_touch();
|
||||
(*_panner)[which]->pan_control()->stop_touch();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2380,7 +2381,7 @@ IO::transport_stopped (nframes_t frame)
|
|||
{
|
||||
_gain_control->list()->reposition_for_rt_add (frame);
|
||||
|
||||
if (_gain_control->list()->automation_state() != Off) {
|
||||
if (_gain_control->automation_state() != Off) {
|
||||
|
||||
/* the src=0 condition is a special signal to not propagate
|
||||
automation gain changes into the mix group when locating.
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ JackMidiPort::cycle_end (nframes_t nframes, nframes_t offset)
|
|||
jack_midi_clear_buffer (jack_buffer);
|
||||
|
||||
for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
|
||||
const MIDI::Event& ev = *i;
|
||||
const Evoral::Event& ev = *i;
|
||||
// event times should be frames, relative to cycle start
|
||||
assert(ev.time() >= 0);
|
||||
assert(ev.time() < nframes);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_f
|
|||
// GUI needs a better MIDI meter, not much information can be
|
||||
// expressed through peaks alone
|
||||
for (MidiBuffer::iterator i = bufs.get_midi(n).begin(); i != bufs.get_midi(n).end(); ++i) {
|
||||
const MIDI::Event& ev = *i;
|
||||
const Evoral::Event& ev = *i;
|
||||
if (ev.is_note_on()) {
|
||||
const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0;
|
||||
//printf("V %d -> %f\n", (int)((Byte)ev.buffer[2]), this_vel);
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@ MidiBuffer::resize (size_t size)
|
|||
_capacity = size;
|
||||
|
||||
#ifdef NO_POSIX_MEMALIGN
|
||||
_events = (MIDI::Event *) malloc(sizeof(MIDI::Event) * _capacity);
|
||||
_events = (Evoral::Event *) malloc(sizeof(Evoral::Event) * _capacity);
|
||||
_data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
|
||||
#else
|
||||
posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(MIDI::Event) * _capacity);
|
||||
posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(Evoral::Event) * _capacity);
|
||||
posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
|
||||
#endif
|
||||
assert(_data);
|
||||
|
|
@ -115,7 +115,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
|
|||
|
||||
// FIXME: slow
|
||||
for (size_t i=0; i < msrc.size(); ++i) {
|
||||
const MIDI::Event& ev = msrc[i];
|
||||
const Evoral::Event& ev = msrc[i];
|
||||
if (ev.time() >= offset && ev.time() < offset+nframes) {
|
||||
//cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
|
||||
push_back(ev);
|
||||
|
|
@ -136,7 +136,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
|
|||
* @return false if operation failed (not enough room)
|
||||
*/
|
||||
bool
|
||||
MidiBuffer::push_back(const MIDI::Event& ev)
|
||||
MidiBuffer::push_back(const Evoral::Event& ev)
|
||||
{
|
||||
if (_size == _capacity)
|
||||
return false;
|
||||
|
|
@ -223,7 +223,7 @@ MidiBuffer::silence(nframes_t dur, nframes_t offset)
|
|||
if (offset != 0)
|
||||
cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
|
||||
|
||||
memset(_events, 0, sizeof(MIDI::Event) * _capacity);
|
||||
memset(_events, 0, sizeof(Evoral::Event) * _capacity);
|
||||
memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
|
||||
_size = 0;
|
||||
_silent = true;
|
||||
|
|
@ -262,8 +262,8 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
|
|||
push_back(b[b_index]);
|
||||
++b_index;
|
||||
} else {
|
||||
const MIDI::Event& a_ev = a[a_index];
|
||||
const MIDI::Event& b_ev = b[b_index];
|
||||
const Evoral::Event& a_ev = a[a_index];
|
||||
const Evoral::Event& b_ev = b[b_index];
|
||||
|
||||
if (a_ev.time() <= b_ev.time()) {
|
||||
push_back(a_ev);
|
||||
|
|
|
|||
|
|
@ -520,7 +520,7 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
|
|||
MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
|
||||
|
||||
for (size_t i=0; i < to_write; ++i) {
|
||||
const MIDI::Event& ev = *port_iter;
|
||||
const Evoral::Event& ev = *port_iter;
|
||||
assert(ev.buffer());
|
||||
_capture_buf->write(ev.time() + transport_frame, ev.size(), ev.buffer());
|
||||
++port_iter;
|
||||
|
|
|
|||
|
|
@ -35,655 +35,15 @@
|
|||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
|
||||
void MidiModel::write_lock() {
|
||||
_lock.writer_lock();
|
||||
_automation_lock.lock();
|
||||
}
|
||||
|
||||
void MidiModel::write_unlock() {
|
||||
_lock.writer_unlock();
|
||||
_automation_lock.unlock();
|
||||
}
|
||||
|
||||
void MidiModel::read_lock() const {
|
||||
_lock.reader_lock();
|
||||
/*_automation_lock.lock();*/
|
||||
}
|
||||
|
||||
void MidiModel::read_unlock() const {
|
||||
_lock.reader_unlock();
|
||||
/*_automation_lock.unlock();*/
|
||||
}
|
||||
|
||||
// Read iterator (const_iterator)
|
||||
|
||||
MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
|
||||
: _model(&model)
|
||||
, _is_end( (t == DBL_MAX) || model.empty() )
|
||||
, _locked( !_is_end )
|
||||
{
|
||||
//cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
|
||||
|
||||
if (_is_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.read_lock();
|
||||
|
||||
_note_iter = model.notes().end();
|
||||
// find first note which begins after t
|
||||
for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
|
||||
if ((*i)->time() >= t) {
|
||||
_note_iter = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MidiControlIterator earliest_control(boost::shared_ptr<AutomationList>(), DBL_MAX, 0.0);
|
||||
|
||||
_control_iters.reserve(model.controls().size());
|
||||
|
||||
// find the earliest control event available
|
||||
for (Automatable::Controls::const_iterator i = model.controls().begin();
|
||||
i != model.controls().end(); ++i) {
|
||||
|
||||
assert(
|
||||
i->first.type() == MidiCCAutomation ||
|
||||
i->first.type() == MidiPgmChangeAutomation ||
|
||||
i->first.type() == MidiPitchBenderAutomation ||
|
||||
i->first.type() == MidiChannelAftertouchAutomation);
|
||||
|
||||
double x, y;
|
||||
bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
|
||||
if (!ret) {
|
||||
//cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
|
||||
// << ") has no events past " << t << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(x >= 0);
|
||||
|
||||
if (y < i->first.min() || y > i->first.max()) {
|
||||
cerr << "ERROR: Controller (" << i->first.to_string() << ") value '" << y
|
||||
<< "' out of range [" << i->first.min() << "," << i->first.max()
|
||||
<< "], event ignored" << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
const MidiControlIterator new_iter(i->second->list(), x, y);
|
||||
|
||||
//cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
|
||||
_control_iters.push_back(new_iter);
|
||||
|
||||
// if the x of the current control is less than earliest_control
|
||||
// we have a new earliest_control
|
||||
if (x < earliest_control.x) {
|
||||
earliest_control = new_iter;
|
||||
_control_iter = _control_iters.end();
|
||||
--_control_iter;
|
||||
// now _control_iter points to the last Element in _control_iters
|
||||
}
|
||||
}
|
||||
|
||||
if (_note_iter != model.notes().end()) {
|
||||
_event = boost::shared_ptr<MIDI::Event>(new MIDI::Event((*_note_iter)->on_event(), true));
|
||||
}
|
||||
|
||||
double time = DBL_MAX;
|
||||
// in case we have no notes in the region, we still want to get controller messages
|
||||
if (_event.get()) {
|
||||
time = _event->time();
|
||||
// if the note is going to make it this turn, advance _note_iter
|
||||
if (earliest_control.x > time) {
|
||||
_active_notes.push(*_note_iter);
|
||||
++_note_iter;
|
||||
}
|
||||
}
|
||||
|
||||
// <=, because we probably would want to send control events first
|
||||
if (earliest_control.automation_list.get() && earliest_control.x <= time) {
|
||||
model.control_to_midi_event(_event, earliest_control);
|
||||
} else {
|
||||
_control_iter = _control_iters.end();
|
||||
}
|
||||
|
||||
if ( (! _event.get()) || _event->size() == 0) {
|
||||
//cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
|
||||
_is_end = true;
|
||||
|
||||
// eliminate possible race condition here (ugly)
|
||||
static Glib::Mutex mutex;
|
||||
Glib::Mutex::Lock lock(mutex);
|
||||
if (_locked) {
|
||||
_model->read_unlock();
|
||||
_locked = false;
|
||||
}
|
||||
} else {
|
||||
//printf("New MIDI Iterator = %X @ %lf\n", _event->type(), _event->time());
|
||||
}
|
||||
|
||||
assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0'));
|
||||
}
|
||||
|
||||
MidiModel::const_iterator::~const_iterator()
|
||||
{
|
||||
if (_locked) {
|
||||
_model->read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
const MidiModel::const_iterator& MidiModel::const_iterator::operator++()
|
||||
{
|
||||
if (_is_end) {
|
||||
throw std::logic_error("Attempt to iterate past end of MidiModel");
|
||||
}
|
||||
|
||||
assert(_event->buffer() && _event->buffer()[0] != '\0');
|
||||
|
||||
/*cerr << "const_iterator::operator++: " << _event->to_string() << endl;*/
|
||||
|
||||
if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
|
||||
cerr << "FAILED event buffer: " << hex << int(_event->buffer()[0]) << int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl;
|
||||
}
|
||||
assert((_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()));
|
||||
|
||||
// Increment past current control event
|
||||
if (!_event->is_note() && _control_iter != _control_iters.end() && _control_iter->automation_list.get()) {
|
||||
double x = 0.0, y = 0.0;
|
||||
const bool ret = _control_iter->automation_list->rt_safe_earliest_event_unlocked(
|
||||
_control_iter->x, DBL_MAX, x, y, false);
|
||||
|
||||
if (ret) {
|
||||
_control_iter->x = x;
|
||||
_control_iter->y = y;
|
||||
} else {
|
||||
_control_iter->automation_list.reset();
|
||||
_control_iter->x = DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<MidiControlIterator>::iterator old_control_iter = _control_iter;
|
||||
_control_iter = _control_iters.begin();
|
||||
|
||||
// find the _control_iter with the earliest event time
|
||||
for (std::vector<MidiControlIterator>::iterator i = _control_iters.begin();
|
||||
i != _control_iters.end(); ++i) {
|
||||
if (i->x < _control_iter->x) {
|
||||
_control_iter = i;
|
||||
}
|
||||
}
|
||||
|
||||
enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
|
||||
|
||||
Type type = NIL;
|
||||
double t = 0;
|
||||
|
||||
// Next earliest note on
|
||||
if (_note_iter != _model->notes().end()) {
|
||||
type = NOTE_ON;
|
||||
t = (*_note_iter)->time();
|
||||
}
|
||||
|
||||
// Use the next earliest note off iff it's earlier than the note on
|
||||
if (_model->note_mode() == Sustained && (! _active_notes.empty())) {
|
||||
if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
|
||||
type = NOTE_OFF;
|
||||
t = _active_notes.top()->end_time();
|
||||
}
|
||||
}
|
||||
|
||||
// Use the next earliest controller iff it's earlier than the note event
|
||||
if (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX /*&& _control_iter != old_control_iter */) {
|
||||
if (type == NIL || _control_iter->x < t) {
|
||||
type = AUTOMATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == NOTE_ON) {
|
||||
//cerr << "********** MIDI Iterator = note on" << endl;
|
||||
*_event = (*_note_iter)->on_event();
|
||||
cerr << "Event contents on note on: " << _event->to_string() << endl;
|
||||
_active_notes.push(*_note_iter);
|
||||
++_note_iter;
|
||||
} else if (type == NOTE_OFF) {
|
||||
//cerr << "********** MIDI Iterator = note off" << endl;
|
||||
*_event = _active_notes.top()->off_event();
|
||||
_active_notes.pop();
|
||||
} else if (type == AUTOMATION) {
|
||||
//cerr << "********** MIDI Iterator = Automation" << endl;
|
||||
_model->control_to_midi_event(_event, *_control_iter);
|
||||
} else {
|
||||
//cerr << "********** MIDI Iterator = End" << endl;
|
||||
_is_end = true;
|
||||
}
|
||||
|
||||
assert(_is_end || _event->size() > 0);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool MidiModel::const_iterator::operator==(const const_iterator& other) const
|
||||
{
|
||||
if (_is_end || other._is_end) {
|
||||
return (_is_end == other._is_end);
|
||||
} else {
|
||||
return (_event == other._event);
|
||||
}
|
||||
}
|
||||
|
||||
MidiModel::const_iterator& MidiModel::const_iterator::operator=(const const_iterator& other)
|
||||
{
|
||||
if (_locked && _model != other._model) {
|
||||
_model->read_unlock();
|
||||
}
|
||||
|
||||
_model = other._model;
|
||||
_active_notes = other._active_notes;
|
||||
_is_end = other._is_end;
|
||||
_locked = other._locked;
|
||||
_note_iter = other._note_iter;
|
||||
_control_iters = other._control_iters;
|
||||
size_t index = other._control_iter - other._control_iters.begin();
|
||||
_control_iter = _control_iters.begin() + index;
|
||||
|
||||
if (!_is_end) {
|
||||
_event = boost::shared_ptr<MIDI::Event>(new MIDI::Event(*other._event, true));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// MidiModel
|
||||
|
||||
MidiModel::MidiModel(MidiSource *s, size_t size)
|
||||
: Automatable(s->session(), "midi model")
|
||||
, _notes(size)
|
||||
, _note_mode(Sustained)
|
||||
, _writing(false)
|
||||
, _edited(false)
|
||||
, _end_iter(*this, DBL_MAX)
|
||||
, _next_read(UINT32_MAX)
|
||||
, _read_iter(*this, DBL_MAX)
|
||||
: ControlSet()
|
||||
, Automatable(s->session(), "midi model")
|
||||
, Sequence(size)
|
||||
, _midi_source(s)
|
||||
{
|
||||
assert(_end_iter._is_end);
|
||||
assert( ! _end_iter._locked);
|
||||
}
|
||||
|
||||
/** Read events in frame range \a start .. \a start+cnt into \a dst,
|
||||
* adding \a stamp_offset to each event's timestamp.
|
||||
* \return number of events written to \a dst
|
||||
*/
|
||||
size_t MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes,
|
||||
nframes_t stamp_offset, nframes_t negative_stamp_offset) const
|
||||
{
|
||||
//cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
|
||||
//cerr << this << " MM # notes: " << n_notes() << endl;
|
||||
|
||||
size_t read_events = 0;
|
||||
|
||||
if (start != _next_read) {
|
||||
_read_iter = const_iterator(*this, (double)start);
|
||||
//cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
||||
} else {
|
||||
//cerr << "Using cached iterator at " << _next_read << endl;
|
||||
}
|
||||
|
||||
_next_read = start + nframes;
|
||||
|
||||
while (_read_iter != end() && _read_iter->time() < start + nframes) {
|
||||
assert(_read_iter->size() > 0);
|
||||
assert(_read_iter->buffer());
|
||||
dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset,
|
||||
_read_iter->size(),
|
||||
_read_iter->buffer());
|
||||
|
||||
/*cerr << this << " MidiModel::read event @ " << _read_iter->time()
|
||||
<< " type: " << hex << int(_read_iter->type()) << dec
|
||||
<< " note: " << int(_read_iter->note())
|
||||
<< " velocity: " << int(_read_iter->velocity())
|
||||
<< endl;*/
|
||||
|
||||
++_read_iter;
|
||||
++read_events;
|
||||
}
|
||||
|
||||
return read_events;
|
||||
}
|
||||
|
||||
/** Write the controller event pointed to by \a iter to \a ev.
|
||||
* The buffer of \a ev will be allocated or resized as necessary.
|
||||
* \return true on success
|
||||
*/
|
||||
bool
|
||||
MidiModel::control_to_midi_event(boost::shared_ptr<MIDI::Event>& ev, const MidiControlIterator& iter) const
|
||||
{
|
||||
assert(iter.automation_list.get());
|
||||
if (!ev) {
|
||||
ev = boost::shared_ptr<MIDI::Event>(new MIDI::Event(0, 3, NULL, true));
|
||||
}
|
||||
|
||||
switch (iter.automation_list->parameter().type()) {
|
||||
case MidiCCAutomation:
|
||||
assert(iter.automation_list.get());
|
||||
assert(iter.automation_list->parameter().channel() < 16);
|
||||
assert(iter.automation_list->parameter().id() <= INT8_MAX);
|
||||
assert(iter.y <= INT8_MAX);
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_CONTROL + iter.automation_list->parameter().channel();
|
||||
ev->buffer()[1] = (uint8_t)iter.automation_list->parameter().id();
|
||||
ev->buffer()[2] = (uint8_t)iter.y;
|
||||
break;
|
||||
|
||||
case MidiPgmChangeAutomation:
|
||||
assert(iter.automation_list.get());
|
||||
assert(iter.automation_list->parameter().channel() < 16);
|
||||
assert(iter.automation_list->parameter().id() == 0);
|
||||
assert(iter.y <= INT8_MAX);
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(2);
|
||||
ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.automation_list->parameter().channel();
|
||||
ev->buffer()[1] = (uint8_t)iter.y;
|
||||
break;
|
||||
|
||||
case MidiPitchBenderAutomation:
|
||||
assert(iter.automation_list.get());
|
||||
assert(iter.automation_list->parameter().channel() < 16);
|
||||
assert(iter.automation_list->parameter().id() == 0);
|
||||
assert(iter.y < (1<<14));
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_BENDER + iter.automation_list->parameter().channel();
|
||||
ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB
|
||||
ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
|
||||
//cerr << "Pitch bender event: " << ev->to_string() << " value: " << ev->pitch_bender_value() << " original value: " << iter.y << std::endl;
|
||||
break;
|
||||
|
||||
case MidiChannelAftertouchAutomation:
|
||||
assert(iter.automation_list.get());
|
||||
assert(iter.automation_list->parameter().channel() < 16);
|
||||
assert(iter.automation_list->parameter().id() == 0);
|
||||
assert(iter.y <= INT8_MAX);
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(2);
|
||||
ev->buffer()[0]
|
||||
= MIDI_CMD_CHANNEL_PRESSURE + iter.automation_list->parameter().channel();
|
||||
ev->buffer()[1] = (uint8_t)iter.y;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Clear all events from the model.
|
||||
*/
|
||||
void MidiModel::clear()
|
||||
{
|
||||
_lock.writer_lock();
|
||||
_notes.clear();
|
||||
clear_automation();
|
||||
_next_read = 0;
|
||||
_read_iter = end();
|
||||
_lock.writer_unlock();
|
||||
}
|
||||
|
||||
|
||||
/** Begin a write of events to the model.
|
||||
*
|
||||
* If \a mode is Sustained, complete notes with duration are constructed as note
|
||||
* on/off events are received. Otherwise (Percussive), only note on events are
|
||||
* stored; note off events are discarded entirely and all contained notes will
|
||||
* have duration 0.
|
||||
*/
|
||||
void MidiModel::start_write()
|
||||
{
|
||||
//cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
|
||||
write_lock();
|
||||
_writing = true;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
_write_notes[i].clear();
|
||||
|
||||
_dirty_automations.clear();
|
||||
write_unlock();
|
||||
}
|
||||
|
||||
/** Finish a write of events to the model.
|
||||
*
|
||||
* If \a delete_stuck is true and the current mode is Sustained, note on events
|
||||
* that were never resolved with a corresonding note off will be deleted.
|
||||
* Otherwise they will remain as notes with duration 0.
|
||||
*/
|
||||
void MidiModel::end_write(bool delete_stuck)
|
||||
{
|
||||
write_lock();
|
||||
assert(_writing);
|
||||
|
||||
//cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
|
||||
|
||||
if (_note_mode == Sustained && delete_stuck) {
|
||||
for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
|
||||
if ((*n)->duration() == 0) {
|
||||
cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
|
||||
n = _notes.erase(n);
|
||||
// we have to break here because erase invalidates the iterator
|
||||
break;
|
||||
} else {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (!_write_notes[i].empty()) {
|
||||
cerr << "WARNING: MidiModel::end_write: Channel " << i << " has "
|
||||
<< _write_notes[i].size() << " stuck notes" << endl;
|
||||
}
|
||||
_write_notes[i].clear();
|
||||
}
|
||||
|
||||
for (AutomationLists::const_iterator i = _dirty_automations.begin(); i != _dirty_automations.end(); ++i) {
|
||||
(*i)->Dirty.emit();
|
||||
(*i)->lookup_cache().left = -1;
|
||||
(*i)->search_cache().left = -1;
|
||||
}
|
||||
|
||||
_writing = false;
|
||||
write_unlock();
|
||||
}
|
||||
|
||||
/** Append \a in_event to model. NOT realtime safe.
|
||||
*
|
||||
* Timestamps of events in \a buf are expected to be relative to
|
||||
* the start of this model (t=0) and MUST be monotonically increasing
|
||||
* and MUST be >= the latest event currently in the model.
|
||||
*/
|
||||
void MidiModel::append(const MIDI::Event& ev)
|
||||
{
|
||||
write_lock();
|
||||
_edited = true;
|
||||
|
||||
assert(_notes.empty() || ev.time() >= _notes.back()->time());
|
||||
assert(_writing);
|
||||
|
||||
if (ev.is_note_on()) {
|
||||
append_note_on_unlocked(ev.channel(), ev.time(), ev.note(),
|
||||
ev.velocity());
|
||||
} else if (ev.is_note_off()) {
|
||||
append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
|
||||
} else if (ev.is_cc()) {
|
||||
append_automation_event_unlocked(MidiCCAutomation, ev.channel(),
|
||||
ev.time(), ev.cc_number(), ev.cc_value());
|
||||
} else if (ev.is_pgm_change()) {
|
||||
append_automation_event_unlocked(MidiPgmChangeAutomation, ev.channel(),
|
||||
ev.time(), ev.pgm_number(), 0);
|
||||
} else if (ev.is_pitch_bender()) {
|
||||
append_automation_event_unlocked(MidiPitchBenderAutomation,
|
||||
ev.channel(), ev.time(), ev.pitch_bender_lsb(),
|
||||
ev.pitch_bender_msb());
|
||||
} else if (ev.is_channel_aftertouch()) {
|
||||
append_automation_event_unlocked(MidiChannelAftertouchAutomation,
|
||||
ev.channel(), ev.time(), ev.channel_aftertouch(), 0);
|
||||
} else {
|
||||
printf("WARNING: MidiModel: Unknown event type %X\n", ev.type());
|
||||
}
|
||||
|
||||
write_unlock();
|
||||
}
|
||||
|
||||
void MidiModel::append_note_on_unlocked(uint8_t chan, double time,
|
||||
uint8_t note_num, uint8_t velocity)
|
||||
{
|
||||
/*cerr << "MidiModel " << this << " chan " << (int)chan <<
|
||||
" note " << (int)note_num << " on @ " << time << endl;*/
|
||||
|
||||
assert(note_num <= 127);
|
||||
assert(chan < 16);
|
||||
assert(_writing);
|
||||
_edited = true;
|
||||
|
||||
boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
|
||||
_notes.push_back(new_note);
|
||||
if (_note_mode == Sustained) {
|
||||
//cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
|
||||
_write_notes[chan].push_back(_notes.size() - 1);
|
||||
}/* else {
|
||||
cerr << "MM Percussive: NOT appending active note on" << endl;
|
||||
}*/
|
||||
}
|
||||
|
||||
void MidiModel::append_note_off_unlocked(uint8_t chan, double time,
|
||||
uint8_t note_num)
|
||||
{
|
||||
/*cerr << "MidiModel " << this << " chan " << (int)chan <<
|
||||
" note " << (int)note_num << " off @ " << time << endl;*/
|
||||
|
||||
assert(note_num <= 127);
|
||||
assert(chan < 16);
|
||||
assert(_writing);
|
||||
_edited = true;
|
||||
|
||||
if (_note_mode == Percussive) {
|
||||
cerr << "MidiModel Ignoring note off (percussive mode)" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: make _write_notes fixed size (127 noted) for speed */
|
||||
|
||||
/* FIXME: note off velocity for that one guy out there who actually has
|
||||
* keys that send it */
|
||||
|
||||
bool resolved = false;
|
||||
|
||||
for (WriteNotes::iterator n = _write_notes[chan].begin(); n
|
||||
!= _write_notes[chan].end(); ++n) {
|
||||
Note& note = *_notes[*n].get();
|
||||
if (note.note() == note_num) {
|
||||
assert(time >= note.time());
|
||||
note.set_duration(time - note.time());
|
||||
_write_notes[chan].erase(n);
|
||||
//cerr << "MM resolved note, duration: " << note.duration() << endl;
|
||||
resolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
cerr << "MidiModel " << this << " spurious note off chan " << (int)chan
|
||||
<< ", note " << (int)note_num << " @ " << time << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiModel::append_automation_event_unlocked(AutomationType type,
|
||||
uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte)
|
||||
{
|
||||
//cerr << "MidiModel " << this << " chan " << (int)chan <<
|
||||
// " CC " << (int)number << " = " << (int)value << " @ " << time << endl;
|
||||
|
||||
assert(chan < 16);
|
||||
assert(_writing);
|
||||
_edited = true;
|
||||
double value;
|
||||
|
||||
uint32_t id = 0;
|
||||
|
||||
switch (type) {
|
||||
case MidiCCAutomation:
|
||||
id = first_byte;
|
||||
value = double(second_byte);
|
||||
break;
|
||||
case MidiChannelAftertouchAutomation:
|
||||
case MidiPgmChangeAutomation:
|
||||
id = 0;
|
||||
value = double(first_byte);
|
||||
break;
|
||||
case MidiPitchBenderAutomation:
|
||||
id = 0;
|
||||
value = double((0x7F & second_byte) << 7 | (0x7F & first_byte));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
Parameter param(type, id, chan);
|
||||
boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
|
||||
control->list()->rt_add(time, value);
|
||||
}
|
||||
|
||||
void MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
|
||||
{
|
||||
//cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
|
||||
_edited = true;
|
||||
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note,
|
||||
note_time_comparator);
|
||||
_notes.insert(i, note);
|
||||
}
|
||||
|
||||
void MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
|
||||
{
|
||||
_edited = true;
|
||||
//cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
|
||||
for (Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
|
||||
Note& _n = *(*n);
|
||||
const Note& _note = *note;
|
||||
// TODO: There is still the issue, that after restarting ardour
|
||||
// persisted undo does not work, because of rounding errors in the
|
||||
// event times after saving/restoring to/from MIDI files
|
||||
/*cerr << "======================================= " << endl;
|
||||
cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl;
|
||||
cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl;
|
||||
cerr << "Equal: " << bool(_n == _note) << endl;
|
||||
cerr << endl << endl;*/
|
||||
if (_n == _note) {
|
||||
_notes.erase(n);
|
||||
// we have to break here, because erase invalidates all iterators, ie. n itself
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Slow! for debugging only. */
|
||||
#ifndef NDEBUG
|
||||
bool MidiModel::is_sorted() const {
|
||||
bool t = 0;
|
||||
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
|
||||
if ((*n)->time() < t)
|
||||
return false;
|
||||
else
|
||||
t = (*n)->time();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Start a new command.
|
||||
*
|
||||
* This has no side-effects on the model or Session, the returned command
|
||||
|
|
@ -701,7 +61,8 @@ MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name)
|
|||
* Ownership of cmd is taken, it must not be deleted by the caller.
|
||||
* The command will constitute one item on the undo stack.
|
||||
*/
|
||||
void MidiModel::apply_command(Command* cmd)
|
||||
void
|
||||
MidiModel::apply_command(Command* cmd)
|
||||
{
|
||||
_session.begin_reversible_command(cmd->name());
|
||||
(*cmd)();
|
||||
|
|
@ -710,7 +71,8 @@ void MidiModel::apply_command(Command* cmd)
|
|||
_edited = true;
|
||||
}
|
||||
|
||||
// MidiEditCommand
|
||||
|
||||
// DeltaCommand
|
||||
|
||||
MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
|
||||
const std::string& name)
|
||||
|
|
@ -727,21 +89,24 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
|
|||
set_state(node);
|
||||
}
|
||||
|
||||
void MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note)
|
||||
void
|
||||
MidiModel::DeltaCommand::add(const boost::shared_ptr<Evoral::Note> note)
|
||||
{
|
||||
//cerr << "MEC: apply" << endl;
|
||||
_removed_notes.remove(note);
|
||||
_added_notes.push_back(note);
|
||||
}
|
||||
|
||||
void MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note)
|
||||
void
|
||||
MidiModel::DeltaCommand::remove(const boost::shared_ptr<Evoral::Note> note)
|
||||
{
|
||||
//cerr << "MEC: remove" << endl;
|
||||
_added_notes.remove(note);
|
||||
_removed_notes.push_back(note);
|
||||
}
|
||||
|
||||
void MidiModel::DeltaCommand::operator()()
|
||||
void
|
||||
MidiModel::DeltaCommand::operator()()
|
||||
{
|
||||
// This could be made much faster by using a priority_queue for added and
|
||||
// removed notes (or sort here), and doing a single iteration over _model
|
||||
|
|
@ -763,10 +128,10 @@ void MidiModel::DeltaCommand::operator()()
|
|||
|
||||
_model->write_lock();
|
||||
|
||||
for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
|
||||
for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
|
||||
_model->add_note_unlocked(*i);
|
||||
|
||||
for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
|
||||
for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
|
||||
_model->remove_note_unlocked(*i);
|
||||
|
||||
_model->write_unlock();
|
||||
|
|
@ -778,7 +143,8 @@ void MidiModel::DeltaCommand::operator()()
|
|||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void MidiModel::DeltaCommand::undo()
|
||||
void
|
||||
MidiModel::DeltaCommand::undo()
|
||||
{
|
||||
// This could be made much faster by using a priority_queue for added and
|
||||
// removed notes (or sort here), and doing a single iteration over _model
|
||||
|
|
@ -800,11 +166,11 @@ void MidiModel::DeltaCommand::undo()
|
|||
|
||||
_model->write_lock();
|
||||
|
||||
for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i
|
||||
for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i = _added_notes.begin(); i
|
||||
!= _added_notes.end(); ++i)
|
||||
_model->remove_note_unlocked(*i);
|
||||
|
||||
for (std::list< boost::shared_ptr<Note> >::iterator i =
|
||||
for (std::list< boost::shared_ptr<Evoral::Note> >::iterator i =
|
||||
_removed_notes.begin(); i != _removed_notes.end(); ++i)
|
||||
_model->add_note_unlocked(*i);
|
||||
|
||||
|
|
@ -817,7 +183,8 @@ void MidiModel::DeltaCommand::undo()
|
|||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
XMLNode & MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> note)
|
||||
XMLNode&
|
||||
MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Evoral::Note> note)
|
||||
{
|
||||
XMLNode *xml_note = new XMLNode("note");
|
||||
ostringstream note_str(ios::ate);
|
||||
|
|
@ -843,7 +210,7 @@ XMLNode & MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> no
|
|||
return *xml_note;
|
||||
}
|
||||
|
||||
boost::shared_ptr<Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
|
||||
boost::shared_ptr<Evoral::Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
|
||||
{
|
||||
unsigned int note;
|
||||
istringstream note_str(xml_note->property("note")->value());
|
||||
|
|
@ -865,7 +232,7 @@ boost::shared_ptr<Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_not
|
|||
istringstream velocity_str(xml_note->property("velocity")->value());
|
||||
velocity_str >> velocity;
|
||||
|
||||
boost::shared_ptr<Note> note_ptr(new Note(channel, time, duration, note, velocity));
|
||||
boost::shared_ptr<Evoral::Note> note_ptr(new Evoral::Note(channel, time, duration, note, velocity));
|
||||
return note_ptr;
|
||||
}
|
||||
|
||||
|
|
@ -913,8 +280,8 @@ XMLNode& MidiModel::DeltaCommand::get_state()
|
|||
}
|
||||
|
||||
struct EventTimeComparator {
|
||||
typedef const MIDI::Event* value_type;
|
||||
inline bool operator()(const MIDI::Event& a, const MIDI::Event& b) const {
|
||||
typedef const Evoral::Event* value_type;
|
||||
inline bool operator()(const Evoral::Event& a, const Evoral::Event& b) const {
|
||||
return a.time() >= b.time();
|
||||
}
|
||||
};
|
||||
|
|
@ -930,14 +297,14 @@ bool MidiModel::write_to(boost::shared_ptr<MidiSource> source)
|
|||
{
|
||||
read_lock();
|
||||
|
||||
const NoteMode old_note_mode = _note_mode;
|
||||
_note_mode = Sustained;
|
||||
const bool old_percussive = percussive();
|
||||
set_percussive(false);
|
||||
|
||||
for (const_iterator i = begin(); i != end(); ++i) {
|
||||
source->append_event_unlocked(Frames, *i);
|
||||
}
|
||||
|
||||
_note_mode = old_note_mode;
|
||||
set_percussive(old_percussive);
|
||||
|
||||
read_unlock();
|
||||
_edited = false;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nfra
|
|||
Glib::Mutex::Lock lm (_lock);
|
||||
if (_model) {
|
||||
//const size_t n_events =
|
||||
_model->read(dst, start, cnt, stamp_offset, negative_stamp_offset);
|
||||
_model->read(dst, start, cnt, stamp_offset - negative_stamp_offset);
|
||||
//cout << "Read " << n_events << " events from model." << endl;
|
||||
return cnt;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ MidiStretch::run (boost::shared_ptr<Region> r)
|
|||
const double new_time = i->time() * _request.time_fraction;
|
||||
|
||||
// FIXME: double copy
|
||||
MIDI::Event ev = MIDI::Event(*i, true);
|
||||
Evoral::Event ev = Evoral::Event(*i, true);
|
||||
ev.time() = new_time;
|
||||
new_model->append(ev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -590,7 +590,7 @@ MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_fra
|
|||
|
||||
uint8_t buf[3]; // CC = 3 bytes
|
||||
buf[0] = MIDI_CMD_CONTROL;
|
||||
MIDI::Event ev(0, 3, buf, false);
|
||||
Evoral::Event ev(0, 3, buf, false);
|
||||
|
||||
// Write track controller automation
|
||||
// This now lives in MidiModel. Any need for track automation like this?
|
||||
|
|
@ -733,7 +733,7 @@ MidiTrack::MidiControl::set_value(float val)
|
|||
assert(val <= _list->parameter().max());
|
||||
size_t size = 3;
|
||||
|
||||
if ( ! _list->automation_playback()) {
|
||||
if ( ! automation_playback()) {
|
||||
uint8_t ev[3] = { _list->parameter().channel(), int(val), 0 };
|
||||
switch(_list->parameter().type()) {
|
||||
case MidiCCAutomation:
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
|
|||
|
||||
/* now that we are done loading */
|
||||
|
||||
_control->list()->StateChanged ();
|
||||
((AutomationList*)_control->list().get())->StateChanged ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -486,7 +486,7 @@ EqualPowerStereoPanner::state (bool full_state)
|
|||
root->add_property (X_("type"), EqualPowerStereoPanner::name);
|
||||
|
||||
XMLNode* autonode = new XMLNode (X_("Automation"));
|
||||
autonode->add_child_nocopy (_control->list()->state (full_state));
|
||||
autonode->add_child_nocopy (((AutomationList*)_control->list().get())->state (full_state));
|
||||
root->add_child_nocopy (*autonode);
|
||||
|
||||
StreamPanner::add_state (*root);
|
||||
|
|
@ -519,9 +519,9 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
|
|||
|
||||
} else if ((*iter)->name() == X_("Automation")) {
|
||||
|
||||
_control->list()->set_state (*((*iter)->children().front()));
|
||||
_control->alist()->set_state (*((*iter)->children().front()));
|
||||
|
||||
if (_control->list()->automation_state() != Off) {
|
||||
if (_control->alist()->automation_state() != Off) {
|
||||
set_position (_control->list()->eval (parent.session().transport_frame()));
|
||||
}
|
||||
}
|
||||
|
|
@ -917,7 +917,7 @@ void
|
|||
Panner::set_automation_style (AutoStyle style)
|
||||
{
|
||||
for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
|
||||
(*i)->pan_control()->list()->set_automation_style (style);
|
||||
((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
|
||||
}
|
||||
_session.set_dirty ();
|
||||
}
|
||||
|
|
@ -926,7 +926,7 @@ void
|
|||
Panner::set_automation_state (AutoState state)
|
||||
{
|
||||
for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
|
||||
(*i)->pan_control()->list()->set_automation_state (state);
|
||||
((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
|
||||
}
|
||||
_session.set_dirty ();
|
||||
}
|
||||
|
|
@ -935,7 +935,7 @@ AutoState
|
|||
Panner::automation_state () const
|
||||
{
|
||||
if (!empty()) {
|
||||
return front()->pan_control()->list()->automation_state ();
|
||||
return ((AutomationList*)front()->pan_control()->list().get())->automation_state ();
|
||||
} else {
|
||||
return Off;
|
||||
}
|
||||
|
|
@ -945,7 +945,7 @@ AutoStyle
|
|||
Panner::automation_style () const
|
||||
{
|
||||
if (!empty()) {
|
||||
return front()->pan_control()->list()->automation_style ();
|
||||
return ((AutomationList*)front()->pan_control()->list().get())->automation_style ();
|
||||
} else {
|
||||
return Absolute;
|
||||
}
|
||||
|
|
@ -955,7 +955,7 @@ void
|
|||
Panner::transport_stopped (nframes_t frame)
|
||||
{
|
||||
for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
|
||||
(*i)->pan_control()->list()->reposition_for_rt_add (frame);
|
||||
((AutomationList*)(*i)->pan_control()->list().get())->reposition_for_rt_add (frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -963,7 +963,7 @@ void
|
|||
Panner::snapshot (nframes_t now)
|
||||
{
|
||||
for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
|
||||
boost::shared_ptr<AutomationList> list = (*i)->pan_control()->list();
|
||||
AutomationList* list = ((AutomationList*)(*i)->pan_control()->list().get());
|
||||
if (list->automation_write())
|
||||
list->rt_add(now, (*i)->pan_control()->get_value());
|
||||
}
|
||||
|
|
@ -1127,7 +1127,7 @@ bool
|
|||
Panner::touching () const
|
||||
{
|
||||
for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) {
|
||||
if ((*i)->pan_control()->list()->touching ()) {
|
||||
if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ using namespace ARDOUR;
|
|||
* (AutomationList automation-id property)
|
||||
*/
|
||||
Parameter::Parameter(const std::string& str)
|
||||
: _type(NullAutomation)
|
||||
, _id(0)
|
||||
, _channel(0)
|
||||
: Evoral::Parameter (NullAutomation, 0)
|
||||
{
|
||||
if (str == "gain") {
|
||||
_type = GainAutomation;
|
||||
|
|
@ -80,6 +78,8 @@ Parameter::Parameter(const std::string& str)
|
|||
} else {
|
||||
PBD::warning << "Unknown Parameter '" << str << "'" << endmsg;
|
||||
}
|
||||
|
||||
init((AutomationType)_type); // set min/max/normal
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ Parameter::Parameter(const std::string& str)
|
|||
* e.g. <AutomationList automation-id="whatthisreturns">
|
||||
*/
|
||||
std::string
|
||||
Parameter::to_string() const
|
||||
Parameter::symbol() const
|
||||
{
|
||||
if (_type == GainAutomation) {
|
||||
return "gain";
|
||||
|
|
|
|||
|
|
@ -152,9 +152,10 @@ PluginInsert::auto_state_changed (Parameter which)
|
|||
if (which.type() != PluginAutomation)
|
||||
return;
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control (which);
|
||||
boost::shared_ptr<AutomationControl> c
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(control (which));
|
||||
|
||||
if (c && c->list()->automation_state() != Off) {
|
||||
if (c && ((AutomationList*)c->list().get())->automation_state() != Off) {
|
||||
_plugins[0]->set_parameter (which.id(), c->list()->eval (_session.transport_frame()));
|
||||
}
|
||||
}
|
||||
|
|
@ -225,15 +226,10 @@ PluginInsert::set_automatable ()
|
|||
if (i->type() == PluginAutomation) {
|
||||
can_automate (*i);
|
||||
_plugins.front()->get_parameter_descriptor(i->id(), desc);
|
||||
boost::shared_ptr<AutomationList> list(new AutomationList(
|
||||
*i,
|
||||
//(desc.min_unbound ? FLT_MIN : desc.lower),
|
||||
//(desc.max_unbound ? FLT_MAX : desc.upper),
|
||||
desc.lower, desc.upper,
|
||||
_plugins.front()->default_value(i->id())));
|
||||
|
||||
add_control(boost::shared_ptr<AutomationControl>(
|
||||
new PluginControl(*this, list)));
|
||||
Parameter param(*i);
|
||||
param.set_range(desc.lower, desc.upper, _plugins.front()->default_value(i->id()));
|
||||
boost::shared_ptr<AutomationList> list(new AutomationList(param));
|
||||
add_control(boost::shared_ptr<AutomationControl>(new PluginControl(*this, list)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -296,9 +292,10 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off
|
|||
|
||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li, ++n) {
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = li->second;
|
||||
boost::shared_ptr<AutomationControl> c
|
||||
= boost::dynamic_pointer_cast<AutomationControl>(li->second);
|
||||
|
||||
if (c->parameter().type() == PluginAutomation && c->list()->automation_playback()) {
|
||||
if (c->parameter().type() == PluginAutomation && c->automation_playback()) {
|
||||
bool valid;
|
||||
|
||||
const float val = c->list()->rt_safe_eval (now, valid);
|
||||
|
|
@ -371,8 +368,7 @@ PluginInsert::set_parameter (Parameter param, float val)
|
|||
|
||||
_plugins[0]->set_parameter (param.id(), val);
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = control (param);
|
||||
|
||||
boost::shared_ptr<Evoral::Control> c = control (param);
|
||||
if (c)
|
||||
c->set_value(val);
|
||||
|
||||
|
|
@ -396,7 +392,7 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs
|
|||
nframes_t now = _session.transport_frame ();
|
||||
nframes_t end = now + nframes;
|
||||
|
||||
Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK);
|
||||
Glib::Mutex::Lock lm (_control_lock, Glib::TRY_LOCK);
|
||||
|
||||
if (!lm.locked()) {
|
||||
connect_and_run (bufs, nframes, offset, false);
|
||||
|
|
@ -434,7 +430,7 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs
|
|||
}
|
||||
|
||||
float
|
||||
PluginInsert::default_parameter_value (Parameter param)
|
||||
PluginInsert::default_parameter_value (Evoral::Parameter param)
|
||||
{
|
||||
if (param.type() != PluginAutomation)
|
||||
return 1.0;
|
||||
|
|
@ -640,7 +636,7 @@ PluginInsert::state (bool full)
|
|||
child->add_child_nocopy (automation_list (*x).state (full));
|
||||
autonode->add_child_nocopy (*child);
|
||||
*/
|
||||
autonode->add_child_nocopy (control(*x)->list()->state (full));
|
||||
autonode->add_child_nocopy (((AutomationList*)control(*x)->list().get())->state (full));
|
||||
}
|
||||
|
||||
node.add_child_nocopy (*autonode);
|
||||
|
|
@ -763,8 +759,11 @@ PluginInsert::set_state(const XMLNode& node)
|
|||
continue;
|
||||
}
|
||||
|
||||
boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(
|
||||
control(Parameter(PluginAutomation, port_id), true));
|
||||
|
||||
if (!child->children().empty()) {
|
||||
control (Parameter(PluginAutomation, port_id), true)->list()->set_state (*child->children().front());
|
||||
c->alist()->set_state (*child->children().front());
|
||||
} else {
|
||||
if ((cprop = child->property("auto")) != 0) {
|
||||
|
||||
|
|
@ -772,13 +771,13 @@ PluginInsert::set_state(const XMLNode& node)
|
|||
|
||||
int x;
|
||||
sscanf (cprop->value().c_str(), "0x%x", &x);
|
||||
control (Parameter(PluginAutomation, port_id), true)->list()->set_automation_state (AutoState (x));
|
||||
c->alist()->set_automation_state (AutoState (x));
|
||||
|
||||
} else {
|
||||
|
||||
/* missing */
|
||||
|
||||
control (Parameter(PluginAutomation, port_id), true)->list()->set_automation_state (Off);
|
||||
c->alist()->set_automation_state (Off);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2528,11 +2528,11 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra
|
|||
apply_gain_automation = false;
|
||||
|
||||
{
|
||||
Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
|
||||
Glib::Mutex::Lock am (_control_lock, Glib::TRY_LOCK);
|
||||
|
||||
if (am.locked() && _session.transport_rolling()) {
|
||||
|
||||
if (_gain_control->list()->automation_playback()) {
|
||||
if (_gain_control->alist()->automation_playback()) {
|
||||
apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
|
||||
start_frame, end_frame, _session.gain_automation_buffer(), nframes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
|||
if (_model && ! _model->writing())
|
||||
_model->start_write();
|
||||
|
||||
MIDI::Event ev(0.0, 4, NULL, true);
|
||||
Evoral::Event ev(0.0, 4, NULL, true);
|
||||
|
||||
while (true) {
|
||||
bool ret = src.full_peek(sizeof(double), (uint8_t*)&time);
|
||||
|
|
@ -500,7 +500,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
|||
|
||||
|
||||
void
|
||||
SMFSource::append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev)
|
||||
SMFSource::append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev)
|
||||
{
|
||||
if (ev.size() == 0)
|
||||
return;
|
||||
|
|
@ -925,7 +925,7 @@ SMFSource::load_model(bool lock, bool force_reload)
|
|||
fseek(_fd, _header_size, SEEK_SET);
|
||||
|
||||
uint64_t time = 0; /* in SMF ticks */
|
||||
MIDI::Event ev;
|
||||
Evoral::Event ev;
|
||||
|
||||
size_t scratch_size = 0; // keep track of scratch and minimize reallocs
|
||||
|
||||
|
|
|
|||
42
libs/evoral/SConscript
Normal file
42
libs/evoral/SConscript
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# -*- python -*-
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
Import('env libraries install_prefix')
|
||||
|
||||
evoral = env.Clone()
|
||||
evoral.Merge([
|
||||
libraries['glibmm2'],
|
||||
libraries['xml'],
|
||||
libraries['pbd'],
|
||||
])
|
||||
|
||||
if evoral['IS_OSX']:
|
||||
evoral.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
|
||||
|
||||
domain = 'evoral'
|
||||
|
||||
evoral.Append(DOMAIN=domain, MAJOR=1, MINOR=0, MICRO=0)
|
||||
evoral.Append(CXXFLAGS="-DEVENT_WITH_XML")
|
||||
|
||||
sources = Split("""
|
||||
src/Control.cpp
|
||||
src/ControlList.cpp
|
||||
src/ControlSet.cpp
|
||||
src/Event.cpp
|
||||
src/Note.cpp
|
||||
src/Sequence.cpp
|
||||
src/Curve.cpp
|
||||
""")
|
||||
|
||||
libevoral = evoral.SharedLibrary('evoral', [ sources ])
|
||||
|
||||
Default(libevoral)
|
||||
|
||||
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour3'), libevoral))
|
||||
|
||||
env.Alias('tarball', env.Distribute (env['DISTTREE'],
|
||||
[ 'SConscript' ] + sources +
|
||||
glob.glob('midi++/*.h')))
|
||||
58
libs/evoral/evoral/Control.hpp
Normal file
58
libs/evoral/evoral/Control.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_CONTROL_HPP
|
||||
#define EVORAL_CONTROL_HPP
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <glibmm/thread.h>
|
||||
#include <evoral/types.hpp>
|
||||
#include <evoral/Parameter.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
class ControlList;
|
||||
class Transport;
|
||||
|
||||
class Control
|
||||
{
|
||||
public:
|
||||
Control(boost::shared_ptr<ControlList>);
|
||||
virtual ~Control() {}
|
||||
|
||||
void set_value(float val, bool to_list=false, nframes_t frame=0);
|
||||
float get_value(bool from_list=false, nframes_t frame=0) const;
|
||||
float user_value() const;
|
||||
|
||||
void set_list(boost::shared_ptr<ControlList>);
|
||||
|
||||
boost::shared_ptr<ControlList> list() { return _list; }
|
||||
boost::shared_ptr<const ControlList> list() const { return _list; }
|
||||
|
||||
Parameter parameter() const;
|
||||
|
||||
protected:
|
||||
boost::shared_ptr<ControlList> _list;
|
||||
float _user_value;
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_CONTROL_HPP
|
||||
274
libs/evoral/evoral/ControlList.hpp
Normal file
274
libs/evoral/evoral/ControlList.hpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_CONTROL_LIST_HPP
|
||||
#define EVORAL_CONTROL_LIST_HPP
|
||||
|
||||
#include <list>
|
||||
#include <boost/pool/pool.hpp>
|
||||
#include <boost/pool/pool_alloc.hpp>
|
||||
#include <glibmm/thread.h>
|
||||
#include <evoral/types.hpp>
|
||||
#include <evoral/Parameter.hpp>
|
||||
#include <evoral/Curve.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
/** A single event (time-stamped value) for a control
|
||||
*/
|
||||
struct ControlEvent {
|
||||
ControlEvent (double w, double v)
|
||||
: when (w), value (v), coeff (0)
|
||||
{}
|
||||
|
||||
ControlEvent (const ControlEvent& other)
|
||||
: when (other.when), value (other.value), coeff (0)
|
||||
{
|
||||
if (other.coeff) {
|
||||
create_coeffs();
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
coeff[i] = other.coeff[i];
|
||||
}
|
||||
}
|
||||
|
||||
~ControlEvent() { if (coeff) delete[] coeff; }
|
||||
|
||||
void create_coeffs() {
|
||||
if (!coeff)
|
||||
coeff = new double[4];
|
||||
|
||||
coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
|
||||
}
|
||||
|
||||
double when;
|
||||
double value;
|
||||
double* coeff; ///< double[4] allocated by Curve as needed
|
||||
};
|
||||
|
||||
|
||||
/** Pool allocator for control lists that does not use a lock
|
||||
* and allocates 8k blocks of new pointers at a time
|
||||
*/
|
||||
typedef boost::fast_pool_allocator<
|
||||
ControlEvent*,
|
||||
boost::default_user_allocator_new_delete,
|
||||
boost::details::pool::null_mutex,
|
||||
8192>
|
||||
ControlEventAllocator;
|
||||
|
||||
|
||||
/** A list (sequence) of time-stamped values for a control
|
||||
*/
|
||||
class ControlList
|
||||
{
|
||||
public:
|
||||
typedef std::list<ControlEvent*,ControlEventAllocator> EventList;
|
||||
typedef EventList::iterator iterator;
|
||||
typedef EventList::reverse_iterator reverse_iterator;
|
||||
typedef EventList::const_iterator const_iterator;
|
||||
|
||||
ControlList (Parameter id);
|
||||
//ControlList (const XMLNode&, Parameter id);
|
||||
~ControlList();
|
||||
|
||||
virtual boost::shared_ptr<ControlList> create(Parameter id);
|
||||
|
||||
ControlList (const ControlList&);
|
||||
ControlList (const ControlList&, double start, double end);
|
||||
ControlList& operator= (const ControlList&);
|
||||
bool operator== (const ControlList&);
|
||||
|
||||
void freeze();
|
||||
void thaw ();
|
||||
|
||||
const Parameter& parameter() const { return _parameter; }
|
||||
void set_parameter(Parameter p) { _parameter = p; }
|
||||
|
||||
EventList::size_type size() const { return _events.size(); }
|
||||
bool empty() const { return _events.empty(); }
|
||||
|
||||
void reset_default (double val) {
|
||||
_default_value = val;
|
||||
}
|
||||
|
||||
void clear ();
|
||||
void x_scale (double factor);
|
||||
bool extend_to (double);
|
||||
void slide (iterator before, double distance);
|
||||
|
||||
void reposition_for_rt_add (double when);
|
||||
void rt_add (double when, double value);
|
||||
void add (double when, double value);
|
||||
void fast_simple_add (double when, double value);
|
||||
|
||||
void reset_range (double start, double end);
|
||||
void erase_range (double start, double end);
|
||||
void erase (iterator);
|
||||
void erase (iterator, iterator);
|
||||
void move_range (iterator start, iterator end, double, double);
|
||||
void modify (iterator, double, double);
|
||||
|
||||
boost::shared_ptr<ControlList> cut (double, double);
|
||||
boost::shared_ptr<ControlList> copy (double, double);
|
||||
void clear (double, double);
|
||||
|
||||
boost::shared_ptr<ControlList> cut (iterator, iterator);
|
||||
boost::shared_ptr<ControlList> copy (iterator, iterator);
|
||||
void clear (iterator, iterator);
|
||||
|
||||
bool paste (ControlList&, double position, float times);
|
||||
|
||||
void set_yrange (double min, double max) {
|
||||
_min_yval = min;
|
||||
_max_yval = max;
|
||||
}
|
||||
|
||||
double get_max_y() const { return _max_yval; }
|
||||
double get_min_y() const { return _min_yval; }
|
||||
|
||||
void truncate_end (double length);
|
||||
void truncate_start (double length);
|
||||
|
||||
iterator begin() { return _events.begin(); }
|
||||
const_iterator begin() const { return _events.begin(); }
|
||||
iterator end() { return _events.end(); }
|
||||
const_iterator end() const { return _events.end(); }
|
||||
ControlEvent* back() { return _events.back(); }
|
||||
const ControlEvent* back() const { return _events.back(); }
|
||||
ControlEvent* front() { return _events.front(); }
|
||||
const ControlEvent* front() const { return _events.front(); }
|
||||
|
||||
std::pair<ControlList::iterator,ControlList::iterator> control_points_adjacent (double when);
|
||||
|
||||
template<class T> void apply_to_points (T& obj, void (T::*method)(const ControlList&)) {
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
(obj.*method)(*this);
|
||||
}
|
||||
|
||||
void set_max_xval (double);
|
||||
double get_max_xval() const { return _max_xval; }
|
||||
|
||||
double eval (double where) {
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
return unlocked_eval (where);
|
||||
}
|
||||
|
||||
double rt_safe_eval (double where, bool& ok) {
|
||||
|
||||
Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
|
||||
|
||||
if ((ok = lm.locked())) {
|
||||
return unlocked_eval (where);
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool time_comparator (const ControlEvent* a, const ControlEvent* b) {
|
||||
return a->when < b->when;
|
||||
}
|
||||
|
||||
/** Lookup cache for eval functions, range contains equivalent values */
|
||||
struct LookupCache {
|
||||
LookupCache() : left(-1) {}
|
||||
double left; /* leftmost x coordinate used when finding "range" */
|
||||
std::pair<ControlList::const_iterator,ControlList::const_iterator> range;
|
||||
};
|
||||
|
||||
/** Lookup cache for point finding, range contains points between left and right */
|
||||
struct SearchCache {
|
||||
SearchCache() : left(-1), right(-1) {}
|
||||
double left; /* leftmost x coordinate used when finding "range" */
|
||||
double right; /* rightmost x coordinate used when finding "range" */
|
||||
std::pair<ControlList::const_iterator,ControlList::const_iterator> range;
|
||||
};
|
||||
|
||||
const EventList& events() const { return _events; }
|
||||
double default_value() const { return _parameter.normal(); }
|
||||
|
||||
// FIXME: const violations for Curve
|
||||
Glib::Mutex& lock() const { return _lock; }
|
||||
LookupCache& lookup_cache() const { return _lookup_cache; }
|
||||
SearchCache& search_cache() const { return _search_cache; }
|
||||
|
||||
/** Called by locked entry point and various private
|
||||
* locations where we already hold the lock.
|
||||
*
|
||||
* FIXME: Should this be private? Curve needs it..
|
||||
*/
|
||||
double unlocked_eval (double x) const;
|
||||
|
||||
bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
|
||||
bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
|
||||
|
||||
Curve& curve() { return *_curve; }
|
||||
const Curve& curve() const { return *_curve; }
|
||||
|
||||
virtual void mark_dirty () const;
|
||||
|
||||
enum InterpolationStyle {
|
||||
Discrete,
|
||||
Linear,
|
||||
Curved
|
||||
};
|
||||
|
||||
InterpolationStyle interpolation() const { return _interpolation; }
|
||||
void set_interpolation(InterpolationStyle style) { _interpolation = style; }
|
||||
|
||||
protected:
|
||||
|
||||
/** Called by unlocked_eval() to handle cases of 3 or more control points. */
|
||||
double multipoint_eval (double x) const;
|
||||
|
||||
void build_search_cache_if_necessary(double start, double end) const;
|
||||
|
||||
bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
|
||||
bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
|
||||
|
||||
boost::shared_ptr<ControlList> cut_copy_clear (double, double, int op);
|
||||
|
||||
virtual void maybe_signal_changed ();
|
||||
|
||||
void _x_scale (double factor);
|
||||
|
||||
mutable LookupCache _lookup_cache;
|
||||
mutable SearchCache _search_cache;
|
||||
|
||||
Parameter _parameter;
|
||||
InterpolationStyle _interpolation;
|
||||
EventList _events;
|
||||
mutable Glib::Mutex _lock;
|
||||
int8_t _frozen;
|
||||
bool _changed_when_thawed;
|
||||
bool _new_value;
|
||||
double _max_xval;
|
||||
double _min_yval;
|
||||
double _max_yval;
|
||||
double _default_value;
|
||||
bool _sort_pending;
|
||||
iterator _rt_insertion_point;
|
||||
double _rt_pos;
|
||||
|
||||
Curve* _curve;
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_CONTROL_LIST_HPP
|
||||
|
||||
69
libs/evoral/evoral/ControlSet.hpp
Normal file
69
libs/evoral/evoral/ControlSet.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_CONTROLLABLE_HPP
|
||||
#define EVORAL_CONTROLLABLE_HPP
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <glibmm/thread.h>
|
||||
#include <evoral/types.hpp>
|
||||
#include <evoral/Parameter.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
class Control;
|
||||
class ControlList;
|
||||
class ControlEvent;
|
||||
|
||||
class ControlSet {
|
||||
public:
|
||||
ControlSet();
|
||||
virtual ~ControlSet() {}
|
||||
|
||||
virtual boost::shared_ptr<Control> control(Evoral::Parameter id, bool create_if_missing=false);
|
||||
virtual boost::shared_ptr<const Control> control(Evoral::Parameter id) const;
|
||||
|
||||
virtual boost::shared_ptr<Control> control_factory(boost::shared_ptr<ControlList> list) const;
|
||||
virtual boost::shared_ptr<ControlList> control_list_factory(const Parameter& param) const;
|
||||
|
||||
typedef std::map< Parameter, boost::shared_ptr<Control> > Controls;
|
||||
Controls& controls() { return _controls; }
|
||||
const Controls& controls() const { return _controls; }
|
||||
|
||||
virtual void add_control(boost::shared_ptr<Control>);
|
||||
|
||||
virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
|
||||
|
||||
virtual float default_parameter_value(Parameter param) { return 1.0f; }
|
||||
|
||||
virtual void clear();
|
||||
|
||||
void what_has_data(std::set<Parameter>&) const;
|
||||
|
||||
Glib::Mutex& control_lock() const { return _control_lock; }
|
||||
|
||||
protected:
|
||||
mutable Glib::Mutex _control_lock;
|
||||
Controls _controls;
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_CONTROLLABLE_HPP
|
||||
58
libs/evoral/evoral/Curve.hpp
Normal file
58
libs/evoral/evoral/Curve.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_CURVE_HPP
|
||||
#define EVORAL_CURVE_HPP
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
class ControlList;
|
||||
|
||||
class Curve : public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
Curve (const ControlList& cl);
|
||||
|
||||
bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen);
|
||||
void get_vector (double x0, double x1, float *arg, int32_t veclen);
|
||||
|
||||
void solve ();
|
||||
|
||||
void mark_dirty() const { _dirty = true; }
|
||||
|
||||
private:
|
||||
double unlocked_eval (double where);
|
||||
double multipoint_eval (double x);
|
||||
|
||||
void _get_vector (double x0, double x1, float *arg, int32_t veclen);
|
||||
|
||||
mutable bool _dirty;
|
||||
const ControlList& _list;
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
extern "C" {
|
||||
void curve_get_vector_from_c (void *arg, double, double, float*, int32_t);
|
||||
}
|
||||
|
||||
#endif // EVORAL_CURVE_HPP
|
||||
|
||||
219
libs/evoral/evoral/Event.hpp
Normal file
219
libs/evoral/evoral/Event.hpp
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_EVENT_HPP
|
||||
#define EVORAL_EVENT_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <assert.h>
|
||||
#include <evoral/midi_events.h>
|
||||
#ifdef EVENT_WITH_XML
|
||||
#include <pbd/xml++.h>
|
||||
#endif
|
||||
|
||||
/** If this is not defined, all methods of MidiEvent are RT safe
|
||||
* but MidiEvent will never deep copy and (depending on the scenario)
|
||||
* may not be usable in STL containers, signals, etc.
|
||||
*/
|
||||
#define EVENT_ALLOW_ALLOC 1
|
||||
|
||||
//#define EVENT_WITH_XML
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
/** Identical to jack_midi_event_t, but with double timestamp
|
||||
*
|
||||
* time is either a frame time (from/to Jack) or a beat time (internal
|
||||
* tempo time, used in MidiModel) depending on context.
|
||||
*/
|
||||
struct Event {
|
||||
#ifdef EVENT_ALLOW_ALLOC
|
||||
Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false);
|
||||
|
||||
/** Copy \a copy.
|
||||
*
|
||||
* If \a owns_buffer is true, the buffer will be copied and this method
|
||||
* is NOT REALTIME SAFE. Otherwise both events share a buffer and
|
||||
* memory management semantics are the caller's problem.
|
||||
*/
|
||||
Event(const Event& copy, bool owns_buffer);
|
||||
|
||||
#ifdef EVENT_WITH_XML
|
||||
/** Event from XML ala http://www.midi.org/dtds/MIDIEvents10.dtd
|
||||
*/
|
||||
Event(const XMLNode& event);
|
||||
|
||||
/** Event to XML ala http://www.midi.org/dtds/MIDIEvents10.dtd
|
||||
*/
|
||||
boost::shared_ptr<XMLNode> to_xml() const;
|
||||
#endif
|
||||
|
||||
~Event();
|
||||
|
||||
inline const Event& operator=(const Event& copy) {
|
||||
_time = copy._time;
|
||||
if (_owns_buffer) {
|
||||
if (copy._buffer) {
|
||||
if (copy._size > _size) {
|
||||
_buffer = (uint8_t*)::realloc(_buffer, copy._size);
|
||||
}
|
||||
memcpy(_buffer, copy._buffer, copy._size);
|
||||
} else {
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
}
|
||||
} else {
|
||||
_buffer = copy._buffer;
|
||||
}
|
||||
|
||||
_size = copy._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void shallow_copy(const Event& copy) {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
_buffer = false;
|
||||
_owns_buffer = false;
|
||||
}
|
||||
|
||||
_time = copy._time;
|
||||
_size = copy._size;
|
||||
_buffer = copy._buffer;
|
||||
}
|
||||
|
||||
inline void set(uint8_t* buf, size_t size, double t) {
|
||||
if (_owns_buffer) {
|
||||
if (_size < size) {
|
||||
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
||||
}
|
||||
memcpy (_buffer, buf, size);
|
||||
} else {
|
||||
_buffer = buf;
|
||||
}
|
||||
|
||||
_size = size;
|
||||
_time = t;
|
||||
}
|
||||
|
||||
inline bool operator==(const Event& other) const {
|
||||
if (_time != other._time)
|
||||
return false;
|
||||
|
||||
if (_size != other._size)
|
||||
return false;
|
||||
|
||||
if (_buffer == other._buffer)
|
||||
return true;
|
||||
|
||||
for (size_t i=0; i < _size; ++i)
|
||||
if (_buffer[i] != other._buffer[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Event& other) const { return ! operator==(other); }
|
||||
|
||||
inline bool owns_buffer() const { return _owns_buffer; }
|
||||
|
||||
inline void set_buffer(size_t size, uint8_t* buf, bool own) {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
}
|
||||
_size = size;
|
||||
_buffer = buf;
|
||||
_owns_buffer = own;
|
||||
}
|
||||
|
||||
inline void realloc(size_t size) {
|
||||
if (_owns_buffer) {
|
||||
if (size > _size)
|
||||
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
||||
} else {
|
||||
_buffer = (uint8_t*) ::malloc(size);
|
||||
_owns_buffer = true;
|
||||
}
|
||||
|
||||
_size = size;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
inline void set_buffer(uint8_t* buf) { _buffer = buf; }
|
||||
|
||||
#endif // EVENT_ALLOW_ALLOC
|
||||
|
||||
inline double time() const { return _time; }
|
||||
inline double& time() { return _time; }
|
||||
inline uint32_t size() const { return _size; }
|
||||
inline uint32_t& size() { return _size; }
|
||||
inline uint8_t type() const { return (_buffer[0] & 0xF0); }
|
||||
inline void set_type(uint8_t type) { _buffer[0] = (0x0F & _buffer[0])
|
||||
| (0xF0 & type); }
|
||||
inline uint8_t channel() const { return (_buffer[0] & 0x0F); }
|
||||
inline void set_channel(uint8_t channel) { _buffer[0] = (0xF0 & _buffer[0])
|
||||
| (0x0F & channel); }
|
||||
inline bool is_note_on() const { return (type() == MIDI_CMD_NOTE_ON); }
|
||||
inline bool is_note_off() const { return (type() == MIDI_CMD_NOTE_OFF); }
|
||||
inline bool is_cc() const { return (type() == MIDI_CMD_CONTROL); }
|
||||
inline bool is_pitch_bender() const { return (type() == MIDI_CMD_BENDER); }
|
||||
inline bool is_pgm_change() const { return (type() == MIDI_CMD_PGM_CHANGE); }
|
||||
inline bool is_note() const { return (is_note_on() || is_note_off()); }
|
||||
inline bool is_aftertouch() const { return (type() == MIDI_CMD_NOTE_PRESSURE); }
|
||||
inline bool is_channel_aftertouch() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); }
|
||||
inline uint8_t note() const { return (_buffer[1]); }
|
||||
inline uint8_t velocity() const { return (_buffer[2]); }
|
||||
inline uint8_t cc_number() const { return (_buffer[1]); }
|
||||
inline uint8_t cc_value() const { return (_buffer[2]); }
|
||||
inline uint8_t pitch_bender_lsb() const { return (_buffer[1]); }
|
||||
inline uint8_t pitch_bender_msb() const { return (_buffer[2]); }
|
||||
inline uint16_t pitch_bender_value() const { return ( ((0x7F & _buffer[2]) << 7)
|
||||
| (0x7F & _buffer[1]) ); }
|
||||
inline uint8_t pgm_number() const { return (_buffer[1]); }
|
||||
inline void set_pgm_number(uint8_t number){ _buffer[1] = number; }
|
||||
inline uint8_t aftertouch() const { return (_buffer[1]); }
|
||||
inline uint8_t channel_aftertouch() const { return (_buffer[1]); }
|
||||
inline bool is_channel_event() const { return (0x80 <= type()) && (type() <= 0xE0); }
|
||||
inline bool is_smf_meta_event() const { return _buffer[0] == 0xFF; }
|
||||
inline bool is_sysex() const { return _buffer[0] == 0xF0
|
||||
|| _buffer[0] == 0xF7; }
|
||||
inline const uint8_t* buffer() const { return _buffer; }
|
||||
inline uint8_t*& buffer() { return _buffer; }
|
||||
|
||||
private:
|
||||
double _time; /**< Sample index (or beat time) at which event is valid */
|
||||
uint32_t _size; /**< Number of uint8_ts of data in \a buffer */
|
||||
uint8_t* _buffer; /**< Raw MIDI data */
|
||||
|
||||
#ifdef EVENT_ALLOW_ALLOC
|
||||
bool _owns_buffer; /**< Whether buffer is locally allocated */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_EVENT_HPP
|
||||
|
||||
40
libs/evoral/evoral/EventSink.hpp
Normal file
40
libs/evoral/evoral/EventSink.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_EVENT_SINK_HPP
|
||||
#define EVORAL_EVENT_SINK_HPP
|
||||
|
||||
#include <evoral/types.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
/** Pure virtual base for anything you can write events to.
|
||||
*/
|
||||
class EventSink {
|
||||
public:
|
||||
virtual size_t write(timestamp_t time,
|
||||
uint32_t size,
|
||||
const uint8_t* buf) = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_EVENT_SINK_HPP
|
||||
|
||||
52
libs/evoral/evoral/MIDIParameters.hpp
Normal file
52
libs/evoral/evoral/MIDIParameters.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_MIDI_PARAMETERS_HPP
|
||||
#define EVORAL_MIDI_PARAMETERS_HPP
|
||||
|
||||
namespace Evoral {
|
||||
namespace MIDI {
|
||||
|
||||
struct ContinuousController : public Parameter {
|
||||
ContinuousController(uint32_t cc_type, uint32_t channel, uint32_t controller)
|
||||
: Parameter(cc_type, controller, channel) { set_range(*this); }
|
||||
static void set_range(Parameter& p) { p.set_range(0.0, 127.0, 0.0); }
|
||||
};
|
||||
|
||||
struct ProgramChange : public Parameter {
|
||||
ProgramChange(uint32_t pc_type, uint32_t channel)
|
||||
: Parameter(pc_type, 0, channel) { set_range(*this); }
|
||||
static void set_range(Parameter& p) { p.set_range(0.0, 127.0, 0.0); }
|
||||
};
|
||||
|
||||
struct ChannelAftertouch : public Parameter {
|
||||
ChannelAftertouch(uint32_t ca_type, uint32_t channel)
|
||||
: Parameter(ca_type, 0, channel) { set_range(*this); }
|
||||
static void set_range(Parameter& p) { p.set_range(0.0, 127.0, 0.0); }
|
||||
};
|
||||
|
||||
struct PitchBender : public Parameter {
|
||||
PitchBender(uint32_t pb_type, uint32_t channel)
|
||||
: Parameter(pb_type, 0, channel) { set_range(*this); }
|
||||
static void set_range(Parameter& p) { p.set_range(0.0, 16383.0, 8192.0); }
|
||||
};
|
||||
|
||||
} // namespace MIDI
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_MIDI_PARAMETERS_HPP
|
||||
79
libs/evoral/evoral/Note.hpp
Normal file
79
libs/evoral/evoral/Note.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_NOTE_HPP
|
||||
#define EVORAL_NOTE_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <evoral/Event.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
/** An abstract (protocol agnostic) note.
|
||||
*
|
||||
* Currently a note is defined as (on event, duration, off event).
|
||||
*/
|
||||
class Note {
|
||||
public:
|
||||
Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
|
||||
Note(const Note& copy);
|
||||
~Note();
|
||||
|
||||
const Note& operator=(const Note& copy);
|
||||
|
||||
inline bool operator==(const Note& other) {
|
||||
return time() == other.time() &&
|
||||
note() == other.note() &&
|
||||
duration() == other.duration() &&
|
||||
velocity() == other.velocity() &&
|
||||
channel() == other.channel();
|
||||
}
|
||||
|
||||
inline double time() const { return _on_event.time(); }
|
||||
inline double end_time() const { return _off_event.time(); }
|
||||
inline uint8_t note() const { return _on_event.note(); }
|
||||
inline uint8_t velocity() const { return _on_event.velocity(); }
|
||||
inline double duration() const { return _off_event.time() - _on_event.time(); }
|
||||
inline uint8_t channel() const {
|
||||
assert(_on_event.channel() == _off_event.channel());
|
||||
return _on_event.channel();
|
||||
}
|
||||
|
||||
inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; }
|
||||
inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; }
|
||||
inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; }
|
||||
inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; }
|
||||
inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); }
|
||||
|
||||
inline Event& on_event() { return _on_event; }
|
||||
inline const Event& on_event() const { return _on_event; }
|
||||
inline Event& off_event() { return _off_event; }
|
||||
inline const Event& off_event() const { return _off_event; }
|
||||
|
||||
private:
|
||||
// Event buffers are self-contained
|
||||
Event _on_event;
|
||||
Event _off_event;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_NOTE_HPP
|
||||
|
||||
135
libs/evoral/evoral/Parameter.hpp
Normal file
135
libs/evoral/evoral/Parameter.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_PARAMETER_HPP
|
||||
#define EVORAL_PARAMETER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
/** ID of a [play|record|automate]able parameter.
|
||||
*
|
||||
* A parameter is defined by (type, id, channel). Type is an integer which
|
||||
* can be used in any way by the application (e.g. cast to a custom enum,
|
||||
* map to/from a URI, etc). ID is type specific (e.g. MIDI controller #).
|
||||
*
|
||||
* This class defines a < operator which is a strict weak ordering, so
|
||||
* Parameter may be stored in a std::set, used as a std::map key, etc.
|
||||
*/
|
||||
class Parameter
|
||||
{
|
||||
public:
|
||||
Parameter(uint32_t type, uint32_t id, int8_t channel=0,
|
||||
double min=0.0f, double max=0.0f, double def=0.0f)
|
||||
: _type(type), _id(id), _channel(channel), _min(min), _max(max), _normal(def)
|
||||
{}
|
||||
|
||||
//Parameter(const std::string& str);
|
||||
|
||||
inline uint32_t type() const { return _type; }
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline uint8_t channel() const { return _channel; }
|
||||
|
||||
/**
|
||||
* Equivalence operator
|
||||
* It is obvious from the definition that this operator
|
||||
* is transitive, as required by stict weak ordering
|
||||
* (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html)
|
||||
*/
|
||||
inline bool operator==(const Parameter& id) const {
|
||||
return (_type == id._type && _id == id._id && _channel == id._channel);
|
||||
}
|
||||
|
||||
/** Strict weak ordering
|
||||
* See: http://www.sgi.com/tech/stl/StrictWeakOrdering.html
|
||||
* Sort Parameters first according to type then to id and lastly to channel.
|
||||
*
|
||||
* Proof:
|
||||
* <ol>
|
||||
* <li>Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.</li>
|
||||
* <li>Antisymmetry: given x != y, f(x, y) implies !f(y, x) because of the same
|
||||
* property of \c < in each branch and the symmetry of operator==. </li>
|
||||
* <li>Transitivity: let f(x, y) and f(y, z) be true.
|
||||
* We prove by contradiction, assuming the contrary (f(x, z) is false).
|
||||
* That would imply exactly one of the following:
|
||||
* <ol>
|
||||
* <li> x == z which contradicts the assumption f(x, y) and f(y, x)
|
||||
* because of antisymmetry.
|
||||
* </li>
|
||||
* <li> f(z, x) is true. That would imply that one of the ivars (we call it i)
|
||||
* of x is greater than the same ivar in z while all "previous" ivars
|
||||
* are equal. That would imply that also in y all those "previous"
|
||||
* ivars are equal and because if x.i > z.i it is impossible
|
||||
* that there is an y that satisfies x.i < y.i < z.i at the same
|
||||
* time which contradicts the assumption.
|
||||
* </li>
|
||||
* Therefore f(x, z) is true (transitivity)
|
||||
* </ol>
|
||||
* </li>
|
||||
* </ol>
|
||||
*/
|
||||
inline bool operator<(const Parameter& id) const {
|
||||
if (_type < id._type) {
|
||||
return true;
|
||||
} else if (_type == id._type && _id < id._id) {
|
||||
return true;
|
||||
} else if (_id == id._id && _channel < id._channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline operator bool() const { return (_type != 0); }
|
||||
|
||||
virtual std::string symbol() const {
|
||||
return (boost::format("%1%_c%2%_n%3%\n") % _type % _channel % _id).str();
|
||||
}
|
||||
|
||||
inline void set_range(double min, double max, double normal) {
|
||||
_min = min;
|
||||
_max = max;
|
||||
_normal = normal;
|
||||
}
|
||||
|
||||
inline const double min() const { return _min; }
|
||||
inline const double max() const { return _max; }
|
||||
inline const double normal() const { return _normal; }
|
||||
|
||||
protected:
|
||||
// Default copy constructor is ok
|
||||
|
||||
// ID (used in comparison)
|
||||
uint32_t _type;
|
||||
uint32_t _id;
|
||||
uint8_t _channel;
|
||||
|
||||
// Metadata (not used in comparison)
|
||||
double _min;
|
||||
double _max;
|
||||
double _normal;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_PARAMETER_HPP
|
||||
|
||||
215
libs/evoral/evoral/Sequence.hpp
Normal file
215
libs/evoral/evoral/Sequence.hpp
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_SEQUENCE_HPP
|
||||
#define EVORAL_SEQUENCE_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
#include <glibmm/thread.h>
|
||||
#include <evoral/types.hpp>
|
||||
#include <evoral/Note.hpp>
|
||||
#include <evoral/Parameter.hpp>
|
||||
#include <evoral/ControlSet.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
class EventSink;
|
||||
class Note;
|
||||
class Event;
|
||||
class ControlList;
|
||||
|
||||
/** This class keeps track of the current x and y for a control
|
||||
*/
|
||||
class ControlIterator {
|
||||
public:
|
||||
boost::shared_ptr<const ControlList> list;
|
||||
double x;
|
||||
double y;
|
||||
|
||||
ControlIterator(boost::shared_ptr<const ControlList> a_list,
|
||||
double a_x,
|
||||
double a_y)
|
||||
: list(a_list)
|
||||
, x(a_x)
|
||||
, y(a_y)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/** This is a higher level view of events, with separate representations for
|
||||
* notes (instead of just unassociated note on/off events) and controller data.
|
||||
* Controller data is represented as a list of time-stamped float values.
|
||||
*/
|
||||
class Sequence : public boost::noncopyable, virtual public ControlSet {
|
||||
public:
|
||||
Sequence(size_t size);
|
||||
|
||||
void write_lock();
|
||||
void write_unlock();
|
||||
|
||||
void read_lock() const;
|
||||
void read_unlock() const;
|
||||
|
||||
void clear();
|
||||
|
||||
bool percussive() const { return _percussive; }
|
||||
void set_percussive(bool p) { _percussive = p; }
|
||||
|
||||
void start_write();
|
||||
bool writing() const { return _writing; }
|
||||
void end_write(bool delete_stuck=false);
|
||||
|
||||
size_t read(EventSink& dst,
|
||||
timestamp_t start,
|
||||
timedur_t length,
|
||||
timestamp_t stamp_offset) const;
|
||||
|
||||
/** Resizes vector if necessary (NOT realtime safe) */
|
||||
void append(const Event& ev);
|
||||
|
||||
inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
|
||||
inline const boost::shared_ptr<Note> note_at(unsigned i) { return _notes[i]; }
|
||||
|
||||
inline size_t n_notes() const { return _notes.size(); }
|
||||
inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; }
|
||||
|
||||
inline static bool note_time_comparator(const boost::shared_ptr<const Note> a,
|
||||
const boost::shared_ptr<const Note> b) {
|
||||
return a->time() < b->time();
|
||||
}
|
||||
|
||||
struct LaterNoteEndComparator {
|
||||
typedef const Note* value_type;
|
||||
inline bool operator()(const boost::shared_ptr<const Note> a,
|
||||
const boost::shared_ptr<const Note> b) const {
|
||||
return a->end_time() > b->end_time();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector< boost::shared_ptr<Note> > Notes;
|
||||
inline Notes& notes() { return _notes; }
|
||||
inline const Notes& notes() const { return _notes; }
|
||||
|
||||
/** Read iterator */
|
||||
class const_iterator {
|
||||
public:
|
||||
const_iterator(const Sequence& seq, double t);
|
||||
~const_iterator();
|
||||
|
||||
inline bool locked() const { return _locked; }
|
||||
|
||||
const Event& operator*() const { return *_event; }
|
||||
const boost::shared_ptr<Event> operator->() const { return _event; }
|
||||
const boost::shared_ptr<Event> get_event_pointer() { return _event; }
|
||||
|
||||
const const_iterator& operator++(); // prefix only
|
||||
bool operator==(const const_iterator& other) const;
|
||||
bool operator!=(const const_iterator& other) const { return ! operator==(other); }
|
||||
|
||||
const_iterator& operator=(const const_iterator& other);
|
||||
|
||||
private:
|
||||
friend class Sequence;
|
||||
|
||||
const Sequence* _seq;
|
||||
boost::shared_ptr<Event> _event;
|
||||
|
||||
typedef std::priority_queue< boost::shared_ptr<Note>,
|
||||
std::deque< boost::shared_ptr<Note> >,
|
||||
LaterNoteEndComparator >
|
||||
ActiveNotes;
|
||||
|
||||
mutable ActiveNotes _active_notes;
|
||||
|
||||
bool _is_end;
|
||||
bool _locked;
|
||||
Notes::const_iterator _note_iter;
|
||||
std::vector<ControlIterator> _control_iters;
|
||||
std::vector<ControlIterator>::iterator _control_iter;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(*this, 0); }
|
||||
const const_iterator& end() const { return _end_iter; }
|
||||
|
||||
bool control_to_midi_event(boost::shared_ptr<Event>& ev,
|
||||
const ControlIterator& iter) const;
|
||||
|
||||
typedef std::map< Parameter, boost::shared_ptr<Control> > Controls;
|
||||
Controls& controls() { return _controls; }
|
||||
const Controls& controls() const { return _controls; }
|
||||
|
||||
bool edited() const { return _edited; }
|
||||
void set_edited(bool yn) { _edited = yn; }
|
||||
|
||||
protected:
|
||||
void add_note_unlocked(const boost::shared_ptr<Note> note);
|
||||
void remove_note_unlocked(const boost::shared_ptr<const Note> note);
|
||||
|
||||
mutable const_iterator _read_iter;
|
||||
bool _edited;
|
||||
#ifndef NDEBUG
|
||||
bool is_sorted() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class const_iterator;
|
||||
|
||||
void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
|
||||
void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
|
||||
void append_control_unlocked(Parameter param, double time, double value);
|
||||
|
||||
mutable Glib::RWLock _lock;
|
||||
|
||||
Notes _notes;
|
||||
Controls _controls;
|
||||
|
||||
typedef std::vector<size_t> WriteNotes;
|
||||
WriteNotes _write_notes[16];
|
||||
bool _writing;
|
||||
|
||||
typedef std::vector< boost::shared_ptr<const ControlList> > ControlLists;
|
||||
ControlLists _dirty_controls;
|
||||
|
||||
const const_iterator _end_iter;
|
||||
mutable nframes_t _next_read;
|
||||
bool _percussive;
|
||||
|
||||
/** FIXME: Make fully dynamic, map to URIs */
|
||||
enum EventTypes {
|
||||
midi_cc_type,
|
||||
midi_pc_type,
|
||||
midi_pb_type,
|
||||
midi_ca_type
|
||||
};
|
||||
|
||||
typedef std::priority_queue<
|
||||
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
|
||||
LaterNoteEndComparator>
|
||||
ActiveNotes;
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_SEQUENCE_HPP
|
||||
|
||||
133
libs/evoral/evoral/midi_events.h
Normal file
133
libs/evoral/evoral/midi_events.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* Definitions to ease working with raw MIDI.
|
||||
*
|
||||
* Adapted from ALSA's asounddef.h
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RAUL_MIDI_EVENTS_H
|
||||
#define RAUL_MIDI_EVENTS_H
|
||||
|
||||
|
||||
/**
|
||||
* \defgroup midi MIDI Definitions
|
||||
* MIDI command and controller number definitions.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
||||
// Controllers
|
||||
#define MIDI_CTL_MSB_BANK 0x00 /**< Bank Selection */
|
||||
#define MIDI_CTL_MSB_MODWHEEL 0x01 /**< Modulation */
|
||||
#define MIDI_CTL_MSB_BREATH 0x02 /**< Breath */
|
||||
#define MIDI_CTL_MSB_FOOT 0x04 /**< Foot */
|
||||
#define MIDI_CTL_MSB_PORTAMENTO_TIME 0x05 /**< Portamento Time */
|
||||
#define MIDI_CTL_MSB_DATA_ENTRY 0x06 /**< Data Entry */
|
||||
#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 /**< Main Volume */
|
||||
#define MIDI_CTL_MSB_BALANCE 0x08 /**< Balance */
|
||||
#define MIDI_CTL_MSB_PAN 0x0A /**< Panpot */
|
||||
#define MIDI_CTL_MSB_EXPRESSION 0x0B /**< Expression */
|
||||
#define MIDI_CTL_MSB_EFFECT1 0x0C /**< Effect1 */
|
||||
#define MIDI_CTL_MSB_EFFECT2 0x0D /**< Effect2 */
|
||||
#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 /**< General Purpose 1 */
|
||||
#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 /**< General Purpose 2 */
|
||||
#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 /**< General Purpose 3 */
|
||||
#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 /**< General Purpose 4 */
|
||||
#define MIDI_CTL_LSB_BANK 0x20 /**< Bank Selection */
|
||||
#define MIDI_CTL_LSB_MODWHEEL 0x21 /**< Modulation */
|
||||
#define MIDI_CTL_LSB_BREATH 0x22 /**< Breath */
|
||||
#define MIDI_CTL_LSB_FOOT 0x24 /**< Foot */
|
||||
#define MIDI_CTL_LSB_PORTAMENTO_TIME 0x25 /**< Portamento Time */
|
||||
#define MIDI_CTL_LSB_DATA_ENTRY 0x26 /**< Data Entry */
|
||||
#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 /**< Main Volume */
|
||||
#define MIDI_CTL_LSB_BALANCE 0x28 /**< Balance */
|
||||
#define MIDI_CTL_LSB_PAN 0x2A /**< Panpot */
|
||||
#define MIDI_CTL_LSB_EXPRESSION 0x2B /**< Expression */
|
||||
#define MIDI_CTL_LSB_EFFECT1 0x2C /**< Effect1 */
|
||||
#define MIDI_CTL_LSB_EFFECT2 0x2D /**< Effect2 */
|
||||
#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 /**< General Purpose 1 */
|
||||
#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 /**< General Purpose 2 */
|
||||
#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 /**< General Purpose 3 */
|
||||
#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 /**< General Purpose 4 */
|
||||
#define MIDI_CTL_SUSTAIN 0x40 /**< Sustain Pedal */
|
||||
#define MIDI_CTL_PORTAMENTO 0x41 /**< Portamento */
|
||||
#define MIDI_CTL_SOSTENUTO 0x42 /**< Sostenuto */
|
||||
#define MIDI_CTL_SOFT_PEDAL 0x43 /**< Soft Pedal */
|
||||
#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 /**< Legato Foot Switch */
|
||||
#define MIDI_CTL_HOLD2 0x45 /**< Hold2 */
|
||||
#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 /**< SC1 Sound Variation */
|
||||
#define MIDI_CTL_SC2_TIMBRE 0x47 /**< SC2 Timbre */
|
||||
#define MIDI_CTL_SC3_RELEASE_TIME 0x48 /**< SC3 Release Time */
|
||||
#define MIDI_CTL_SC4_ATTACK_TIME 0x49 /**< SC4 Attack Time */
|
||||
#define MIDI_CTL_SC5_BRIGHTNESS 0x4A /**< SC5 Brightness */
|
||||
#define MIDI_CTL_SC6 0x4B /**< SC6 */
|
||||
#define MIDI_CTL_SC7 0x4C /**< SC7 */
|
||||
#define MIDI_CTL_SC8 0x4D /**< SC8 */
|
||||
#define MIDI_CTL_SC9 0x4E /**< SC9 */
|
||||
#define MIDI_CTL_SC10 0x4F /**< SC10 */
|
||||
#define MIDI_CTL_GENERAL_PURPOSE5 0x50 /**< General Purpose 5 */
|
||||
#define MIDI_CTL_GENERAL_PURPOSE6 0x51 /**< General Purpose 6 */
|
||||
#define MIDI_CTL_GENERAL_PURPOSE7 0x52 /**< General Purpose 7 */
|
||||
#define MIDI_CTL_GENERAL_PURPOSE8 0x53 /**< General Purpose 8 */
|
||||
#define MIDI_CTL_PORTAMENTO_CONTROL 0x54 /**< Portamento Control */
|
||||
#define MIDI_CTL_E1_REVERB_DEPTH 0x5B /**< E1 Reverb Depth */
|
||||
#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5C /**< E2 Tremolo Depth */
|
||||
#define MIDI_CTL_E3_CHORUS_DEPTH 0x5D /**< E3 Chorus Depth */
|
||||
#define MIDI_CTL_E4_DETUNE_DEPTH 0x5E /**< E4 Detune Depth */
|
||||
#define MIDI_CTL_E5_PHASER_DEPTH 0x5F /**< E5 Phaser Depth */
|
||||
#define MIDI_CTL_DATA_INCREMENT 0x60 /**< Data Increment */
|
||||
#define MIDI_CTL_DATA_DECREMENT 0x61 /**< Data Decrement */
|
||||
#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 /**< Non-registered Parameter Number */
|
||||
#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 /**< Non-registered Parameter Number */
|
||||
#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 /**< Registered Parameter Number */
|
||||
#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 /**< Registered Parameter Number */
|
||||
#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 /**< All Sounds Off */
|
||||
#define MIDI_CTL_RESET_CONTROLLERS 0x79 /**< Reset Controllers */
|
||||
#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7A /**< Local Control Switch */
|
||||
#define MIDI_CTL_ALL_NOTES_OFF 0x7B /**< All Notes Off */
|
||||
#define MIDI_CTL_OMNI_OFF 0x7C /**< Omni Off */
|
||||
#define MIDI_CTL_OMNI_ON 0x7D /**< Omni On */
|
||||
#define MIDI_CTL_MONO1 0x7E /**< Mono1 */
|
||||
#define MIDI_CTL_MONO2 0x7F /**< Mono2 */
|
||||
|
||||
// Commands
|
||||
#define MIDI_CMD_NOTE_OFF 0x80 /**< Note Off */
|
||||
#define MIDI_CMD_NOTE_ON 0x90 /**< Note On */
|
||||
#define MIDI_CMD_NOTE_PRESSURE 0xA0 /**< Key Pressure */
|
||||
#define MIDI_CMD_CONTROL 0xB0 /**< Control Change */
|
||||
#define MIDI_CMD_PGM_CHANGE 0xC0 /**< Program Change */
|
||||
#define MIDI_CMD_CHANNEL_PRESSURE 0xD0 /**< Channel Pressure */
|
||||
#define MIDI_CMD_BENDER 0xE0 /**< Pitch Bender */
|
||||
#define MIDI_CMD_COMMON_SYSEX 0xF0 /**< Sysex (System Exclusive) Begin */
|
||||
#define MIDI_CMD_COMMON_MTC_QUARTER 0xF1 /**< MTC Quarter Frame */
|
||||
#define MIDI_CMD_COMMON_SONG_POS 0xF2 /**< Song Position */
|
||||
#define MIDI_CMD_COMMON_SONG_SELECT 0xF3 /**< Song Select */
|
||||
#define MIDI_CMD_COMMON_TUNE_REQUEST 0xF6 /**< Tune Request */
|
||||
#define MIDI_CMD_COMMON_SYSEX_END 0xF7 /**< End of Sysex */
|
||||
#define MIDI_CMD_COMMON_CLOCK 0xF8 /**< Clock */
|
||||
#define MIDI_CMD_COMMON_TICK 0xF9 /**< Tick */
|
||||
#define MIDI_CMD_COMMON_START 0xFA /**< Start */
|
||||
#define MIDI_CMD_COMMON_CONTINUE 0xFB /**< Continue */
|
||||
#define MIDI_CMD_COMMON_STOP 0xFC /**< Stop */
|
||||
#define MIDI_CMD_COMMON_SENSING 0xFE /**< Active Sensing */
|
||||
#define MIDI_CMD_COMMON_RESET 0xFF /**< Reset */
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/** \} */
|
||||
|
||||
#endif /* RAUL_MIDI_EVENTS_H */
|
||||
31
libs/evoral/evoral/types.hpp
Normal file
31
libs/evoral/evoral/types.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EVORAL_TYPES_HPP
|
||||
#define EVORAL_TYPES_HPP
|
||||
|
||||
/** Frame count (i.e. length of time in audio frames) */
|
||||
typedef uint32_t nframes_t;
|
||||
|
||||
/** Time-stamp of an event */
|
||||
typedef double timestamp_t;
|
||||
|
||||
/** Duration of time in timestamp_t units */
|
||||
typedef timestamp_t timedur_t;
|
||||
|
||||
#endif // EVORAL_TYPES_HPP
|
||||
82
libs/evoral/src/Control.cpp
Normal file
82
libs/evoral/src/Control.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <evoral/Control.hpp>
|
||||
#include <evoral/ControlList.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
Control::Control(boost::shared_ptr<ControlList> list)
|
||||
: _list(list)
|
||||
, _user_value(list->default_value())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Get the currently effective value (ie the one that corresponds to current output)
|
||||
*/
|
||||
float
|
||||
Control::get_value(bool from_list, nframes_t frame) const
|
||||
{
|
||||
if (from_list)
|
||||
return _list->eval(frame);
|
||||
else
|
||||
return _user_value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Control::set_value(float value, bool to_list, nframes_t frame)
|
||||
{
|
||||
_user_value = value;
|
||||
|
||||
if (to_list)
|
||||
_list->add(frame, value);
|
||||
}
|
||||
|
||||
|
||||
/** Get the latest user-set value, which may not equal get_value() when automation
|
||||
* is playing back, etc.
|
||||
*
|
||||
* Automation write/touch works by periodically sampling this value and adding it
|
||||
* to the AutomationList.
|
||||
*/
|
||||
float
|
||||
Control::user_value() const
|
||||
{
|
||||
return _user_value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Control::set_list(boost::shared_ptr<ControlList> list)
|
||||
{
|
||||
_list = list;
|
||||
_user_value = list->default_value();
|
||||
}
|
||||
|
||||
|
||||
Parameter
|
||||
Control::parameter() const
|
||||
{
|
||||
return _list->parameter();
|
||||
}
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
1310
libs/evoral/src/ControlList.cpp
Normal file
1310
libs/evoral/src/ControlList.cpp
Normal file
File diff suppressed because it is too large
Load diff
139
libs/evoral/src/ControlSet.cpp
Normal file
139
libs/evoral/src/ControlSet.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <evoral/ControlSet.hpp>
|
||||
#include <evoral/ControlList.hpp>
|
||||
#include <evoral/Control.hpp>
|
||||
#include <evoral/Event.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
ControlSet::ControlSet()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ControlSet::add_control(boost::shared_ptr<Control> ac)
|
||||
{
|
||||
_controls[ac->parameter()] = ac;
|
||||
}
|
||||
|
||||
void
|
||||
ControlSet::what_has_data (set<Parameter>& s) const
|
||||
{
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
Controls::const_iterator li;
|
||||
|
||||
// FIXME: correct semantics?
|
||||
for (li = _controls.begin(); li != _controls.end(); ++li) {
|
||||
s.insert ((*li).first);
|
||||
}
|
||||
}
|
||||
|
||||
/** If \a create_if_missing is true, a control list will be created and returned
|
||||
* if one does not already exists. Otherwise NULL will be returned if a control list
|
||||
* for \a parameter does not exist.
|
||||
*/
|
||||
boost::shared_ptr<Control>
|
||||
ControlSet::control (Parameter parameter, bool create_if_missing)
|
||||
{
|
||||
Controls::iterator i = _controls.find(parameter);
|
||||
|
||||
if (i != _controls.end()) {
|
||||
return i->second;
|
||||
|
||||
} else if (create_if_missing) {
|
||||
boost::shared_ptr<ControlList> al (control_list_factory(parameter));
|
||||
boost::shared_ptr<Control> ac(control_factory(al));
|
||||
add_control(ac);
|
||||
return ac;
|
||||
|
||||
} else {
|
||||
//warning << "ControlList " << parameter.to_string() << " not found for " << _name << endmsg;
|
||||
return boost::shared_ptr<Control>();
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<const Control>
|
||||
ControlSet::control (Parameter parameter) const
|
||||
{
|
||||
Controls::const_iterator i = _controls.find(parameter);
|
||||
|
||||
if (i != _controls.end()) {
|
||||
return i->second;
|
||||
} else {
|
||||
//warning << "ControlList " << parameter.to_string() << " not found for " << _name << endmsg;
|
||||
return boost::shared_ptr<Control>();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ControlSet::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
|
||||
{
|
||||
Controls::const_iterator li;
|
||||
|
||||
next_event.when = std::numeric_limits<nframes_t>::max();
|
||||
|
||||
for (li = _controls.begin(); li != _controls.end(); ++li) {
|
||||
ControlList::const_iterator i;
|
||||
boost::shared_ptr<const ControlList> alist (li->second->list());
|
||||
ControlEvent cp (now, 0.0f);
|
||||
|
||||
for (i = lower_bound (alist->begin(), alist->end(), &cp, ControlList::time_comparator);
|
||||
i != alist->end() && (*i)->when < end; ++i) {
|
||||
if ((*i)->when > now) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != alist->end() && (*i)->when < end) {
|
||||
if ((*i)->when < next_event.when) {
|
||||
next_event.when = (*i)->when;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next_event.when != std::numeric_limits<nframes_t>::max();
|
||||
}
|
||||
|
||||
void
|
||||
ControlSet::clear ()
|
||||
{
|
||||
Glib::Mutex::Lock lm (_control_lock);
|
||||
|
||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
|
||||
li->second->list()->clear();
|
||||
}
|
||||
|
||||
boost::shared_ptr<Control>
|
||||
ControlSet::control_factory(boost::shared_ptr<ControlList> list) const
|
||||
{
|
||||
return boost::shared_ptr<Control>(new Control(list));
|
||||
}
|
||||
|
||||
boost::shared_ptr<ControlList>
|
||||
ControlSet::control_list_factory(const Parameter& param) const
|
||||
{
|
||||
return boost::shared_ptr<ControlList>(new ControlList(param));
|
||||
}
|
||||
|
||||
|
||||
} // namespace Evoral
|
||||
401
libs/evoral/src/Curve.cpp
Normal file
401
libs/evoral/src/Curve.cpp
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <float.h>
|
||||
#include <cmath>
|
||||
#include <climits>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <evoral/Curve.hpp>
|
||||
#include <evoral/ControlList.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
Curve::Curve (const ControlList& cl)
|
||||
: _dirty (true)
|
||||
, _list (cl)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Curve::solve ()
|
||||
{
|
||||
uint32_t npoints;
|
||||
|
||||
if (!_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((npoints = _list.events().size()) > 2) {
|
||||
|
||||
/* Compute coefficients needed to efficiently compute a constrained spline
|
||||
curve. See "Constrained Cubic Spline Interpolation" by CJC Kruger
|
||||
(www.korf.co.uk/spline.pdf) for more details.
|
||||
*/
|
||||
|
||||
double x[npoints];
|
||||
double y[npoints];
|
||||
uint32_t i;
|
||||
ControlList::EventList::const_iterator xx;
|
||||
|
||||
for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
|
||||
x[i] = (double) (*xx)->when;
|
||||
y[i] = (double) (*xx)->value;
|
||||
}
|
||||
|
||||
double lp0, lp1, fpone;
|
||||
|
||||
lp0 = (x[1] - x[0])/(y[1] - y[0]);
|
||||
lp1 = (x[2] - x[1])/(y[2] - y[1]);
|
||||
|
||||
if (lp0*lp1 < 0) {
|
||||
fpone = 0;
|
||||
} else {
|
||||
fpone = 2 / (lp1 + lp0);
|
||||
}
|
||||
|
||||
double fplast = 0;
|
||||
|
||||
for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
|
||||
|
||||
double xdelta; /* gcc is wrong about possible uninitialized use */
|
||||
double xdelta2; /* ditto */
|
||||
double ydelta; /* ditto */
|
||||
double fppL, fppR;
|
||||
double fpi;
|
||||
|
||||
if (i > 0) {
|
||||
xdelta = x[i] - x[i-1];
|
||||
xdelta2 = xdelta * xdelta;
|
||||
ydelta = y[i] - y[i-1];
|
||||
}
|
||||
|
||||
/* compute (constrained) first derivatives */
|
||||
|
||||
if (i == 0) {
|
||||
|
||||
/* first segment */
|
||||
|
||||
fplast = ((3 * (y[1] - y[0]) / (2 * (x[1] - x[0]))) - (fpone * 0.5));
|
||||
|
||||
/* we don't store coefficients for i = 0 */
|
||||
|
||||
continue;
|
||||
|
||||
} else if (i == npoints - 1) {
|
||||
|
||||
/* last segment */
|
||||
|
||||
fpi = ((3 * ydelta) / (2 * xdelta)) - (fplast * 0.5);
|
||||
|
||||
} else {
|
||||
|
||||
/* all other segments */
|
||||
|
||||
double slope_before = ((x[i+1] - x[i]) / (y[i+1] - y[i]));
|
||||
double slope_after = (xdelta / ydelta);
|
||||
|
||||
if (slope_after * slope_before < 0.0) {
|
||||
/* slope changed sign */
|
||||
fpi = 0.0;
|
||||
} else {
|
||||
fpi = 2 / (slope_before + slope_after);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* compute second derivative for either side of control point `i' */
|
||||
|
||||
fppL = (((-2 * (fpi + (2 * fplast))) / (xdelta))) +
|
||||
((6 * ydelta) / xdelta2);
|
||||
|
||||
fppR = (2 * ((2 * fpi) + fplast) / xdelta) -
|
||||
((6 * ydelta) / xdelta2);
|
||||
|
||||
/* compute polynomial coefficients */
|
||||
|
||||
double b, c, d;
|
||||
|
||||
d = (fppR - fppL) / (6 * xdelta);
|
||||
c = ((x[i] * fppL) - (x[i-1] * fppR))/(2 * xdelta);
|
||||
|
||||
double xim12, xim13;
|
||||
double xi2, xi3;
|
||||
|
||||
xim12 = x[i-1] * x[i-1]; /* "x[i-1] squared" */
|
||||
xim13 = xim12 * x[i-1]; /* "x[i-1] cubed" */
|
||||
xi2 = x[i] * x[i]; /* "x[i] squared" */
|
||||
xi3 = xi2 * x[i]; /* "x[i] cubed" */
|
||||
|
||||
b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta;
|
||||
|
||||
/* store */
|
||||
|
||||
(*xx)->create_coeffs();
|
||||
(*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
|
||||
(*xx)->coeff[1] = b;
|
||||
(*xx)->coeff[2] = c;
|
||||
(*xx)->coeff[3] = d;
|
||||
|
||||
fplast = fpi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen)
|
||||
{
|
||||
Glib::Mutex::Lock lm(_list.lock(), Glib::TRY_LOCK);
|
||||
|
||||
if (!lm.locked()) {
|
||||
return false;
|
||||
} else {
|
||||
_get_vector (x0, x1, vec, veclen);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
|
||||
{
|
||||
Glib::Mutex::Lock lm(_list.lock());
|
||||
_get_vector (x0, x1, vec, veclen);
|
||||
}
|
||||
|
||||
void
|
||||
Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
|
||||
{
|
||||
double rx, dx, lx, hx, max_x, min_x;
|
||||
int32_t i;
|
||||
int32_t original_veclen;
|
||||
int32_t npoints;
|
||||
|
||||
if ((npoints = _list.events().size()) == 0) {
|
||||
for (i = 0; i < veclen; ++i) {
|
||||
vec[i] = _list.default_value();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* events is now known not to be empty */
|
||||
|
||||
max_x = _list.events().back()->when;
|
||||
min_x = _list.events().front()->when;
|
||||
|
||||
lx = max (min_x, x0);
|
||||
|
||||
if (x1 < 0) {
|
||||
x1 = _list.events().back()->when;
|
||||
}
|
||||
|
||||
hx = min (max_x, x1);
|
||||
|
||||
original_veclen = veclen;
|
||||
|
||||
if (x0 < min_x) {
|
||||
|
||||
/* fill some beginning section of the array with the
|
||||
initial (used to be default) value
|
||||
*/
|
||||
|
||||
double frac = (min_x - x0) / (x1 - x0);
|
||||
int32_t subveclen = (int32_t) floor (veclen * frac);
|
||||
|
||||
subveclen = min (subveclen, veclen);
|
||||
|
||||
for (i = 0; i < subveclen; ++i) {
|
||||
vec[i] = _list.events().front()->value;
|
||||
}
|
||||
|
||||
veclen -= subveclen;
|
||||
vec += subveclen;
|
||||
}
|
||||
|
||||
if (veclen && x1 > max_x) {
|
||||
|
||||
/* fill some end section of the array with the default or final value */
|
||||
|
||||
double frac = (x1 - max_x) / (x1 - x0);
|
||||
|
||||
int32_t subveclen = (int32_t) floor (original_veclen * frac);
|
||||
|
||||
float val;
|
||||
|
||||
subveclen = min (subveclen, veclen);
|
||||
|
||||
val = _list.events().back()->value;
|
||||
|
||||
i = veclen - subveclen;
|
||||
|
||||
for (i = veclen - subveclen; i < veclen; ++i) {
|
||||
vec[i] = val;
|
||||
}
|
||||
|
||||
veclen -= subveclen;
|
||||
}
|
||||
|
||||
if (veclen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (npoints == 1 ) {
|
||||
|
||||
for (i = 0; i < veclen; ++i) {
|
||||
vec[i] = _list.events().front()->value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (npoints == 2) {
|
||||
|
||||
/* linear interpolation between 2 points */
|
||||
|
||||
/* XXX I'm not sure that this is the right thing to
|
||||
do here. but its not a common case for the envisaged
|
||||
uses.
|
||||
*/
|
||||
|
||||
if (veclen > 1) {
|
||||
dx = (hx - lx) / (veclen - 1) ;
|
||||
} else {
|
||||
dx = 0; // not used
|
||||
}
|
||||
|
||||
double slope = (_list.events().back()->value - _list.events().front()->value)/
|
||||
(_list.events().back()->when - _list.events().front()->when);
|
||||
double yfrac = dx*slope;
|
||||
|
||||
vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
|
||||
|
||||
for (i = 1; i < veclen; ++i) {
|
||||
vec[i] = vec[i-1] + yfrac;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dirty) {
|
||||
solve ();
|
||||
}
|
||||
|
||||
rx = lx;
|
||||
|
||||
if (veclen > 1) {
|
||||
|
||||
dx = (hx - lx) / veclen;
|
||||
|
||||
for (i = 0; i < veclen; ++i, rx += dx) {
|
||||
vec[i] = multipoint_eval (rx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
Curve::unlocked_eval (double x)
|
||||
{
|
||||
// I don't see the point of this...
|
||||
|
||||
if (_dirty) {
|
||||
solve ();
|
||||
}
|
||||
|
||||
return _list.unlocked_eval (x);
|
||||
}
|
||||
|
||||
double
|
||||
Curve::multipoint_eval (double x)
|
||||
{
|
||||
pair<ControlList::EventList::const_iterator,ControlList::EventList::const_iterator> range;
|
||||
|
||||
ControlList::LookupCache& lookup_cache = _list.lookup_cache();
|
||||
|
||||
if ((lookup_cache.left < 0) ||
|
||||
((lookup_cache.left > x) ||
|
||||
(lookup_cache.range.first == _list.events().end()) ||
|
||||
((*lookup_cache.range.second)->when < x))) {
|
||||
|
||||
ControlEvent cp (x, 0.0);
|
||||
|
||||
lookup_cache.range = equal_range (_list.events().begin(), _list.events().end(), &cp, ControlList::time_comparator);
|
||||
}
|
||||
|
||||
range = lookup_cache.range;
|
||||
|
||||
/* EITHER
|
||||
|
||||
a) x is an existing control point, so first == existing point, second == next point
|
||||
|
||||
OR
|
||||
|
||||
b) x is between control points, so range is empty (first == second, points to where
|
||||
to insert x)
|
||||
|
||||
*/
|
||||
|
||||
if (range.first == range.second) {
|
||||
|
||||
/* x does not exist within the list as a control point */
|
||||
|
||||
lookup_cache.left = x;
|
||||
|
||||
if (range.first == _list.events().begin()) {
|
||||
/* we're before the first point */
|
||||
// return default_value;
|
||||
_list.events().front()->value;
|
||||
}
|
||||
|
||||
if (range.second == _list.events().end()) {
|
||||
/* we're after the last point */
|
||||
return _list.events().back()->value;
|
||||
}
|
||||
|
||||
double x2 = x * x;
|
||||
ControlEvent* ev = *range.second;
|
||||
|
||||
return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
|
||||
}
|
||||
|
||||
/* x is a control point in the data */
|
||||
/* invalidate the cached range because its not usable */
|
||||
lookup_cache.left = -1;
|
||||
return (*range.first)->value;
|
||||
}
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
extern "C" {
|
||||
|
||||
void
|
||||
curve_get_vector_from_c (void *arg, double x0, double x1, float* vec, int32_t vecsize)
|
||||
{
|
||||
static_cast<Evoral::Curve*>(arg)->get_vector (x0, x1, vec, vecsize);
|
||||
}
|
||||
|
||||
}
|
||||
107
libs/evoral/src/Event.cpp
Normal file
107
libs/evoral/src/Event.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <evoral/Event.hpp>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
#ifdef EVENT_ALLOW_ALLOC
|
||||
Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer)
|
||||
: _time(t)
|
||||
, _size(s)
|
||||
, _buffer(b)
|
||||
, _owns_buffer(owns_buffer)
|
||||
{
|
||||
if (owns_buffer) {
|
||||
_buffer = (uint8_t*)malloc(_size);
|
||||
if (b) {
|
||||
memcpy(_buffer, b, _size);
|
||||
} else {
|
||||
memset(_buffer, 0, _size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event::Event(const Event& copy, bool owns_buffer)
|
||||
: _time(copy._time)
|
||||
, _size(copy._size)
|
||||
, _buffer(copy._buffer)
|
||||
, _owns_buffer(owns_buffer)
|
||||
{
|
||||
if (owns_buffer) {
|
||||
_buffer = (uint8_t*)malloc(_size);
|
||||
if (copy._buffer) {
|
||||
memcpy(_buffer, copy._buffer, _size);
|
||||
} else {
|
||||
memset(_buffer, 0, _size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // EVENT_ALLOW_ALLOC
|
||||
|
||||
#ifdef EVENT_WITH_XML
|
||||
|
||||
Event::Event(const XMLNode& event)
|
||||
{
|
||||
string name = event.name();
|
||||
|
||||
if (name == "ControlChange") {
|
||||
|
||||
} else if (name == "ProgramChange") {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<XMLNode>
|
||||
Event::to_xml() const
|
||||
{
|
||||
XMLNode *result = 0;
|
||||
|
||||
switch (type()) {
|
||||
case MIDI_CMD_CONTROL:
|
||||
result = new XMLNode("ControlChange");
|
||||
result->add_property("Channel", channel());
|
||||
result->add_property("Control", cc_number());
|
||||
result->add_property("Value", cc_value());
|
||||
break;
|
||||
|
||||
case MIDI_CMD_PGM_CHANGE:
|
||||
result = new XMLNode("ProgramChange");
|
||||
result->add_property("Channel", channel());
|
||||
result->add_property("Number", pgm_number());
|
||||
break;
|
||||
|
||||
default:
|
||||
// The implementation is continued as needed
|
||||
break;
|
||||
}
|
||||
|
||||
return boost::shared_ptr<XMLNode>(result);
|
||||
}
|
||||
#endif // EVENT_WITH_XML
|
||||
|
||||
} // namespace MIDI
|
||||
|
||||
|
|
@ -1,27 +1,25 @@
|
|||
/*
|
||||
Copyright (C) 2007 Paul Davis
|
||||
Author: Dave Robillard
|
||||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <ardour/note.h>
|
||||
#include <iostream>
|
||||
#include <evoral/Note.hpp>
|
||||
|
||||
namespace ARDOUR {
|
||||
namespace Evoral {
|
||||
|
||||
Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
|
||||
: _on_event(t, 3, NULL, true)
|
||||
|
|
@ -71,14 +69,9 @@ Note::Note(const Note& copy)
|
|||
assert(channel() == copy.channel());
|
||||
}
|
||||
|
||||
|
||||
Note::~Note()
|
||||
{
|
||||
std::cerr << "Note::~Note() Note time: " << time()
|
||||
<< " pitch: " << int(note())
|
||||
<< " duration: " << duration()
|
||||
<< " end-time: " << end_time()
|
||||
<< " velocity: " << int(velocity())
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -107,4 +100,4 @@ Note::operator=(const Note& copy)
|
|||
return *this;
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
} // namespace Evoral
|
||||
643
libs/evoral/src/Sequence.cpp
Normal file
643
libs/evoral/src/Sequence.cpp
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2000-2008 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
#include <evoral/Sequence.hpp>
|
||||
#include <evoral/ControlList.hpp>
|
||||
#include <evoral/Control.hpp>
|
||||
#include <evoral/ControlSet.hpp>
|
||||
#include <evoral/EventSink.hpp>
|
||||
#include <evoral/MIDIParameters.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
void Sequence::write_lock() {
|
||||
_lock.writer_lock();
|
||||
_control_lock.lock();
|
||||
}
|
||||
|
||||
void Sequence::write_unlock() {
|
||||
_lock.writer_unlock();
|
||||
_control_lock.unlock();
|
||||
}
|
||||
|
||||
void Sequence::read_lock() const {
|
||||
_lock.reader_lock();
|
||||
}
|
||||
|
||||
void Sequence::read_unlock() const {
|
||||
_lock.reader_unlock();
|
||||
}
|
||||
|
||||
// Read iterator (const_iterator)
|
||||
|
||||
Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
|
||||
: _seq(&seq)
|
||||
, _is_end( (t == DBL_MAX) || seq.empty() )
|
||||
, _locked( !_is_end )
|
||||
{
|
||||
//cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
|
||||
|
||||
if (_is_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
seq.read_lock();
|
||||
|
||||
_note_iter = seq.notes().end();
|
||||
// find first note which begins after t
|
||||
for (Sequence::Notes::const_iterator i = seq.notes().begin(); i != seq.notes().end(); ++i) {
|
||||
if ((*i)->time() >= t) {
|
||||
_note_iter = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
|
||||
|
||||
_control_iters.reserve(seq.controls().size());
|
||||
|
||||
// find the earliest control event available
|
||||
for (Controls::const_iterator i = seq.controls().begin(); i != seq.controls().end(); ++i) {
|
||||
double x, y;
|
||||
bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
|
||||
if (!ret) {
|
||||
//cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
|
||||
// << ") has no events past " << t << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(x >= 0);
|
||||
|
||||
if (y < i->first.min() || y > i->first.max()) {
|
||||
cerr << "ERROR: Controller (" << i->first.type() << ") value '" << y
|
||||
<< "' out of range [" << i->first.min() << "," << i->first.max()
|
||||
<< "], event ignored" << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
const ControlIterator new_iter(i->second->list(), x, y);
|
||||
|
||||
//cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
|
||||
_control_iters.push_back(new_iter);
|
||||
|
||||
// if the x of the current control is less than earliest_control
|
||||
// we have a new earliest_control
|
||||
if (x < earliest_control.x) {
|
||||
earliest_control = new_iter;
|
||||
_control_iter = _control_iters.end();
|
||||
--_control_iter;
|
||||
// now _control_iter points to the last Element in _control_iters
|
||||
}
|
||||
}
|
||||
|
||||
if (_note_iter != seq.notes().end()) {
|
||||
_event = boost::shared_ptr<Event>(new Event((*_note_iter)->on_event(), true));
|
||||
}
|
||||
|
||||
double time = DBL_MAX;
|
||||
// in case we have no notes in the region, we still want to get controller messages
|
||||
if (_event.get()) {
|
||||
time = _event->time();
|
||||
// if the note is going to make it this turn, advance _note_iter
|
||||
if (earliest_control.x > time) {
|
||||
_active_notes.push(*_note_iter);
|
||||
++_note_iter;
|
||||
}
|
||||
}
|
||||
|
||||
// <=, because we probably would want to send control events first
|
||||
if (earliest_control.list.get() && earliest_control.x <= time) {
|
||||
seq.control_to_midi_event(_event, earliest_control);
|
||||
} else {
|
||||
_control_iter = _control_iters.end();
|
||||
}
|
||||
|
||||
if ( (! _event.get()) || _event->size() == 0) {
|
||||
//cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
|
||||
_is_end = true;
|
||||
|
||||
// eliminate possible race condition here (ugly)
|
||||
static Glib::Mutex mutex;
|
||||
Glib::Mutex::Lock lock(mutex);
|
||||
if (_locked) {
|
||||
_seq->read_unlock();
|
||||
_locked = false;
|
||||
}
|
||||
} else {
|
||||
//printf("New MIDI Iterator = %X @ %lf\n", _event->type(), _event->time());
|
||||
}
|
||||
|
||||
assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0'));
|
||||
}
|
||||
|
||||
Sequence::const_iterator::~const_iterator()
|
||||
{
|
||||
if (_locked) {
|
||||
_seq->read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
const Sequence::const_iterator& Sequence::const_iterator::operator++()
|
||||
{
|
||||
if (_is_end) {
|
||||
throw std::logic_error("Attempt to iterate past end of Sequence");
|
||||
}
|
||||
|
||||
assert(_event->buffer() && _event->buffer()[0] != '\0');
|
||||
|
||||
/*cerr << "const_iterator::operator++: " << _event->to_string() << endl;*/
|
||||
|
||||
if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
|
||||
cerr << "FAILED event buffer: " << hex << int(_event->buffer()[0]) << int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl;
|
||||
}
|
||||
assert((_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()));
|
||||
|
||||
// Increment past current control event
|
||||
if (!_event->is_note() && _control_iter != _control_iters.end() && _control_iter->list.get()) {
|
||||
double x = 0.0, y = 0.0;
|
||||
const bool ret = _control_iter->list->rt_safe_earliest_event_unlocked(
|
||||
_control_iter->x, DBL_MAX, x, y, false);
|
||||
|
||||
if (ret) {
|
||||
_control_iter->x = x;
|
||||
_control_iter->y = y;
|
||||
} else {
|
||||
_control_iter->list.reset();
|
||||
_control_iter->x = DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ControlIterator>::iterator old_control_iter = _control_iter;
|
||||
_control_iter = _control_iters.begin();
|
||||
|
||||
// find the _control_iter with the earliest event time
|
||||
for (std::vector<ControlIterator>::iterator i = _control_iters.begin();
|
||||
i != _control_iters.end(); ++i) {
|
||||
if (i->x < _control_iter->x) {
|
||||
_control_iter = i;
|
||||
}
|
||||
}
|
||||
|
||||
enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
|
||||
|
||||
Type type = NIL;
|
||||
double t = 0;
|
||||
|
||||
// Next earliest note on
|
||||
if (_note_iter != _seq->notes().end()) {
|
||||
type = NOTE_ON;
|
||||
t = (*_note_iter)->time();
|
||||
}
|
||||
|
||||
// Use the next earliest note off iff it's earlier than the note on
|
||||
if (!_seq->percussive() && (! _active_notes.empty())) {
|
||||
if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
|
||||
type = NOTE_OFF;
|
||||
t = _active_notes.top()->end_time();
|
||||
}
|
||||
}
|
||||
|
||||
// Use the next earliest controller iff it's earlier than the note event
|
||||
if (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX /*&& _control_iter != old_control_iter */) {
|
||||
if (type == NIL || _control_iter->x < t) {
|
||||
type = AUTOMATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == NOTE_ON) {
|
||||
//cerr << "********** MIDI Iterator = note on" << endl;
|
||||
*_event = (*_note_iter)->on_event();
|
||||
_active_notes.push(*_note_iter);
|
||||
++_note_iter;
|
||||
} else if (type == NOTE_OFF) {
|
||||
//cerr << "********** MIDI Iterator = note off" << endl;
|
||||
*_event = _active_notes.top()->off_event();
|
||||
_active_notes.pop();
|
||||
} else if (type == AUTOMATION) {
|
||||
//cerr << "********** MIDI Iterator = Automation" << endl;
|
||||
_seq->control_to_midi_event(_event, *_control_iter);
|
||||
} else {
|
||||
//cerr << "********** MIDI Iterator = End" << endl;
|
||||
_is_end = true;
|
||||
}
|
||||
|
||||
assert(_is_end || _event->size() > 0);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Sequence::const_iterator::operator==(const const_iterator& other) const
|
||||
{
|
||||
if (_is_end || other._is_end) {
|
||||
return (_is_end == other._is_end);
|
||||
} else {
|
||||
return (_event == other._event);
|
||||
}
|
||||
}
|
||||
|
||||
Sequence::const_iterator& Sequence::const_iterator::operator=(const const_iterator& other)
|
||||
{
|
||||
if (_locked && _seq != other._seq) {
|
||||
_seq->read_unlock();
|
||||
}
|
||||
|
||||
_seq = other._seq;
|
||||
_active_notes = other._active_notes;
|
||||
_is_end = other._is_end;
|
||||
_locked = other._locked;
|
||||
_note_iter = other._note_iter;
|
||||
_control_iters = other._control_iters;
|
||||
size_t index = other._control_iter - other._control_iters.begin();
|
||||
_control_iter = _control_iters.begin() + index;
|
||||
|
||||
if (!_is_end) {
|
||||
_event = boost::shared_ptr<Event>(new Event(*other._event, true));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sequence
|
||||
|
||||
Sequence::Sequence(size_t size)
|
||||
: _read_iter(*this, DBL_MAX)
|
||||
, _edited(false)
|
||||
, _notes(size)
|
||||
, _writing(false)
|
||||
, _end_iter(*this, DBL_MAX)
|
||||
, _next_read(UINT32_MAX)
|
||||
, _percussive(false)
|
||||
{
|
||||
assert(_end_iter._is_end);
|
||||
assert( ! _end_iter._locked);
|
||||
}
|
||||
|
||||
/** Read events in frame range \a start .. \a start+cnt into \a dst,
|
||||
* adding \a offset to each event's timestamp.
|
||||
* \return number of events written to \a dst
|
||||
*/
|
||||
size_t Sequence::read(EventSink& dst, timestamp_t start, timestamp_t nframes, timedur_t offset) const
|
||||
{
|
||||
//cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
|
||||
//cerr << this << " MM # notes: " << n_notes() << endl;
|
||||
|
||||
size_t read_events = 0;
|
||||
|
||||
if (start != _next_read) {
|
||||
_read_iter = const_iterator(*this, (double)start);
|
||||
//cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
||||
} else {
|
||||
//cerr << "Using cached iterator at " << _next_read << endl;
|
||||
}
|
||||
|
||||
_next_read = start + nframes;
|
||||
|
||||
while (_read_iter != end() && _read_iter->time() < start + nframes) {
|
||||
assert(_read_iter->size() > 0);
|
||||
assert(_read_iter->buffer());
|
||||
dst.write(_read_iter->time() + offset,
|
||||
_read_iter->size(),
|
||||
_read_iter->buffer());
|
||||
|
||||
/*cerr << this << " Sequence::read event @ " << _read_iter->time()
|
||||
<< " type: " << hex << int(_read_iter->type()) << dec
|
||||
<< " note: " << int(_read_iter->note())
|
||||
<< " velocity: " << int(_read_iter->velocity())
|
||||
<< endl;*/
|
||||
|
||||
++_read_iter;
|
||||
++read_events;
|
||||
}
|
||||
|
||||
return read_events;
|
||||
}
|
||||
|
||||
/** Write the controller event pointed to by \a iter to \a ev.
|
||||
* The buffer of \a ev will be allocated or resized as necessary.
|
||||
* \return true on success
|
||||
*/
|
||||
bool
|
||||
Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlIterator& iter) const
|
||||
{
|
||||
assert(iter.list.get());
|
||||
if (!ev) {
|
||||
ev = boost::shared_ptr<Event>(new Event(0, 3, NULL, true));
|
||||
}
|
||||
|
||||
switch (iter.list->parameter().type()) {
|
||||
case midi_cc_type:
|
||||
assert(iter.list.get());
|
||||
assert(iter.list->parameter().channel() < 16);
|
||||
assert(iter.list->parameter().id() <= INT8_MAX);
|
||||
assert(iter.y <= INT8_MAX);
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_CONTROL + iter.list->parameter().channel();
|
||||
ev->buffer()[1] = (uint8_t)iter.list->parameter().id();
|
||||
ev->buffer()[2] = (uint8_t)iter.y;
|
||||
break;
|
||||
|
||||
case midi_pc_type:
|
||||
assert(iter.list.get());
|
||||
assert(iter.list->parameter().channel() < 16);
|
||||
assert(iter.list->parameter().id() == 0);
|
||||
assert(iter.y <= INT8_MAX);
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(2);
|
||||
ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.list->parameter().channel();
|
||||
ev->buffer()[1] = (uint8_t)iter.y;
|
||||
break;
|
||||
|
||||
case midi_pb_type:
|
||||
assert(iter.list.get());
|
||||
assert(iter.list->parameter().channel() < 16);
|
||||
assert(iter.list->parameter().id() == 0);
|
||||
assert(iter.y < (1<<14));
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_BENDER + iter.list->parameter().channel();
|
||||
ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB
|
||||
ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
|
||||
break;
|
||||
|
||||
case midi_ca_type:
|
||||
assert(iter.list.get());
|
||||
assert(iter.list->parameter().channel() < 16);
|
||||
assert(iter.list->parameter().id() == 0);
|
||||
assert(iter.y <= INT8_MAX);
|
||||
|
||||
ev->time() = iter.x;
|
||||
ev->realloc(2);
|
||||
ev->buffer()[0] = MIDI_CMD_CHANNEL_PRESSURE + iter.list->parameter().channel();
|
||||
ev->buffer()[1] = (uint8_t)iter.y;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Clear all events from the model.
|
||||
*/
|
||||
void Sequence::clear()
|
||||
{
|
||||
_lock.writer_lock();
|
||||
_notes.clear();
|
||||
for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
|
||||
li->second->list()->clear();
|
||||
_next_read = 0;
|
||||
_read_iter = end();
|
||||
_lock.writer_unlock();
|
||||
}
|
||||
|
||||
|
||||
/** Begin a write of events to the model.
|
||||
*
|
||||
* If \a mode is Sustained, complete notes with duration are constructed as note
|
||||
* on/off events are received. Otherwise (Percussive), only note on events are
|
||||
* stored; note off events are discarded entirely and all contained notes will
|
||||
* have duration 0.
|
||||
*/
|
||||
void Sequence::start_write()
|
||||
{
|
||||
//cerr << "MM " << this << " START WRITE, PERCUSSIVE = " << _percussive << endl;
|
||||
write_lock();
|
||||
_writing = true;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
_write_notes[i].clear();
|
||||
|
||||
_dirty_controls.clear();
|
||||
write_unlock();
|
||||
}
|
||||
|
||||
/** Finish a write of events to the model.
|
||||
*
|
||||
* If \a delete_stuck is true and the current mode is Sustained, note on events
|
||||
* that were never resolved with a corresonding note off will be deleted.
|
||||
* Otherwise they will remain as notes with duration 0.
|
||||
*/
|
||||
void Sequence::end_write(bool delete_stuck)
|
||||
{
|
||||
write_lock();
|
||||
assert(_writing);
|
||||
|
||||
//cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
|
||||
|
||||
if (!_percussive && delete_stuck) {
|
||||
for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
|
||||
if ((*n)->duration() == 0) {
|
||||
cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
|
||||
n = _notes.erase(n);
|
||||
// we have to break here because erase invalidates the iterator
|
||||
break;
|
||||
} else {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (!_write_notes[i].empty()) {
|
||||
cerr << "WARNING: Sequence::end_write: Channel " << i << " has "
|
||||
<< _write_notes[i].size() << " stuck notes" << endl;
|
||||
}
|
||||
_write_notes[i].clear();
|
||||
}
|
||||
|
||||
for (ControlLists::const_iterator i = _dirty_controls.begin(); i != _dirty_controls.end(); ++i) {
|
||||
(*i)->mark_dirty();
|
||||
}
|
||||
|
||||
_writing = false;
|
||||
write_unlock();
|
||||
}
|
||||
|
||||
/** Append \a ev to model. NOT realtime safe.
|
||||
*
|
||||
* Timestamps of events in \a buf are expected to be relative to
|
||||
* the start of this model (t=0) and MUST be monotonically increasing
|
||||
* and MUST be >= the latest event currently in the model.
|
||||
*/
|
||||
void Sequence::append(const Event& ev)
|
||||
{
|
||||
write_lock();
|
||||
_edited = true;
|
||||
|
||||
assert(_notes.empty() || ev.time() >= _notes.back()->time());
|
||||
assert(_writing);
|
||||
|
||||
if (ev.is_note_on()) {
|
||||
append_note_on_unlocked(ev.channel(), ev.time(), ev.note(),
|
||||
ev.velocity());
|
||||
} else if (ev.is_note_off()) {
|
||||
append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
|
||||
} else if (ev.is_cc()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ContinuousController(midi_cc_type, ev.cc_number(), ev.channel()),
|
||||
ev.time(), ev.cc_value());
|
||||
} else if (ev.is_pgm_change()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ProgramChange(midi_pc_type, ev.channel()),
|
||||
ev.time(), ev.pgm_number());
|
||||
} else if (ev.is_pitch_bender()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::PitchBender(midi_pb_type, ev.channel()),
|
||||
ev.time(), double( (0x7F & ev.pitch_bender_msb()) << 7
|
||||
| (0x7F & ev.pitch_bender_lsb()) ));
|
||||
} else if (ev.is_channel_aftertouch()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ChannelAftertouch(midi_ca_type, ev.channel()),
|
||||
ev.time(), ev.channel_aftertouch());
|
||||
} else {
|
||||
printf("WARNING: Sequence: Unknown event type %X\n", ev.type());
|
||||
}
|
||||
|
||||
write_unlock();
|
||||
}
|
||||
|
||||
void Sequence::append_note_on_unlocked(uint8_t chan, double time,
|
||||
uint8_t note_num, uint8_t velocity)
|
||||
{
|
||||
/*cerr << "Sequence " << this << " chan " << (int)chan <<
|
||||
" note " << (int)note_num << " on @ " << time << endl;*/
|
||||
|
||||
assert(note_num <= 127);
|
||||
assert(chan < 16);
|
||||
assert(_writing);
|
||||
_edited = true;
|
||||
|
||||
boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
|
||||
_notes.push_back(new_note);
|
||||
if (!_percussive) {
|
||||
//cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
|
||||
_write_notes[chan].push_back(_notes.size() - 1);
|
||||
}/* else {
|
||||
cerr << "MM Percussive: NOT appending active note on" << endl;
|
||||
}*/
|
||||
}
|
||||
|
||||
void Sequence::append_note_off_unlocked(uint8_t chan, double time,
|
||||
uint8_t note_num)
|
||||
{
|
||||
/*cerr << "Sequence " << this << " chan " << (int)chan <<
|
||||
" note " << (int)note_num << " off @ " << time << endl;*/
|
||||
|
||||
assert(note_num <= 127);
|
||||
assert(chan < 16);
|
||||
assert(_writing);
|
||||
_edited = true;
|
||||
|
||||
if (_percussive) {
|
||||
cerr << "Sequence Ignoring note off (percussive mode)" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: make _write_notes fixed size (127 noted) for speed */
|
||||
|
||||
/* FIXME: note off velocity for that one guy out there who actually has
|
||||
* keys that send it */
|
||||
|
||||
bool resolved = false;
|
||||
|
||||
for (WriteNotes::iterator n = _write_notes[chan].begin(); n
|
||||
!= _write_notes[chan].end(); ++n) {
|
||||
Note& note = *_notes[*n].get();
|
||||
if (note.note() == note_num) {
|
||||
assert(time >= note.time());
|
||||
note.set_duration(time - note.time());
|
||||
_write_notes[chan].erase(n);
|
||||
//cerr << "MM resolved note, duration: " << note.duration() << endl;
|
||||
resolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
cerr << "Sequence " << this << " spurious note off chan " << (int)chan
|
||||
<< ", note " << (int)note_num << " @ " << time << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::append_control_unlocked(Parameter param, double time, double value)
|
||||
{
|
||||
control(param, true)->list()->rt_add(time, value);
|
||||
}
|
||||
|
||||
|
||||
void Sequence::add_note_unlocked(const boost::shared_ptr<Note> note)
|
||||
{
|
||||
//cerr << "Sequence " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
|
||||
_edited = true;
|
||||
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note,
|
||||
note_time_comparator);
|
||||
_notes.insert(i, note);
|
||||
}
|
||||
|
||||
void Sequence::remove_note_unlocked(const boost::shared_ptr<const Note> note)
|
||||
{
|
||||
_edited = true;
|
||||
//cerr << "Sequence " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
|
||||
for (Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
|
||||
Note& _n = *(*n);
|
||||
const Note& _note = *note;
|
||||
// TODO: There is still the issue, that after restarting ardour
|
||||
// persisted undo does not work, because of rounding errors in the
|
||||
// event times after saving/restoring to/from MIDI files
|
||||
/*cerr << "======================================= " << endl;
|
||||
cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl;
|
||||
cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl;
|
||||
cerr << "Equal: " << bool(_n == _note) << endl;
|
||||
cerr << endl << endl;*/
|
||||
if (_n == _note) {
|
||||
_notes.erase(n);
|
||||
// we have to break here, because erase invalidates all iterators, ie. n itself
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Slow! for debugging only. */
|
||||
#ifndef NDEBUG
|
||||
bool Sequence::is_sorted() const {
|
||||
bool t = 0;
|
||||
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
|
||||
if ((*n)->time() < t)
|
||||
return false;
|
||||
else
|
||||
t = (*n)->time();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
12
libs/evoral/test/sequence.cpp
Normal file
12
libs/evoral/test/sequence.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#include <evoral/Sequence.hpp>
|
||||
|
||||
using namespace Evoral;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
Glib::thread_init();
|
||||
|
||||
Sequence s(100);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ Import('env libraries install_prefix')
|
|||
midi2 = env.Clone()
|
||||
midi2.Merge([ libraries['sigc2'],
|
||||
libraries['xml'],
|
||||
libraries['evoral'],
|
||||
libraries['glibmm2'],
|
||||
libraries['glib2'],
|
||||
libraries['pbd'],
|
||||
|
|
@ -25,7 +26,6 @@ midi2.Append(DOMAIN=domain,MAJOR=2,MINOR=1,MICRO=1)
|
|||
sources = Split("""
|
||||
fd_midiport.cc
|
||||
fifomidi.cc
|
||||
event.cc
|
||||
midi.cc
|
||||
midichannel.cc
|
||||
midifactory.cc
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
#include "midi++/event.h"
|
||||
|
||||
namespace MIDI {
|
||||
|
||||
#ifdef MIDI_EVENT_ALLOW_ALLOC
|
||||
Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer)
|
||||
: _time(t)
|
||||
, _size(s)
|
||||
, _buffer(b)
|
||||
, _owns_buffer(owns_buffer)
|
||||
{
|
||||
if (owns_buffer) {
|
||||
_buffer = (uint8_t*)malloc(_size);
|
||||
if (b) {
|
||||
memcpy(_buffer, b, _size);
|
||||
} else {
|
||||
memset(_buffer, 0, _size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event::Event(const XMLNode& event)
|
||||
{
|
||||
string name = event.name();
|
||||
|
||||
if (name == "ControlChange") {
|
||||
|
||||
} else if (name == "ProgramChange") {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Event::Event(const Event& copy, bool owns_buffer)
|
||||
: _time(copy._time)
|
||||
, _size(copy._size)
|
||||
, _buffer(copy._buffer)
|
||||
, _owns_buffer(owns_buffer)
|
||||
{
|
||||
if (owns_buffer) {
|
||||
_buffer = (uint8_t*)malloc(_size);
|
||||
if (copy._buffer) {
|
||||
memcpy(_buffer, copy._buffer, _size);
|
||||
} else {
|
||||
memset(_buffer, 0, _size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // MIDI_EVENT_ALLOW_ALLOC
|
||||
|
||||
std::string
|
||||
Event::to_string() const
|
||||
{
|
||||
std::ostringstream result(std::ios::ate);
|
||||
result << "MIDI::Event type:" << std::hex << "0x" << int(type()) << " buffer: ";
|
||||
|
||||
for(uint32_t i = 0; i < size(); ++i) {
|
||||
result << " 0x" << int(_buffer[i]);
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
boost::shared_ptr<XMLNode>
|
||||
Event::to_xml() const
|
||||
{
|
||||
XMLNode *result = 0;
|
||||
|
||||
switch (type()) {
|
||||
case MIDI_CMD_CONTROL:
|
||||
result = new XMLNode("ControlChange");
|
||||
result->add_property("Channel", channel());
|
||||
result->add_property("Control", cc_number());
|
||||
result->add_property("Value", cc_value());
|
||||
break;
|
||||
|
||||
case MIDI_CMD_PGM_CHANGE:
|
||||
result = new XMLNode("ProgramChange");
|
||||
result->add_property("Channel", channel());
|
||||
result->add_property("number", pgm_number());
|
||||
break;
|
||||
|
||||
default:
|
||||
// The implementation is continued as needed
|
||||
break;
|
||||
}
|
||||
|
||||
return boost::shared_ptr<XMLNode>(result);
|
||||
}
|
||||
|
||||
} // namespace MIDI
|
||||
|
|
@ -93,7 +93,7 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
|
|||
if (!is_process_thread()) {
|
||||
|
||||
Glib::Mutex::Lock lm (non_process_thread_fifo_lock);
|
||||
RingBuffer<Event>::rw_vector vec;
|
||||
RingBuffer<Evoral::Event>::rw_vector vec;
|
||||
|
||||
non_process_thread_fifo.get_write_vector (&vec);
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
|
|||
void
|
||||
JACK_MidiPort::flush (void* jack_port_buffer)
|
||||
{
|
||||
RingBuffer<Event>::rw_vector vec;
|
||||
RingBuffer<Evoral::Event>::rw_vector vec;
|
||||
size_t written;
|
||||
|
||||
non_process_thread_fifo.get_read_vector (&vec);
|
||||
|
|
@ -167,7 +167,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
|
|||
}
|
||||
|
||||
if (vec.len[0]) {
|
||||
Event* evp = vec.buf[0];
|
||||
Evoral::Event* evp = vec.buf[0];
|
||||
|
||||
for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
|
||||
jack_midi_event_write (jack_port_buffer,
|
||||
|
|
@ -176,7 +176,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
|
|||
}
|
||||
|
||||
if (vec.len[1]) {
|
||||
Event* evp = vec.buf[1];
|
||||
Evoral::Event* evp = vec.buf[1];
|
||||
|
||||
for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
|
||||
jack_midi_event_write (jack_port_buffer,
|
||||
|
|
|
|||
|
|
@ -35,186 +35,11 @@
|
|||
* but MidiEvent will never deep copy and (depending on the scenario)
|
||||
* may not be usable in STL containers, signals, etc.
|
||||
*/
|
||||
#define MIDI_EVENT_ALLOW_ALLOC 1
|
||||
#define EVENT_ALLOW_ALLOC 1
|
||||
|
||||
namespace MIDI {
|
||||
/** Support serialisation of MIDI events to/from XML */
|
||||
#define EVENT_WITH_XML 1
|
||||
|
||||
|
||||
/** Identical to jack_midi_event_t, but with double timestamp
|
||||
*
|
||||
* time is either a frame time (from/to Jack) or a beat time (internal
|
||||
* tempo time, used in MidiModel) depending on context.
|
||||
*/
|
||||
struct Event {
|
||||
#ifdef MIDI_EVENT_ALLOW_ALLOC
|
||||
Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false);
|
||||
|
||||
/** Copy \a copy.
|
||||
*
|
||||
* If \a owns_buffer is true, the buffer will be copied and this method
|
||||
* is NOT REALTIME SAFE. Otherwise both events share a buffer and
|
||||
* memory management semantics are the caller's problem.
|
||||
*/
|
||||
Event(const Event& copy, bool owns_buffer);
|
||||
|
||||
/**
|
||||
* see the MIDI XML specification: http://www.midi.org/dtds/MIDIEvents10.dtd
|
||||
*/
|
||||
Event(const XMLNode &event);
|
||||
|
||||
~Event();
|
||||
|
||||
inline const Event& operator=(const Event& copy) {
|
||||
_time = copy._time;
|
||||
if (_owns_buffer) {
|
||||
if (copy._buffer) {
|
||||
if (copy._size > _size) {
|
||||
_buffer = (uint8_t*)::realloc(_buffer, copy._size);
|
||||
}
|
||||
memcpy(_buffer, copy._buffer, copy._size);
|
||||
} else {
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
}
|
||||
} else {
|
||||
_buffer = copy._buffer;
|
||||
}
|
||||
|
||||
_size = copy._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void shallow_copy(const Event& copy) {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
_buffer = false;
|
||||
_owns_buffer = false;
|
||||
}
|
||||
|
||||
_time = copy._time;
|
||||
_size = copy._size;
|
||||
_buffer = copy._buffer;
|
||||
}
|
||||
|
||||
inline void set(uint8_t* buf, size_t size, double t) {
|
||||
if (_owns_buffer) {
|
||||
if (_size < size) {
|
||||
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
||||
}
|
||||
memcpy (_buffer, buf, size);
|
||||
} else {
|
||||
_buffer = buf;
|
||||
}
|
||||
|
||||
_size = size;
|
||||
_time = t;
|
||||
}
|
||||
|
||||
inline bool operator==(const Event& other) const {
|
||||
if (_time != other._time)
|
||||
return false;
|
||||
|
||||
if (_size != other._size)
|
||||
return false;
|
||||
|
||||
if (_buffer == other._buffer)
|
||||
return true;
|
||||
|
||||
for (size_t i=0; i < _size; ++i)
|
||||
if (_buffer[i] != other._buffer[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Event& other) const { return ! operator==(other); }
|
||||
|
||||
inline bool owns_buffer() const { return _owns_buffer; }
|
||||
|
||||
inline void set_buffer(size_t size, uint8_t* buf, bool own) {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
}
|
||||
_size = size;
|
||||
_buffer = buf;
|
||||
_owns_buffer = own;
|
||||
}
|
||||
|
||||
inline void realloc(size_t size) {
|
||||
if (_owns_buffer) {
|
||||
if (size > _size)
|
||||
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
||||
} else {
|
||||
_buffer = (uint8_t*) ::malloc(size);
|
||||
_owns_buffer = true;
|
||||
}
|
||||
|
||||
_size = size;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
inline void set_buffer(uint8_t* buf) { _buffer = buf; }
|
||||
|
||||
#endif // MIDI_EVENT_ALLOW_ALLOC
|
||||
|
||||
inline double time() const { return _time; }
|
||||
inline double& time() { return _time; }
|
||||
inline uint32_t size() const { return _size; }
|
||||
inline uint32_t& size() { return _size; }
|
||||
inline uint8_t type() const { return (_buffer[0] & 0xF0); }
|
||||
inline void set_type(uint8_t type) { _buffer[0] = (0x0F & _buffer[0]) | (0xF0 & type); }
|
||||
inline uint8_t channel() const { return (_buffer[0] & 0x0F); }
|
||||
inline void set_channel(uint8_t channel) { _buffer[0] = (0xF0 & _buffer[0]) | (0x0F & channel); }
|
||||
inline bool is_note_on() const { return (type() == MIDI_CMD_NOTE_ON); }
|
||||
inline bool is_note_off() const { return (type() == MIDI_CMD_NOTE_OFF); }
|
||||
inline bool is_cc() const { return (type() == MIDI_CMD_CONTROL); }
|
||||
inline bool is_pitch_bender() const { return (type() == MIDI_CMD_BENDER); }
|
||||
inline bool is_pgm_change() const { return (type() == MIDI_CMD_PGM_CHANGE); }
|
||||
inline bool is_note() const { return (is_note_on() || is_note_off()); }
|
||||
inline bool is_aftertouch() const { return (type() == MIDI_CMD_NOTE_PRESSURE); }
|
||||
inline bool is_channel_aftertouch() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); }
|
||||
inline uint8_t note() const { return (_buffer[1]); }
|
||||
inline uint8_t velocity() const { return (_buffer[2]); }
|
||||
inline uint8_t cc_number() const { return (_buffer[1]); }
|
||||
inline uint8_t cc_value() const { return (_buffer[2]); }
|
||||
inline uint8_t pitch_bender_lsb() const { return (_buffer[1]); }
|
||||
inline uint8_t pitch_bender_msb() const { return (_buffer[2]); }
|
||||
inline uint16_t pitch_bender_value() const { return (((0x7F & _buffer[2]) << 7) | (0x7F & _buffer[1])); }
|
||||
inline uint8_t pgm_number() const { return (_buffer[1]); }
|
||||
inline void set_pgm_number(uint8_t number){ _buffer[1] = number; }
|
||||
inline uint8_t aftertouch() const { return (_buffer[1]); }
|
||||
inline uint8_t channel_aftertouch() const { return (_buffer[1]); }
|
||||
// midi channel events range from 0x80 to 0xE0
|
||||
inline bool is_channel_event() const { return (0x80 <= type()) && (type() <= 0xE0); }
|
||||
inline bool is_smf_meta_event() const { return _buffer[0] == 0xFF; }
|
||||
inline bool is_sysex() const { return _buffer[0] == 0xF0 || _buffer[0] == 0xF7; }
|
||||
inline const uint8_t* buffer() const { return _buffer; }
|
||||
inline uint8_t*& buffer() { return _buffer; }
|
||||
|
||||
/**
|
||||
* mainly used for debugging purposes
|
||||
*/
|
||||
std::string to_string() const;
|
||||
|
||||
/**
|
||||
* see the MIDI XML specification: http://www.midi.org/dtds/MIDIEvents10.dtd
|
||||
*/
|
||||
boost::shared_ptr<XMLNode> to_xml() const;
|
||||
|
||||
private:
|
||||
double _time; /**< Sample index (or beat time) at which event is valid */
|
||||
uint32_t _size; /**< Number of uint8_ts of data in \a buffer */
|
||||
uint8_t* _buffer; /**< Raw MIDI data */
|
||||
|
||||
#ifdef MIDI_EVENT_ALLOW_ALLOC
|
||||
bool _owns_buffer; /**< Whether buffer is locally allocated */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
#include <evoral/Event.hpp>
|
||||
|
||||
#endif /* __libmidipp_midi_event_h__ */
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ private:
|
|||
static pthread_t _process_thread;
|
||||
static bool is_process_thread();
|
||||
|
||||
RingBuffer<MIDI::Event> non_process_thread_fifo;
|
||||
RingBuffer<Evoral::Event> non_process_thread_fifo;
|
||||
Glib::Mutex non_process_thread_fifo_lock;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Name
|
|||
class Patch : public PBD::Stateful
|
||||
{
|
||||
public:
|
||||
typedef std::list<MIDI::Event> PatchMidiCommands;
|
||||
typedef std::list<Evoral::Event> PatchMidiCommands;
|
||||
|
||||
Patch() {};
|
||||
Patch(string a_number, string a_name) : _number(a_number), _name(a_name) {};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Patch::set_state (const XMLNode& node)
|
|||
assert(commands);
|
||||
const XMLNodeList events = commands->children();
|
||||
for (XMLNodeList::const_iterator i = events.begin(); i != events.end(); ++i) {
|
||||
_patch_midi_commands.push_back(*(new Event(*(*i))));
|
||||
_patch_midi_commands.push_back(*(new Evoral::Event(*(*i))));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -132,13 +132,16 @@ ChannelNameSet::set_state (const XMLNode& node)
|
|||
}
|
||||
|
||||
int
|
||||
MIDINameDocument::set_state(const XMLNode & a_node)
|
||||
MIDINameDocument::set_state(const XMLNode& a_node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MIDINameDocument::get_state(void)
|
||||
{
|
||||
static XMLNode nothing("<nothing>");
|
||||
return nothing;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ cp.Merge ([
|
|||
libraries['sigc2'],
|
||||
libraries['pbd'],
|
||||
libraries['midi++2'],
|
||||
libraries['evoral'],
|
||||
libraries['xml'],
|
||||
libraries['glib2'],
|
||||
libraries['glibmm2']
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ tranzport.Merge ([
|
|||
libraries['sigc2'],
|
||||
libraries['pbd'],
|
||||
libraries['midi++2'],
|
||||
libraries['evoral'],
|
||||
libraries['xml'],
|
||||
libraries['usb'],
|
||||
libraries['glib2'],
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ genericmidi.Merge ([
|
|||
libraries['ardour_cp'],
|
||||
libraries['sndfile'],
|
||||
libraries['midi++2'],
|
||||
libraries['evoral'],
|
||||
libraries['pbd'],
|
||||
libraries['sigc2'],
|
||||
libraries['usb'],
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue