#include <algorithm>
#include <stdexcept>
#include <stdint.h>
+#include <pbd/error.h>
#include <pbd/enumwriter.h>
#include <midi++/events.h>
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<TimeType>(s->session(), size)
, _midi_source(s)
{
+ //cerr << "MidiModel \"" << s->name() << "\" constructed: " << this << endl;
}
/** Start a new command.
* 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);
}
}
void
-MidiModel::DeltaCommand::add(const boost::shared_ptr<Evoral::Note> note)
+MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note<TimeType> > note)
{
//cerr << "MEC: apply" << endl;
_removed_notes.remove(note);
}
void
-MidiModel::DeltaCommand::remove(const boost::shared_ptr<Evoral::Note> note)
+MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note<TimeType> > note)
{
//cerr << "MEC: remove" << endl;
_added_notes.remove(note);
{
// 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<Evoral::Note> >::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<Evoral::Note> >::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 */
}
{
// 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<Evoral::Note> >::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<Evoral::Note> >::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<Evoral::Note> note)
+MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note)
{
XMLNode *xml_note = new XMLNode("note");
ostringstream note_str(ios::ate);
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();
return *xml_note;
}
-boost::shared_ptr<Evoral::Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
+boost::shared_ptr< Evoral::Note<MidiModel::TimeType> >
+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<Evoral::Note> 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<TimeType> > note_ptr(new Evoral::Note<TimeType>(
+ channel, time, length, note, velocity));
return note_ptr;
}
#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;
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(
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
* to percussive, save, reload, then switch it back to sustained without
* destroying the original note durations.
*/
-bool MidiModel::write_to(boost::shared_ptr<MidiSource> source)
+bool
+MidiModel::write_to(boost::shared_ptr<MidiSource> 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<TimeType>::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;