rationale pathways that add notes to Sequence<T> so that there is only final insertion step; when changing note properties that affect "indexing" within a Sequence<T> (i.e. via set<T,comparator> indexing/ordering/hashing), remove the note and add it back so that indexing stays consistent; fix marshalling of MidiModel::DiffCommand (boost::bind(&Object::method, reference_to_object) will COPY the reference, so use a pointer); fix MidiModel::find_note() to compare note properties not pointer addresses

git-svn-id: svn://localhost/ardour2/branches/3.0@7203 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-06-01 20:40:45 +00:00
parent df99f37047
commit 0cd3bd4f19
4 changed files with 246 additions and 146 deletions

View File

@ -291,13 +291,13 @@ MidiModel::DeltaCommand::get_state()
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 (&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 (&XMLNode::add_child_nocopy, removed_notes, _1),
boost::bind (&DeltaCommand::marshal_note, this, _1)));
return *delta_command;
@ -329,17 +329,27 @@ MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> >
{
NoteChange change;
change.note = note;
change.property = prop;
change.new_value = new_value;
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*/
@ -348,11 +358,12 @@ MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> >
fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
/*NOTREACHED*/
break;
case Channel:
change.old_value = note->channel();
break;
}
change.note = note;
change.property = prop;
change.new_value = new_value;
_changes.push_back (change);
}
@ -362,84 +373,130 @@ MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> >
{
NoteChange change;
change.note = note;
change.property = prop;
change.new_time = new_time;
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());
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:
i->note->set_note (i->new_value);
break;
case Velocity:
i->note->set_velocity (i->new_value);
break;
case StartTime:
i->note->set_time (i->new_time);
break;
case Length:
i->note->set_length (i->new_time);
break;
case Channel:
i->note->set_channel (i->new_value);
break;
}
}
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);
}
}
lock.reset();
_model->ContentsChanged(); /* EMIT SIGNAL */
}
void
MidiModel::DiffCommand::undo()
{
MidiModel::WriteLock lock(_model->edit_lock());
{
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:
i->note->set_note (i->old_value);
break;
case Velocity:
i->note->set_velocity (i->old_value);
break;
case StartTime:
i->note->set_time (i->old_time);
break;
case Length:
i->note->set_length (i->old_time);
break;
case Channel:
i->note->set_channel (i->old_value);
break;
}
}
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);
}
}
lock.reset();
_model->ContentsChanged(); /* EMIT SIGNAL */
}
@ -670,7 +727,7 @@ MidiModel::DiffCommand::get_state ()
XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
for_each(_changes.begin(), _changes.end(),
boost::bind (
boost::bind (&XMLNode::add_child_nocopy, *changes, _1),
boost::bind (&XMLNode::add_child_nocopy, changes, _1),
boost::bind (&DiffCommand::marshal_change, this, _1)));
return *diff_command;
@ -801,7 +858,12 @@ MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
if (l != notes().end()) {
for (; (*l)->time() == other->time(); ++l) {
if (*l == other) {
/* NB: compare note contents, not note pointers.
If "other" was a ptr to a note already in
the model, we wouldn't be looking for it,
would we now?
*/
if (**l == *other) {
return *l;
}
}

View File

@ -299,7 +299,7 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
_write_data_count += ev.size();
if (_model) {
_model->append(ev);
_model->append (ev);
}
}
@ -338,7 +338,7 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev, sfr
const double ev_time_beats = converter.from(ev.time());
const Evoral::Event<double> beat_ev(
ev.event_type(), ev_time_beats, ev.size(), (uint8_t*)ev.buffer());
_model->append(beat_ev);
_model->append (beat_ev);
}
}
@ -439,9 +439,10 @@ SMFSource::load_model (bool lock, bool force_reload)
if (ret > 0) { // didn't skip (meta) event
ev.set_event_type(EventTypeMap::instance().midi_event_type(buf[0]));
#ifndef NDEBUG
std::string ss;
for (int xx = 0; xx < size; ++xx) {
for (uint32_t xx = 0; xx < size; ++xx) {
char b[8];
snprintf (b, sizeof (b), "0x%x ", buf[xx]);
ss += b;
@ -449,8 +450,9 @@ SMFSource::load_model (bool lock, bool force_reload)
DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %6 load model delta %1, time %2, size %3 buf %4, type %5\n",
delta_t, time, size, ss , ev.event_type(), name()));
#endif
_model->append(ev);
_model->append (ev);
}
if (ev.size() > scratch_size) {

View File

@ -239,8 +239,8 @@ private:
bool overlaps_unlocked (const boost::shared_ptr< Note<Time> > ev) const;
bool contains_unlocked (const boost::shared_ptr< Note<Time> > ev) const;
void append_note_on_unlocked(uint8_t chan, Time time, uint8_t note, uint8_t velocity);
void append_note_off_unlocked(uint8_t chan, Time time, uint8_t note, uint8_t velocity);
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_sysex_unlocked(const MIDIEvent<Time>& ev);

View File

@ -535,7 +535,7 @@ Sequence<Time>::start_write()
*/
template<typename Time>
void
Sequence<Time>::end_write(bool delete_stuck)
Sequence<Time>::end_write (bool delete_stuck)
{
WriteLock lock(write_lock());
@ -545,17 +545,18 @@ Sequence<Time>::end_write(bool delete_stuck)
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 : end_write (%2 notes)\n", this, _notes.size()));
if (!_percussive && delete_stuck) {
for (typename Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
typename Notes::iterator next = n;
++next;
if ((*n)->length() == 0) {
cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
_notes.erase(n);
}
n = next;
}
}
if (!_percussive && delete_stuck) {
for (typename Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
typename Notes::iterator next = n;
++next;
if ((*n)->length() == 0) {
cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
_notes.erase(n);
}
n = next;
}
}
for (int i = 0; i < 16; ++i) {
if (!_write_notes[i].empty()) {
@ -572,9 +573,79 @@ Sequence<Time>::end_write(bool delete_stuck)
_writing = false;
}
template<typename Time>
bool
Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
{
/* 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 (contains_unlocked (note)) {
return false;
}
if (overlaps_unlocked (note)) {
return false;
}
_edited = true;
if (note->note() < _lowest_note)
_lowest_note = note->note();
if (note->note() > _highest_note)
_highest_note = note->note();
_notes.insert(note);
return true;
}
template<typename Time>
void
Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note)
{
bool erased = false;
_edited = true;
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 remove note %2 @ %3\n", this, (int)note->note(), note->time()));
for (typename Sequence<Time>::Notes::iterator i = note_lower_bound(note->time());
i != _notes.end() && (*i)->time() == note->time(); ++i) {
if (*i == note) {
_notes.erase (i);
if ((*i)->note() == _lowest_note || (*i)->note() == _highest_note) {
_lowest_note = 127;
_highest_note = 0;
for (typename Sequence<Time>::Notes::iterator ii = _notes.begin(); ii != _notes.end(); ++ii) {
if ((*ii)->note() < _lowest_note)
_lowest_note = (*ii)->note();
if ((*ii)->note() > _highest_note)
_highest_note = (*ii)->note();
}
}
erased = true;
}
}
if (!erased) {
cerr << "Unable to find note to erase" << endl;
}
}
/** Append \a ev to model. NOT realtime safe.
*
* Timestamps of events in \a buf are 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
* and MUST be >= the latest event currently in the model.
*/
@ -596,9 +667,11 @@ Sequence<Time>::append(const Event<Time>& event)
}
if (ev.is_note_on()) {
append_note_on_unlocked(ev.channel(), ev.time(), ev.note(), ev.velocity());
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()) {
append_note_off_unlocked(ev.channel(), ev.time(), ev.note(), ev.velocity());
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())) {
@ -631,31 +704,26 @@ Sequence<Time>::append(const Event<Time>& event)
template<typename Time>
void
Sequence<Time>::append_note_on_unlocked(uint8_t chan, Time time, uint8_t note_num, uint8_t velocity)
Sequence<Time>::append_note_on_unlocked (boost::shared_ptr< Note<Time> > note)
{
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n",
this, (int)chan, (int)note_num, time, (int)velocity));
assert(note_num <= 127);
assert(chan < 16);
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n", this,
(int) note->channel(), (int) note->note(),
note->time(), (int) note->velocity()));
assert(note->note() <= 127);
assert(note->channel() < 16);
assert(_writing);
_edited = true;
if (velocity == 0) {
append_note_off_unlocked(chan, time, note_num, velocity);
if (note->velocity() == 0) {
append_note_off_unlocked (note);
return;
}
if (note_num < _lowest_note)
_lowest_note = note_num;
if (note_num > _highest_note)
_highest_note = note_num;
boost::shared_ptr< Note<Time> > new_note(new Note<Time>(chan, time, 0, note_num, velocity));
_notes.insert(new_note);
add_note_unlocked (note);
if (!_percussive) {
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Sustained: Appending active note on %1 channel %2\n",
(unsigned)(uint8_t)note_num, chan));
_write_notes[chan].insert(new_note);
(unsigned)(uint8_t)note->note(), note->channel()));
_write_notes[note->channel()].insert (note);
} else {
DEBUG_TRACE(DEBUG::Sequence, "Percussive: NOT appending active note on\n");
}
@ -663,12 +731,13 @@ Sequence<Time>::append_note_on_unlocked(uint8_t chan, Time time, uint8_t note_nu
template<typename Time>
void
Sequence<Time>::append_note_off_unlocked(uint8_t chan, Time time, uint8_t note_num, uint8_t velocity)
Sequence<Time>::append_note_off_unlocked (boost::shared_ptr< Note<Time> > note)
{
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 off @ %4\n",
this, (int)chan, (int)note_num, time));
assert(note_num <= 127);
assert(chan < 16);
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n",
this, (int)note->channel(),
(int)note->note(), note->time(), (int)note->velocity()));
assert(note->note() <= 127);
assert(note->channel() < 16);
assert(_writing);
_edited = true;
@ -685,13 +754,15 @@ Sequence<Time>::append_note_off_unlocked(uint8_t chan, Time time, uint8_t note_n
format.
*/
for (typename WriteNotes::iterator n = _write_notes[chan].begin(); n != _write_notes[chan].end(); ++n) {
boost::shared_ptr< Note<Time> > note = *n;
if (note->note() == note_num && note->channel() == chan) {
assert(time >= note->time());
note->set_length (time - note->time());
note->set_off_velocity (velocity);
_write_notes[chan].erase(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;
if (note->note() == nn->note() && nn->channel() == note->channel()) {
assert(note->time() >= nn->time());
nn->set_length (note->time() - nn->time());
nn->set_off_velocity (note->velocity());
_write_notes[note->channel()].erase(n);
DEBUG_TRACE (DEBUG::Sequence, string_compose ("resolved note, length: %1\n", note->length()));
resolved = true;
break;
@ -699,8 +770,8 @@ Sequence<Time>::append_note_off_unlocked(uint8_t chan, Time time, uint8_t note_n
}
if (!resolved) {
cerr << this << " spurious note off chan " << (int)chan
<< ", note " << (int)note_num << " @ " << time << endl;
cerr << this << " spurious note off chan " << (int)note->channel()
<< ", note " << (int)note->note() << " @ " << note->time() << endl;
}
}
@ -747,7 +818,6 @@ Sequence<Time>::contains_unlocked (const boost::shared_ptr< Note<Time> > note) c
return true;
}
}
cerr << "No matching note for " << note << endl;
return false;
}
@ -787,40 +857,6 @@ Sequence<Time>::overlaps_unlocked (const boost::shared_ptr< Note<Time> > note) c
return false;
}
template<typename Time>
bool
Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
{
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 add note %2 @ %3\n", this, (int)note->note(), note->time()));
if (contains_unlocked (note)) {
return false;
}
if (overlaps_unlocked (note)) {
return false;
}
_edited = true;
_notes.insert(note);
return true;
}
template<typename Time>
void
Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note)
{
_edited = true;
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 remove note %2 @ %3\n", this, (int)note->note(), note->time()));
for (typename Sequence<Time>::Notes::iterator i = note_lower_bound(note->time());
i != _notes.end() && (*i)->time() == note->time(); ++i) {
if (*i == note) {
_notes.erase(i);
}
}
cerr << "Unable to find note to erase" << endl;
}
template<typename Time>
void
Sequence<Time>::set_notes (const Sequence<Time>::Notes& n)