mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-12 01:26:31 +01:00
* 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:
parent
f39606f985
commit
1a0044b35d
7 changed files with 149 additions and 11 deletions
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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); }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue