* implemented persistent undo for MidiModel::DeltaCommand. Deserializing works, but...
authorHans Baier <hansfbaier@googlemail.com>
Wed, 9 Apr 2008 15:33:01 +0000 (15:33 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Wed, 9 Apr 2008 15:33:01 +0000 (15:33 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@3240 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/midi_model.h
libs/ardour/ardour/note.h
libs/ardour/midi_model.cc
libs/ardour/midi_source.cc
libs/ardour/session_state.cc
libs/ardour/smf_source.cc
libs/midi++2/midi++/event.h

index f99855d238905775a5e413ac26118252572f0fb4..5d16c3083626e6b62111d2281124e511c89d581a 100644 (file)
@@ -55,7 +55,7 @@ typedef std::pair<boost::shared_ptr<const AutomationList>, std::pair<double,doub
  */
 class MidiModel : public boost::noncopyable, public Automatable {
 public:
-       MidiModel(Session& s, size_t size=0);
+       MidiModel(MidiSource& s, size_t size=0);
        
        // This is crap.
        void write_lock();
@@ -110,20 +110,30 @@ public:
        public:
                DeltaCommand (MidiModel& m, const std::string& name)
                        : Command(name), _model(m), _name(name) {}
-               //DeltaCommand (MidiModel&, const XMLNode& node);
+               DeltaCommand (MidiModel&, const XMLNode& node);
 
                const std::string& name() const { return _name; }
                
                void operator()();
                void undo();
                
-               /*int set_state (const XMLNode&);
-               XMLNode& get_state ();*/
+               int set_state (const XMLNode&);
+               XMLNode& get_state ();
 
                void add(const boost::shared_ptr<Note> note);
                void remove(const boost::shared_ptr<Note> note);
 
        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;
                const std::string                    _name;
                std::list< boost::shared_ptr<Note> > _added_notes;
@@ -164,7 +174,7 @@ public:
                friend class MidiModel;
 
                const MidiModel* _model;
-               MIDI::Event        _event;
+               MIDI::Event      _event;
 
                typedef std::priority_queue<
                                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 const_iterator& end()   const { return _end_iter; }
        
+       const MidiSource& midi_source() const { return _midi_source; }
+       
 private:
        friend class DeltaCommand;
        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> >,
                        LaterNoteEndComparator>
                ActiveNotes;
+       
+       MidiSource& _midi_source;
 };
 
 } /* namespace ARDOUR */
index 5b5a38d645b9ce5580c324bec6dfad910453b7f8..a53b134be822db4064b90846bf2868195edc1f51 100644 (file)
@@ -47,11 +47,13 @@ public:
        inline uint8_t note()     const { return _on_event.note(); }
        inline uint8_t velocity() const { return _on_event.velocity(); }
        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_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_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& off_event() { return _off_event; }
index 43ac4b0f960ae471deb695be7291bce0b9d1331b..94a725d272909effd7a451ce22e9e2b2c9e0656d 100644 (file)
@@ -272,8 +272,8 @@ MidiModel::const_iterator::operator=(const const_iterator& other)
        
 // MidiModel
 
-MidiModel::MidiModel(Session& s, size_t size)
-       : Automatable(s, "midi model")
+MidiModel::MidiModel(MidiSource& s, size_t size)
+       : Automatable(s.session(), "midi model")
        , _notes(size)
        , _note_mode(Sustained)
        , _writing(false)
@@ -281,6 +281,7 @@ MidiModel::MidiModel(Session& s, size_t size)
        , _end_iter(*this, DBL_MAX)
        , _next_read(UINT32_MAX)
        , _read_iter(*this, DBL_MAX)
+       , _midi_source(s)
 {
        assert(_end_iter._is_end);
        assert( ! _end_iter._locked);
@@ -657,6 +658,118 @@ MidiModel::DeltaCommand::undo()
        _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
 MidiModel::write_to(boost::shared_ptr<MidiSource> source)
index aeb898a31be68d170806bbd243c2cd4f5af1790b..7b2c4009bb92dab6dc0aceb8027bd1f722234602 100644 (file)
@@ -49,7 +49,7 @@ sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
 MidiSource::MidiSource (Session& s, string name)
        : Source (s, name, DataType::MIDI)
        , _timeline_position(0)
-       , _model(new MidiModel(s))
+       , _model(new MidiModel(*this))
        , _writing (false)
 {
        _read_data_count = 0;
@@ -59,7 +59,7 @@ MidiSource::MidiSource (Session& s, string name)
 MidiSource::MidiSource (Session& s, const XMLNode& node) 
        : Source (s, node)
        , _timeline_position(0)
-       , _model(new MidiModel(s))
+       , _model(new MidiModel(*this))
        , _writing (false)
 {
        _read_data_count = 0;
index 41261fdfbc9138b185ae809c9c28c127a903d5fd..a2575caadd3734f0b284707eada34003c1056a78 100644 (file)
@@ -2989,8 +2989,16 @@ Session::restore_history (string snapshot_name)
                                    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 {
-
                            error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
                    }
            }
index 18d7e87d88e57596ec86458c14f22c26ccf8fe72..bee3fd96d47a0811b0b1d7864cfb556476cebbde 100644 (file)
@@ -872,7 +872,7 @@ SMFSource::load_model(bool lock, bool force_reload)
        }
 
        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;
        } else {
                cerr << _name << " reloading model " << _model.get()
index 6ef1c49ee90cd2aeb85f574b552c1566a71b8fb9..cce17b0625a7f5c63f7579dfd9a5e109113867dd 100644 (file)
@@ -167,6 +167,7 @@ struct Event {
        inline uint32_t&   size()              { return _size; }
        inline uint8_t     type()        const { return (_buffer[0] & 0xF0); }
        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_off() const { return (type() == MIDI_CMD_NOTE_OFF); }
         inline bool        is_cc()       const { return (type() == MIDI_CMD_CONTROL); }