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
|
@ -83,7 +83,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
|
|||
, _custom_device_mode(string())
|
||||
, _active_notes(0)
|
||||
, _note_group(new ArdourCanvas::Group(*parent))
|
||||
, _delta_command(0)
|
||||
, _diff_command(0)
|
||||
, _ghost_note(0)
|
||||
, _drag_rect (0)
|
||||
|
@ -108,7 +107,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
|
|||
, _custom_device_mode(string())
|
||||
, _active_notes(0)
|
||||
, _note_group(new ArdourCanvas::Group(*parent))
|
||||
, _delta_command(0)
|
||||
, _diff_command(0)
|
||||
, _ghost_note(0)
|
||||
, _drag_rect (0)
|
||||
|
@ -132,7 +130,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
|
|||
, _custom_device_mode(string())
|
||||
, _active_notes(0)
|
||||
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
|
||||
, _delta_command(0)
|
||||
, _diff_command(0)
|
||||
, _ghost_note(0)
|
||||
, _drag_rect (0)
|
||||
|
@ -160,7 +157,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
|
|||
, _custom_device_mode(string())
|
||||
, _active_notes(0)
|
||||
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
|
||||
, _delta_command(0)
|
||||
, _diff_command(0)
|
||||
, _ghost_note(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());
|
||||
|
||||
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
|
||||
MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
|
||||
cmd->add(new_note);
|
||||
_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
|
||||
MidiRegionView::start_diff_command(string name)
|
||||
{
|
||||
|
@ -742,10 +729,10 @@ MidiRegionView::start_diff_command(string name)
|
|||
}
|
||||
|
||||
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) {
|
||||
_delta_command->add(note);
|
||||
if (_diff_command) {
|
||||
_diff_command->add(note);
|
||||
}
|
||||
if (selected) {
|
||||
_marked_for_selection.insert(note);
|
||||
|
@ -756,10 +743,10 @@ MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool sele
|
|||
}
|
||||
|
||||
void
|
||||
MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
|
||||
MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
|
||||
{
|
||||
if (_delta_command && ev->note()) {
|
||||
_delta_command->remove(ev->note());
|
||||
if (_diff_command && ev->note()) {
|
||||
_diff_command->remove(ev->note());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,85 +770,64 @@ MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::apply_delta()
|
||||
{
|
||||
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(*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 ()
|
||||
{
|
||||
bool add_or_remove;
|
||||
|
||||
if (!_diff_command) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((add_or_remove = _diff_command->adds_or_removes())) {
|
||||
// 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(*trackview.session(), _diff_command);
|
||||
_diff_command = 0;
|
||||
midi_view()->midi_track()->playlist_modified();
|
||||
|
||||
_marked_for_velocity.clear();
|
||||
}
|
||||
|
||||
if (add_or_remove) {
|
||||
_marked_for_selection.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();
|
||||
|
||||
_marked_for_selection.clear();
|
||||
_marked_for_velocity.clear();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::apply_diff_as_subcommand()
|
||||
{
|
||||
bool add_or_remove;
|
||||
|
||||
if (!_diff_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());
|
||||
}
|
||||
if ((add_or_remove = _diff_command->adds_or_removes())) {
|
||||
// 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(), _diff_command);
|
||||
_diff_command = 0;
|
||||
midi_view()->midi_track()->playlist_modified();
|
||||
|
||||
_marked_for_selection.clear();
|
||||
if (add_or_remove) {
|
||||
_marked_for_selection.clear();
|
||||
}
|
||||
_marked_for_velocity.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiRegionView::abort_command()
|
||||
{
|
||||
delete _delta_command;
|
||||
_delta_command = 0;
|
||||
delete _diff_command;
|
||||
_diff_command = 0;
|
||||
clear_selection();
|
||||
|
@ -1096,7 +1062,7 @@ MidiRegionView::~MidiRegionView ()
|
|||
_selection.clear();
|
||||
clear_events();
|
||||
delete _note_group;
|
||||
delete _delta_command;
|
||||
delete _diff_command;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
start_delta_command (_("step add"));
|
||||
delta_add_note (new_note, true, false);
|
||||
apply_delta();
|
||||
start_diff_command (_("step add"));
|
||||
diff_add_note (new_note, true, false);
|
||||
apply_diff();
|
||||
|
||||
/* potentially extend region to hold new note */
|
||||
|
||||
|
@ -1627,25 +1593,25 @@ MidiRegionView::delete_selection()
|
|||
return;
|
||||
}
|
||||
|
||||
start_delta_command (_("delete selection"));
|
||||
start_diff_command (_("delete selection"));
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||
if ((*i)->selected()) {
|
||||
_delta_command->remove((*i)->note());
|
||||
_diff_command->remove((*i)->note());
|
||||
}
|
||||
}
|
||||
|
||||
_selection.clear();
|
||||
|
||||
apply_delta ();
|
||||
apply_diff ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
|
||||
{
|
||||
start_delta_command (_("delete note"));
|
||||
_delta_command->remove (n);
|
||||
apply_delta ();
|
||||
start_diff_command (_("delete note"));
|
||||
_diff_command->remove (n);
|
||||
apply_diff ();
|
||||
|
||||
trackview.editor().hide_verbose_canvas_cursor ();
|
||||
}
|
||||
|
@ -2622,7 +2588,7 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
|
|||
|
||||
if (op != Copy) {
|
||||
|
||||
start_delta_command();
|
||||
start_diff_command();
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||
switch (op) {
|
||||
|
@ -2630,12 +2596,12 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
|
|||
break;
|
||||
case Cut:
|
||||
case Clear:
|
||||
delta_remove_note (*i);
|
||||
diff_remove_note (*i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
apply_delta();
|
||||
apply_diff();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2662,7 +2628,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
|||
return;
|
||||
}
|
||||
|
||||
start_delta_command (_("paste"));
|
||||
start_diff_command (_("paste"));
|
||||
|
||||
Evoral::MusicalTime beat_delta;
|
||||
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) {
|
||||
|
||||
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) {
|
||||
|
||||
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 */
|
||||
|
||||
delta_add_note (copied_note, true);
|
||||
diff_add_note (copied_note, true);
|
||||
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) {
|
||||
|
||||
cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
|
||||
|
||||
trackview.session()->begin_reversible_command (_("paste"));
|
||||
|
||||
_region->clear_history ();
|
||||
|
@ -2710,8 +2672,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
|||
trackview.session()->add_command (new StatefulDiffCommand (_region));
|
||||
}
|
||||
|
||||
cerr << "region end finally at " << _region->position() + _region->length() - 1;
|
||||
apply_delta ();
|
||||
apply_diff ();
|
||||
}
|
||||
|
||||
struct EventNoteTimeEarlyFirstComparator {
|
||||
|
|
|
@ -173,17 +173,13 @@ class MidiRegionView : public RegionView
|
|||
|
||||
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 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_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_delta_as_subcommand();
|
||||
void apply_diff_as_subcommand();
|
||||
void abort_command();
|
||||
|
||||
|
@ -355,7 +351,6 @@ class MidiRegionView : public RegionView
|
|||
SysExes _sys_exes;
|
||||
ArdourCanvas::CanvasNote** _active_notes;
|
||||
ArdourCanvas::Group* _note_group;
|
||||
ARDOUR::MidiModel::DeltaCommand* _delta_command;
|
||||
ARDOUR::MidiModel::DiffCommand* _diff_command;
|
||||
ArdourCanvas::CanvasNote* _ghost_note;
|
||||
double _last_ghost_x;
|
||||
|
|
|
@ -49,53 +49,13 @@ class MidiSource;
|
|||
*/
|
||||
class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
|
||||
public:
|
||||
typedef double TimeType;
|
||||
typedef Evoral::MusicalTime TimeType;
|
||||
|
||||
MidiModel(MidiSource* s);
|
||||
|
||||
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
|
||||
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 {
|
||||
public:
|
||||
enum Property {
|
||||
|
@ -117,18 +77,23 @@ public:
|
|||
int set_state (const XMLNode&, int version);
|
||||
XMLNode& get_state ();
|
||||
|
||||
void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
|
||||
Property prop, uint8_t new_value);
|
||||
void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
|
||||
Property prop, TimeType new_time);
|
||||
void add(const NotePtr note);
|
||||
void remove(const NotePtr note);
|
||||
|
||||
private:
|
||||
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:
|
||||
boost::shared_ptr<MidiModel> _model;
|
||||
const std::string _name;
|
||||
|
||||
struct NoteChange {
|
||||
DiffCommand::Property property;
|
||||
boost::shared_ptr< Evoral::Note<TimeType> > note;
|
||||
NotePtr note;
|
||||
union {
|
||||
uint8_t old_value;
|
||||
TimeType old_time;
|
||||
|
@ -142,11 +107,19 @@ public:
|
|||
typedef std::list<NoteChange> ChangeList;
|
||||
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&);
|
||||
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");
|
||||
void apply_command(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; }
|
||||
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:
|
||||
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)
|
||||
: AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl(s, c)
|
||||
: AutomatableSequence<TimeType>::WriteLockImpl(s, c)
|
||||
, source_lock(source_lock)
|
||||
{}
|
||||
~WriteLockImpl() {
|
||||
|
@ -188,6 +167,7 @@ private:
|
|||
|
||||
// We cannot use a boost::shared_ptr here to avoid a retain cycle
|
||||
MidiSource* _midi_source;
|
||||
InsertMergePolicy _insert_merge_policy;
|
||||
};
|
||||
|
||||
} /* namespace ARDOUR */
|
||||
|
|
|
@ -86,6 +86,19 @@ namespace ARDOUR {
|
|||
ARDOUR::OverlapType coverage (framepos_t sa, framepos_t ea,
|
||||
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
|
||||
* XXX: I don't think/hope these hex values matter anymore.
|
||||
*/
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#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.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/************** 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)
|
||||
, _model(m)
|
||||
, _name(name)
|
||||
|
@ -109,7 +101,7 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std:
|
|||
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)
|
||||
{
|
||||
assert(_model);
|
||||
|
@ -117,63 +109,242 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLN
|
|||
}
|
||||
|
||||
void
|
||||
MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note<TimeType> > note)
|
||||
MidiModel::DiffCommand::add(const NotePtr note)
|
||||
{
|
||||
_removed_notes.remove(note);
|
||||
_added_notes.push_back(note);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note<TimeType> > note)
|
||||
MidiModel::DiffCommand::remove(const NotePtr note)
|
||||
{
|
||||
_added_notes.remove(note);
|
||||
_removed_notes.push_back(note);
|
||||
}
|
||||
|
||||
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
|
||||
// removed notes (or sort here), and doing a single iteration over _model
|
||||
NoteChange change;
|
||||
|
||||
MidiModel::WriteLock lock(_model->edit_lock());
|
||||
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;
|
||||
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||
_model->add_note_unlocked(*i);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
|
||||
_model->remove_note_unlocked(*i);
|
||||
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;
|
||||
}
|
||||
|
||||
lock.reset();
|
||||
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());
|
||||
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||
_model->add_note_unlocked(*i);
|
||||
}
|
||||
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
|
||||
_model->remove_note_unlocked(*i);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||
_model->remove_note_unlocked(*i);
|
||||
}
|
||||
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
|
||||
_model->add_note_unlocked(*i);
|
||||
}
|
||||
|
||||
MidiModel::WriteLock lock(_model->edit_lock());;
|
||||
/* notes we modify in a way that requires remove-then-add to maintain ordering */
|
||||
set<NotePtr> temporary_removals;
|
||||
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||
_model->remove_note_unlocked(*i);
|
||||
}
|
||||
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 (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
|
||||
_model->add_note_unlocked(*i);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
lock.reset();
|
||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
cerr << "Marshalling note: " << *note << endl;
|
||||
|
||||
ostringstream note_str(ios::ate);
|
||||
note_str << int(note->note());
|
||||
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());
|
||||
|
||||
ostringstream time_str(ios::ate);
|
||||
time_str << int(note->time());
|
||||
time_str << note->time();
|
||||
xml_note->add_property("time", time_str.str());
|
||||
|
||||
|
||||
ostringstream length_str(ios::ate);
|
||||
length_str <<(unsigned int) note->length();
|
||||
length_str << note->length();
|
||||
xml_note->add_property("length", length_str.str());
|
||||
|
||||
ostringstream velocity_str(ios::ate);
|
||||
|
@ -197,8 +368,8 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<Time
|
|||
return *xml_note;
|
||||
}
|
||||
|
||||
boost::shared_ptr< Evoral::Note<MidiModel::TimeType> >
|
||||
MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
|
||||
Evoral::Sequence<MidiModel::TimeType>::NotePtr
|
||||
MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
|
||||
{
|
||||
unsigned int note;
|
||||
XMLProperty* prop;
|
||||
|
@ -247,259 +418,11 @@ MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
|
|||
velocity = 127;
|
||||
}
|
||||
|
||||
boost::shared_ptr< Evoral::Note<TimeType> > note_ptr(new Evoral::Note<TimeType>(
|
||||
channel, time, length, note, velocity));
|
||||
NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
|
||||
|
||||
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&
|
||||
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());
|
||||
}
|
||||
|
||||
/* and now notes that were remove as a side-effect */
|
||||
|
||||
return *xml_change;
|
||||
}
|
||||
|
||||
|
@ -684,7 +609,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
|
|||
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);
|
||||
|
||||
|
@ -704,6 +629,30 @@ MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
|
|||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -730,6 +693,29 @@ MidiModel::DiffCommand::get_state ()
|
|||
boost::bind (&XMLNode::add_child_nocopy, changes, _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;
|
||||
}
|
||||
|
||||
|
@ -851,8 +837,8 @@ MidiModel::get_state()
|
|||
return *node;
|
||||
}
|
||||
|
||||
boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
|
||||
MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
|
||||
Evoral::Sequence<MidiModel::TimeType>::NotePtr
|
||||
MidiModel::find_note (NotePtr 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.
|
||||
|
@ -892,3 +878,198 @@ MidiModel::write_lock()
|
|||
assert(!_midi_source->mutex().trylock());
|
||||
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);
|
||||
}
|
||||
|
||||
} 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") {
|
||||
PBD::ID id(n->property("midi-source")->value());
|
||||
boost::shared_ptr<MidiSource> midi_source =
|
||||
|
@ -3097,7 +3087,7 @@ Session::restore_history (string snapshot_name)
|
|||
if (midi_source) {
|
||||
ut->add_command(new MidiModel::DiffCommand(midi_source->model(), *n));
|
||||
} else {
|
||||
error << _("Failed to downcast MidiSource for DeltaCommand") << endmsg;
|
||||
error << _("Failed to downcast MidiSource for DiffCommand") << endmsg;
|
||||
}
|
||||
|
||||
} else if (n->name() == "StatefulDiffCommand") {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <vector>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <glibmm/thread.h>
|
||||
|
@ -78,6 +79,9 @@ protected:
|
|||
};
|
||||
|
||||
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<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 const Notes& notes() const { return _notes; }
|
||||
|
||||
|
@ -173,10 +177,7 @@ public:
|
|||
inline const SysExes& sysexes() const { return _sysexes; }
|
||||
|
||||
private:
|
||||
typedef std::priority_queue< boost::shared_ptr< Note<Time> >,
|
||||
std::deque< boost::shared_ptr< Note<Time> > >,
|
||||
LaterNoteEndComparator >
|
||||
ActiveNotes;
|
||||
typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
|
||||
public:
|
||||
|
||||
/** Read iterator */
|
||||
|
@ -231,12 +232,12 @@ public:
|
|||
bool edited() const { return _edited; }
|
||||
void set_edited(bool yn) { _edited = yn; }
|
||||
|
||||
bool overlaps (const boost::shared_ptr< Note<Time> >& ev,
|
||||
const boost::shared_ptr< Note<Time> >& ignore_this_note) const;
|
||||
bool contains (const boost::shared_ptr< Note<Time> >& ev) const;
|
||||
bool overlaps (const NotePtr& ev,
|
||||
const NotePtr& ignore_this_note) const;
|
||||
bool contains (const NotePtr& ev) const;
|
||||
|
||||
bool add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
|
||||
void remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note);
|
||||
bool add_note_unlocked (const NotePtr note, std::set<NotePtr>* removed = 0);
|
||||
void remove_note_unlocked(const constNotePtr note);
|
||||
|
||||
uint8_t lowest_note() const { return _lowest_note; }
|
||||
uint8_t highest_note() const { return _highest_note; }
|
||||
|
@ -247,20 +248,24 @@ protected:
|
|||
bool _overlapping_pitches_accepted;
|
||||
OverlapPitchResolution _overlap_pitch_resolution;
|
||||
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:
|
||||
friend class const_iterator;
|
||||
|
||||
typedef std::multiset<boost::shared_ptr< Note<Time> >, NoteNumberComparator> Pitches;
|
||||
inline Pitches& pitches(uint8_t chan) { return _pitches[chan&0xf]; }
|
||||
inline const Pitches& pitches(uint8_t chan) const { return _pitches[chan&0xf]; }
|
||||
bool overlaps_unlocked (const NotePtr& ev, const NotePtr& ignore_this_note) const;
|
||||
bool contains_unlocked (const NotePtr& ev) const;
|
||||
|
||||
bool overlaps_unlocked (const boost::shared_ptr< Note<Time> >& ev,
|
||||
const boost::shared_ptr< Note<Time> >& ignore_this_note) const;
|
||||
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_note_on_unlocked (NotePtr);
|
||||
void append_note_off_unlocked(NotePtr);
|
||||
void append_control_unlocked(const Parameter& param, Time time, double value);
|
||||
void append_sysex_unlocked(const MIDIEvent<Time>& ev);
|
||||
|
||||
|
@ -273,9 +278,8 @@ private:
|
|||
Pitches _pitches[16]; // notes indexed by channel+pitch
|
||||
SysExes _sysexes;
|
||||
|
||||
typedef std::multiset<boost::shared_ptr< Note<Time> >, EarlierNoteComparator> WriteNotes;
|
||||
typedef std::multiset<NotePtr, EarlierNoteComparator> WriteNotes;
|
||||
WriteNotes _write_notes[16];
|
||||
bool _writing;
|
||||
|
||||
typedef std::vector< boost::shared_ptr<const ControlList> > ControlLists;
|
||||
ControlLists _dirty_controls;
|
||||
|
|
|
@ -383,8 +383,8 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
|
|||
: _edited(false)
|
||||
, _overlapping_pitches_accepted (true)
|
||||
, _overlap_pitch_resolution (FirstOnFirstOff)
|
||||
, _type_map(type_map)
|
||||
, _writing(false)
|
||||
, _type_map(type_map)
|
||||
, _end_iter(*this, DBL_MAX)
|
||||
, _percussive(false)
|
||||
, _lowest_note(127)
|
||||
|
@ -401,15 +401,15 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
|
|||
, _edited(false)
|
||||
, _overlapping_pitches_accepted (other._overlapping_pitches_accepted)
|
||||
, _overlap_pitch_resolution (other._overlap_pitch_resolution)
|
||||
, _type_map(other._type_map)
|
||||
, _writing(false)
|
||||
, _type_map(other._type_map)
|
||||
, _end_iter(*this, DBL_MAX)
|
||||
, _percussive(other._percussive)
|
||||
, _lowest_note(other._lowest_note)
|
||||
, _highest_note(other._highest_note)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -580,14 +580,15 @@ Sequence<Time>::end_write (bool delete_stuck)
|
|||
|
||||
template<typename Time>
|
||||
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
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -606,7 +607,7 @@ Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
|
|||
|
||||
template<typename Time>
|
||||
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;
|
||||
|
||||
|
@ -641,7 +642,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
|||
|
||||
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);
|
||||
i != p.end() && (*i)->note() == note->note(); ++i) {
|
||||
|
@ -650,74 +651,74 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
|||
p.erase (i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!erased) {
|
||||
cerr << "Unable to find note to erase" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!erased) {
|
||||
cerr << "Unable to find note to erase" << endl;
|
||||
}
|
||||
}
|
||||
/** Append \a ev to model. NOT realtime safe.
|
||||
*
|
||||
* The timestamp of event is 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.
|
||||
*/
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::append(const Event<Time>& event)
|
||||
{
|
||||
WriteLock lock(write_lock());
|
||||
_edited = true;
|
||||
|
||||
/** Append \a ev to model. NOT realtime safe.
|
||||
*
|
||||
* The timestamp of event is 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.
|
||||
*/
|
||||
template<typename Time>
|
||||
const MIDIEvent<Time>& ev = (const MIDIEvent<Time>&)event;
|
||||
|
||||
assert(_notes.empty() || ev.time() >= (*_notes.rbegin())->time());
|
||||
assert(_writing);
|
||||
|
||||
if (!midi_event_is_valid(ev.buffer(), ev.size())) {
|
||||
cerr << "WARNING: Sequence ignoring illegal MIDI event" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.is_note_on()) {
|
||||
NotePtr note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
||||
append_note_on_unlocked (note);
|
||||
} else if (ev.is_note_off()) {
|
||||
NotePtr note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
||||
append_note_off_unlocked (note);
|
||||
} else if (ev.is_sysex()) {
|
||||
append_sysex_unlocked(ev);
|
||||
} else if (!_type_map.type_is_midi(ev.event_type())) {
|
||||
printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
|
||||
for (size_t i=0; i < ev.size(); ++i) {
|
||||
printf("%X ", ev.buffer()[i]);
|
||||
}
|
||||
printf("\n");
|
||||
} else if (ev.is_cc()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()),
|
||||
ev.time(), ev.cc_value());
|
||||
} else if (ev.is_pgm_change()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
|
||||
ev.time(), ev.pgm_number());
|
||||
} else if (ev.is_pitch_bender()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()),
|
||||
ev.time(), double( (0x7F & ev.pitch_bender_msb()) << 7
|
||||
| (0x7F & ev.pitch_bender_lsb()) ));
|
||||
} else if (ev.is_channel_pressure()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ChannelPressure(ev.event_type(), ev.channel()),
|
||||
ev.time(), ev.channel_pressure());
|
||||
} else {
|
||||
printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::append(const Event<Time>& event)
|
||||
{
|
||||
WriteLock lock(write_lock());
|
||||
_edited = true;
|
||||
|
||||
const MIDIEvent<Time>& ev = (const MIDIEvent<Time>&)event;
|
||||
|
||||
assert(_notes.empty() || ev.time() >= (*_notes.rbegin())->time());
|
||||
assert(_writing);
|
||||
|
||||
if (!midi_event_is_valid(ev.buffer(), ev.size())) {
|
||||
cerr << "WARNING: Sequence ignoring illegal MIDI event" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.is_note_on()) {
|
||||
boost::shared_ptr< Note<Time> > note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
||||
append_note_on_unlocked (note);
|
||||
} else if (ev.is_note_off()) {
|
||||
boost::shared_ptr< Note<Time> > note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
|
||||
append_note_off_unlocked (note);
|
||||
} else if (ev.is_sysex()) {
|
||||
append_sysex_unlocked(ev);
|
||||
} else if (!_type_map.type_is_midi(ev.event_type())) {
|
||||
printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
|
||||
for (size_t i=0; i < ev.size(); ++i) {
|
||||
printf("%X ", ev.buffer()[i]);
|
||||
}
|
||||
printf("\n");
|
||||
} else if (ev.is_cc()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()),
|
||||
ev.time(), ev.cc_value());
|
||||
} else if (ev.is_pgm_change()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
|
||||
ev.time(), ev.pgm_number());
|
||||
} else if (ev.is_pitch_bender()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()),
|
||||
ev.time(), double( (0x7F & ev.pitch_bender_msb()) << 7
|
||||
| (0x7F & ev.pitch_bender_lsb()) ));
|
||||
} else if (ev.is_channel_pressure()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ChannelPressure(ev.event_type(), ev.channel()),
|
||||
ev.time(), ev.channel_pressure());
|
||||
} else {
|
||||
printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
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,
|
||||
(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>
|
||||
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",
|
||||
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 ... */
|
||||
|
||||
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()) {
|
||||
assert(note->time() >= nn->time());
|
||||
|
||||
|
@ -817,17 +818,17 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
|||
|
||||
template<typename Time>
|
||||
bool
|
||||
Sequence<Time>::contains (const boost::shared_ptr< Note<Time> >& note) const
|
||||
Sequence<Time>::contains (const NotePtr& note) const
|
||||
{
|
||||
return contains_unlocked (note);
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
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()));
|
||||
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);
|
||||
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>
|
||||
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());
|
||||
return overlaps_unlocked (note, without);
|
||||
|
@ -851,13 +852,13 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
|
|||
|
||||
template<typename Time>
|
||||
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 ea = note->end_time();
|
||||
|
||||
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);
|
||||
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
|
||||
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);
|
||||
assert(i == _notes.end() || (*i)->time() >= t);
|
||||
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));
|
||||
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;
|
||||
switch (op) {
|
||||
case PitchEqual:
|
||||
|
|
Loading…
Reference in New Issue