more MIDI paste improvements, plus move region-mute binding to PRIMARY-m and use...
[ardour.git] / gtk2_ardour / midi_region_view.cc
index 3087d3436ee10cf950997fbb0a0f818501d2d884..215f7c34da5fd6d769eea5d0b7b509b0eeb67053 100644 (file)
 
 #include <sigc++/signal.h>
 
-#include <ardour/playlist.h>
-#include <ardour/tempo.h>
-#include <ardour/midi_region.h>
-#include <ardour/midi_source.h>
-#include <ardour/midi_diskstream.h>
-#include <ardour/midi_model.h>
-#include <ardour/midi_patch_manager.h>
+#include "ardour/playlist.h"
+#include "ardour/tempo.h"
+#include "ardour/midi_region.h"
+#include "ardour/midi_source.h"
+#include "ardour/midi_diskstream.h"
+#include "ardour/midi_model.h"
+#include "ardour/midi_patch_manager.h"
 
-#include <evoral/Parameter.hpp>
-#include <evoral/Control.hpp>
+#include "evoral/Parameter.hpp"
+#include "evoral/Control.hpp"
 
-#include "streamview.h"
-#include "midi_region_view.h"
-#include "midi_streamview.h"
-#include "midi_time_axis.h"
-#include "simpleline.h"
+#include "automation_region_view.h"
+#include "automation_time_axis.h"
 #include "canvas-hit.h"
 #include "canvas-note.h"
 #include "canvas-program-change.h"
-#include "public_editor.h"
 #include "ghostregion.h"
-#include "midi_time_axis.h"
-#include "automation_time_axis.h"
-#include "automation_region_view.h"
-#include "utils.h"
-#include "midi_util.h"
 #include "gui_thread.h"
 #include "keyboard.h"
+#include "midi_cut_buffer.h"
+#include "midi_region_view.h"
+#include "midi_streamview.h"
+#include "midi_time_axis.h"
+#include "midi_time_axis.h"
+#include "midi_util.h"
+#include "public_editor.h"
+#include "selection.h"
+#include "simpleline.h"
+#include "streamview.h"
+#include "utils.h"
 
 #include "i18n.h"
 
@@ -66,7 +68,7 @@ using namespace Editing;
 using namespace ArdourCanvas;
 
 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
-               boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color)
+               boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
        : RegionView (parent, tv, r, spu, basic_color)
        , _force_channel(-1)
        , _last_channel_selection(0xFFFF)
@@ -105,7 +107,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
 
 
 MidiRegionView::MidiRegionView (const MidiRegionView& other)
-       : RegionView (other)
+       : sigc::trackable(other)
+       , RegionView (other)
        , _force_channel(-1)
        , _last_channel_selection(0xFFFF)
        , _default_note_length(1.0)
@@ -149,7 +152,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
 }
 
 void
-MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
+MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
 {
        if (wfd) {
                midi_region()->midi_source(0)->load_model();
@@ -194,8 +197,11 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
 bool
 MidiRegionView::canvas_event(GdkEvent* ev)
 {
-       static bool delete_mod = false;
-       static Editing::MidiEditMode original_mode;
+       PublicEditor& editor (trackview.editor());
+
+       if (!editor.internal_editing()) {
+               return false;
+       }
 
        static double drag_start_x, drag_start_y;
        static double last_x, last_y;
@@ -204,21 +210,9 @@ MidiRegionView::canvas_event(GdkEvent* ev)
 
        static ArdourCanvas::SimpleRect* drag_rect = NULL;
 
-       if (trackview.editor().current_mouse_mode() != MouseNote)
-               return false;
-       
-       const Editing::MidiEditMode midi_edit_mode = trackview.editor().current_midi_edit_mode();
-
        switch (ev->type) {
        case GDK_KEY_PRESS:
-               if (ev->key.keyval == GDK_Delete && !delete_mod) {
-                       delete_mod = true;
-                       original_mode = midi_edit_mode;
-                       trackview.editor().set_midi_edit_mode(MidiEditErase);
-                       start_delta_command(_("erase notes"));
-                       _mouse_state = EraseTouchDragging;
-                       return true;
-               } else if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) {
+               if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) {
                        _mouse_state = SelectTouchDragging;
                        return true;
                } else if (ev->key.keyval == GDK_Escape) {
@@ -229,15 +223,8 @@ MidiRegionView::canvas_event(GdkEvent* ev)
 
        case GDK_KEY_RELEASE:
                if (ev->key.keyval == GDK_Delete) {
-                       if (_mouse_state == EraseTouchDragging) {
-                               delete_selection();
-                               apply_command();
-                       }
-                       if (delete_mod) {
-                               trackview.editor().set_midi_edit_mode(original_mode);
-                               _mouse_state = None;
-                               delete_mod = false;
-                       }
+                       delete_selection();
+                       apply_command();
                        return true;
                } else if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) {
                        _mouse_state = None;
@@ -246,9 +233,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                return false;
 
        case GDK_BUTTON_PRESS:
-               if (_mouse_state != SelectTouchDragging && 
-                       _mouse_state != EraseTouchDragging &&
-                       ev->button.button == 1) {
+               if (_mouse_state != SelectTouchDragging && ev->button.button == 1) {
                        _pressed_button = ev->button.button;
                        _mouse_state = Pressed;
                        return true;
@@ -280,7 +265,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                case Pressed: // Drag start
 
                        // Select drag start
-                       if (_pressed_button == 1 && midi_edit_mode == MidiEditSelect) {
+                       if (_pressed_button == 1 && editor.current_mouse_mode() == MouseRange) {
                                group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                                                Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
                                last_x = event_x;
@@ -303,7 +288,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                                return true;
 
                        // Add note drag start
-                       } else if (midi_edit_mode == MidiEditPencil) {
+                       } else if (editor.current_mouse_mode() == MouseObject) {
                                group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                                                Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
                                last_x = event_x;
@@ -362,7 +347,6 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                        last_x = event_x;
                        last_y = event_y;
 
-               case EraseTouchDragging:
                case SelectTouchDragging:
                        return false;
 
@@ -384,12 +368,12 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                        
                switch (_mouse_state) {
                case Pressed: // Clicked
-                       switch (midi_edit_mode) {
-                       case MidiEditSelect:
-                       case MidiEditResize:
+                       switch (editor.current_mouse_mode()) {
+                       case MouseRange:
+                       case MouseTimeFX:
                                clear_selection();
                                break;
-                       case MidiEditPencil:
+                       case MouseObject:
                                create_note_at(event_x, event_y, _default_note_length);
                        default: break;
                        }
@@ -438,12 +422,12 @@ MidiRegionView::create_note_at(double x, double y, double length)
        assert(note <= 127.0);
 
        // Start of note in frames relative to region start
-       nframes64_t start_frames = snap_to_frame(trackview.editor().pixel_to_frame(x));
+       nframes64_t start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
        assert(start_frames >= 0);
 
        // Snap length
        length = frames_to_beats(
-                       snap_to_frame(start_frames + beats_to_frames(length)) - start_frames);
+                       snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
 
        const boost::shared_ptr<NoteType> new_note(new NoteType(0,
                        frames_to_beats(start_frames + _region->start()), length,
@@ -498,7 +482,7 @@ MidiRegionView::start_delta_command(string name)
 }
 
 void
-MidiRegionView::command_add_note(const boost::shared_ptr<NoteType> note, bool selected)
+MidiRegionView::command_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
 {
        if (_delta_command) {
                _delta_command->add(note);
@@ -506,6 +490,9 @@ MidiRegionView::command_add_note(const boost::shared_ptr<NoteType> note, bool se
        if (selected) {
                _marked_for_selection.insert(note);
        }
+       if (show_velocity) {
+               _marked_for_velocity.insert(note);
+       }
 }
 
 void
@@ -533,6 +520,7 @@ MidiRegionView::apply_command()
        midi_view()->midi_track()->diskstream()->playlist_modified();
 
        _marked_for_selection.clear();
+       _marked_for_velocity.clear();
 }
        
 
@@ -678,13 +666,13 @@ MidiRegionView::~MidiRegionView ()
        delete _delta_command;
 }
 
-
 void
 MidiRegionView::region_resized (Change what_changed)
 {
        RegionView::region_resized(what_changed);
        
        if (what_changed & ARDOUR::PositionChanged) {
+               set_duration(_region->length(), 0);
                if (_enable_display) {
                        redisplay_model();
                }
@@ -714,8 +702,8 @@ MidiRegionView::set_height (double height)
                         midi_stream_view()->highest_note(),
                         height != old_height + FUDGE);
        
-       if (name_text) {
-               name_text->raise_to_top();
+       if (name_pixbuf) {
+               name_pixbuf->raise_to_top();
        }
 }
 
@@ -752,7 +740,6 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
                                        item->show();
                                }
 
-                               event->hide_velocity();
                                if (CanvasNote* note = dynamic_cast<CanvasNote*>(event)) {
                                        const double y1 = midi_stream_view()->note_to_y(event->note()->note());
                                        const double y2 = y1 + floor(midi_stream_view()->note_height());
@@ -770,9 +757,6 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
                                        hit->move(x-hit->x1(), y-hit->y1());
                                        hit->show();
                                }
-                               if (event->selected()) {
-                                       event->show_velocity();
-                               }
                        }
                }
        }
@@ -782,9 +766,7 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
 GhostRegion*
 MidiRegionView::add_ghost (TimeAxisView& tv)
 {
-       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&trackview);
        CanvasNote* note;
-       assert(rtv);
 
        double unit_position = _region->position () / samples_per_unit;
        MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
@@ -836,6 +818,7 @@ MidiRegionView::end_write()
        delete[] _active_notes;
        _active_notes = NULL;
        _marked_for_selection.clear();
+       _marked_for_velocity.clear();
 }
 
 
@@ -996,6 +979,9 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note)
                if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
                        note_selected(event, true);
                }
+               if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
+                       event->show_velocity();
+               }
                event->on_channel_selection_change(_last_channel_selection);
                _events.push_back(event);
                if (note_in_visible_range(note)) {
@@ -1144,7 +1130,13 @@ MidiRegionView::next_program(CanvasProgramChange& program)
 void
 MidiRegionView::delete_selection()
 {
-       assert(_delta_command);
+       if (_selection.empty()) {
+               return;
+       }
+
+       if (!_delta_command) {
+               _delta_command = _model->new_delta_command("delete selection");
+       }
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                if ((*i)->selected()) {
@@ -1161,6 +1153,7 @@ MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                if ((*i)->selected() && (*i) != ev) {
                        (*i)->selected(false);
+                       (*i)->hide_velocity();
                }
        }
 
@@ -1170,33 +1163,32 @@ MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
 void
 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
 {
-       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
+
+               Selection::iterator tmp = i;
+               ++tmp;
+
                if ((*i) != ev) {
-                       (*i)->selected(false);
-               }
-       }
+                       remove_from_selection (*i);
+               } 
 
-       _selection.clear();
-       _selection.insert(ev);
+               i = tmp;
+       }
 
-       if ( ! ev->selected()) {
-               ev->selected(true);
+       if (!ev->selected()) {
+               add_to_selection (ev);
        }
 }
 
 void
 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add)
 {
-       if ( ! add) {
+       if (!add) {
                clear_selection_except(ev);
        }
 
-       if (_selection.insert(ev).second) {
-               play_midi_note(ev->note());
-       }
-
-       if ( ! ev->selected()) {
-               ev->selected(true);
+       if (!ev->selected()) {
+               add_to_selection (ev);
        }
 }
 
@@ -1204,15 +1196,11 @@ MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add)
 void
 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev, bool add)
 {
-       if ( ! add) {
+       if (!add) {
                clear_selection_except(ev);
        }
 
-       _selection.erase(ev);
-
-       if (ev->selected()) {
-               ev->selected(false);
-       }
+       remove_from_selection (ev);
 }
 
 
@@ -1237,17 +1225,12 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2
                        assert((*i)->x1() >= last_x1);
                        last_x1 = (*i)->x1();
 #endif
-                       // Inside rectangle
                        if ((*i)->x1() >= x1 && (*i)->x1() <= x2 && (*i)->y1() >= last_y && (*i)->y1() <= y) {
-                               if (!(*i)->selected()) {
-                                       (*i)->selected(true);
-                                       _selection.insert(*i);
-                                       play_midi_note((*i)->note());
-                               }
-                       // Not inside rectangle
+                               // Inside rectangle
+                               add_to_selection (*i);
                        } else if ((*i)->selected()) {
-                               (*i)->selected(false);
-                               _selection.erase(*i);
+                               // Not inside rectangle
+                               remove_from_selection (*i);
                        }
                }
        } else {
@@ -1257,31 +1240,63 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2
                        assert((*i)->x1() >= last_x1);
                        last_x1 = (*i)->x1();
 #endif
-                       // Inside rectangle
                        if ((*i)->x2() <= x1 && (*i)->x2() >= x2 && (*i)->y1() >= last_y && (*i)->y1() <= y) {
-                               if (!(*i)->selected()) {
-                                       (*i)->selected(true);
-                                       _selection.insert(*i);
-                                       play_midi_note((*i)->note());
-                               }
-                       // Not inside rectangle
+                               // Inside rectangle
+                               add_to_selection (*i);
                        } else if ((*i)->selected()) {
-                               (*i)->selected(false);
-                               _selection.erase(*i);
+                               // Not inside rectangle
+                               remove_from_selection (*i);
                        }
                }
        }
 }
 
+void
+MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
+{
+       Selection::iterator i = _selection.find (ev);
+
+       if (i != _selection.end()) {
+               _selection.erase (i);
+       }
+
+       ev->selected (false);
+       ev->hide_velocity ();
+       
+       if (_selection.empty()) {
+               PublicEditor& editor (trackview.editor());
+               editor.get_selection().remove (this);
+       }
+}
+
+void
+MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
+{
+       bool add_mrv_selection = false;
+
+       if (_selection.empty()) {
+               add_mrv_selection = true;
+       }
+
+       if (_selection.insert (ev).second) {
+               ev->selected (true);
+               play_midi_note ((ev)->note());
+       }
+
+       if (add_mrv_selection) {
+               PublicEditor& editor (trackview.editor());
+               editor.get_selection().add (this);
+       }
+}
 
 void
 MidiRegionView::move_selection(double dx, double dy)
 {
-       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i)
+       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                (*i)->move_event(dx, dy);
+       }
 }
 
-
 void
 MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
 {
@@ -1324,18 +1339,17 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
 
                const boost::shared_ptr<NoteType> copy(new NoteType(*(*i)->note().get()));
 
-               // we need to snap here again in nframes64_t in order to be sample accurate 
-               double start_frames = snap_to_frame(beats_to_frames((*i)->note()->time()) + dt);
-
-               // keep notes inside region if dragged beyond left region bound
-               if (start_frames < _region->start()) {                          
-                       start_frames = _region->start();
+               nframes64_t start_frames = beats_to_frames((*i)->note()->time());
+               if (dt >= 0) {
+                       start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
+               } else {
+                       start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
                }
-               
+
                copy->set_time(frames_to_beats(start_frames));
 
                uint8_t original_pitch = (*i)->note()->note();
-               uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+               uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
                
                // keep notes in standard midi range
                clamp_to_0_127(new_pitch);
@@ -1367,9 +1381,9 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
 }
 
 nframes64_t
-MidiRegionView::snap_to_frame(double x)
+MidiRegionView::snap_pixel_to_frame(double x)
 {
-       PublicEditor &editor = trackview.editor();
+       PublicEditoreditor = trackview.editor();
        // x is region relative, convert it to global absolute frames
        nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
        editor.snap_to(frame);
@@ -1377,22 +1391,19 @@ MidiRegionView::snap_to_frame(double x)
 }
 
 nframes64_t
-MidiRegionView::snap_to_frame(nframes64_t x)
+MidiRegionView::snap_frame_to_frame(nframes64_t x)
 {
-       PublicEditor &editor = trackview.editor();
-       // x is region relative
-       // convert x to global frame
+       PublicEditor& editor = trackview.editor();
+       // x is region relative, convert it to global absolute frames
        nframes64_t frame = x + _region->position();
        editor.snap_to(frame);
-       // convert event_frame back to local coordinates relative to position
-       frame -= _region->position();
-       return frame;
+       return frame - _region->position(); // convert back to region relative
 }
 
 double
 MidiRegionView::snap_to_pixel(double x)
 {
-       return (double) trackview.editor().frame_to_pixel(snap_to_frame(x));
+       return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
 }
 
 double
@@ -1507,7 +1518,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo
 
                // because snapping works on world coordinates we have to transform current_x
                // to world coordinates before snapping and transform it back afterwards
-               nframes64_t current_frame = snap_to_frame(current_x);
+               nframes64_t current_frame = snap_pixel_to_frame(current_x);
                // transform to region start relative
                current_frame += _region->start();
                
@@ -1548,7 +1559,7 @@ MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bo
        }
 
        command_remove_note(event);
-       command_add_note(copy, event->selected());
+       command_add_note(copy, event->selected(), true);
 }
 
 void
@@ -1596,11 +1607,7 @@ MidiRegionView::change_channel(uint8_t channel)
 void
 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
 {
-       if (ev->note() && _mouse_state == EraseTouchDragging) {
-               start_delta_command(_("note entered"));
-               ev->selected(true);
-               _delta_command->remove(ev->note());
-       } else if (_mouse_state == SelectTouchDragging) {
+       if (_mouse_state == SelectTouchDragging) {
                note_selected(ev, true);
        }
 }
@@ -1655,3 +1662,94 @@ MidiRegionView::midi_patch_settings_changed(std::string model, std::string custo
        redisplay_model();
 }
 
+void
+MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
+{
+       if (_selection.empty()) {
+               return;
+       }
+
+       PublicEditor& editor (trackview.editor());
+
+       switch (op) {
+       case Cut:
+       case Copy:
+               editor.get_cut_buffer().add (selection_as_cut_buffer());
+               break;
+       default:
+               break;
+       }
+               
+       start_delta_command();
+
+       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+               switch (op) {
+               case Copy:
+                       break;
+               case Cut:
+                       command_remove_note (*i);
+                       break;
+               case Clear:
+                       break;
+               }
+       }
+
+       apply_command();
+}
+
+MidiCutBuffer*
+MidiRegionView::selection_as_cut_buffer () const
+{
+       Evoral::Sequence<MidiModel::TimeType>::Notes notes;
+
+       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+               notes.push_back (boost::shared_ptr<NoteType> (new NoteType (*((*i)->note().get()))));
+       }
+
+       /* sort them into time order */
+
+       sort (notes.begin(), notes.end(), Evoral::Sequence<MidiModel::TimeType>::note_time_comparator);
+
+       MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
+       cb->set (notes);
+       
+       return cb;
+}
+
+void
+MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
+{
+       if (mcb.empty()) {
+               return;
+       }
+
+       start_delta_command (_("paste"));
+
+       MidiModel::TimeType beat_delta;
+       MidiModel::TimeType paste_pos_beats;
+       MidiModel::TimeType duration;
+
+       duration = mcb.notes().back()->end_time() - mcb.notes().front()->time();
+       paste_pos_beats = frames_to_beats (pos);
+       beat_delta = mcb.notes().front()->time() - paste_pos_beats;
+       paste_pos_beats = 0;
+
+       _selection.clear ();
+
+       for (int n = 0; n < (int) times; ++n) {
+
+               for (Evoral::Sequence<MidiModel::TimeType>::Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
+                       
+                       boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
+                       copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
+
+                       /* make all newly added notes selected */
+
+                       command_add_note (copied_note, true);
+               }
+
+               paste_pos_beats += duration;
+       }
+       
+       apply_command ();
+}