X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_model.cc;h=6573bcfd68bdbac4317a11f77d1d4034e2db7b2d;hb=ecaf107ed3dd2bb3443a92fc3dd9cf566d3439e3;hp=da0fa364b980cd7a97a14938acdb7a10b64aaa16;hpb=d357eca668044badcb4bab318e2e74cfffa9a0b0;p=ardour.git diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index da0fa364b9..6573bcfd68 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -34,14 +35,13 @@ using namespace std; using namespace ARDOUR; - +using namespace PBD; MidiModel::MidiModel(MidiSource *s, size_t size) - : ControlSet() - , Automatable(s->session(), "midi model") - , Sequence(size) + : AutomatableSequence(s->session(), size) , _midi_source(s) { + //cerr << "MidiModel \"" << s->name() << "\" constructed: " << this << endl; } /** Start a new command. @@ -62,13 +62,12 @@ MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name) * The command will constitute one item on the undo stack. */ void -MidiModel::apply_command(Command* cmd) +MidiModel::apply_command(Session& session, Command* cmd) { - _session.begin_reversible_command(cmd->name()); + session.begin_reversible_command(cmd->name()); (*cmd)(); - assert(is_sorted()); - _session.commit_reversible_command(cmd); - _edited = true; + session.commit_reversible_command(cmd); + set_edited(true); } @@ -90,7 +89,7 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, } void -MidiModel::DeltaCommand::add(const boost::shared_ptr note) +MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note > note) { //cerr << "MEC: apply" << endl; _removed_notes.remove(note); @@ -98,7 +97,7 @@ MidiModel::DeltaCommand::add(const boost::shared_ptr note) } void -MidiModel::DeltaCommand::remove(const boost::shared_ptr note) +MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note > note) { //cerr << "MEC: remove" << endl; _added_notes.remove(note); @@ -110,35 +109,22 @@ MidiModel::DeltaCommand::operator()() { // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model - - // Need to reset iterator to drop the read lock it holds, or we'll deadlock - const bool reset_iter = (_model->_read_iter.locked()); - double iter_time = -1.0; - - if (reset_iter) { - if (_model->_read_iter.get_event_pointer().get()) { - iter_time = _model->_read_iter->time(); - } else { - cerr << "MidiModel::DeltaCommand::operator(): WARNING: _read_iter points to no event" << endl; - } - _model->_read_iter = _model->end(); // drop read lock - } - - assert( ! _model->_read_iter.locked()); - + _model->write_lock(); - for (std::list< boost::shared_ptr >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + // Store the current seek position so we can restore the read iterator + // after modifying the contents of the model + const TimeType read_time = _model->read_time(); + + for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) _model->add_note_unlocked(*i); - for (std::list< boost::shared_ptr >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) _model->remove_note_unlocked(*i); _model->write_unlock(); - - if (reset_iter && iter_time != -1.0) { - _model->_read_iter = const_iterator(*_model.get(), iter_time); - } + // FIXME: race? + _model->read_seek(read_time); // restore read position _model->ContentsChanged(); /* EMIT SIGNAL */ } @@ -148,43 +134,28 @@ MidiModel::DeltaCommand::undo() { // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model - - // Need to reset iterator to drop the read lock it holds, or we'll deadlock - const bool reset_iter = (_model->_read_iter.locked()); - double iter_time = -1.0; - - if (reset_iter) { - if (_model->_read_iter.get_event_pointer().get()) { - iter_time = _model->_read_iter->time(); - } else { - cerr << "MidiModel::DeltaCommand::undo(): WARNING: _read_iter points to no event" << endl; - } - _model->_read_iter = _model->end(); // drop read lock - } - - assert( ! _model->_read_iter.locked()); - + _model->write_lock(); - for (std::list< boost::shared_ptr >::iterator i = _added_notes.begin(); i - != _added_notes.end(); ++i) + // Store the current seek position so we can restore the read iterator + // after modifying the contents of the model + const TimeType read_time = _model->read_time(); + + for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) _model->remove_note_unlocked(*i); - for (std::list< boost::shared_ptr >::iterator i = - _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) _model->add_note_unlocked(*i); _model->write_unlock(); - - if (reset_iter && iter_time != -1.0) { - _model->_read_iter = const_iterator(*_model.get(), iter_time); - } + // FIXME: race? + _model->read_seek(read_time); // restore read position _model->ContentsChanged(); /* EMIT SIGNAL */ } XMLNode& -MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr note) +MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note > note) { XMLNode *xml_note = new XMLNode("note"); ostringstream note_str(ios::ate); @@ -199,9 +170,9 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr note time_str << int(note->time()); xml_note->add_property("time", time_str.str()); - ostringstream duration_str(ios::ate); - duration_str <<(unsigned int) note->duration(); - xml_note->add_property("duration", duration_str.str()); + ostringstream length_str(ios::ate); + length_str <<(unsigned int) note->length(); + xml_note->add_property("length", length_str.str()); ostringstream velocity_str(ios::ate); velocity_str << (unsigned int) note->velocity(); @@ -210,29 +181,58 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr note return *xml_note; } -boost::shared_ptr MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note) +boost::shared_ptr< Evoral::Note > +MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note) { unsigned int note; - istringstream note_str(xml_note->property("note")->value()); - note_str >> note; - + XMLProperty* prop; unsigned int channel; - istringstream channel_str(xml_note->property("channel")->value()); - channel_str >> channel; - unsigned int time; - istringstream time_str(xml_note->property("time")->value()); - time_str >> time; + unsigned int length; + unsigned int velocity; - unsigned int duration; - istringstream duration_str(xml_note->property("duration")->value()); - duration_str >> duration; + if ((prop = xml_note->property("note")) != 0) { + istringstream note_str(prop->value()); + note_str >> note; + } else { + warning << "note information missing note value" << endmsg; + note = 127; + } - unsigned int velocity; - istringstream velocity_str(xml_note->property("velocity")->value()); - velocity_str >> velocity; + if ((prop = xml_note->property("channel")) != 0) { + istringstream channel_str(prop->value()); + channel_str >> channel; + } else { + warning << "note information missing channel" << endmsg; + channel = 0; + } + + if ((prop = xml_note->property("time")) != 0) { + istringstream time_str(prop->value()); + time_str >> time; + } else { + warning << "note information missing time" << endmsg; + time = 0; + } - boost::shared_ptr note_ptr(new Evoral::Note(channel, time, duration, note, velocity)); + if ((prop = xml_note->property("length")) != 0) { + istringstream length_str(prop->value()); + length_str >> length; + } else { + warning << "note information missing length" << endmsg; + note = 1; + } + + if ((prop = xml_note->property("velocity")) != 0) { + istringstream velocity_str(prop->value()); + velocity_str >> velocity; + } else { + warning << "note information missing velocity" << endmsg; + velocity = 127; + } + + boost::shared_ptr< Evoral::Note > note_ptr(new Evoral::Note( + channel, time, length, note, velocity)); return note_ptr; } @@ -240,7 +240,8 @@ boost::shared_ptr MidiModel::DeltaCommand::unmarshal_note(XMLNode #define REMOVED_NOTES_ELEMENT "removed_notes" #define DELTA_COMMAND_ELEMENT "DeltaCommand" -int MidiModel::DeltaCommand::set_state(const XMLNode& delta_command) +int +MidiModel::DeltaCommand::set_state(const XMLNode& delta_command) { if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) { return 1; @@ -261,10 +262,11 @@ int MidiModel::DeltaCommand::set_state(const XMLNode& delta_command) return 0; } -XMLNode& MidiModel::DeltaCommand::get_state() +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()); + 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(), sigc::compose( @@ -279,13 +281,6 @@ XMLNode& MidiModel::DeltaCommand::get_state() return *delta_command; } -struct EventTimeComparator { - typedef const Evoral::Event* value_type; - inline bool operator()(const Evoral::Event& a, const Evoral::Event& b) const { - return a.time() >= b.time(); - } -}; - /** Write the model to a MidiSource (i.e. save the model). * This is different from manually using read to write to a source in that * note off events are written regardless of the track mode. This is so the @@ -293,26 +288,30 @@ struct EventTimeComparator { * to percussive, save, reload, then switch it back to sustained without * destroying the original note durations. */ -bool MidiModel::write_to(boost::shared_ptr source) +bool +MidiModel::write_to(boost::shared_ptr source) { read_lock(); const bool old_percussive = percussive(); set_percussive(false); + + source->drop_model(); - for (const_iterator i = begin(); i != end(); ++i) { - source->append_event_unlocked(Frames, *i); + for (Evoral::Sequence::const_iterator i = begin(); i != end(); ++i) { + source->append_event_unlocked_beats(*i); } set_percussive(old_percussive); read_unlock(); - _edited = false; + set_edited(false); return true; } -XMLNode& MidiModel::get_state() +XMLNode& +MidiModel::get_state() { XMLNode *node = new XMLNode("MidiModel"); return *node;