various minor MIDI fixes: prevent duplicate note entry with mouse, show note info...
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 20 May 2010 22:38:12 +0000 (22:38 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Thu, 20 May 2010 22:38:12 +0000 (22:38 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7128 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/midi_region_view.cc
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_source.h
libs/ardour/midi_model.cc
libs/ardour/midi_source.cc
libs/evoral/evoral/Sequence.hpp
libs/evoral/src/Sequence.cpp

index a35357884a04c90e96bcf5d1fa91104b6ba3e836..66c67c0c371a49f9ef12c25b47df87c3d8f4a328 100644 (file)
@@ -3243,7 +3243,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                        } else {
                                /* new selection */
 
-                               if (!_editor->selection->selected (_editor->clicked_axisview)) {
+                               if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
                                        _editor->selection->set (_editor->clicked_axisview);
                                }
                                
@@ -3361,7 +3361,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->selection->clear_time();
                }
 
-               if (!_editor->selection->selected (_editor->clicked_axisview)) {
+               if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
                        _editor->selection->set (_editor->clicked_axisview);
                }
                
@@ -3747,9 +3747,10 @@ NoteDrag::motion (GdkEvent*, bool)
                region->move_selection (dx, dy);
 
                CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
-                char buf[4];
-               snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
-               //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
+
+                char buf[12];
+                snprintf (buf, sizeof (buf), "%s (%g)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
+                          (int) cnote->note()->note() + drag_delta_note);
                _editor->show_verbose_canvas_cursor_with (buf);
         }
 }
index 75e81a64d6b510096ad5eb1a8090e1eae8391936..d60edaccc2e9c1ddf808e780439995efee45b0dc 100644 (file)
@@ -669,7 +669,6 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                switch (item_type) {
                case NoteItem:
                        if (internal_editing()) {
-                               /* Note: we don't get here if not in internal_editing() mode */
                                _drags->set (new NoteDrag (this, item), event);
                                return true;
                        }
index d1c1656ce36861b487a6c7e709a897bf47498c0b..d81c0d76d991a937147bc2de9753f0caf2a51b29 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "evoral/Parameter.hpp"
 #include "evoral/Control.hpp"
+#include "evoral/midi_util.h"
 
 #include "automation_region_view.h"
 #include "automation_time_axis.h"
@@ -566,6 +567,10 @@ MidiRegionView::create_note_at(double x, double y, double length)
                        frames_to_beats(start_frames + _region->start()), length,
                        (uint8_t)note, 0x40));
 
+        if (_model->contains (new_note)) {
+                return;
+        }
+
        view->update_note_range(new_note->note());
 
        MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
@@ -797,8 +802,6 @@ MidiRegionView::redisplay_model()
        MidiModel::Notes& notes (_model->notes());
        _optimization_iterator = _events.begin();
 
-        cerr << "++++++++++ MIDI REdisplay\n";
-
        for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
 
                boost::shared_ptr<NoteType> note (*n);
@@ -2394,12 +2397,10 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
                note_selected(ev, true);
        }
 
-       char buf[4];
-       snprintf (buf, sizeof (buf), "%d", (int) ev->note()->note());
-       // This causes an infinite loop on note add sometimes
-       //PublicEditor& editor (trackview.editor());
-       //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
-       //editor.show_verbose_canvas_cursor_with (buf);
+       char buf[12];
+       snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (ev->note()->note()).c_str(), (int) ev->note()->note());
+       PublicEditor& editor (trackview.editor());
+       editor.show_verbose_canvas_cursor_with (buf);
 }
 
 void
index 8d949cadbb6f46922b5442fe4f97d16501681f64..b3a4d746a195613e03fcad0756894bc354f71df9 100644 (file)
@@ -151,8 +151,9 @@ public:
        void                     apply_command(Session& session, Command* cmd);
        void                     apply_command_as_subcommand(Session& session, Command* cmd);
 
-       bool write_to(boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
-                      Evoral::MusicalTime end = Evoral::MaxMusicalTime);
+       bool write_to(boost::shared_ptr<MidiSource> source);
+       bool write_section_to(boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
+                              Evoral::MusicalTime end = Evoral::MaxMusicalTime);
 
        // MidiModel doesn't use the normal AutomationList serialisation code
        // since controller data is stored in the .mid
index 2484d3575a21f06e46e7d5535b47061be4a80d25..c418559d6eeffe222a7119e19c8c8207627f986b 100644 (file)
@@ -111,7 +111,7 @@ class MidiSource : virtual public Source
 
        boost::shared_ptr<MidiModel> model() { return _model; }
        void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
-       void drop_model() { _model.reset(); }
+       void drop_model();
 
   protected:
        virtual void flush_midi() = 0;
index 88ef60e8e56ccd7d78ba43e4c92a71d3089c1919..3b258eab09e8dc48e84983ea68674fa3b7491273 100644 (file)
@@ -676,6 +676,36 @@ MidiModel::DiffCommand::get_state ()
        return *diff_command;
 }
 
+/** Write all of 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
+ * user can switch a recorded track (with note durations from some instrument)
+ * 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)
+{
+       ReadLock lock(read_lock());
+
+       const bool old_percussive = percussive();
+       set_percussive(false);
+
+       source->drop_model();
+       source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
+
+       for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
+                source->append_event_unlocked_beats(*i);
+       }
+
+       set_percussive(old_percussive);
+       source->mark_streaming_write_completed();
+
+       set_edited(false);
+
+       return true;
+}
+
 /** Write part or all of 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
@@ -684,7 +714,7 @@ MidiModel::DiffCommand::get_state ()
  * destroying the original note durations.
  */
 bool
-MidiModel::write_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
+MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
 {
        ReadLock lock(read_lock());
         MidiStateTracker mst;
index 5e8bda1ea29ce2f190899526a859924fd5dd457b..0c66879330a3526302cfe77781297ec520c48ad2 100644 (file)
@@ -250,7 +250,11 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
         newsrc->set_timeline_position(_timeline_position);
 
         if (_model) {
-                _model->write_to (newsrc, begin, end);
+                if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
+                        _model->write_to (newsrc);
+                } else {
+                        _model->write_section_to (newsrc, begin, end);
+                }
         } else {
                 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
                 return boost::shared_ptr<MidiSource>();
@@ -290,3 +294,9 @@ MidiSource::set_note_mode(NoteMode mode)
        }
 }
 
+void
+MidiSource::drop_model ()
+{
+        cerr << name() << " drop model\n";
+        _model.reset(); 
+}
index 3aafb15312e718222b2a750022882518a07743d5..038cf2c344feb896e0e90d66380d3e3089760bcc 100644 (file)
@@ -202,7 +202,8 @@ public:
        bool edited() const      { return _edited; }
        void set_edited(bool yn) { _edited = yn; }
 
-       void add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
+        bool contains (const boost::shared_ptr< Note<Time> > ev) const;
+       bool add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
        void remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note);
 
        uint8_t lowest_note()  const { return _lowest_note; }
index 281aec514bacb413b57c4393711e869ef1581ae6..523643665fb49d176abbe273042274f143964ae9 100644 (file)
@@ -730,12 +730,38 @@ Sequence<Time>::append_sysex_unlocked(const MIDIEvent<Time>& ev)
 }
 
 template<typename Time>
-void
+bool
+Sequence<Time>::contains(const boost::shared_ptr< Note<Time> > note) const
+{
+        ReadLock lock (read_lock());
+
+       for (typename Sequence<Time>::Notes::const_iterator i = note_lower_bound(note->time());
+                       i != _notes.end() && (*i)->time() == note->time(); ++i) {
+               if (*i == note) {
+                        cerr << "Existing note matches: " << *i << endl;
+                       return true;
+               }
+       }
+        cerr << "No matching note for " << note << endl;
+        return false;
+}
+
+template<typename Time>
+bool
 Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
 {
        DUMP(format("%1% add note %2% @ %3%\n") % this % (int)note->note() % note->time());
+
+       for (typename Sequence<Time>::Notes::iterator i = note_lower_bound(note->time());
+                       i != _notes.end() && (*i)->time() == note->time(); ++i) {
+               if (*i == note) {
+                       return false;
+               }
+       }
+
        _edited = true;
        _notes.insert(note);
+        return true;
 }
 
 template<typename Time>