mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-18 04:36:30 +01:00
introduce the notion that note additions and property changes can cause the removal of other notes because of overlaps; merge Diff and Delta commands in MidiModel; fix marshalling of notes to avoid float->int conversion of length+time properties; initial implementation (not tested much so far) of different policies for how to handle note overlaps
git-svn-id: svn://localhost/ardour2/branches/3.0@7254 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
fc611af4b4
commit
fddb377812
8 changed files with 683 additions and 558 deletions
|
|
@ -83,7 +83,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
|
||||||
, _custom_device_mode(string())
|
, _custom_device_mode(string())
|
||||||
, _active_notes(0)
|
, _active_notes(0)
|
||||||
, _note_group(new ArdourCanvas::Group(*parent))
|
, _note_group(new ArdourCanvas::Group(*parent))
|
||||||
, _delta_command(0)
|
|
||||||
, _diff_command(0)
|
, _diff_command(0)
|
||||||
, _ghost_note(0)
|
, _ghost_note(0)
|
||||||
, _drag_rect (0)
|
, _drag_rect (0)
|
||||||
|
|
@ -108,7 +107,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
|
||||||
, _custom_device_mode(string())
|
, _custom_device_mode(string())
|
||||||
, _active_notes(0)
|
, _active_notes(0)
|
||||||
, _note_group(new ArdourCanvas::Group(*parent))
|
, _note_group(new ArdourCanvas::Group(*parent))
|
||||||
, _delta_command(0)
|
|
||||||
, _diff_command(0)
|
, _diff_command(0)
|
||||||
, _ghost_note(0)
|
, _ghost_note(0)
|
||||||
, _drag_rect (0)
|
, _drag_rect (0)
|
||||||
|
|
@ -132,7 +130,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
|
||||||
, _custom_device_mode(string())
|
, _custom_device_mode(string())
|
||||||
, _active_notes(0)
|
, _active_notes(0)
|
||||||
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
|
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
|
||||||
, _delta_command(0)
|
|
||||||
, _diff_command(0)
|
, _diff_command(0)
|
||||||
, _ghost_note(0)
|
, _ghost_note(0)
|
||||||
, _drag_rect (0)
|
, _drag_rect (0)
|
||||||
|
|
@ -160,7 +157,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
|
||||||
, _custom_device_mode(string())
|
, _custom_device_mode(string())
|
||||||
, _active_notes(0)
|
, _active_notes(0)
|
||||||
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
|
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
|
||||||
, _delta_command(0)
|
|
||||||
, _diff_command(0)
|
, _diff_command(0)
|
||||||
, _ghost_note(0)
|
, _ghost_note(0)
|
||||||
, _drag_rect (0)
|
, _drag_rect (0)
|
||||||
|
|
@ -680,7 +676,7 @@ MidiRegionView::create_note_at(double x, double y, double length)
|
||||||
|
|
||||||
view->update_note_range(new_note->note());
|
view->update_note_range(new_note->note());
|
||||||
|
|
||||||
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
|
MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
|
||||||
cmd->add(new_note);
|
cmd->add(new_note);
|
||||||
_model->apply_command(*trackview.session(), cmd);
|
_model->apply_command(*trackview.session(), cmd);
|
||||||
|
|
||||||
|
|
@ -724,15 +720,6 @@ MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiRegionView::start_delta_command(string name)
|
|
||||||
{
|
|
||||||
if (!_delta_command) {
|
|
||||||
_delta_command = _model->new_delta_command(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::start_diff_command(string name)
|
MidiRegionView::start_diff_command(string name)
|
||||||
{
|
{
|
||||||
|
|
@ -742,10 +729,10 @@ MidiRegionView::start_diff_command(string name)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
|
MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
|
||||||
{
|
{
|
||||||
if (_delta_command) {
|
if (_diff_command) {
|
||||||
_delta_command->add(note);
|
_diff_command->add(note);
|
||||||
}
|
}
|
||||||
if (selected) {
|
if (selected) {
|
||||||
_marked_for_selection.insert(note);
|
_marked_for_selection.insert(note);
|
||||||
|
|
@ -756,10 +743,10 @@ MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool sele
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
|
MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
|
||||||
{
|
{
|
||||||
if (_delta_command && ev->note()) {
|
if (_diff_command && ev->note()) {
|
||||||
_delta_command->remove(ev->note());
|
_diff_command->remove(ev->note());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -784,84 +771,63 @@ MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::apply_delta()
|
MidiRegionView::apply_diff ()
|
||||||
{
|
{
|
||||||
if (!_delta_command) {
|
bool add_or_remove;
|
||||||
|
|
||||||
|
if (!_diff_command) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((add_or_remove = _diff_command->adds_or_removes())) {
|
||||||
// Mark all selected notes for selection when model reloads
|
// Mark all selected notes for selection when model reloads
|
||||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||||
_marked_for_selection.insert((*i)->note());
|
_marked_for_selection.insert((*i)->note());
|
||||||
}
|
}
|
||||||
|
|
||||||
_model->apply_command(*trackview.session(), _delta_command);
|
|
||||||
_delta_command = 0;
|
|
||||||
midi_view()->midi_track()->playlist_modified();
|
|
||||||
|
|
||||||
_marked_for_selection.clear();
|
|
||||||
_marked_for_velocity.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiRegionView::apply_diff ()
|
|
||||||
{
|
|
||||||
if (!_diff_command) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_model->apply_command(*trackview.session(), _diff_command);
|
_model->apply_command(*trackview.session(), _diff_command);
|
||||||
_diff_command = 0;
|
_diff_command = 0;
|
||||||
midi_view()->midi_track()->playlist_modified();
|
midi_view()->midi_track()->playlist_modified();
|
||||||
|
|
||||||
_marked_for_velocity.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiRegionView::apply_delta_as_subcommand()
|
|
||||||
{
|
|
||||||
if (!_delta_command) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark all selected notes for selection when model reloads
|
|
||||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
||||||
_marked_for_selection.insert((*i)->note());
|
|
||||||
}
|
|
||||||
|
|
||||||
_model->apply_command_as_subcommand(*trackview.session(), _delta_command);
|
|
||||||
_delta_command = 0;
|
|
||||||
midi_view()->midi_track()->playlist_modified();
|
|
||||||
|
|
||||||
|
if (add_or_remove) {
|
||||||
_marked_for_selection.clear();
|
_marked_for_selection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
_marked_for_velocity.clear();
|
_marked_for_velocity.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::apply_diff_as_subcommand()
|
MidiRegionView::apply_diff_as_subcommand()
|
||||||
{
|
{
|
||||||
|
bool add_or_remove;
|
||||||
|
|
||||||
if (!_diff_command) {
|
if (!_diff_command) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((add_or_remove = _diff_command->adds_or_removes())) {
|
||||||
// Mark all selected notes for selection when model reloads
|
// Mark all selected notes for selection when model reloads
|
||||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||||
_marked_for_selection.insert((*i)->note());
|
_marked_for_selection.insert((*i)->note());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_model->apply_command_as_subcommand(*trackview.session(), _diff_command);
|
_model->apply_command_as_subcommand(*trackview.session(), _diff_command);
|
||||||
_diff_command = 0;
|
_diff_command = 0;
|
||||||
midi_view()->midi_track()->playlist_modified();
|
midi_view()->midi_track()->playlist_modified();
|
||||||
|
|
||||||
|
if (add_or_remove) {
|
||||||
_marked_for_selection.clear();
|
_marked_for_selection.clear();
|
||||||
|
}
|
||||||
_marked_for_velocity.clear();
|
_marked_for_velocity.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::abort_command()
|
MidiRegionView::abort_command()
|
||||||
{
|
{
|
||||||
delete _delta_command;
|
|
||||||
_delta_command = 0;
|
|
||||||
delete _diff_command;
|
delete _diff_command;
|
||||||
_diff_command = 0;
|
_diff_command = 0;
|
||||||
clear_selection();
|
clear_selection();
|
||||||
|
|
@ -1096,7 +1062,7 @@ MidiRegionView::~MidiRegionView ()
|
||||||
_selection.clear();
|
_selection.clear();
|
||||||
clear_events();
|
clear_events();
|
||||||
delete _note_group;
|
delete _note_group;
|
||||||
delete _delta_command;
|
delete _diff_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1457,9 +1423,9 @@ MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
|
||||||
{
|
{
|
||||||
boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
|
boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
|
||||||
|
|
||||||
start_delta_command (_("step add"));
|
start_diff_command (_("step add"));
|
||||||
delta_add_note (new_note, true, false);
|
diff_add_note (new_note, true, false);
|
||||||
apply_delta();
|
apply_diff();
|
||||||
|
|
||||||
/* potentially extend region to hold new note */
|
/* potentially extend region to hold new note */
|
||||||
|
|
||||||
|
|
@ -1627,25 +1593,25 @@ MidiRegionView::delete_selection()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_delta_command (_("delete selection"));
|
start_diff_command (_("delete selection"));
|
||||||
|
|
||||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||||
if ((*i)->selected()) {
|
if ((*i)->selected()) {
|
||||||
_delta_command->remove((*i)->note());
|
_diff_command->remove((*i)->note());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_selection.clear();
|
_selection.clear();
|
||||||
|
|
||||||
apply_delta ();
|
apply_diff ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
|
MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
|
||||||
{
|
{
|
||||||
start_delta_command (_("delete note"));
|
start_diff_command (_("delete note"));
|
||||||
_delta_command->remove (n);
|
_diff_command->remove (n);
|
||||||
apply_delta ();
|
apply_diff ();
|
||||||
|
|
||||||
trackview.editor().hide_verbose_canvas_cursor ();
|
trackview.editor().hide_verbose_canvas_cursor ();
|
||||||
}
|
}
|
||||||
|
|
@ -2622,7 +2588,7 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
|
||||||
|
|
||||||
if (op != Copy) {
|
if (op != Copy) {
|
||||||
|
|
||||||
start_delta_command();
|
start_diff_command();
|
||||||
|
|
||||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
|
|
@ -2630,12 +2596,12 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
|
||||||
break;
|
break;
|
||||||
case Cut:
|
case Cut:
|
||||||
case Clear:
|
case Clear:
|
||||||
delta_remove_note (*i);
|
diff_remove_note (*i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_delta();
|
apply_diff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2662,7 +2628,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_delta_command (_("paste"));
|
start_diff_command (_("paste"));
|
||||||
|
|
||||||
Evoral::MusicalTime beat_delta;
|
Evoral::MusicalTime beat_delta;
|
||||||
Evoral::MusicalTime paste_pos_beats;
|
Evoral::MusicalTime paste_pos_beats;
|
||||||
|
|
@ -2678,8 +2644,6 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||||
|
|
||||||
for (int n = 0; n < (int) times; ++n) {
|
for (int n = 0; n < (int) times; ++n) {
|
||||||
|
|
||||||
cerr << "Pasting " << mcb.notes().size() << " for the " << n+1 << "th time\n";
|
|
||||||
|
|
||||||
for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
|
for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
|
||||||
|
|
||||||
boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
|
boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
|
||||||
|
|
@ -2687,7 +2651,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||||
|
|
||||||
/* make all newly added notes selected */
|
/* make all newly added notes selected */
|
||||||
|
|
||||||
delta_add_note (copied_note, true);
|
diff_add_note (copied_note, true);
|
||||||
end_point = copied_note->end_time();
|
end_point = copied_note->end_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2701,8 +2665,6 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||||
|
|
||||||
if (end_frame > region_end) {
|
if (end_frame > region_end) {
|
||||||
|
|
||||||
cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
|
|
||||||
|
|
||||||
trackview.session()->begin_reversible_command (_("paste"));
|
trackview.session()->begin_reversible_command (_("paste"));
|
||||||
|
|
||||||
_region->clear_history ();
|
_region->clear_history ();
|
||||||
|
|
@ -2710,8 +2672,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||||
trackview.session()->add_command (new StatefulDiffCommand (_region));
|
trackview.session()->add_command (new StatefulDiffCommand (_region));
|
||||||
}
|
}
|
||||||
|
|
||||||
cerr << "region end finally at " << _region->position() + _region->length() - 1;
|
apply_diff ();
|
||||||
apply_delta ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventNoteTimeEarlyFirstComparator {
|
struct EventNoteTimeEarlyFirstComparator {
|
||||||
|
|
|
||||||
|
|
@ -173,17 +173,13 @@ class MidiRegionView : public RegionView
|
||||||
|
|
||||||
void display_model(boost::shared_ptr<ARDOUR::MidiModel> model);
|
void display_model(boost::shared_ptr<ARDOUR::MidiModel> model);
|
||||||
|
|
||||||
void start_delta_command(std::string name = "midi edit");
|
|
||||||
void delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity=false);
|
|
||||||
void delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev);
|
|
||||||
|
|
||||||
void start_diff_command(std::string name = "midi edit");
|
void start_diff_command(std::string name = "midi edit");
|
||||||
void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, uint8_t val);
|
void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, uint8_t val);
|
||||||
void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, Evoral::MusicalTime val);
|
void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, Evoral::MusicalTime val);
|
||||||
|
void diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity=false);
|
||||||
|
void diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev);
|
||||||
|
|
||||||
void apply_delta();
|
|
||||||
void apply_diff();
|
void apply_diff();
|
||||||
void apply_delta_as_subcommand();
|
|
||||||
void apply_diff_as_subcommand();
|
void apply_diff_as_subcommand();
|
||||||
void abort_command();
|
void abort_command();
|
||||||
|
|
||||||
|
|
@ -355,7 +351,6 @@ class MidiRegionView : public RegionView
|
||||||
SysExes _sys_exes;
|
SysExes _sys_exes;
|
||||||
ArdourCanvas::CanvasNote** _active_notes;
|
ArdourCanvas::CanvasNote** _active_notes;
|
||||||
ArdourCanvas::Group* _note_group;
|
ArdourCanvas::Group* _note_group;
|
||||||
ARDOUR::MidiModel::DeltaCommand* _delta_command;
|
|
||||||
ARDOUR::MidiModel::DiffCommand* _diff_command;
|
ARDOUR::MidiModel::DiffCommand* _diff_command;
|
||||||
ArdourCanvas::CanvasNote* _ghost_note;
|
ArdourCanvas::CanvasNote* _ghost_note;
|
||||||
double _last_ghost_x;
|
double _last_ghost_x;
|
||||||
|
|
|
||||||
|
|
@ -49,53 +49,13 @@ class MidiSource;
|
||||||
*/
|
*/
|
||||||
class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
|
class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
|
||||||
public:
|
public:
|
||||||
typedef double TimeType;
|
typedef Evoral::MusicalTime TimeType;
|
||||||
|
|
||||||
MidiModel(MidiSource* s);
|
MidiModel(MidiSource* s);
|
||||||
|
|
||||||
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
|
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
|
||||||
void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
|
void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
|
||||||
|
|
||||||
/** Add/Remove notes.
|
|
||||||
* Technically all note operations can be implemented as one of these, but
|
|
||||||
* a custom command can be more efficient.
|
|
||||||
*/
|
|
||||||
class DeltaCommand : public Command {
|
|
||||||
public:
|
|
||||||
DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
|
|
||||||
DeltaCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
|
|
||||||
|
|
||||||
const std::string& name() const { return _name; }
|
|
||||||
|
|
||||||
void operator()();
|
|
||||||
void undo();
|
|
||||||
|
|
||||||
int set_state (const XMLNode&, int version);
|
|
||||||
XMLNode& get_state ();
|
|
||||||
|
|
||||||
void add(const boost::shared_ptr< Evoral::Note<TimeType> > note);
|
|
||||||
void remove(const boost::shared_ptr< Evoral::Note<TimeType> > note);
|
|
||||||
|
|
||||||
private:
|
|
||||||
XMLNode &marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note);
|
|
||||||
boost::shared_ptr< Evoral::Note<TimeType> > unmarshal_note(XMLNode *xml_note);
|
|
||||||
|
|
||||||
boost::shared_ptr<MidiModel> _model;
|
|
||||||
const std::string _name;
|
|
||||||
|
|
||||||
typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
|
|
||||||
|
|
||||||
NoteList _added_notes;
|
|
||||||
NoteList _removed_notes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** Change note properties.
|
|
||||||
* More efficient than DeltaCommand and has the important property that
|
|
||||||
* it leaves the objects in the MidiModel (Notes) the same, thus
|
|
||||||
* enabling selection and other state to persist across command
|
|
||||||
* do/undo/redo.
|
|
||||||
*/
|
|
||||||
class DiffCommand : public Command {
|
class DiffCommand : public Command {
|
||||||
public:
|
public:
|
||||||
enum Property {
|
enum Property {
|
||||||
|
|
@ -117,10 +77,15 @@ public:
|
||||||
int set_state (const XMLNode&, int version);
|
int set_state (const XMLNode&, int version);
|
||||||
XMLNode& get_state ();
|
XMLNode& get_state ();
|
||||||
|
|
||||||
void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
|
void add(const NotePtr note);
|
||||||
Property prop, uint8_t new_value);
|
void remove(const NotePtr note);
|
||||||
void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
|
|
||||||
Property prop, TimeType new_time);
|
void change (const NotePtr note, Property prop, uint8_t new_value);
|
||||||
|
void change (const NotePtr note, Property prop, TimeType new_time);
|
||||||
|
|
||||||
|
bool adds_or_removes() const {
|
||||||
|
return !_added_notes.empty() || !_removed_notes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::shared_ptr<MidiModel> _model;
|
boost::shared_ptr<MidiModel> _model;
|
||||||
|
|
@ -128,7 +93,7 @@ public:
|
||||||
|
|
||||||
struct NoteChange {
|
struct NoteChange {
|
||||||
DiffCommand::Property property;
|
DiffCommand::Property property;
|
||||||
boost::shared_ptr< Evoral::Note<TimeType> > note;
|
NotePtr note;
|
||||||
union {
|
union {
|
||||||
uint8_t old_value;
|
uint8_t old_value;
|
||||||
TimeType old_time;
|
TimeType old_time;
|
||||||
|
|
@ -142,11 +107,19 @@ public:
|
||||||
typedef std::list<NoteChange> ChangeList;
|
typedef std::list<NoteChange> ChangeList;
|
||||||
ChangeList _changes;
|
ChangeList _changes;
|
||||||
|
|
||||||
|
typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
|
||||||
|
NoteList _added_notes;
|
||||||
|
NoteList _removed_notes;
|
||||||
|
|
||||||
|
std::set<NotePtr> side_effect_removals;
|
||||||
|
|
||||||
XMLNode &marshal_change(const NoteChange&);
|
XMLNode &marshal_change(const NoteChange&);
|
||||||
NoteChange unmarshal_change(XMLNode *xml_note);
|
NoteChange unmarshal_change(XMLNode *xml_note);
|
||||||
|
|
||||||
|
XMLNode &marshal_note(const NotePtr note);
|
||||||
|
NotePtr unmarshal_note(XMLNode *xml_note);
|
||||||
};
|
};
|
||||||
|
|
||||||
MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
|
|
||||||
MidiModel::DiffCommand* new_diff_command(const std::string name="midi edit");
|
MidiModel::DiffCommand* new_diff_command(const std::string name="midi edit");
|
||||||
void apply_command(Session& session, Command* cmd);
|
void apply_command(Session& session, Command* cmd);
|
||||||
void apply_command_as_subcommand(Session& session, Command* cmd);
|
void apply_command_as_subcommand(Session& session, Command* cmd);
|
||||||
|
|
@ -165,12 +138,18 @@ public:
|
||||||
const MidiSource* midi_source() const { return _midi_source; }
|
const MidiSource* midi_source() const { return _midi_source; }
|
||||||
void set_midi_source(MidiSource* source) { _midi_source = source; }
|
void set_midi_source(MidiSource* source) { _midi_source = source; }
|
||||||
|
|
||||||
boost::shared_ptr<Evoral::Note<TimeType> > find_note (boost::shared_ptr<Evoral::Note<TimeType> >);
|
boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr);
|
||||||
|
|
||||||
|
InsertMergePolicy insert_merge_policy () const;
|
||||||
|
void set_insert_merge_policy (InsertMergePolicy);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int resolve_overlaps_unlocked (const NotePtr, std::set<NotePtr>* removed = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct WriteLockImpl : public AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl {
|
struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl {
|
||||||
WriteLockImpl(Glib::Mutex::Lock* source_lock, Glib::RWLock& s, Glib::Mutex& c)
|
WriteLockImpl(Glib::Mutex::Lock* source_lock, Glib::RWLock& s, Glib::Mutex& c)
|
||||||
: AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl(s, c)
|
: AutomatableSequence<TimeType>::WriteLockImpl(s, c)
|
||||||
, source_lock(source_lock)
|
, source_lock(source_lock)
|
||||||
{}
|
{}
|
||||||
~WriteLockImpl() {
|
~WriteLockImpl() {
|
||||||
|
|
@ -188,6 +167,7 @@ private:
|
||||||
|
|
||||||
// We cannot use a boost::shared_ptr here to avoid a retain cycle
|
// We cannot use a boost::shared_ptr here to avoid a retain cycle
|
||||||
MidiSource* _midi_source;
|
MidiSource* _midi_source;
|
||||||
|
InsertMergePolicy _insert_merge_policy;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace ARDOUR */
|
} /* namespace ARDOUR */
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,19 @@ namespace ARDOUR {
|
||||||
ARDOUR::OverlapType coverage (framepos_t sa, framepos_t ea,
|
ARDOUR::OverlapType coverage (framepos_t sa, framepos_t ea,
|
||||||
framepos_t sb, framepos_t eb);
|
framepos_t sb, framepos_t eb);
|
||||||
|
|
||||||
|
/* policies for inserting/pasting material where overlaps
|
||||||
|
might be an issue.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum InsertMergePolicy {
|
||||||
|
InsertMergeReject, // no overlaps allowed
|
||||||
|
InsertMergeRelax, // we just don't care about overlaps
|
||||||
|
InsertMergeReplace, // replace old with new
|
||||||
|
InsertMergeTruncateExisting, // shorten existing to avoid overlap
|
||||||
|
InsertMergeTruncateAddition, // shorten new to avoid overlap
|
||||||
|
InsertMergeExtend // extend new (or old) to the range of old+new
|
||||||
|
};
|
||||||
|
|
||||||
/** See parameter.h
|
/** See parameter.h
|
||||||
* XXX: I don't think/hope these hex values matter anymore.
|
* XXX: I don't think/hope these hex values matter anymore.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define __STDC_LIMIT_MACROS 1
|
#define __STDC_LIMIT_MACROS 1
|
||||||
|
#include <set>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
@ -45,19 +45,6 @@ MidiModel::MidiModel(MidiSource* s)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start a new Delta command.
|
|
||||||
*
|
|
||||||
* This has no side-effects on the model or Session, the returned command
|
|
||||||
* can be held on to for as long as the caller wishes, or discarded without
|
|
||||||
* formality, until apply_command is called and ownership is taken.
|
|
||||||
*/
|
|
||||||
MidiModel::DeltaCommand*
|
|
||||||
MidiModel::new_delta_command(const string name)
|
|
||||||
{
|
|
||||||
DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start a new Diff command.
|
/** Start a new Diff command.
|
||||||
*
|
*
|
||||||
* This has no side-effects on the model or Session, the returned command
|
* This has no side-effects on the model or Session, the returned command
|
||||||
|
|
@ -98,10 +85,15 @@ MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
|
||||||
set_edited(true);
|
set_edited(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/************** DIFF COMMAND ********************/
|
||||||
|
|
||||||
// DeltaCommand
|
#define DIFF_COMMAND_ELEMENT "DiffCommand"
|
||||||
|
#define DIFF_NOTES_ELEMENT "ChangedNotes"
|
||||||
|
#define ADDED_NOTES_ELEMENT "AddedNotes"
|
||||||
|
#define REMOVED_NOTES_ELEMENT "RemovedNotes"
|
||||||
|
#define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
|
||||||
|
|
||||||
MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
|
MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
|
||||||
: Command(name)
|
: Command(name)
|
||||||
, _model(m)
|
, _model(m)
|
||||||
, _name(name)
|
, _name(name)
|
||||||
|
|
@ -109,7 +101,7 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std:
|
||||||
assert(_model);
|
assert(_model);
|
||||||
}
|
}
|
||||||
|
|
||||||
MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
|
MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
|
||||||
: _model(m)
|
: _model(m)
|
||||||
{
|
{
|
||||||
assert(_model);
|
assert(_model);
|
||||||
|
|
@ -117,25 +109,101 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLN
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note<TimeType> > note)
|
MidiModel::DiffCommand::add(const NotePtr note)
|
||||||
{
|
{
|
||||||
_removed_notes.remove(note);
|
_removed_notes.remove(note);
|
||||||
_added_notes.push_back(note);
|
_added_notes.push_back(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note<TimeType> > note)
|
MidiModel::DiffCommand::remove(const NotePtr note)
|
||||||
{
|
{
|
||||||
_added_notes.remove(note);
|
_added_notes.remove(note);
|
||||||
_removed_notes.push_back(note);
|
_removed_notes.push_back(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::DeltaCommand::operator()()
|
MidiModel::DiffCommand::change(const NotePtr note, Property prop,
|
||||||
|
uint8_t new_value)
|
||||||
{
|
{
|
||||||
// This could be made much faster by using a priority_queue for added and
|
NoteChange change;
|
||||||
// removed notes (or sort here), and doing a single iteration over _model
|
|
||||||
|
|
||||||
|
switch (prop) {
|
||||||
|
case NoteNumber:
|
||||||
|
if (new_value == note->note()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change.old_value = note->note();
|
||||||
|
break;
|
||||||
|
case Velocity:
|
||||||
|
if (new_value == note->velocity()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change.old_value = note->velocity();
|
||||||
|
break;
|
||||||
|
case Channel:
|
||||||
|
if (new_value == note->channel()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change.old_value = note->channel();
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case StartTime:
|
||||||
|
fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
|
||||||
|
/*NOTREACHED*/
|
||||||
|
break;
|
||||||
|
case Length:
|
||||||
|
fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
|
||||||
|
/*NOTREACHED*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
change.note = note;
|
||||||
|
change.property = prop;
|
||||||
|
change.new_value = new_value;
|
||||||
|
|
||||||
|
_changes.push_back (change);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiModel::DiffCommand::change(const NotePtr note, Property prop,
|
||||||
|
TimeType new_time)
|
||||||
|
{
|
||||||
|
NoteChange change;
|
||||||
|
|
||||||
|
switch (prop) {
|
||||||
|
case NoteNumber:
|
||||||
|
case Channel:
|
||||||
|
case Velocity:
|
||||||
|
fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StartTime:
|
||||||
|
if (Evoral::musical_time_equal (note->time(), new_time)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change.old_time = note->time();
|
||||||
|
break;
|
||||||
|
case Length:
|
||||||
|
if (Evoral::musical_time_equal (note->length(), new_time)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change.old_time = note->length();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
change.note = note;
|
||||||
|
change.property = prop;
|
||||||
|
change.new_time = new_time;
|
||||||
|
|
||||||
|
_changes.push_back (change);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiModel::DiffCommand::operator()()
|
||||||
|
{
|
||||||
|
{
|
||||||
MidiModel::WriteLock lock(_model->edit_lock());
|
MidiModel::WriteLock lock(_model->edit_lock());
|
||||||
|
|
||||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||||
|
|
@ -146,17 +214,67 @@ MidiModel::DeltaCommand::operator()()
|
||||||
_model->remove_note_unlocked(*i);
|
_model->remove_note_unlocked(*i);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.reset();
|
/* notes we modify in a way that requires remove-then-add to maintain ordering */
|
||||||
|
set<NotePtr> temporary_removals;
|
||||||
|
|
||||||
|
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
|
||||||
|
Property prop = i->property;
|
||||||
|
switch (prop) {
|
||||||
|
case NoteNumber:
|
||||||
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
|
_model->remove_note_unlocked (i->note);
|
||||||
|
temporary_removals.insert (i->note);
|
||||||
|
}
|
||||||
|
i->note->set_note (i->new_value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Velocity:
|
||||||
|
i->note->set_velocity (i->new_value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StartTime:
|
||||||
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
|
_model->remove_note_unlocked (i->note);
|
||||||
|
temporary_removals.insert (i->note);
|
||||||
|
|
||||||
|
}
|
||||||
|
i->note->set_time (i->new_time);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Length:
|
||||||
|
i->note->set_length (i->new_time);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Channel:
|
||||||
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
|
_model->remove_note_unlocked (i->note);
|
||||||
|
temporary_removals.insert (i->note);
|
||||||
|
}
|
||||||
|
i->note->set_channel (i->new_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
|
||||||
|
_model->add_note_unlocked (*i, &side_effect_removals);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!side_effect_removals.empty()) {
|
||||||
|
cerr << "SER: \n";
|
||||||
|
for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
|
||||||
|
cerr << "\t" << *i << ' ' << **i << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::DeltaCommand::undo()
|
MidiModel::DiffCommand::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
|
MidiModel::WriteLock lock(_model->edit_lock());
|
||||||
|
|
||||||
MidiModel::WriteLock lock(_model->edit_lock());;
|
|
||||||
|
|
||||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||||
_model->remove_note_unlocked(*i);
|
_model->remove_note_unlocked(*i);
|
||||||
|
|
@ -166,14 +284,67 @@ MidiModel::DeltaCommand::undo()
|
||||||
_model->add_note_unlocked(*i);
|
_model->add_note_unlocked(*i);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.reset();
|
/* notes we modify in a way that requires remove-then-add to maintain ordering */
|
||||||
|
set<NotePtr> temporary_removals;
|
||||||
|
|
||||||
|
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
|
||||||
|
Property prop = i->property;
|
||||||
|
switch (prop) {
|
||||||
|
case NoteNumber:
|
||||||
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
|
_model->remove_note_unlocked (i->note);
|
||||||
|
temporary_removals.insert (i->note);
|
||||||
|
}
|
||||||
|
i->note->set_note (i->old_value);
|
||||||
|
break;
|
||||||
|
case Velocity:
|
||||||
|
i->note->set_velocity (i->old_value);
|
||||||
|
break;
|
||||||
|
case StartTime:
|
||||||
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
|
_model->remove_note_unlocked (i->note);
|
||||||
|
temporary_removals.insert (i->note);
|
||||||
|
}
|
||||||
|
i->note->set_time (i->old_time);
|
||||||
|
break;
|
||||||
|
case Length:
|
||||||
|
i->note->set_length (i->old_time);
|
||||||
|
break;
|
||||||
|
case Channel:
|
||||||
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
|
_model->remove_note_unlocked (i->note);
|
||||||
|
temporary_removals.insert (i->note);
|
||||||
|
}
|
||||||
|
i->note->set_channel (i->old_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
|
||||||
|
_model->add_note_unlocked (*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* finally add back notes that were removed by the "do". we don't care
|
||||||
|
about side effects here since the model should be back to its original
|
||||||
|
state once this is done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cerr << "This undo has " << side_effect_removals.size() << " SER's\n";
|
||||||
|
for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
|
||||||
|
_model->add_note_unlocked (*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLNode&
|
XMLNode&
|
||||||
MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note)
|
MidiModel::DiffCommand::marshal_note(const NotePtr note)
|
||||||
{
|
{
|
||||||
XMLNode* xml_note = new XMLNode("note");
|
XMLNode* xml_note = new XMLNode("note");
|
||||||
|
|
||||||
|
cerr << "Marshalling note: " << *note << endl;
|
||||||
|
|
||||||
ostringstream note_str(ios::ate);
|
ostringstream note_str(ios::ate);
|
||||||
note_str << int(note->note());
|
note_str << int(note->note());
|
||||||
xml_note->add_property("note", note_str.str());
|
xml_note->add_property("note", note_str.str());
|
||||||
|
|
@ -183,11 +354,11 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<Time
|
||||||
xml_note->add_property("channel", channel_str.str());
|
xml_note->add_property("channel", channel_str.str());
|
||||||
|
|
||||||
ostringstream time_str(ios::ate);
|
ostringstream time_str(ios::ate);
|
||||||
time_str << int(note->time());
|
time_str << note->time();
|
||||||
xml_note->add_property("time", time_str.str());
|
xml_note->add_property("time", time_str.str());
|
||||||
|
|
||||||
ostringstream length_str(ios::ate);
|
ostringstream length_str(ios::ate);
|
||||||
length_str <<(unsigned int) note->length();
|
length_str << note->length();
|
||||||
xml_note->add_property("length", length_str.str());
|
xml_note->add_property("length", length_str.str());
|
||||||
|
|
||||||
ostringstream velocity_str(ios::ate);
|
ostringstream velocity_str(ios::ate);
|
||||||
|
|
@ -197,8 +368,8 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<Time
|
||||||
return *xml_note;
|
return *xml_note;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr< Evoral::Note<MidiModel::TimeType> >
|
Evoral::Sequence<MidiModel::TimeType>::NotePtr
|
||||||
MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
|
MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
|
||||||
{
|
{
|
||||||
unsigned int note;
|
unsigned int note;
|
||||||
XMLProperty* prop;
|
XMLProperty* prop;
|
||||||
|
|
@ -247,259 +418,11 @@ MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
|
||||||
velocity = 127;
|
velocity = 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr< Evoral::Note<TimeType> > note_ptr(new Evoral::Note<TimeType>(
|
NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
|
||||||
channel, time, length, note, velocity));
|
|
||||||
return note_ptr;
|
return note_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADDED_NOTES_ELEMENT "AddedNotes"
|
|
||||||
#define REMOVED_NOTES_ELEMENT "RemovedNotes"
|
|
||||||
#define DELTA_COMMAND_ELEMENT "DeltaCommand"
|
|
||||||
|
|
||||||
int
|
|
||||||
MidiModel::DeltaCommand::set_state (const XMLNode& delta_command, int /*version*/)
|
|
||||||
{
|
|
||||||
if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_added_notes.clear();
|
|
||||||
XMLNode* added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
|
|
||||||
if (added_notes) {
|
|
||||||
XMLNodeList notes = added_notes->children();
|
|
||||||
transform(notes.begin(), notes.end(), back_inserter(_added_notes),
|
|
||||||
boost::bind (&DeltaCommand::unmarshal_note, this, _1));
|
|
||||||
}
|
|
||||||
|
|
||||||
_removed_notes.clear();
|
|
||||||
XMLNode* removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
|
|
||||||
if (removed_notes) {
|
|
||||||
XMLNodeList notes = removed_notes->children();
|
|
||||||
transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
|
|
||||||
boost::bind (&DeltaCommand::unmarshal_note, this, _1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
XMLNode&
|
|
||||||
MidiModel::DeltaCommand::get_state()
|
|
||||||
{
|
|
||||||
XMLNode* delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
|
|
||||||
delta_command->add_property("midi-source", _model->midi_source()->id().to_s());
|
|
||||||
|
|
||||||
XMLNode* added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
|
|
||||||
for_each(_added_notes.begin(), _added_notes.end(),
|
|
||||||
boost::bind(
|
|
||||||
boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
|
|
||||||
boost::bind (&DeltaCommand::marshal_note, this, _1)));
|
|
||||||
|
|
||||||
XMLNode* removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
|
|
||||||
for_each(_removed_notes.begin(), _removed_notes.end(),
|
|
||||||
boost::bind (
|
|
||||||
boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
|
|
||||||
boost::bind (&DeltaCommand::marshal_note, this, _1)));
|
|
||||||
|
|
||||||
return *delta_command;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************** DIFF COMMAND ********************/
|
|
||||||
|
|
||||||
#define DIFF_NOTES_ELEMENT "ChangedNotes"
|
|
||||||
#define DIFF_COMMAND_ELEMENT "DiffCommand"
|
|
||||||
|
|
||||||
MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
|
|
||||||
: Command(name)
|
|
||||||
, _model(m)
|
|
||||||
, _name(name)
|
|
||||||
{
|
|
||||||
assert(_model);
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
|
|
||||||
: _model(m)
|
|
||||||
{
|
|
||||||
assert(_model);
|
|
||||||
set_state(node, Stateful::loading_state_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
|
|
||||||
uint8_t new_value)
|
|
||||||
{
|
|
||||||
NoteChange change;
|
|
||||||
|
|
||||||
switch (prop) {
|
|
||||||
case NoteNumber:
|
|
||||||
if (new_value == note->note()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change.old_value = note->note();
|
|
||||||
break;
|
|
||||||
case Velocity:
|
|
||||||
if (new_value == note->velocity()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change.old_value = note->velocity();
|
|
||||||
break;
|
|
||||||
case Channel:
|
|
||||||
if (new_value == note->channel()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change.old_value = note->channel();
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
case StartTime:
|
|
||||||
fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
|
|
||||||
/*NOTREACHED*/
|
|
||||||
break;
|
|
||||||
case Length:
|
|
||||||
fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
|
|
||||||
/*NOTREACHED*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
change.note = note;
|
|
||||||
change.property = prop;
|
|
||||||
change.new_value = new_value;
|
|
||||||
|
|
||||||
_changes.push_back (change);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
|
|
||||||
TimeType new_time)
|
|
||||||
{
|
|
||||||
NoteChange change;
|
|
||||||
|
|
||||||
switch (prop) {
|
|
||||||
case NoteNumber:
|
|
||||||
case Channel:
|
|
||||||
case Velocity:
|
|
||||||
fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StartTime:
|
|
||||||
if (Evoral::musical_time_equal (note->time(), new_time)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change.old_time = note->time();
|
|
||||||
break;
|
|
||||||
case Length:
|
|
||||||
if (Evoral::musical_time_equal (note->length(), new_time)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change.old_time = note->length();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
change.note = note;
|
|
||||||
change.property = prop;
|
|
||||||
change.new_time = new_time;
|
|
||||||
|
|
||||||
_changes.push_back (change);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiModel::DiffCommand::operator()()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
MidiModel::WriteLock lock(_model->edit_lock());
|
|
||||||
|
|
||||||
set<boost::shared_ptr<Evoral::Note<TimeType> > > removed_notes;
|
|
||||||
|
|
||||||
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
|
|
||||||
Property prop = i->property;
|
|
||||||
switch (prop) {
|
|
||||||
case NoteNumber:
|
|
||||||
if (removed_notes.find (i->note) == removed_notes.end()) {
|
|
||||||
_model->remove_note_unlocked (i->note);
|
|
||||||
removed_notes.insert (i->note);
|
|
||||||
}
|
|
||||||
i->note->set_note (i->new_value);
|
|
||||||
break;
|
|
||||||
case Velocity:
|
|
||||||
i->note->set_velocity (i->new_value);
|
|
||||||
break;
|
|
||||||
case StartTime:
|
|
||||||
if (removed_notes.find (i->note) == removed_notes.end()) {
|
|
||||||
_model->remove_note_unlocked (i->note);
|
|
||||||
removed_notes.insert (i->note);
|
|
||||||
|
|
||||||
}
|
|
||||||
i->note->set_time (i->new_time);
|
|
||||||
break;
|
|
||||||
case Length:
|
|
||||||
i->note->set_length (i->new_time);
|
|
||||||
break;
|
|
||||||
case Channel:
|
|
||||||
if (removed_notes.find (i->note) == removed_notes.end()) {
|
|
||||||
_model->remove_note_unlocked (i->note);
|
|
||||||
removed_notes.insert (i->note);
|
|
||||||
}
|
|
||||||
i->note->set_channel (i->new_value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (set<boost::shared_ptr<Evoral::Note<TimeType> > >::iterator i = removed_notes.begin(); i != removed_notes.end(); ++i) {
|
|
||||||
_model->add_note_unlocked (*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MidiModel::DiffCommand::undo()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
MidiModel::WriteLock lock(_model->edit_lock());
|
|
||||||
|
|
||||||
set<boost::shared_ptr<Evoral::Note<TimeType> > > removed_notes;
|
|
||||||
|
|
||||||
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
|
|
||||||
Property prop = i->property;
|
|
||||||
switch (prop) {
|
|
||||||
case NoteNumber:
|
|
||||||
if (removed_notes.find (i->note) == removed_notes.end()) {
|
|
||||||
_model->remove_note_unlocked (i->note);
|
|
||||||
removed_notes.insert (i->note);
|
|
||||||
}
|
|
||||||
i->note->set_note (i->old_value);
|
|
||||||
break;
|
|
||||||
case Velocity:
|
|
||||||
i->note->set_velocity (i->old_value);
|
|
||||||
break;
|
|
||||||
case StartTime:
|
|
||||||
if (removed_notes.find (i->note) == removed_notes.end()) {
|
|
||||||
_model->remove_note_unlocked (i->note);
|
|
||||||
removed_notes.insert (i->note);
|
|
||||||
}
|
|
||||||
i->note->set_time (i->old_time);
|
|
||||||
break;
|
|
||||||
case Length:
|
|
||||||
i->note->set_length (i->old_time);
|
|
||||||
break;
|
|
||||||
case Channel:
|
|
||||||
if (removed_notes.find (i->note) == removed_notes.end()) {
|
|
||||||
_model->remove_note_unlocked (i->note);
|
|
||||||
removed_notes.insert (i->note);
|
|
||||||
}
|
|
||||||
i->note->set_channel (i->old_value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (set<boost::shared_ptr<Evoral::Note<TimeType> > >::iterator i = removed_notes.begin(); i != removed_notes.end(); ++i) {
|
|
||||||
_model->add_note_unlocked (*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
|
||||||
}
|
|
||||||
|
|
||||||
XMLNode&
|
XMLNode&
|
||||||
MidiModel::DiffCommand::marshal_change(const NoteChange& change)
|
MidiModel::DiffCommand::marshal_change(const NoteChange& change)
|
||||||
{
|
{
|
||||||
|
|
@ -571,6 +494,8 @@ MidiModel::DiffCommand::marshal_change(const NoteChange& change)
|
||||||
xml_change->add_property("velocity", velocity_str.str());
|
xml_change->add_property("velocity", velocity_str.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* and now notes that were remove as a side-effect */
|
||||||
|
|
||||||
return *xml_change;
|
return *xml_change;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -684,7 +609,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
|
||||||
so go look for it ...
|
so go look for it ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
boost::shared_ptr<Evoral::Note<TimeType> > new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
|
NotePtr new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
|
||||||
|
|
||||||
change.note = _model->find_note (new_note);
|
change.note = _model->find_note (new_note);
|
||||||
|
|
||||||
|
|
@ -704,6 +629,30 @@ MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* additions */
|
||||||
|
|
||||||
|
_added_notes.clear();
|
||||||
|
XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
|
||||||
|
if (added_notes) {
|
||||||
|
XMLNodeList notes = added_notes->children();
|
||||||
|
transform(notes.begin(), notes.end(), back_inserter(_added_notes),
|
||||||
|
boost::bind (&DiffCommand::unmarshal_note, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* removals */
|
||||||
|
|
||||||
|
_removed_notes.clear();
|
||||||
|
XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
|
||||||
|
if (removed_notes) {
|
||||||
|
XMLNodeList notes = removed_notes->children();
|
||||||
|
transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
|
||||||
|
boost::bind (&DiffCommand::unmarshal_note, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* changes */
|
||||||
|
|
||||||
_changes.clear();
|
_changes.clear();
|
||||||
|
|
||||||
XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
|
XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
|
||||||
|
|
@ -715,6 +664,20 @@ MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* side effect removals caused by changes */
|
||||||
|
|
||||||
|
side_effect_removals.clear();
|
||||||
|
|
||||||
|
XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
|
||||||
|
|
||||||
|
if (side_effect_notes) {
|
||||||
|
XMLNodeList notes = side_effect_notes->children();
|
||||||
|
cerr << "Reconstruct DiffCommand with " << notes.size() << " SER's\n";
|
||||||
|
for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
|
||||||
|
side_effect_removals.insert (unmarshal_note (*n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -730,6 +693,29 @@ MidiModel::DiffCommand::get_state ()
|
||||||
boost::bind (&XMLNode::add_child_nocopy, changes, _1),
|
boost::bind (&XMLNode::add_child_nocopy, changes, _1),
|
||||||
boost::bind (&DiffCommand::marshal_change, this, _1)));
|
boost::bind (&DiffCommand::marshal_change, this, _1)));
|
||||||
|
|
||||||
|
XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
|
||||||
|
for_each(_added_notes.begin(), _added_notes.end(),
|
||||||
|
boost::bind(
|
||||||
|
boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
|
||||||
|
boost::bind (&DiffCommand::marshal_note, this, _1)));
|
||||||
|
|
||||||
|
XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
|
||||||
|
for_each(_removed_notes.begin(), _removed_notes.end(),
|
||||||
|
boost::bind (
|
||||||
|
boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
|
||||||
|
boost::bind (&DiffCommand::marshal_note, this, _1)));
|
||||||
|
|
||||||
|
/* if this command had side-effects, store that state too
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!side_effect_removals.empty()) {
|
||||||
|
XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
|
||||||
|
for_each(side_effect_removals.begin(), side_effect_removals.end(),
|
||||||
|
boost::bind (
|
||||||
|
boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
|
||||||
|
boost::bind (&DiffCommand::marshal_note, this, _1)));
|
||||||
|
}
|
||||||
|
|
||||||
return *diff_command;
|
return *diff_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -851,8 +837,8 @@ MidiModel::get_state()
|
||||||
return *node;
|
return *node;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
|
Evoral::Sequence<MidiModel::TimeType>::NotePtr
|
||||||
MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
|
MidiModel::find_note (NotePtr other)
|
||||||
{
|
{
|
||||||
Notes::iterator l = notes().lower_bound(other);
|
Notes::iterator l = notes().lower_bound(other);
|
||||||
|
|
||||||
|
|
@ -869,7 +855,7 @@ MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::shared_ptr<Evoral::Note<TimeType> >();
|
return NotePtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Lock and invalidate the source.
|
/** Lock and invalidate the source.
|
||||||
|
|
@ -892,3 +878,198 @@ MidiModel::write_lock()
|
||||||
assert(!_midi_source->mutex().trylock());
|
assert(!_midi_source->mutex().trylock());
|
||||||
return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
|
return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
||||||
|
|
||||||
|
{
|
||||||
|
using namespace Evoral;
|
||||||
|
|
||||||
|
if (_writing || insert_merge_policy() == InsertMergeRelax) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeType sa = note->time();
|
||||||
|
TimeType ea = note->end_time();
|
||||||
|
|
||||||
|
const Pitches& p (pitches (note->channel()));
|
||||||
|
NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
|
||||||
|
set<NotePtr> to_be_deleted;
|
||||||
|
bool set_note_length = false;
|
||||||
|
bool set_note_time = false;
|
||||||
|
TimeType note_time = note->time();
|
||||||
|
TimeType note_length = note->length();
|
||||||
|
|
||||||
|
for (Pitches::const_iterator i = p.lower_bound (search_note);
|
||||||
|
i != p.end() && (*i)->note() == note->note(); ++i) {
|
||||||
|
|
||||||
|
TimeType sb = (*i)->time();
|
||||||
|
TimeType eb = (*i)->end_time();
|
||||||
|
OverlapType overlap = OverlapNone;
|
||||||
|
|
||||||
|
if ((sb > sa) && (eb <= ea)) {
|
||||||
|
overlap = OverlapInternal;
|
||||||
|
} else if ((eb >= sa) && (eb <= ea)) {
|
||||||
|
overlap = OverlapStart;
|
||||||
|
} else if ((sb > sa) && (sb <= ea)) {
|
||||||
|
overlap = OverlapEnd;
|
||||||
|
} else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
|
||||||
|
overlap = OverlapExternal;
|
||||||
|
} else {
|
||||||
|
/* no overlap */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insert_merge_policy() == InsertMergeReject) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (overlap) {
|
||||||
|
case OverlapStart:
|
||||||
|
/* existing note covers start of new note */
|
||||||
|
switch (insert_merge_policy()) {
|
||||||
|
case InsertMergeReplace:
|
||||||
|
to_be_deleted.insert (*i);
|
||||||
|
break;
|
||||||
|
case InsertMergeTruncateExisting:
|
||||||
|
(*i)->set_length (note->time() - (*i)->time());
|
||||||
|
break;
|
||||||
|
case InsertMergeTruncateAddition:
|
||||||
|
set_note_time = true;
|
||||||
|
note_time = (*i)->time() + (*i)->length();
|
||||||
|
break;
|
||||||
|
case InsertMergeExtend:
|
||||||
|
(*i)->set_length (note->end_time() - (*i)->time());
|
||||||
|
return -1; /* do not add the new note */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*NOTREACHED*/
|
||||||
|
/* stupid gcc */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OverlapEnd:
|
||||||
|
/* existing note covers end of new note */
|
||||||
|
switch (insert_merge_policy()) {
|
||||||
|
case InsertMergeReplace:
|
||||||
|
to_be_deleted.insert (*i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InsertMergeTruncateExisting:
|
||||||
|
/* resetting the start time of the existing note
|
||||||
|
is a problem because of time ordering.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InsertMergeTruncateAddition:
|
||||||
|
set_note_length = true;
|
||||||
|
note_length = min (note_length, ((*i)->time() - note->time()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InsertMergeExtend:
|
||||||
|
/* we can't reset the time of the existing note because
|
||||||
|
that will corrupt time ordering. So remove the
|
||||||
|
existing note and change the position/length
|
||||||
|
of the new note (which has not been added yet)
|
||||||
|
*/
|
||||||
|
to_be_deleted.insert (*i);
|
||||||
|
set_note_length = true;
|
||||||
|
note_length = min (note_length, (*i)->end_time() - note->time());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*NOTREACHED*/
|
||||||
|
/* stupid gcc */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OverlapExternal:
|
||||||
|
/* existing note overlaps all the new note */
|
||||||
|
switch (insert_merge_policy()) {
|
||||||
|
case InsertMergeReplace:
|
||||||
|
to_be_deleted.insert (*i);
|
||||||
|
break;
|
||||||
|
case InsertMergeTruncateExisting:
|
||||||
|
case InsertMergeTruncateAddition:
|
||||||
|
case InsertMergeExtend:
|
||||||
|
/* cannot add in this case */
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
/*NOTREACHED*/
|
||||||
|
/* stupid gcc */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OverlapInternal:
|
||||||
|
/* new note fully overlaps an existing note */
|
||||||
|
switch (insert_merge_policy()) {
|
||||||
|
case InsertMergeReplace:
|
||||||
|
case InsertMergeTruncateExisting:
|
||||||
|
case InsertMergeTruncateAddition:
|
||||||
|
case InsertMergeExtend:
|
||||||
|
/* delete the existing note, the new one will cover it */
|
||||||
|
to_be_deleted.insert (*i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*NOTREACHED*/
|
||||||
|
/* stupid gcc */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*NOTREACHED*/
|
||||||
|
/* stupid gcc */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
|
||||||
|
remove_note_unlocked (*i);
|
||||||
|
|
||||||
|
if (removed) {
|
||||||
|
removed->insert (*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_note_time) {
|
||||||
|
note->set_time (note_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_note_length) {
|
||||||
|
note->set_length (note_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertMergePolicy
|
||||||
|
MidiModel::insert_merge_policy () const
|
||||||
|
{
|
||||||
|
char* c = getenv ("AMP");
|
||||||
|
|
||||||
|
if (!c || c[0] == 0) {
|
||||||
|
return InsertMergeReject;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c[0]) {
|
||||||
|
case 'x':
|
||||||
|
return InsertMergeRelax;
|
||||||
|
case 'p':
|
||||||
|
return InsertMergeReplace;
|
||||||
|
case 't':
|
||||||
|
return InsertMergeTruncateExisting;
|
||||||
|
case 'a':
|
||||||
|
return InsertMergeTruncateAddition;
|
||||||
|
case 'e':
|
||||||
|
default:
|
||||||
|
return InsertMergeExtend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3080,16 +3080,6 @@ Session::restore_history (string snapshot_name)
|
||||||
ut->add_command(c);
|
ut->add_command(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (n->name() == "DeltaCommand") {
|
|
||||||
PBD::ID id(n->property("midi-source")->value());
|
|
||||||
boost::shared_ptr<MidiSource> midi_source =
|
|
||||||
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
|
|
||||||
if (midi_source) {
|
|
||||||
ut->add_command(new MidiModel::DeltaCommand(midi_source->model(), *n));
|
|
||||||
} else {
|
|
||||||
error << _("Failed to downcast MidiSource for DeltaCommand") << endmsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (n->name() == "DiffCommand") {
|
} else if (n->name() == "DiffCommand") {
|
||||||
PBD::ID id(n->property("midi-source")->value());
|
PBD::ID id(n->property("midi-source")->value());
|
||||||
boost::shared_ptr<MidiSource> midi_source =
|
boost::shared_ptr<MidiSource> midi_source =
|
||||||
|
|
@ -3097,7 +3087,7 @@ Session::restore_history (string snapshot_name)
|
||||||
if (midi_source) {
|
if (midi_source) {
|
||||||
ut->add_command(new MidiModel::DiffCommand(midi_source->model(), *n));
|
ut->add_command(new MidiModel::DiffCommand(midi_source->model(), *n));
|
||||||
} else {
|
} else {
|
||||||
error << _("Failed to downcast MidiSource for DeltaCommand") << endmsg;
|
error << _("Failed to downcast MidiSource for DiffCommand") << endmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (n->name() == "StatefulDiffCommand") {
|
} else if (n->name() == "StatefulDiffCommand") {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <list>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <glibmm/thread.h>
|
#include <glibmm/thread.h>
|
||||||
|
|
@ -78,6 +79,9 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
typedef typename boost::shared_ptr<Evoral::Note<Time> > NotePtr;
|
||||||
|
typedef typename boost::shared_ptr<const Evoral::Note<Time> > constNotePtr;
|
||||||
|
|
||||||
typedef boost::shared_ptr<Glib::RWLock::ReaderLock> ReadLock;
|
typedef boost::shared_ptr<Glib::RWLock::ReaderLock> ReadLock;
|
||||||
typedef boost::shared_ptr<WriteLockImpl> WriteLock;
|
typedef boost::shared_ptr<WriteLockImpl> WriteLock;
|
||||||
|
|
||||||
|
|
@ -133,7 +137,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::multiset<boost::shared_ptr< Note<Time> >, EarlierNoteComparator> Notes;
|
typedef std::multiset<NotePtr, EarlierNoteComparator> Notes;
|
||||||
inline Notes& notes() { return _notes; }
|
inline Notes& notes() { return _notes; }
|
||||||
inline const Notes& notes() const { return _notes; }
|
inline const Notes& notes() const { return _notes; }
|
||||||
|
|
||||||
|
|
@ -173,10 +177,7 @@ public:
|
||||||
inline const SysExes& sysexes() const { return _sysexes; }
|
inline const SysExes& sysexes() const { return _sysexes; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::priority_queue< boost::shared_ptr< Note<Time> >,
|
typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
|
||||||
std::deque< boost::shared_ptr< Note<Time> > >,
|
|
||||||
LaterNoteEndComparator >
|
|
||||||
ActiveNotes;
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Read iterator */
|
/** Read iterator */
|
||||||
|
|
@ -231,12 +232,12 @@ public:
|
||||||
bool edited() const { return _edited; }
|
bool edited() const { return _edited; }
|
||||||
void set_edited(bool yn) { _edited = yn; }
|
void set_edited(bool yn) { _edited = yn; }
|
||||||
|
|
||||||
bool overlaps (const boost::shared_ptr< Note<Time> >& ev,
|
bool overlaps (const NotePtr& ev,
|
||||||
const boost::shared_ptr< Note<Time> >& ignore_this_note) const;
|
const NotePtr& ignore_this_note) const;
|
||||||
bool contains (const boost::shared_ptr< Note<Time> >& ev) const;
|
bool contains (const NotePtr& ev) const;
|
||||||
|
|
||||||
bool add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
|
bool add_note_unlocked (const NotePtr note, std::set<NotePtr>* removed = 0);
|
||||||
void remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note);
|
void remove_note_unlocked(const constNotePtr note);
|
||||||
|
|
||||||
uint8_t lowest_note() const { return _lowest_note; }
|
uint8_t lowest_note() const { return _lowest_note; }
|
||||||
uint8_t highest_note() const { return _highest_note; }
|
uint8_t highest_note() const { return _highest_note; }
|
||||||
|
|
@ -247,20 +248,24 @@ protected:
|
||||||
bool _overlapping_pitches_accepted;
|
bool _overlapping_pitches_accepted;
|
||||||
OverlapPitchResolution _overlap_pitch_resolution;
|
OverlapPitchResolution _overlap_pitch_resolution;
|
||||||
mutable Glib::RWLock _lock;
|
mutable Glib::RWLock _lock;
|
||||||
|
bool _writing;
|
||||||
|
|
||||||
|
virtual int resolve_overlaps_unlocked (const NotePtr, std::set<NotePtr>* removed = 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::multiset<NotePtr, NoteNumberComparator> Pitches;
|
||||||
|
inline Pitches& pitches(uint8_t chan) { return _pitches[chan&0xf]; }
|
||||||
|
inline const Pitches& pitches(uint8_t chan) const { return _pitches[chan&0xf]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class const_iterator;
|
friend class const_iterator;
|
||||||
|
|
||||||
typedef std::multiset<boost::shared_ptr< Note<Time> >, NoteNumberComparator> Pitches;
|
bool overlaps_unlocked (const NotePtr& ev, const NotePtr& ignore_this_note) const;
|
||||||
inline Pitches& pitches(uint8_t chan) { return _pitches[chan&0xf]; }
|
bool contains_unlocked (const NotePtr& ev) const;
|
||||||
inline const Pitches& pitches(uint8_t chan) const { return _pitches[chan&0xf]; }
|
|
||||||
|
|
||||||
bool overlaps_unlocked (const boost::shared_ptr< Note<Time> >& ev,
|
void append_note_on_unlocked (NotePtr);
|
||||||
const boost::shared_ptr< Note<Time> >& ignore_this_note) const;
|
void append_note_off_unlocked(NotePtr);
|
||||||
bool contains_unlocked (const boost::shared_ptr< Note<Time> >& ev) const;
|
|
||||||
|
|
||||||
void append_note_on_unlocked (boost::shared_ptr< Note<Time> >);
|
|
||||||
void append_note_off_unlocked(boost::shared_ptr< Note<Time> >);
|
|
||||||
void append_control_unlocked(const Parameter& param, Time time, double value);
|
void append_control_unlocked(const Parameter& param, Time time, double value);
|
||||||
void append_sysex_unlocked(const MIDIEvent<Time>& ev);
|
void append_sysex_unlocked(const MIDIEvent<Time>& ev);
|
||||||
|
|
||||||
|
|
@ -273,9 +278,8 @@ private:
|
||||||
Pitches _pitches[16]; // notes indexed by channel+pitch
|
Pitches _pitches[16]; // notes indexed by channel+pitch
|
||||||
SysExes _sysexes;
|
SysExes _sysexes;
|
||||||
|
|
||||||
typedef std::multiset<boost::shared_ptr< Note<Time> >, EarlierNoteComparator> WriteNotes;
|
typedef std::multiset<NotePtr, EarlierNoteComparator> WriteNotes;
|
||||||
WriteNotes _write_notes[16];
|
WriteNotes _write_notes[16];
|
||||||
bool _writing;
|
|
||||||
|
|
||||||
typedef std::vector< boost::shared_ptr<const ControlList> > ControlLists;
|
typedef std::vector< boost::shared_ptr<const ControlList> > ControlLists;
|
||||||
ControlLists _dirty_controls;
|
ControlLists _dirty_controls;
|
||||||
|
|
|
||||||
|
|
@ -383,8 +383,8 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
|
||||||
: _edited(false)
|
: _edited(false)
|
||||||
, _overlapping_pitches_accepted (true)
|
, _overlapping_pitches_accepted (true)
|
||||||
, _overlap_pitch_resolution (FirstOnFirstOff)
|
, _overlap_pitch_resolution (FirstOnFirstOff)
|
||||||
, _type_map(type_map)
|
|
||||||
, _writing(false)
|
, _writing(false)
|
||||||
|
, _type_map(type_map)
|
||||||
, _end_iter(*this, DBL_MAX)
|
, _end_iter(*this, DBL_MAX)
|
||||||
, _percussive(false)
|
, _percussive(false)
|
||||||
, _lowest_note(127)
|
, _lowest_note(127)
|
||||||
|
|
@ -401,15 +401,15 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
|
||||||
, _edited(false)
|
, _edited(false)
|
||||||
, _overlapping_pitches_accepted (other._overlapping_pitches_accepted)
|
, _overlapping_pitches_accepted (other._overlapping_pitches_accepted)
|
||||||
, _overlap_pitch_resolution (other._overlap_pitch_resolution)
|
, _overlap_pitch_resolution (other._overlap_pitch_resolution)
|
||||||
, _type_map(other._type_map)
|
|
||||||
, _writing(false)
|
, _writing(false)
|
||||||
|
, _type_map(other._type_map)
|
||||||
, _end_iter(*this, DBL_MAX)
|
, _end_iter(*this, DBL_MAX)
|
||||||
, _percussive(other._percussive)
|
, _percussive(other._percussive)
|
||||||
, _lowest_note(other._lowest_note)
|
, _lowest_note(other._lowest_note)
|
||||||
, _highest_note(other._highest_note)
|
, _highest_note(other._highest_note)
|
||||||
{
|
{
|
||||||
for (typename Notes::const_iterator i = other._notes.begin(); i != other._notes.end(); ++i) {
|
for (typename Notes::const_iterator i = other._notes.begin(); i != other._notes.end(); ++i) {
|
||||||
boost::shared_ptr<Note<Time> > n (new Note<Time> (**i));
|
NotePtr n (new Note<Time> (**i));
|
||||||
_notes.insert (n);
|
_notes.insert (n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -580,14 +580,15 @@ Sequence<Time>::end_write (bool delete_stuck)
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
bool
|
bool
|
||||||
Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
|
Sequence<Time>::add_note_unlocked(const NotePtr note,
|
||||||
|
set<NotePtr >* removed)
|
||||||
{
|
{
|
||||||
/* This is the core method to add notes to a Sequence
|
/* This is the core method to add notes to a Sequence
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 add note %2 @ %3\n", this, (int)note->note(), note->time()));
|
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 add note %2 @ %3\n", this, (int)note->note(), note->time()));
|
||||||
|
|
||||||
if (!_overlapping_pitches_accepted && overlaps_unlocked (note, boost::shared_ptr<Note<Time> >())) {
|
if (resolve_overlaps_unlocked (note, removed)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,7 +607,7 @@ Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
void
|
void
|
||||||
Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note)
|
Sequence<Time>::remove_note_unlocked(const constNotePtr note)
|
||||||
{
|
{
|
||||||
bool erased = false;
|
bool erased = false;
|
||||||
|
|
||||||
|
|
@ -641,7 +642,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
|
|
||||||
Pitches& p (pitches (note->channel()));
|
Pitches& p (pitches (note->channel()));
|
||||||
|
|
||||||
boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, note->note(), 0));
|
NotePtr search_note(new Note<Time>(0, 0, 0, note->note(), 0));
|
||||||
|
|
||||||
for (typename Pitches::iterator i = p.lower_bound (search_note);
|
for (typename Pitches::iterator i = p.lower_bound (search_note);
|
||||||
i != p.end() && (*i)->note() == note->note(); ++i) {
|
i != p.end() && (*i)->note() == note->note(); ++i) {
|
||||||
|
|
@ -654,18 +655,18 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
if (!erased) {
|
if (!erased) {
|
||||||
cerr << "Unable to find note to erase" << endl;
|
cerr << "Unable to find note to erase" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Append \a ev to model. NOT realtime safe.
|
/** Append \a ev to model. NOT realtime safe.
|
||||||
*
|
*
|
||||||
* The timestamp of event is expected to be relative to
|
* The timestamp of event is expected to be relative to
|
||||||
* the start of this model (t=0) and MUST be monotonically increasing
|
* the start of this model (t=0) and MUST be monotonically increasing
|
||||||
* and MUST be >= the latest event currently in the model.
|
* and MUST be >= the latest event currently in the model.
|
||||||
*/
|
*/
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
void
|
void
|
||||||
Sequence<Time>::append(const Event<Time>& event)
|
Sequence<Time>::append(const Event<Time>& event)
|
||||||
{
|
{
|
||||||
WriteLock lock(write_lock());
|
WriteLock lock(write_lock());
|
||||||
_edited = true;
|
_edited = true;
|
||||||
|
|
||||||
|
|
@ -680,10 +681,10 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.is_note_on()) {
|
if (ev.is_note_on()) {
|
||||||
boost::shared_ptr< Note<Time> > note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
NotePtr note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
||||||
append_note_on_unlocked (note);
|
append_note_on_unlocked (note);
|
||||||
} else if (ev.is_note_off()) {
|
} else if (ev.is_note_off()) {
|
||||||
boost::shared_ptr< Note<Time> > note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
NotePtr note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
||||||
append_note_off_unlocked (note);
|
append_note_off_unlocked (note);
|
||||||
} else if (ev.is_sysex()) {
|
} else if (ev.is_sysex()) {
|
||||||
append_sysex_unlocked(ev);
|
append_sysex_unlocked(ev);
|
||||||
|
|
@ -713,11 +714,11 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
} else {
|
} else {
|
||||||
printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
|
printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
void
|
void
|
||||||
Sequence<Time>::append_note_on_unlocked (boost::shared_ptr< Note<Time> > note)
|
Sequence<Time>::append_note_on_unlocked (NotePtr note)
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n", this,
|
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n", this,
|
||||||
(int) note->channel(), (int) note->note(),
|
(int) note->channel(), (int) note->note(),
|
||||||
|
|
@ -744,7 +745,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
void
|
void
|
||||||
Sequence<Time>::append_note_off_unlocked (boost::shared_ptr< Note<Time> > note)
|
Sequence<Time>::append_note_off_unlocked (NotePtr note)
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n",
|
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n",
|
||||||
this, (int)note->channel(),
|
this, (int)note->channel(),
|
||||||
|
|
@ -770,7 +771,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
/* XXX use _overlap_pitch_resolution to determine FIFO/LIFO ... */
|
/* XXX use _overlap_pitch_resolution to determine FIFO/LIFO ... */
|
||||||
|
|
||||||
for (typename WriteNotes::iterator n = _write_notes[note->channel()].begin(); n != _write_notes[note->channel()].end(); ++n) {
|
for (typename WriteNotes::iterator n = _write_notes[note->channel()].begin(); n != _write_notes[note->channel()].end(); ++n) {
|
||||||
boost::shared_ptr< Note<Time> > nn = *n;
|
NotePtr nn = *n;
|
||||||
if (note->note() == nn->note() && nn->channel() == note->channel()) {
|
if (note->note() == nn->note() && nn->channel() == note->channel()) {
|
||||||
assert(note->time() >= nn->time());
|
assert(note->time() >= nn->time());
|
||||||
|
|
||||||
|
|
@ -817,17 +818,17 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
bool
|
bool
|
||||||
Sequence<Time>::contains (const boost::shared_ptr< Note<Time> >& note) const
|
Sequence<Time>::contains (const NotePtr& note) const
|
||||||
{
|
{
|
||||||
return contains_unlocked (note);
|
return contains_unlocked (note);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
bool
|
bool
|
||||||
Sequence<Time>::contains_unlocked (const boost::shared_ptr< Note<Time> >& note) const
|
Sequence<Time>::contains_unlocked (const NotePtr& note) const
|
||||||
{
|
{
|
||||||
const Pitches& p (pitches (note->channel()));
|
const Pitches& p (pitches (note->channel()));
|
||||||
boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, note->note()));
|
NotePtr search_note(new Note<Time>(0, 0, 0, note->note()));
|
||||||
|
|
||||||
for (typename Pitches::const_iterator i = p.lower_bound (search_note);
|
for (typename Pitches::const_iterator i = p.lower_bound (search_note);
|
||||||
i != p.end() && (*i)->note() == note->note(); ++i) {
|
i != p.end() && (*i)->note() == note->note(); ++i) {
|
||||||
|
|
@ -843,7 +844,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
bool
|
bool
|
||||||
Sequence<Time>::overlaps (const boost::shared_ptr< Note<Time> >& note, const boost::shared_ptr<Note<Time> >& without) const
|
Sequence<Time>::overlaps (const NotePtr& note, const NotePtr& without) const
|
||||||
{
|
{
|
||||||
ReadLock lock (read_lock());
|
ReadLock lock (read_lock());
|
||||||
return overlaps_unlocked (note, without);
|
return overlaps_unlocked (note, without);
|
||||||
|
|
@ -851,13 +852,13 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
bool
|
bool
|
||||||
Sequence<Time>::overlaps_unlocked (const boost::shared_ptr< Note<Time> >& note, const boost::shared_ptr<Note<Time> >& without) const
|
Sequence<Time>::overlaps_unlocked (const NotePtr& note, const NotePtr& without) const
|
||||||
{
|
{
|
||||||
Time sa = note->time();
|
Time sa = note->time();
|
||||||
Time ea = note->end_time();
|
Time ea = note->end_time();
|
||||||
|
|
||||||
const Pitches& p (pitches (note->channel()));
|
const Pitches& p (pitches (note->channel()));
|
||||||
boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, note->note()));
|
NotePtr search_note(new Note<Time>(0, 0, 0, note->note()));
|
||||||
|
|
||||||
for (typename Pitches::const_iterator i = p.lower_bound (search_note);
|
for (typename Pitches::const_iterator i = p.lower_bound (search_note);
|
||||||
i != p.end() && (*i)->note() == note->note(); ++i) {
|
i != p.end() && (*i)->note() == note->note(); ++i) {
|
||||||
|
|
@ -892,7 +893,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
||||||
typename Sequence<Time>::Notes::const_iterator
|
typename Sequence<Time>::Notes::const_iterator
|
||||||
Sequence<Time>::note_lower_bound (Time t) const
|
Sequence<Time>::note_lower_bound (Time t) const
|
||||||
{
|
{
|
||||||
boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, t, 0, 0, 0));
|
NotePtr search_note(new Note<Time>(0, t, 0, 0, 0));
|
||||||
typename Sequence<Time>::Notes::const_iterator i = _notes.lower_bound(search_note);
|
typename Sequence<Time>::Notes::const_iterator i = _notes.lower_bound(search_note);
|
||||||
assert(i == _notes.end() || (*i)->time() >= t);
|
assert(i == _notes.end() || (*i)->time() >= t);
|
||||||
return i;
|
return i;
|
||||||
|
|
@ -932,7 +933,7 @@ Sequence<Time>::get_notes_by_pitch (Notes& n, NoteOperator op, uint8_t val, int
|
||||||
}
|
}
|
||||||
|
|
||||||
const Pitches& p (pitches (c));
|
const Pitches& p (pitches (c));
|
||||||
boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, val, 0));
|
NotePtr search_note(new Note<Time>(0, 0, 0, val, 0));
|
||||||
typename Pitches::const_iterator i;
|
typename Pitches::const_iterator i;
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case PitchEqual:
|
case PitchEqual:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue