Fix thinkos in cubasish theme
[ardour.git] / gtk2_ardour / midi_region_view.cc
index ad6a7f60bceac3ec97a6093120a10702a8ef366b..3db3be3fdfec3e401b9fa95c3a6e1f049d11453b 100644 (file)
@@ -1,21 +1,29 @@
 /*
-    Copyright (C) 2001-2011 Paul Davis
-    Author: David Robillard
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
+ * Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2008-2012 Hans Baier <hansfbaier@googlemail.com>
+ * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
+ * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2015-2017 AndrĂ© Nusser <andre.nusser@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 #include <cmath>
 #include <algorithm>
@@ -40,9 +48,9 @@
 #include "ardour/operations.h"
 #include "ardour/session.h"
 
-#include "evoral/Parameter.hpp"
-#include "evoral/Event.hpp"
-#include "evoral/Control.hpp"
+#include "evoral/Parameter.h"
+#include "evoral/Event.h"
+#include "evoral/Control.h"
 #include "evoral/midi_util.h"
 
 #include "canvas/debug.h"
@@ -133,6 +141,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
        PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
 
        Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
+       UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
+
        connect_to_diskstream ();
 }
 
@@ -191,6 +201,8 @@ MidiRegionView::parameter_changed (std::string const & p)
                }
        } else if (p == "color-regions-using-track-color") {
                set_colors ();
+       } else if (p == "use-note-color-for-velocity") {
+               color_handler ();
        }
 }
 
@@ -347,6 +359,9 @@ MidiRegionView::canvas_group_event(GdkEvent* ev)
                return RegionView::canvas_group_event (ev);
        }
 
+       //For now, move the snapped cursor aside so it doesn't bother you during internal editing
+       //trackview.editor().set_snapped_cursor_position(_region->position());
+
        bool r;
 
        switch (ev->type) {
@@ -680,16 +695,17 @@ MidiRegionView::motion (GdkEventMotion* ev)
 
        }
 
-       /* we may be dragging some non-note object (eg. patch-change, sysex)
-        */
-
-       return editor.drags()->motion_handler ((GdkEvent *) ev, false);
+       //let RegionView do it's thing.  drags are handled in here
+       return RegionView::canvas_group_event ((GdkEvent *) ev);
 }
 
 
 bool
 MidiRegionView::scroll (GdkEventScroll* ev)
 {
+       if (trackview.editor().drags()->active()) {
+               return false;
+       }
        if (_selection.empty()) {
                return false;
        }
@@ -748,7 +764,7 @@ MidiRegionView::key_press (GdkEventKey* ev)
                bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
                bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
 
-               change_note_lengths (fine, shorter, Evoral::Beats(), start, end);
+               change_note_lengths (fine, shorter, Temporal::Beats(), start, end);
 
                return true;
 
@@ -917,13 +933,13 @@ MidiRegionView::show_list_editor ()
 }
 
 /** Add a note to the model, and the view, at a canvas (click) coordinate.
- * \param t time in frames relative to the position of the region
+ * \param t time in samples relative to the position of the region
  * \param y vertical position in pixels
  * \param length duration of the note in beats
  * \param snap_t true to snap t to the grid, otherwise false.
  */
 void
-MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap)
+MidiRegionView::create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap)
 {
        if (length < 2 * DBL_EPSILON) {
                return;
@@ -937,9 +953,9 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, ui
                return;
        }
 
-       // Start of note in frames relative to region start
+       // Start of note in samples relative to region start
        const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
-       Evoral::Beats beat_time = snap_frame_to_grid_underneath (t, divisions, shift_snap);
+       Temporal::Beats beat_time = snap_sample_to_grid_underneath (t, divisions, shift_snap);
 
        const double  note     = view->y_to_note(y);
        const uint8_t chan     = mtv->get_channel_for_add();
@@ -956,7 +972,6 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, ui
 
        start_note_diff_command(_("add note"));
 
-       clear_editor_note_selection ();
        note_diff_add_note (new_note, true, false);
 
        apply_diff();
@@ -1044,7 +1059,7 @@ MidiRegionView::note_diff_add_change (NoteBase* ev,
 void
 MidiRegionView::note_diff_add_change (NoteBase* ev,
                                       MidiModel::NoteDiffCommand::Property property,
-                                      Evoral::Beats val)
+                                      Temporal::Beats val)
 {
        if (_note_diff_command) {
                _note_diff_command->change (ev->note(), property, val);
@@ -1054,22 +1069,21 @@ MidiRegionView::note_diff_add_change (NoteBase* ev,
 void
 MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
 {
-       bool add_or_remove;
        bool commit = false;
 
        if (!_note_diff_command) {
                return;
        }
 
-       if (!was_copy && (add_or_remove = _note_diff_command->adds_or_removes())) {
+       bool add_or_remove = _note_diff_command->adds_or_removes();
+
+       if (!was_copy && add_or_remove) {
                // Mark all selected notes for selection when model reloads
                for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                        _marked_for_selection.insert((*i)->note());
                }
        }
 
-       midi_view()->midi_track()->midi_playlist()->region_edited (_region, _note_diff_command);
-
        if (as_subcommand) {
                _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
        } else {
@@ -1084,6 +1098,7 @@ MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
        }
 
        _marked_for_velocity.clear();
+
        if (commit) {
                trackview.editor().commit_reversible_command ();
        }
@@ -1094,6 +1109,7 @@ MidiRegionView::abort_command()
 {
        delete _note_diff_command;
        _note_diff_command = 0;
+       trackview.editor().abort_reversible_command();
        clear_editor_note_selection();
 }
 
@@ -1153,11 +1169,11 @@ MidiRegionView::find_canvas_sys_ex (MidiModel::SysExPtr s)
                return f->second;
        }
 
-       return 0;
+       return boost::shared_ptr<SysEx>();
 }
 
 void
-MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
+MidiRegionView::get_events (Events& e, Evoral::Sequence<Temporal::Beats>::NoteOperator op, uint8_t val, int chan_mask)
 {
        MidiModel::Notes notes;
        _model->get_notes (notes, op, val, chan_mask);
@@ -1175,13 +1191,13 @@ MidiRegionView::redisplay_model()
 {
        if (_active_notes) {
                // Currently recording
-               const framecnt_t zoom = trackview.editor().get_current_zoom();
+               const samplecnt_t zoom = trackview.editor().get_current_zoom();
                if (zoom != _last_display_zoom) {
                        /* Update resolved canvas notes to reflect changes in zoom without
-                          touching model.  Leave active notes (with length 0) alone since
+                          touching model.  Leave active notes (with length max) alone since
                           they are being extended. */
                        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-                               if (i->second->note()->length() > 0) {
+                               if (i->second->note()->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
                                        update_note(i->second);
                                }
                        }
@@ -1331,12 +1347,12 @@ MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_c
 
                if ((p = find_canvas_patch_change (*i)) != 0) {
 
-                       const framecnt_t region_frames = source_beats_to_region_frames ((*i)->time());
+                       const samplecnt_t region_samples = source_beats_to_region_samples ((*i)->time());
 
-                       if (region_frames < 0 || region_frames >= _region->length()) {
+                       if (region_samples < 0 || region_samples >= _region->length()) {
                                p->hide();
                        } else {
-                               const double x = trackview.editor().sample_to_pixel (region_frames);
+                               const double x = trackview.editor().sample_to_pixel (region_samples);
                                const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
                                p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
                                p->set_text (patch_name);
@@ -1367,14 +1383,14 @@ MidiRegionView::display_sysexes()
                }
 
                if (have_periodic_system_messages) {
-                       double zoom = trackview.editor().get_current_zoom (); // frames per pixel
+                       double zoom = trackview.editor().get_current_zoom (); // samples per pixel
 
                        /* get an approximate value for the number of samples per video frame */
 
-                       double video_frame = trackview.session()->frame_rate() * (1.0/30);
+                       double video_frame = trackview.session()->sample_rate() * (1.0/30);
 
                        /* if we are zoomed out beyond than the cutoff (i.e. more
-                        * frames per pixel than frames per 4 video frames), don't
+                        * samples per pixel than samples per 4 video frames), don't
                         * show periodic sysex messages.
                         */
 
@@ -1386,9 +1402,11 @@ MidiRegionView::display_sysexes()
                display_periodic_messages = false;
        }
 
+       const boost::shared_ptr<MidiRegion> mregion (midi_region());
+
        for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
                MidiModel::SysExPtr sysex_ptr = *i;
-               Evoral::Beats time = sysex_ptr->time();
+               Temporal::Beats time = sysex_ptr->time();
 
                if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
                        if (!display_periodic_messages) {
@@ -1406,7 +1424,7 @@ MidiRegionView::display_sysexes()
                }
                string text = str.str();
 
-               const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
+               const double x = trackview.editor().sample_to_pixel(source_beats_to_region_samples(time));
 
                double height = midi_stream_view()->contents_height();
 
@@ -1424,7 +1442,7 @@ MidiRegionView::display_sysexes()
                }
 
                // Show unless message is beyond the region bounds
-               if (time - _region->start() >= _region->length() || time < _region->start()) {
+               if (time - mregion->start_beats() >= mregion->length_beats() || time < mregion->start_beats()) {
                        sysex->hide();
                } else {
                        sysex->show();
@@ -1608,7 +1626,7 @@ MidiRegionView::end_write()
 /** Resolve an active MIDI note (while recording).
  */
 void
-MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
+MidiRegionView::resolve_note (uint8_t note, Temporal::Beats end_time)
 {
        if (midi_view()->note_mode() != Sustained) {
                return;
@@ -1617,13 +1635,12 @@ MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
        if (_active_notes && _active_notes[note]) {
                /* Set note length so update_note() works.  Note this is a local note
                   for recording, not from a model, so we can safely mess with it. */
-               _active_notes[note]->note()->set_length(
-                       end_time - _active_notes[note]->note()->time());
+               _active_notes[note]->note()->set_length (end_time - _active_notes[note]->note()->time());
 
                /* End time is relative to the region being recorded. */
-               const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
+               const samplepos_t end_time_samples = region_beats_to_region_samples (end_time);
 
-               _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
+               _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_samples));
                _active_notes[note]->set_outline_all ();
                _active_notes[note] = 0;
        }
@@ -1641,8 +1658,7 @@ MidiRegionView::extend_active_notes()
 
        for (unsigned i = 0; i < 128; ++i) {
                if (_active_notes[i]) {
-                       _active_notes[i]->set_x1(
-                               trackview.editor().sample_to_pixel(_region->length()));
+                       _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
                }
        }
 }
@@ -1736,33 +1752,46 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
        boost::shared_ptr<NoteType> note = ev->note();
 
        const double session_source_start = _region->quarter_note() - mr->start_beats();
-       const framepos_t note_start_frames = map.frame_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
+       const samplepos_t note_start_samples = map.sample_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
 
-       const double x0 = trackview.editor().sample_to_pixel (note_start_frames);
+       const double x0 = max (0.,trackview.editor().sample_to_pixel (note_start_samples));
        double x1;
        const double y0 = 1 + floor(note_to_y(note->note()));
        double y1;
 
-       /* trim note display to not overlap the end of its region */
-       if (note->length().to_double() > 0.0) {
+       if (note->length() == 0) {
+
+               /* special case actual zero-length notes */
+
+               x1 = x0 + 1.;
+
+       } else if (note->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
+
+               /* normal note */
+
                double note_end_time = note->end_time().to_double();
 
-               if (note_end_time > mr->start_beats() + mr->length_beats()) {
+               if (note->end_time() > mr->start_beats() + mr->length_beats()) {
                        note_end_time = mr->start_beats() + mr->length_beats();
                }
 
-               const framepos_t note_end_frames = map.frame_at_quarter_note (session_source_start + note_end_time) - _region->position();
+               const samplepos_t note_end_samples = map.sample_at_quarter_note (session_source_start + note_end_time) - _region->position();
+
+               x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_samples)) - 1;
 
-               x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
        } else {
+
+               /* nascent note currently being recorded, noteOff has not yet arrived */
+
                x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
        }
 
        y1 = y0 + std::max(1., floor(note_height()) - 1);
 
        ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
+       ev->set_velocity (note->velocity()/127.0);
 
-       if (!note->length()) {
+       if (note->end_time() == std::numeric_limits<Temporal::Beats>::max())  {
                if (_active_notes && note->note() < 128) {
                        Note* const old_rect = _active_notes[note->note()];
                        if (old_rect) {
@@ -1796,9 +1825,9 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
        boost::shared_ptr<NoteType> note = ev->note();
 
        const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
-       const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_quarter_note (note_time_qn) - _region->position();
+       const samplepos_t note_start_samples = trackview.session()->tempo_map().sample_at_quarter_note (note_time_qn) - _region->position();
 
-       const double x = trackview.editor().sample_to_pixel(note_start_frames);
+       const double x = trackview.editor().sample_to_pixel(note_start_samples);
        const double diamond_size = std::max(1., floor(note_height()) - 2.);
        const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
 
@@ -1821,9 +1850,9 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
 
 /** Add a MIDI note to the view (with length).
  *
- * If in sustained mode, notes with length 0 will be considered active
- * notes, and resolve_note should be called when the corresponding note off
- * event arrives, to properly display the note.
+ * If in sustained mode, notes with an end at numeric_limits<Beats>::max() will be
+ * considered active notes, and resolve_note should be called when the
+ * corresponding note off event arrives, to properly display the note.
  */
 NoteBase*
 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
@@ -1888,18 +1917,18 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
 
 void
 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
-                               Evoral::Beats pos, Evoral::Beats len)
+                               Temporal::Beats pos, Temporal::Beats len)
 {
        boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
 
        /* potentially extend region to hold new note */
 
-       framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
-       framepos_t region_end = _region->last_frame();
+       samplepos_t end_sample = source_beats_to_absolute_samples (new_note->end_time());
+       samplepos_t region_end = _region->last_sample();
 
-       if (end_frame > region_end) {
+       if (end_sample > region_end) {
                /* XX sets length in beats from audio space. make musical */
-               _region->set_length (end_frame - _region->position(), 0);
+               _region->set_length (end_sample - _region->position(), 0);
        }
 
        MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
@@ -1920,7 +1949,7 @@ MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity
 }
 
 void
-MidiRegionView::step_sustain (Evoral::Beats beats)
+MidiRegionView::step_sustain (Temporal::Beats beats)
 {
        change_note_lengths (false, false, beats, false, true);
 }
@@ -1933,8 +1962,8 @@ MidiRegionView::step_sustain (Evoral::Beats beats)
 void
 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
 {
-       framecnt_t region_frames = source_beats_to_region_frames (patch->time());
-       const double x = trackview.editor().sample_to_pixel (region_frames);
+       samplecnt_t region_samples = source_beats_to_region_samples (patch->time());
+       const double x = trackview.editor().sample_to_pixel (region_samples);
 
        double const height = midi_stream_view()->contents_height();
 
@@ -1955,7 +1984,7 @@ MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const
 
        if (patch_change->item().width() < _pixel_width) {
                // Show unless patch change is beyond the region bounds
-               if (region_frames < 0 || region_frames >= _region->length()) {
+               if (region_samples < 0 || region_samples >= _region->length()) {
                        patch_change->hide();
                } else {
                        patch_change->show();
@@ -1987,13 +2016,13 @@ MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
 
 /// Return true iff @p pc applies to the given time on the given channel.
 static bool
-patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
+patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Temporal::Beats time, uint8_t channel)
 {
        return pc->time() <= time && pc->channel() == channel;
 }
 
 void
-MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
+MidiRegionView::get_patch_key_at (Temporal::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
 {
        // The earliest event not before time
        MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
@@ -2039,7 +2068,7 @@ MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPri
 }
 
 void
-MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
+MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Temporal::Beats> & new_change)
 {
        string name = _("alter patch change");
        trackview.editor().begin_reversible_command (name);
@@ -2075,22 +2104,21 @@ MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const
 }
 
 /** Add a patch change to the region.
- *  @param t Time in frames relative to region position
+ *  @param t Time in samples relative to region position
  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
  *  MidiTimeAxisView::get_channel_for_add())
  */
 void
-MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
+MidiRegionView::add_patch_change (samplecnt_t t, Evoral::PatchChange<Temporal::Beats> const & patch)
 {
-       MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
        string name = _("add patch change");
 
        trackview.editor().begin_reversible_command (name);
        MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
        c->add (MidiModel::PatchChangePtr (
-                       new Evoral::PatchChange<Evoral::Beats> (
-                               absolute_frames_to_source_beats (_region->position() + t),
-                               mtv->get_channel_for_add(), patch.program(), patch.bank()
+                       new Evoral::PatchChange<Temporal::Beats> (
+                               absolute_samples_to_source_beats (_region->position() + t),
+                               patch.channel(), patch.program(), patch.bank()
                                )
                        )
                );
@@ -2102,7 +2130,7 @@ MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beat
 }
 
 void
-MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
+MidiRegionView::move_patch_change (PatchChange& pc, Temporal::Beats t)
 {
        trackview.editor().begin_reversible_command (_("move patch change"));
        MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
@@ -2241,12 +2269,12 @@ MidiRegionView::select_all_notes ()
 }
 
 void
-MidiRegionView::select_range (framepos_t start, framepos_t end)
+MidiRegionView::select_range (samplepos_t start, samplepos_t end)
 {
        clear_editor_note_selection ();
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               framepos_t t = source_beats_to_absolute_frames(i->first->time());
+               samplepos_t t = source_beats_to_absolute_samples(i->first->time());
                if (t >= start && t <= end) {
                        add_to_selection (i->second);
                }
@@ -2396,8 +2424,8 @@ MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
        } else {
                /* find end of latest note selected, select all between that and the start of "ev" */
 
-               Evoral::Beats earliest = Evoral::MaxBeats;
-               Evoral::Beats latest   = Evoral::Beats();
+               Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
+               Temporal::Beats latest   = Temporal::Beats();
 
                for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                        if ((*i)->note()->end_time() > latest) {
@@ -2435,15 +2463,15 @@ MidiRegionView::note_deselected(NoteBase* ev)
 }
 
 void
-MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
+MidiRegionView::update_drag_selection(samplepos_t start, samplepos_t end, double gy0, double gy1, bool extend)
 {
        PublicEditor& editor = trackview.editor();
 
        // Convert to local coordinates
-       const framepos_t p  = _region->position();
+       const samplepos_t p  = _region->position();
        const double     y  = midi_view()->y_position();
-       const double     x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
-       const double     x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
+       const double     x0 = editor.sample_to_pixel(max((samplepos_t)0, start - p));
+       const double     x1 = editor.sample_to_pixel(max((samplepos_t)0, end - p));
        const double     y0 = max(0.0, gy0 - y);
        const double     y1 = max(0.0, gy1 - y);
 
@@ -2549,12 +2577,10 @@ MidiRegionView::add_to_selection (NoteBase* ev)
        }
 }
 
-void
-MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
+Temporal::Beats
+MidiRegionView::earliest_in_selection ()
 {
-       typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
-       PossibleChord to_play;
-       Evoral::Beats earliest = Evoral::MaxBeats;
+       Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                if ((*i)->note()->time() < earliest) {
@@ -2562,11 +2588,47 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
                }
        }
 
+       return earliest;
+}
+
+void
+MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
+{
+       typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
+       Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
+       TempoMap& tmap (editor->session()->tempo_map());
+       PossibleChord to_play;
+       Temporal::Beats earliest = earliest_in_selection();
+
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-               if ((*i)->note()->time() == earliest) {
-                       to_play.push_back ((*i)->note());
+               NoteBase* n = *i;
+               if (n->note()->time() == earliest) {
+                       to_play.push_back (n->note());
                }
+               double const note_time_qn = session_relative_qn (n->note()->time().to_double());
+               double dx = 0.0;
+               if (midi_view()->note_mode() == Sustained) {
+                       dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
+                               - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
+               } else {
+                       /* Hit::x0() is offset by _position.x, unlike Note::x0() */
+                       Hit* hit = dynamic_cast<Hit*>(n);
+                       if (hit) {
+                               dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
+                                       - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
+                       }
+               }
+
                (*i)->move_event(dx, dy);
+
+               /* update length */
+               if (midi_view()->note_mode() == Sustained) {
+                       Note* sus = dynamic_cast<Note*> (*i);
+                       double const len_dx = editor->sample_to_pixel_unrounded (
+                               tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
+
+                       sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
+               }
        }
 
        if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
@@ -2593,15 +2655,17 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
 }
 
 NoteBase*
-MidiRegionView::copy_selection ()
+MidiRegionView::copy_selection (NoteBase* primary)
 {
-       NoteBase* note;
        _copy_drag_events.clear ();
 
        if (_selection.empty()) {
                return 0;
        }
 
+       NoteBase* note;
+       NoteBase* ret = 0;
+
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
                if (midi_view()->note_mode() == Sustained) {
@@ -2614,30 +2678,52 @@ MidiRegionView::copy_selection ()
                        note = h;
                }
 
+               if ((*i) == primary) {
+                       ret = note;
+               }
+
                _copy_drag_events.push_back (note);
        }
 
-       return _copy_drag_events.front ();
+       return ret;
 }
 
 void
-MidiRegionView::move_copies (double dx, double dy, double cumulative_dy)
+MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
 {
        typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
+       Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
+       TempoMap& tmap (editor->session()->tempo_map());
        PossibleChord to_play;
-       Evoral::Beats earliest = Evoral::MaxBeats;
+       Temporal::Beats earliest = earliest_in_selection();
 
        for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
-               if ((*i)->note()->time() < earliest) {
-                       earliest = (*i)->note()->time();
+               NoteBase* n = *i;
+               if (n->note()->time() == earliest) {
+                       to_play.push_back (n->note());
                }
-       }
-
-       for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
-               if ((*i)->note()->time() == earliest) {
-                       to_play.push_back ((*i)->note());
+               double const note_time_qn = session_relative_qn (n->note()->time().to_double());
+               double dx = 0.0;
+               if (midi_view()->note_mode() == Sustained) {
+                       dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
+                               - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
+               } else {
+                       Hit* hit = dynamic_cast<Hit*>(n);
+                       if (hit) {
+                               dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
+                                       - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
+                       }
                }
+
                (*i)->move_event(dx, dy);
+
+               if (midi_view()->note_mode() == Sustained) {
+                       Note* sus = dynamic_cast<Note*> (*i);
+                       double const len_dx = editor->sample_to_pixel_unrounded (
+                               tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
+
+                       sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
+               }
        }
 
        if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
@@ -2664,7 +2750,7 @@ MidiRegionView::move_copies (double dx, double dy, double cumulative_dy)
 }
 
 void
-MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool copy)
+MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
 {
        uint8_t lowest_note_in_selection  = 127;
        uint8_t highest_note_in_selection = 0;
@@ -2693,15 +2779,13 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool co
                if (highest_note_in_selection + dnote > 127) {
                        highest_note_difference = highest_note_in_selection - 127;
                }
-               TempoMap& map (trackview.session()->tempo_map());
 
                start_note_diff_command (_("move notes"));
 
                for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
 
-                       double const start_qn = _region->quarter_note() - midi_region()->start_beats();
-                       framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
-                       Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
+                       Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
+
                        if (new_time < 0) {
                                continue;
                        }
@@ -2734,16 +2818,12 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool co
                        highest_note_difference = highest_note_in_selection - 127;
                }
 
-               TempoMap& map (trackview.session()->tempo_map());
                start_note_diff_command (_("copy notes"));
 
                for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
 
                        /* update time */
-
-                       double const start_qn = _region->quarter_note() - midi_region()->start_beats();
-                       framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
-                       Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
+                       Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
 
                        if (new_time < 0) {
                                continue;
@@ -2784,13 +2864,13 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool co
 /** @param x Pixel relative to the region position.
  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
- *  @return Snapped frame relative to the region position.
+ *  @return Snapped sample relative to the region position.
  */
-framepos_t
+samplepos_t
 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
 {
        PublicEditor& editor (trackview.editor());
-       return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
+       return snap_sample_to_sample (editor.pixel_to_sample (x), ensure_snap).sample;
 }
 
 /** @param x Pixel relative to the region position.
@@ -2806,55 +2886,55 @@ MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
 double
 MidiRegionView::get_position_pixels()
 {
-       framepos_t region_frame = get_position();
-       return trackview.editor().sample_to_pixel(region_frame);
+       samplepos_t region_sample = get_position();
+       return trackview.editor().sample_to_pixel(region_sample);
 }
 
 double
 MidiRegionView::get_end_position_pixels()
 {
-       framepos_t frame = get_position() + get_duration ();
-       return trackview.editor().sample_to_pixel(frame);
+       samplepos_t sample = get_position() + get_duration ();
+       return trackview.editor().sample_to_pixel(sample);
 }
 
-framepos_t
-MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
+samplepos_t
+MidiRegionView::source_beats_to_absolute_samples(Temporal::Beats beats) const
 {
-       /* the time converter will return the frame corresponding to `beats'
+       /* the time converter will return the sample corresponding to `beats'
           relative to the start of the source. The start of the source
           is an implied position given by region->position - region->start
        */
-       const framepos_t source_start = _region->position() - _region->start();
+       const samplepos_t source_start = _region->position() - _region->start();
        return  source_start +  _source_relative_time_converter.to (beats);
 }
 
-Evoral::Beats
-MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
+Temporal::Beats
+MidiRegionView::absolute_samples_to_source_beats(samplepos_t samples) const
 {
-       /* the `frames' argument needs to be converted into a frame count
+       /* the `samples' argument needs to be converted into a sample count
           relative to the start of the source before being passed in to the
           converter.
        */
-       const framepos_t source_start = _region->position() - _region->start();
-       return  _source_relative_time_converter.from (frames - source_start);
+       const samplepos_t source_start = _region->position() - _region->start();
+       return  _source_relative_time_converter.from (samples - source_start);
 }
 
-framepos_t
-MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
+samplepos_t
+MidiRegionView::region_beats_to_region_samples(Temporal::Beats beats) const
 {
        return _region_relative_time_converter.to(beats);
 }
 
-Evoral::Beats
-MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
+Temporal::Beats
+MidiRegionView::region_samples_to_region_beats(samplepos_t samples) const
 {
-       return _region_relative_time_converter.from(frames);
+       return _region_relative_time_converter.from(samples);
 }
 
 double
-MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
+MidiRegionView::region_samples_to_region_beats_double (samplepos_t samples) const
 {
-       return _region_relative_time_converter_double.from(frames);
+       return _region_relative_time_converter_double.from(samples);
 }
 
 void
@@ -2875,9 +2955,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/)
                                                                                            ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
 
                        // calculate the colors: get the color settings
-                       uint32_t fill_color = UINT_RGBA_CHANGE_A(
-                               UIConfiguration::instance().color ("midi note selected"),
-                               128);
+                       uint32_t fill_color = NoteBase::meter_style_fill_color (note->note()->velocity(), true);
 
                        // make the resize preview notes more transparent and bright
                        fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
@@ -2889,7 +2967,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/)
                                0.85));
 
                        resize_rect->set_outline_color (NoteBase::calculate_outline (
-                                                               UIConfiguration::instance().color ("midi note selected")));
+                                                               UIConfiguration::instance().color ("midi note selected outline")));
 
                        resize_data->resize_rect = resize_rect;
                        _resize_data.push_back(resize_data);
@@ -2935,8 +3013,9 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
                }
 
                if (current_x < 0) {
-                       // This works even with snapping because RegionView::snap_frame_to_frame()
-                       // snaps forward if the snapped sample is before the beginning of the region
+                       /* This works even with snapping because RegionView::snap_sample_to_sample()
+                        * snaps forward if the snapped sample is before the beginning of the region
+                        */
                        current_x = 0;
                }
                if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
@@ -2961,15 +3040,15 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
 
                if (!cursor_set) {
                        /* Convert snap delta from pixels to beats. */
-                       framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
+                       samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
                        double snap_delta_beats = 0.0;
                        int sign = 1;
 
                        /* negative beat offsets aren't allowed */
                        if (snap_delta_samps > 0) {
-                               snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
+                               snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
                        } else if (snap_delta_samps < 0) {
-                               snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
+                               snap_delta_beats = region_samples_to_region_beats_double (- snap_delta_samps);
                                sign = -1;
                        }
 
@@ -2982,10 +3061,12 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
                        } else {
                                snapped_x = trackview.editor ().pixel_to_sample (current_x);
                        }
-                       const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions)
-                                                                    - midi_region()->beat()) + midi_region()->start_beats();
 
-                       Evoral::Beats len         = Evoral::Beats();
+                       const Temporal::Beats beats = Temporal::Beats (tmap.exact_beat_at_sample (snapped_x + midi_region()->position(), divisions)
+                                                                      - midi_region()->beat())
+                                                     + midi_region()->start_beats();
+
+                       Temporal::Beats len         = Temporal::Beats();
 
                        if (at_front) {
                                if (beats < canvas_note->note()->end_time()) {
@@ -2998,13 +3079,15 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
                                }
                        }
 
-                       len = std::max(Evoral::Beats(1 / 512.0), len);
+                       len = std::max(Temporal::Beats(1 / 512.0), len);
 
                        char buf[16];
                        snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
                        show_verbose_cursor (buf, 0, 0);
 
                        cursor_set = true;
+
+                       trackview.editor().set_snapped_cursor_position ( snapped_x + midi_region()->position() );
                }
 
        }
@@ -3055,20 +3138,20 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
                }
 
                /* Convert snap delta from pixels to beats with sign. */
-               framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
+               samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
                double snap_delta_beats = 0.0;
                int sign = 1;
 
                if (snap_delta_samps > 0) {
-                       snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
+                       snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
                } else if (snap_delta_samps < 0) {
-                       snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
+                       snap_delta_beats = region_samples_to_region_beats_double ( - snap_delta_samps);
                        sign = -1;
                }
 
                uint32_t divisions = 0;
-               /* Convert the new x position to a frame within the source */
-               framepos_t current_fr;
+               /* Convert the new x position to a sample within the source */
+               samplepos_t current_fr;
                if (with_snap) {
                        current_fr = snap_pixel_to_sample (current_x, ensure_snap);
                        divisions = trackview.editor().get_grid_music_divisions (0);
@@ -3077,13 +3160,13 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
                }
 
                /* and then to beats */
-               const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions);
+               const double e_qaf = tmap.exact_qn_at_sample (current_fr + midi_region()->position(), divisions);
                const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
-               const Evoral::Beats x_beats = Evoral::Beats (e_qaf - quarter_note_start);
+               const Temporal::Beats x_beats = Temporal::Beats (e_qaf - quarter_note_start);
 
                if (at_front && x_beats < canvas_note->note()->end_time()) {
                        note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
-                       Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
+                       Temporal::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
                        len += canvas_note->note()->length();
 
                        if (!!len) {
@@ -3092,7 +3175,7 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
                }
 
                if (!at_front) {
-                       Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
+                       Temporal::Beats len = std::max(Temporal::Beats(1 / 512.0),
                                                     x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
                        note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
                }
@@ -3149,12 +3232,12 @@ MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
 }
 
 void
-MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
+MidiRegionView::trim_note (NoteBase* event, Temporal::Beats front_delta, Temporal::Beats end_delta)
 {
        bool change_start = false;
        bool change_length = false;
-       Evoral::Beats new_start;
-       Evoral::Beats new_length;
+       Temporal::Beats new_start;
+       Temporal::Beats new_length;
 
        /* NOTE: the semantics of the two delta arguments are slightly subtle:
 
@@ -3169,7 +3252,7 @@ MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::B
                if (front_delta < 0) {
 
                        if (event->note()->time() < -front_delta) {
-                               new_start = Evoral::Beats();
+                               new_start = Temporal::Beats();
                        } else {
                                new_start = event->note()->time() + front_delta; // moves earlier
                        }
@@ -3184,7 +3267,7 @@ MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::B
 
                } else {
 
-                       Evoral::Beats new_pos = event->note()->time() + front_delta;
+                       Temporal::Beats new_pos = event->note()->time() + front_delta;
 
                        if (new_pos < event->note()->end_time()) {
                                new_start = event->note()->time() + front_delta;
@@ -3243,14 +3326,14 @@ MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
 }
 
 void
-MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
+MidiRegionView::change_note_time (NoteBase* event, Temporal::Beats delta, bool relative)
 {
-       Evoral::Beats new_time;
+       Temporal::Beats new_time;
 
        if (relative) {
                if (delta < 0.0) {
                        if (event->note()->time() < -delta) {
-                               new_time = Evoral::Beats();
+                               new_time = Temporal::Beats();
                        } else {
                                new_time = event->note()->time() + delta;
                        }
@@ -3265,7 +3348,7 @@ MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool rel
 }
 
 void
-MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
+MidiRegionView::change_note_length (NoteBase* event, Temporal::Beats t)
 {
        note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
 }
@@ -3377,11 +3460,11 @@ MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
 }
 
 void
-MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
+MidiRegionView::change_note_lengths (bool fine, bool shorter, Temporal::Beats delta, bool start, bool end)
 {
        if (!delta) {
                if (fine) {
-                       delta = Evoral::Beats(1.0/128.0);
+                       delta = Temporal::Beats(1.0/128.0);
                } else {
                        /* grab the current grid distance */
                        delta = get_grid_beats(_region->position());
@@ -3401,8 +3484,8 @@ MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delt
                /* note the negation of the delta for start */
 
                trim_note (*i,
-                          (start ? -delta : Evoral::Beats()),
-                          (end   ? delta  : Evoral::Beats()));
+                          (start ? -delta : Temporal::Beats()),
+                          (end   ? delta  : Temporal::Beats()));
                i = next;
        }
 
@@ -3422,41 +3505,41 @@ MidiRegionView::nudge_notes (bool forward, bool fine)
           into a vector and sort before using the first one.
        */
 
-       const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
-       Evoral::Beats    delta;
+       const samplepos_t ref_point = source_beats_to_absolute_samples ((*(_selection.begin()))->note()->time());
+       Temporal::Beats  delta;
 
        if (!fine) {
 
                /* non-fine, move by 1 bar regardless of snap */
-               delta = Evoral::Beats(trackview.session()->tempo_map().meter_at_frame (ref_point).divisions_per_bar());
+               delta = Temporal::Beats(trackview.session()->tempo_map().meter_at_sample (ref_point).divisions_per_bar());
 
        } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
 
                /* grid is off - use nudge distance */
 
-               framepos_t       unused;
-               const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
-               delta = region_frames_to_region_beats (fabs ((double)distance));
+               samplepos_t       unused;
+               const samplecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
+               delta = region_samples_to_region_beats (fabs ((double)distance));
 
        } else {
 
                /* use grid */
 
-               MusicFrame next_pos (ref_point, 0);
+               MusicSample next_pos (ref_point, 0);
                if (forward) {
-                       if (max_framepos - 1 < next_pos.frame) {
-                               next_pos.frame += 1;
+                       if (max_samplepos - 1 < next_pos.sample) {
+                               next_pos.sample += 1;
                        }
                } else {
-                       if (next_pos.frame == 0) {
+                       if (next_pos.sample == 0) {
                                return;
                        }
-                       next_pos.frame -= 1;
+                       next_pos.sample -= 1;
                }
 
-               trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
-               const framecnt_t distance = ref_point - next_pos.frame;
-               delta = region_frames_to_region_beats (fabs ((double)distance));
+               trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), SnapToGrid_Unscaled, false);
+               const samplecnt_t distance = ref_point - next_pos.sample;
+               delta = region_samples_to_region_beats (fabs ((double)distance));
        }
 
        if (!delta) {
@@ -3685,7 +3768,7 @@ MidiRegionView::selection_as_cut_buffer () const
 
 /** This method handles undo */
 bool
-MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
+MidiRegionView::paste (samplepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
 {
        bool commit = false;
        // Paste notes, if available
@@ -3718,7 +3801,7 @@ MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContex
 
 /** This method handles undo */
 void
-MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
+MidiRegionView::paste_internal (samplepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
 {
        if (mcb.empty()) {
                return;
@@ -3726,14 +3809,14 @@ MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float time
 
        start_note_diff_command (_("paste"));
 
-       const Evoral::Beats snap_beats    = get_grid_beats(pos);
-       const Evoral::Beats first_time    = (*mcb.notes().begin())->time();
-       const Evoral::Beats last_time     = (*mcb.notes().rbegin())->end_time();
-       const Evoral::Beats duration      = last_time - first_time;
-       const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
-       const Evoral::Beats paste_offset  = snap_duration * paste_count;
-       const Evoral::Beats quarter_note     = absolute_frames_to_source_beats(pos) + paste_offset;
-       Evoral::Beats       end_point     = Evoral::Beats();
+       const Temporal::Beats snap_beats    = get_grid_beats(pos);
+       const Temporal::Beats first_time    = (*mcb.notes().begin())->time();
+       const Temporal::Beats last_time     = (*mcb.notes().rbegin())->end_time();
+       const Temporal::Beats duration      = last_time - first_time;
+       const Temporal::Beats snap_duration = duration.snap_to(snap_beats);
+       const Temporal::Beats paste_offset  = snap_duration * paste_count;
+       const Temporal::Beats quarter_note  = absolute_samples_to_source_beats(pos) + paste_offset;
+       Temporal::Beats       end_point     = Temporal::Beats();
 
        DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
                                                       first_time,
@@ -3760,16 +3843,16 @@ MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float time
 
        /* if we pasted past the current end of the region, extend the region */
 
-       framepos_t end_frame = source_beats_to_absolute_frames (end_point);
-       framepos_t region_end = _region->position() + _region->length() - 1;
+       samplepos_t end_sample = source_beats_to_absolute_samples (end_point);
+       samplepos_t region_end = _region->position() + _region->length() - 1;
 
-       if (end_frame > region_end) {
+       if (end_sample > region_end) {
 
-               DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
+               DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_sample));
 
                _region->clear_changes ();
                /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
-               _region->set_length (end_frame - _region->position(), trackview.editor().get_grid_music_divisions (0));
+               _region->set_length (end_sample - _region->position(), trackview.editor().get_grid_music_divisions (0));
                trackview.session()->add_command (new StatefulDiffCommand (_region));
        }
 
@@ -3906,11 +3989,11 @@ MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
 
        PublicEditor& editor = trackview.editor ();
 
-       framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
+       samplepos_t const unsnapped_sample = editor.pixel_to_sample (x);
 
        const int32_t divisions = editor.get_grid_music_divisions (state);
        const bool shift_snap = midi_view()->note_mode() != Percussive;
-       const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap);
+       const Temporal::Beats snapped_beats = snap_sample_to_grid_underneath (unsnapped_sample, divisions, shift_snap);
 
        /* prevent Percussive mode from displaying a ghost hit at region end */
        if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
@@ -3929,7 +4012,7 @@ MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
        }
 
        /* calculate time in beats relative to start of source */
-       const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
+       const Temporal::Beats length = get_grid_beats(unsnapped_sample + _region->position());
 
        _ghost_note->note()->set_time (snapped_beats);
        _ghost_note->note()->set_length (length);
@@ -4006,9 +4089,9 @@ MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, doub
        uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
 
        if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
-               get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
+               get_events (e, Evoral::Sequence<Temporal::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
        } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
-               get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
+               get_events (e, Evoral::Sequence<Temporal::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
        } else {
                return;
        }
@@ -4053,7 +4136,7 @@ MidiRegionView::enable_display (bool yn)
 }
 
 void
-MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
+MidiRegionView::show_step_edit_cursor (Temporal::Beats pos)
 {
        if (_step_edit_cursor == 0) {
                ArdourCanvas::Item* const group = get_canvas_group();
@@ -4070,12 +4153,12 @@ MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
 }
 
 void
-MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
+MidiRegionView::move_step_edit_cursor (Temporal::Beats pos)
 {
        _step_edit_cursor_position = pos;
 
        if (_step_edit_cursor) {
-               double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
+               double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_samples (pos));
                _step_edit_cursor->set_x0 (pixel);
                set_step_edit_cursor_width (_step_edit_cursor_width);
        }
@@ -4090,14 +4173,15 @@ MidiRegionView::hide_step_edit_cursor ()
 }
 
 void
-MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
+MidiRegionView::set_step_edit_cursor_width (Temporal::Beats beats)
 {
        _step_edit_cursor_width = beats;
 
        if (_step_edit_cursor) {
-               _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (
-                                                  region_beats_to_region_frames (_step_edit_cursor_position + beats)
-                                                  - region_beats_to_region_frames (_step_edit_cursor_position)));
+               _step_edit_cursor->set_x1 (_step_edit_cursor->x0()
+                                          + trackview.editor().sample_to_pixel (
+                                            region_beats_to_region_samples (_step_edit_cursor_position + beats)
+                                            - region_beats_to_region_samples (_step_edit_cursor_position)));
        }
 }
 
@@ -4122,7 +4206,7 @@ MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
 
        boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
 
-       framepos_t back = max_framepos;
+       samplepos_t back = max_samplepos;
 
        for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
                const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
@@ -4135,13 +4219,15 @@ MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
                        }
                }
 
-               /* convert from session frames to source beats */
-               Evoral::Beats const time_beats = _source_relative_time_converter.from(
-                       ev.time() - src->timeline_position() + _region->start());
+               /* convert from session samples to source beats */
+               Temporal::Beats const time_beats = _source_relative_time_converter.from(
+                       ev.time() - src->natural_position() + _region->start());
 
                if (ev.type() == MIDI_CMD_NOTE_ON) {
-                       boost::shared_ptr<NoteType> note (
-                               new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
+
+                       boost::shared_ptr<NoteType> note (new NoteType (ev.channel(), time_beats, std::numeric_limits<Temporal::Beats>::max() - time_beats, ev.note(), ev.velocity()));
+
+                       assert (note->end_time() == std::numeric_limits<Temporal::Beats>::max());
 
                        add_note (note, true);
 
@@ -4295,23 +4381,23 @@ MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
        return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
 }
 
-/** @param p A session framepos.
+/** @param p A session samplepos.
  *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
  *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
  *  @return beat duration of p snapped to the grid subdivision underneath it.
  */
-Evoral::Beats
-MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const
+Temporal::Beats
+MidiRegionView::snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions, bool shift_snap) const
 {
        TempoMap& map (trackview.session()->tempo_map());
-       double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
+       double eqaf = map.exact_qn_at_sample (p + _region->position(), divisions);
 
        if (divisions != 0 && shift_snap) {
-               const double qaf = map.quarter_note_at_frame (p + _region->position());
+               const double qaf = map.quarter_note_at_sample (p + _region->position());
                /* Hack so that we always snap to the note that we are over, instead of snapping
                   to the next one if we're more than halfway through the one we're over.
                */
-               const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
+               const Temporal::Beats grid_beats = get_grid_beats (p + _region->position());
                const double rem = eqaf - qaf;
                if (rem >= 0.0) {
                        eqaf -= grid_beats.to_double();
@@ -4319,7 +4405,7 @@ MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions,
        }
        const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
 
-       return Evoral::Beats (eqaf - session_start_off);
+       return Temporal::Beats (eqaf - session_start_off);
 }
 
 ChannelMode
@@ -4337,14 +4423,14 @@ MidiRegionView::get_selected_channels () const
 }
 
 
-Evoral::Beats
-MidiRegionView::get_grid_beats(framepos_t pos) const
+Temporal::Beats
+MidiRegionView::get_grid_beats(samplepos_t pos) const
 {
        PublicEditor& editor  = trackview.editor();
        bool          success = false;
-       Evoral::Beats beats   = editor.get_grid_type_as_beats (success, pos);
+       Temporal::Beats beats   = editor.get_grid_type_as_beats (success, pos);
        if (!success) {
-               beats = Evoral::Beats(1);
+               beats = Temporal::Beats(1);
        }
        return beats;
 }
@@ -4369,3 +4455,9 @@ MidiRegionView::note_to_y(uint8_t note) const
 {
        return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
 }
+
+double
+MidiRegionView::session_relative_qn (double qn) const
+{
+       return qn + (region()->quarter_note() - midi_region()->start_beats());
+}