* implemented persistent undo for MidiModel::DeltaCommand. Deserializing works, but weirdly has no effect when undo/redo is applied in the editor

git-svn-id: svn://localhost/ardour2/branches/3.0@3240 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Hans Baier 2008-04-09 15:33:01 +00:00
parent f39606f985
commit 1a0044b35d
7 changed files with 149 additions and 11 deletions

View file

@ -55,7 +55,7 @@ typedef std::pair<boost::shared_ptr<const AutomationList>, std::pair<double,doub
*/ */
class MidiModel : public boost::noncopyable, public Automatable { class MidiModel : public boost::noncopyable, public Automatable {
public: public:
MidiModel(Session& s, size_t size=0); MidiModel(MidiSource& s, size_t size=0);
// This is crap. // This is crap.
void write_lock(); void write_lock();
@ -110,20 +110,30 @@ public:
public: public:
DeltaCommand (MidiModel& m, const std::string& name) DeltaCommand (MidiModel& m, const std::string& name)
: Command(name), _model(m), _name(name) {} : Command(name), _model(m), _name(name) {}
//DeltaCommand (MidiModel&, const XMLNode& node); DeltaCommand (MidiModel&, const XMLNode& node);
const std::string& name() const { return _name; } const std::string& name() const { return _name; }
void operator()(); void operator()();
void undo(); void undo();
/*int set_state (const XMLNode&); int set_state (const XMLNode&);
XMLNode& get_state ();*/ XMLNode& get_state ();
void add(const boost::shared_ptr<Note> note); void add(const boost::shared_ptr<Note> note);
void remove(const boost::shared_ptr<Note> note); void remove(const boost::shared_ptr<Note> note);
private: private:
class NoteMarshaller {
public:
XMLNode *operator()(const boost::shared_ptr<Note> note);
};
class NoteUnmarshaller {
public:
boost::shared_ptr<Note> operator()(XMLNode *xml_note);
};
MidiModel& _model; MidiModel& _model;
const std::string _name; const std::string _name;
std::list< boost::shared_ptr<Note> > _added_notes; std::list< boost::shared_ptr<Note> > _added_notes;
@ -164,7 +174,7 @@ public:
friend class MidiModel; friend class MidiModel;
const MidiModel* _model; const MidiModel* _model;
MIDI::Event _event; MIDI::Event _event;
typedef std::priority_queue< typedef std::priority_queue<
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >, boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
@ -183,6 +193,8 @@ public:
const_iterator begin() const { return const_iterator(*this, 0); } const_iterator begin() const { return const_iterator(*this, 0); }
const const_iterator& end() const { return _end_iter; } const const_iterator& end() const { return _end_iter; }
const MidiSource& midi_source() const { return _midi_source; }
private: private:
friend class DeltaCommand; friend class DeltaCommand;
void add_note_unlocked(const boost::shared_ptr<Note> note); void add_note_unlocked(const boost::shared_ptr<Note> note);
@ -218,6 +230,8 @@ private:
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >, boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
LaterNoteEndComparator> LaterNoteEndComparator>
ActiveNotes; ActiveNotes;
MidiSource& _midi_source;
}; };
} /* namespace ARDOUR */ } /* namespace ARDOUR */

View file

@ -47,11 +47,13 @@ public:
inline uint8_t note() const { return _on_event.note(); } inline uint8_t note() const { return _on_event.note(); }
inline uint8_t velocity() const { return _on_event.velocity(); } inline uint8_t velocity() const { return _on_event.velocity(); }
inline double duration() const { return _off_event.time() - _on_event.time(); } inline double duration() const { return _off_event.time() - _on_event.time(); }
inline uint8_t channel() const { return _on_event.channel(); }
inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; } inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; }
inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; } inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; }
inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; } inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; }
inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; } inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; }
inline void set_channel(uint8_t channel) { _on_event.set_channel(channel); _off_event.set_channel(channel); }
inline MIDI::Event& on_event() { return _on_event; } inline MIDI::Event& on_event() { return _on_event; }
inline MIDI::Event& off_event() { return _off_event; } inline MIDI::Event& off_event() { return _off_event; }

View file

@ -272,8 +272,8 @@ MidiModel::const_iterator::operator=(const const_iterator& other)
// MidiModel // MidiModel
MidiModel::MidiModel(Session& s, size_t size) MidiModel::MidiModel(MidiSource& s, size_t size)
: Automatable(s, "midi model") : Automatable(s.session(), "midi model")
, _notes(size) , _notes(size)
, _note_mode(Sustained) , _note_mode(Sustained)
, _writing(false) , _writing(false)
@ -281,6 +281,7 @@ MidiModel::MidiModel(Session& s, size_t size)
, _end_iter(*this, DBL_MAX) , _end_iter(*this, DBL_MAX)
, _next_read(UINT32_MAX) , _next_read(UINT32_MAX)
, _read_iter(*this, DBL_MAX) , _read_iter(*this, DBL_MAX)
, _midi_source(s)
{ {
assert(_end_iter._is_end); assert(_end_iter._is_end);
assert( ! _end_iter._locked); assert( ! _end_iter._locked);
@ -657,6 +658,118 @@ MidiModel::DeltaCommand::undo()
_model.ContentsChanged(); /* EMIT SIGNAL */ _model.ContentsChanged(); /* EMIT SIGNAL */
} }
XMLNode *
MidiModel::DeltaCommand::NoteMarshaller::operator()(const boost::shared_ptr<Note> note)
{
XMLNode *xml_note = new XMLNode("note");
ostringstream note_str(ios::ate);
note_str << int(note->note());
xml_note->add_property("note", note_str.str());
ostringstream channel_str(ios::ate);
channel_str << int(note->channel());
xml_note->add_property("channel", channel_str.str());
ostringstream time_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 velocity_str(ios::ate);
velocity_str << (unsigned int) note->velocity();
xml_note->add_property("velocity", velocity_str.str());
return xml_note;
}
boost::shared_ptr<Note>
MidiModel::DeltaCommand::NoteUnmarshaller::operator()(XMLNode *xml_note)
{
unsigned int note;
istringstream note_str(xml_note->property("note")->value());
note_str >> note;
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 duration;
istringstream duration_str(xml_note->property("duration")->value());
duration_str >> duration;
unsigned int velocity;
istringstream velocity_str(xml_note->property("velocity")->value());
velocity_str >> velocity;
cerr << "creating note channel: " << channel_str.str() << " time " << time_str.str() << " duration " << duration_str.str() << " pitch " << note_str.str() << " velo " << velocity_str.str() <<endl;
cerr << "creating note channel: " << channel << " time " << time << " duration " << duration << " pitch " << note << " velo " << velocity <<endl;
boost::shared_ptr<Note> note_ptr(new Note(channel, time, duration, note, velocity));
return note_ptr;
}
#define ADDED_NOTES_ELEMENT "added_notes"
#define REMOVED_NOTES_ELEMENT "removed_notes"
#define DELTA_COMMAND_ELEMENT "DeltaCommand"
int
MidiModel::DeltaCommand::set_state (const XMLNode& delta_command)
{
cerr << "Unmarshalling Deltacommand" << endl;
if(delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
return 1;
}
_added_notes.clear();
XMLNode *added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
XMLNodeList notes = added_notes->children();
transform(notes.begin(), notes.end(), back_inserter(_added_notes),
MidiModel::DeltaCommand::NoteUnmarshaller());
_removed_notes.clear();
XMLNode *removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
notes = removed_notes->children();
transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
MidiModel::DeltaCommand::NoteUnmarshaller());
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 (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
NoteMarshaller marshaller;
added_notes->add_child_nocopy(*marshaller(*i));
}
XMLNode *removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
NoteMarshaller marshaller;
removed_notes->add_child_nocopy(*marshaller(*i));
}
return *delta_command;
}
MidiModel::DeltaCommand::DeltaCommand (MidiModel& m, const XMLNode& node)
: _model(m)
{
set_state(node);
}
bool bool
MidiModel::write_to(boost::shared_ptr<MidiSource> source) MidiModel::write_to(boost::shared_ptr<MidiSource> source)

View file

@ -49,7 +49,7 @@ sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
MidiSource::MidiSource (Session& s, string name) MidiSource::MidiSource (Session& s, string name)
: Source (s, name, DataType::MIDI) : Source (s, name, DataType::MIDI)
, _timeline_position(0) , _timeline_position(0)
, _model(new MidiModel(s)) , _model(new MidiModel(*this))
, _writing (false) , _writing (false)
{ {
_read_data_count = 0; _read_data_count = 0;
@ -59,7 +59,7 @@ MidiSource::MidiSource (Session& s, string name)
MidiSource::MidiSource (Session& s, const XMLNode& node) MidiSource::MidiSource (Session& s, const XMLNode& node)
: Source (s, node) : Source (s, node)
, _timeline_position(0) , _timeline_position(0)
, _model(new MidiModel(s)) , _model(new MidiModel(*this))
, _writing (false) , _writing (false)
{ {
_read_data_count = 0; _read_data_count = 0;

View file

@ -2989,8 +2989,16 @@ Session::restore_history (string snapshot_name)
ut->add_command (c); ut->add_command (c);
} }
} else if (n->name() == "DeltaCommand") {
PBD::ID id(n->property("midi_source")->value());
boost::shared_ptr<MidiSource> midi_source =
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
if(midi_source) {
ut->add_command(new MidiModel::DeltaCommand(*(midi_source->model()), *n));
} else {
error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg;
}
} else { } else {
error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg; error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
} }
} }

View file

@ -872,7 +872,7 @@ SMFSource::load_model(bool lock, bool force_reload)
} }
if (! _model) { if (! _model) {
_model = boost::shared_ptr<MidiModel>(new MidiModel(_session)); _model = boost::shared_ptr<MidiModel>(new MidiModel(*this));
cerr << _name << " loaded new model " << _model.get() << endl; cerr << _name << " loaded new model " << _model.get() << endl;
} else { } else {
cerr << _name << " reloading model " << _model.get() cerr << _name << " reloading model " << _model.get()

View file

@ -167,6 +167,7 @@ struct Event {
inline uint32_t& size() { return _size; } inline uint32_t& size() { return _size; }
inline uint8_t type() const { return (_buffer[0] & 0xF0); } inline uint8_t type() const { return (_buffer[0] & 0xF0); }
inline uint8_t channel() const { return (_buffer[0] & 0x0F); } inline uint8_t channel() const { return (_buffer[0] & 0x0F); }
inline void set_channel(uint8_t channel) { _buffer[0] = (0xF0 & _buffer[0]) | channel; }
inline bool is_note_on() const { return (type() == MIDI_CMD_NOTE_ON); } inline bool is_note_on() const { return (type() == MIDI_CMD_NOTE_ON); }
inline bool is_note_off() const { return (type() == MIDI_CMD_NOTE_OFF); } inline bool is_note_off() const { return (type() == MIDI_CMD_NOTE_OFF); }
inline bool is_cc() const { return (type() == MIDI_CMD_CONTROL); } inline bool is_cc() const { return (type() == MIDI_CMD_CONTROL); }