From aae8262a363b3d7b85b5baa3b2d0ffb07e604b73 Mon Sep 17 00:00:00 2001 From: Hans Baier Date: Fri, 11 Apr 2008 15:49:52 +0000 Subject: * persistent undo for MIDI edits works now * fixed bug: dragging of notes beyond left region bounds made it disappear (unsigned int wrap around) git-svn-id: svn://localhost/ardour2/branches/3.0@3249 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/midi_region_view.cc | 26 +++++++------- gtk2_ardour/midi_region_view.h | 4 +-- libs/ardour/ardour/midi_model.h | 15 ++++---- libs/ardour/ardour/note.h | 12 +++++-- libs/ardour/midi_model.cc | 78 ++++++++++++++++++++++------------------- libs/ardour/midi_source.cc | 9 ++--- libs/ardour/session_state.cc | 2 +- libs/ardour/smf_source.cc | 2 +- 8 files changed, 82 insertions(+), 66 deletions(-) diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 52d74dc757..21d2e4dc71 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -154,7 +154,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) delete_mod = true; original_mode = trackview.editor.current_midi_edit_mode(); trackview.editor.set_midi_edit_mode(MidiEditErase); - start_delta_command(); + start_delta_command(_("erase notes")); _mouse_state = EraseTouchDragging; return true; } else if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) { @@ -899,7 +899,7 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote) highest_note_difference = highest_note_in_selection - 127; } - start_delta_command(); + start_delta_command(_("move notes")); for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ) { Selection::iterator next = i; @@ -909,15 +909,17 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote) const boost::shared_ptr copy(new Note(*(*i)->note().get())); // we need to snap here again in nframes_t in order to be sample accurate - nframes_t new_note_time = nframes_t((*i)->note()->time()); - new_note_time += nframes_t(dt); + double new_note_time = (*i)->note()->time(); + new_note_time += dt; + + // keep notes inside region if dragged beyond left region bound + if(new_note_time < _region->start()) { + new_note_time = _region->start(); + } + // since note time is region-absolute but snap_to_frame expects position-relative // time we have to coordinate transform back and forth here. - new_note_time = snap_to_frame(new_note_time - _region->start()) + _region->start(); - - if(new_note_time < 0) { - new_note_time = 0; - } + new_note_time = snap_to_frame(nframes_t(new_note_time) - _region->start()) + _region->start(); copy->set_time(new_note_time); @@ -1078,7 +1080,7 @@ MidiRegionView::update_resizing(CanvasNote::NoteEnd note_end, double x, bool rel void MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bool relative) { - start_delta_command(); + start_delta_command(_("resize notes")); for (std::vector::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) { CanvasNote *canvas_note = (*i)->canvas_note; @@ -1127,7 +1129,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo void MidiRegionView::change_velocity(uint8_t velocity, bool relative) { - start_delta_command(); + start_delta_command(_("change velocity")); for (Selection::iterator i = _selection.begin(); i != _selection.end();) { Selection::iterator next = i; ++next; @@ -1164,7 +1166,7 @@ void MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev) { if (ev->note() && _mouse_state == EraseTouchDragging) { - start_delta_command(); + start_delta_command(_("note entered")); ev->selected(true); _delta_command->remove(ev->note()); } else if (_mouse_state == SelectTouchDragging) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index e209aecb7e..c6b28c0f53 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -91,9 +91,9 @@ class MidiRegionView : public RegionView /* This stuff is a bit boilerplatey ATM. Work in progress. */ - inline void start_delta_command() { + inline void start_delta_command(string name = "midi edit") { if (!_delta_command) - _delta_command = _model->new_delta_command(); + _delta_command = _model->new_delta_command(name); } void command_add_note(const boost::shared_ptr note) { diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 3ba0fc1279..b6cdac1864 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -55,7 +55,7 @@ typedef std::pair, std::pair m, const std::string& name); + DeltaCommand (boost::shared_ptr, const XMLNode& node); const std::string& name() const { return _name; } @@ -127,7 +126,7 @@ public: XMLNode &marshal_note(const boost::shared_ptr note); boost::shared_ptr unmarshal_note(XMLNode *xml_note); - MidiModel& _model; + boost::shared_ptr _model; const std::string _name; typedef std::list< boost::shared_ptr > NoteList; @@ -189,7 +188,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; } + const MidiSource *midi_source() const { return _midi_source; } + void set_midi_source(MidiSource *source) { _midi_source = source; } private: friend class DeltaCommand; @@ -227,7 +227,8 @@ private: LaterNoteEndComparator> ActiveNotes; - MidiSource& _midi_source; + // We cannot use a boost::shared_ptr here to avoid a retain cycle + MidiSource *_midi_source; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/note.h b/libs/ardour/ardour/note.h index a53b134be8..713d732113 100644 --- a/libs/ardour/ardour/note.h +++ b/libs/ardour/ardour/note.h @@ -40,14 +40,22 @@ public: const Note& operator=(const Note& copy); inline bool operator==(const Note& other) - { return time() == other.time() && note() == other.note() && duration() == other.duration(); } + { return time() == other.time() && + note() == other.note() && + duration() == other.duration() && + velocity() == other.velocity() && + channel() == other.channel(); + } inline double time() const { return _on_event.time(); } inline double end_time() const { return _off_event.time(); } 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 uint8_t channel() const { + assert(_on_event.channel() == _off_event.channel()); + 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; } diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index 14441d2ee4..439be8a481 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -272,8 +272,8 @@ MidiModel::const_iterator::operator=(const const_iterator& other) // MidiModel -MidiModel::MidiModel(MidiSource& s, size_t size) - : Automatable(s.session(), "midi model") +MidiModel::MidiModel(MidiSource *s, size_t size) + : Automatable(s->session(), "midi model") , _notes(size) , _note_mode(Sustained) , _writing(false) @@ -287,7 +287,6 @@ MidiModel::MidiModel(MidiSource& s, size_t size) assert( ! _end_iter._locked); } - /** Read events in frame range \a start .. \a start+cnt into \a dst, * adding \a stamp_offset to each event's timestamp. * \return number of events written to \a dst @@ -521,9 +520,12 @@ void MidiModel::remove_note_unlocked(const boost::shared_ptr note) { //cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl; - Notes::iterator n = find(_notes.begin(), _notes.end(), note); - if (n != _notes.end()) - _notes.erase(n); + for(Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) { + if(**n == *note) { + _notes.erase(n); + } + } + } /** Slow! for debugging only. */ @@ -551,7 +553,7 @@ MidiModel::is_sorted() const MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name) { - DeltaCommand* cmd = new DeltaCommand(*this, name); + DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name); return cmd; } @@ -574,6 +576,17 @@ MidiModel::apply_command(Command* cmd) // MidiEditCommand +MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const std::string& name) + : Command(name), _model(m), _name(name) +{ + +} + +MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const XMLNode& node) + : _model(m) +{ + set_state(node); +} void MidiModel::DeltaCommand::add(const boost::shared_ptr note) @@ -602,28 +615,28 @@ MidiModel::DeltaCommand::operator()() // 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()); - const double iter_time = _model._read_iter->time(); + const bool reset_iter = (_model->_read_iter.locked()); + const double iter_time = _model->_read_iter->time(); if (reset_iter) - _model._read_iter = _model.end(); // drop read lock + _model->_read_iter = _model->end(); // drop read lock - assert( ! _model._read_iter.locked()); + assert( ! _model->_read_iter.locked()); - _model.write_lock(); + _model->write_lock(); for (std::list< boost::shared_ptr >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) - _model.add_note_unlocked(*i); + _model->add_note_unlocked(*i); for (std::list< boost::shared_ptr >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) - _model.remove_note_unlocked(*i); + _model->remove_note_unlocked(*i); - _model.write_unlock(); + _model->write_unlock(); if (reset_iter) - _model._read_iter = const_iterator(_model, iter_time); + _model->_read_iter = const_iterator(*_model.get(), iter_time); - _model.ContentsChanged(); /* EMIT SIGNAL */ + _model->ContentsChanged(); /* EMIT SIGNAL */ } @@ -634,28 +647,28 @@ MidiModel::DeltaCommand::undo() // 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()); - const double iter_time = _model._read_iter->time(); + const bool reset_iter = (_model->_read_iter.locked()); + const double iter_time = _model->_read_iter->time(); if (reset_iter) - _model._read_iter = _model.end(); // drop read lock + _model->_read_iter = _model->end(); // drop read lock - assert( ! _model._read_iter.locked()); + assert( ! _model->_read_iter.locked()); - _model.write_lock(); + _model->write_lock(); for (std::list< boost::shared_ptr >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) - _model.remove_note_unlocked(*i); + _model->remove_note_unlocked(*i); for (std::list< boost::shared_ptr >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) - _model.add_note_unlocked(*i); + _model->add_note_unlocked(*i); - _model.write_unlock(); + _model->write_unlock(); if (reset_iter) - _model._read_iter = const_iterator(_model, iter_time); + _model->_read_iter = const_iterator(*_model.get(), iter_time); - _model.ContentsChanged(); /* EMIT SIGNAL */ + _model->ContentsChanged(); /* EMIT SIGNAL */ } XMLNode & @@ -708,9 +721,6 @@ MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note) 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() < note_ptr(new Note(channel, time, duration, note, velocity)); return note_ptr; } @@ -745,8 +755,7 @@ 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_name", _model.midi_source().name()); + 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(), @@ -761,11 +770,6 @@ MidiModel::DeltaCommand::get_state () return *delta_command; } -MidiModel::DeltaCommand::DeltaCommand (MidiModel& m, const XMLNode& node) - : _model(m) -{ - set_state(node); -} bool MidiModel::write_to(boost::shared_ptr source) diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 7b2c4009bb..aa93729d37 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -49,7 +49,7 @@ sigc::signal MidiSource::MidiSourceCreated; MidiSource::MidiSource (Session& s, string name) : Source (s, name, DataType::MIDI) , _timeline_position(0) - , _model(new MidiModel(*this)) + , _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(*this)) + , _model(new MidiModel(this)) , _writing (false) { _read_data_count = 0; @@ -192,12 +192,13 @@ MidiSource::session_saved() newsrc->set_timeline_position(_timeline_position); _model->write_to(newsrc); + // cyclic dependency here, ugly :( newsrc->set_model(_model); - _model.reset(); + _model->set_midi_source(newsrc.get()); newsrc->flush_header(); newsrc->flush_footer(); - + Switched.emit(newsrc); } } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index a2575caadd..0d0499b5a1 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -2994,7 +2994,7 @@ Session::restore_history (string snapshot_name) boost::shared_ptr midi_source = boost::dynamic_pointer_cast(source_by_id(id)); if(midi_source) { - ut->add_command(new MidiModel::DeltaCommand(*(midi_source->model()), *n)); + ut->add_command(new MidiModel::DeltaCommand(midi_source->model(), *n)); } else { error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg; } diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index bee3fd96d4..aef011ed00 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -872,7 +872,7 @@ SMFSource::load_model(bool lock, bool force_reload) } if (! _model) { - _model = boost::shared_ptr(new MidiModel(*this)); + _model = boost::shared_ptr(new MidiModel(this)); cerr << _name << " loaded new model " << _model.get() << endl; } else { cerr << _name << " reloading model " << _model.get() -- cgit v1.2.3