diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index 52e7ac5eef..20e0880f05 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -224,24 +224,8 @@ CanvasNoteEvent::on_event(GdkEvent* ev) static double last_x, last_y; double event_x, event_y, dx, dy; bool select_mod; - uint8_t d_velocity = 10; switch (ev->type) { - case GDK_SCROLL: - if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) { - d_velocity = 1; - } - - if (ev->scroll.direction == GDK_SCROLL_UP) { - _region.change_velocity(this, d_velocity, true); - return true; - } else if (ev->scroll.direction == GDK_SCROLL_DOWN) { - _region.change_velocity(this, -d_velocity, true); - return true; - } else { - return false; - } - case GDK_ENTER_NOTIFY: _region.note_entered(this); //_item->grab_focus(); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 1f7447ca0e..70f5f4d692 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -891,10 +891,14 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) /* get the playlist where this drag started. we can't use rv->region()->playlist() because we may have copied the region and it has not been attached to a playlist. */ + + source_tv = dynamic_cast (&rv->get_time_axis_view()); + ds = source_tv->get_diskstream(); + from_playlist = ds->playlist(); - assert ((source_tv = dynamic_cast (&rv->get_time_axis_view()))); - assert ((ds = source_tv->get_diskstream())); - assert ((from_playlist = ds->playlist())); + assert (source_tv); + assert (ds); + assert (from_playlist); /* moved to a different audio track, without copying */ diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 02c13fb251..41d14c4f1e 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -2558,5 +2558,14 @@ void Editor::set_internal_edit (bool yn) { _internal_editing = yn; + + if (yn) { + mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_select"))))); + mouse_move_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil"))))); + } else { + mouse_select_button.set_image (*(manage (new Image (::get_xpm("tool_range.xpm"))))); + mouse_move_button.set_image (*(manage (new Image (::get_icon("tool_object"))))); + } + set_canvas_cursor (); } diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 4967886b7c..248b2cfb9e 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -210,10 +210,27 @@ MidiRegionView::canvas_event(GdkEvent* ev) static double last_x, last_y; double event_x, event_y; nframes64_t event_frame = 0; + uint8_t d_velocity = 10; static ArdourCanvas::SimpleRect* drag_rect = NULL; switch (ev->type) { + case GDK_SCROLL: + if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) { + d_velocity = 1; + } + + if (ev->scroll.direction == GDK_SCROLL_UP) { + change_velocities (d_velocity, true); + return true; + } else if (ev->scroll.direction == GDK_SCROLL_DOWN) { + change_velocities(-d_velocity, true); + return true; + } else { + return false; + } + break; + case GDK_KEY_PRESS: if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) { _mouse_state = SelectTouchDragging; @@ -232,6 +249,36 @@ MidiRegionView::canvas_event(GdkEvent* ev) } else if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) { _mouse_state = None; return true; + } else if (ev->key.keyval == GDK_Tab) { + if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) { + goto_previous_note (); + } else { + goto_next_note (); + } + return true; + } else if (ev->key.keyval == GDK_Up) { + + if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) { + change_velocities (1, true); + } else { + transpose (true, false); + } + + } else if (ev->key.keyval == GDK_Down) { + + if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) { + change_velocities (-1, true); + } else { + transpose (false, false); + } + } else if (ev->key.keyval == GDK_Left) { + + nudge_notes (-1.0); + + } else if (ev->key.keyval == GDK_Right) { + + nudge_notes (1.0); + } return false; @@ -468,7 +515,6 @@ MidiRegionView::clear_events() void MidiRegionView::display_model(boost::shared_ptr model) { - cerr << "MRV: display mode, enable = " << _enable_display << endl; _model = model; content_connection.disconnect (); content_connection = _model->ContentsChanged.connect(sigc::mem_fun(this, &MidiRegionView::redisplay_model)); @@ -542,7 +588,6 @@ MidiRegionView::abort_command() void MidiRegionView::redisplay_model() { - cerr << "MRV: display mode, active notes = " << _active_notes << endl; // Don't redisplay the model if we're currently recording and displaying that if (_active_notes) { return; @@ -1569,6 +1614,46 @@ MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bo command_add_note(copy, event->selected(), true); } +void +MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative) +{ + const boost::shared_ptr copy(new NoteType(*(event->note().get()))); + + if (relative) { + uint8_t new_note = copy->note() + note; + clamp_to_0_127(new_note); + copy->set_note(new_note); + } else { + copy->set_note(note); + } + + command_remove_note(event); + command_add_note(copy, event->selected(), false); +} + +void +MidiRegionView::change_note_time (CanvasNoteEvent* event, MidiModel::TimeType delta, bool relative) +{ + const boost::shared_ptr copy(new NoteType(*(event->note().get()))); + + if (relative) { + if (delta < 0.0) { + if (copy->time() < -delta) { + copy->set_time (0); + } else { + copy->set_time (copy->time() + delta); + } + } else { + copy->set_time (copy->time() + delta); + } + } else { + copy->set_time (delta); + } + + command_remove_note(event); + command_add_note(copy, event->selected(), false); +} + void MidiRegionView::change_velocity(CanvasNoteEvent* ev, int8_t velocity, bool relative) { @@ -1588,6 +1673,66 @@ MidiRegionView::change_velocity(CanvasNoteEvent* ev, int8_t velocity, bool relat apply_command(); } + +void +MidiRegionView::change_velocities (int8_t velocity, bool relative) +{ + start_delta_command(_("change velocities")); + + for (Selection::iterator i = _selection.begin(); i != _selection.end();) { + Selection::iterator next = i; + ++next; + change_note_velocity (*i, velocity, relative); + i = next; + } + + apply_command(); +} + + +void +MidiRegionView::transpose (bool up, bool fine) +{ + int8_t delta; + + if (fine) { + delta = 1; + } else { + delta = 12; + } + + if (!up) { + delta = -delta; + } + + start_delta_command (_("transpose")); + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + Selection::iterator next = i; + ++next; + change_note_note (*i, delta, true); + i = next; + } + + apply_command (); +} + +void +MidiRegionView::nudge_notes (MidiModel::TimeType delta) +{ + start_delta_command (_("nudge")); + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + Selection::iterator next = i; + ++next; + change_note_time (*i, delta, true); + i = next; + } + + apply_command (); +} + + void MidiRegionView::change_channel(uint8_t channel) { @@ -1752,6 +1897,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta); /* make all newly added notes selected */ + cerr << "\tadd @ " << copied_note->time() << endl; command_add_note (copied_note, true); end_point = copied_note->end_time(); @@ -1762,14 +1908,51 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) /* if we pasted past the current end of the region, extend the region */ - nframes64_t end_frame = beats_to_frames (end_point); + nframes64_t end_frame = _region->position() + beats_to_frames (end_point); nframes64_t region_end = _region->position() + _region->length() - 1; + cerr << "\tEnd frame = " << end_frame << " from " << end_point << " region end = " << region_end << endl; + if (end_frame > region_end) { + + trackview.session().begin_reversible_command (_("paste")); + XMLNode& before (_region->get_state()); _region->set_length (end_frame, this); + cerr << "\textended to " << end_frame << endl; trackview.session().add_command (new MementoCommand(*_region, &before, &_region->get_state())); } apply_command (); } + +void +MidiRegionView::goto_next_note () +{ + nframes64_t pos = trackview.session().transport_frame(); + + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { + nframes64_t npos = _region->position() + beats_to_frames ((*i)->note()->time()); + + if (npos >= pos && !(*i)->selected()) { + unique_select (*i); + trackview.session().request_locate (npos); + return; + } + } +} + +void +MidiRegionView::goto_previous_note () +{ + nframes64_t pos = trackview.session().transport_frame(); + + for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) { + nframes64_t npos = _region->position() + beats_to_frames ((*i)->note()->time()); + if (npos <= pos && !(*i)->selected()) { + unique_select (*i); + trackview.session().request_locate (npos); + return; + } + } +} diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index d759b7c411..83cbbc1d77 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -260,7 +260,13 @@ class MidiRegionView : public RegionView /** Return the current selection as a MidiModel or null if there is no selection */ ARDOUR::MidiModel* selection_as_model () const; - + + void goto_previous_note (); + void goto_next_note (); + void change_velocities (int8_t velocity, bool relative); + void transpose (bool up, bool fine); + void nudge_notes (ARDOUR::MidiModel::TimeType delta); + protected: /** Allows derived types to specify their visibility requirements * to the TimeAxisViewItem parent class. @@ -300,6 +306,8 @@ class MidiRegionView : public RegionView void midi_patch_settings_changed(std::string model, std::string custom_device_mode); void change_note_velocity(ArdourCanvas::CanvasNoteEvent* ev, int8_t vel, bool relative=false); + void change_note_note(ArdourCanvas::CanvasNoteEvent* ev, int8_t note, bool relative=false); + void change_note_time(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::TimeType, bool relative=false); void clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev); void clear_selection() { clear_selection_except(NULL); } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 32ef8f58c5..c782882c0f 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -2151,10 +2151,12 @@ Session::begin_reversible_command(const string& name) { UndoTransaction* trans = new UndoTransaction(); trans->set_name(name); + if (!_current_trans.empty()) { - _current_trans.top()->add_command(trans); + _current_trans.top()->add_command (trans); + } else { + _current_trans.push(trans); } - _current_trans.push(trans); } void diff --git a/libs/evoral/src/Note.cpp b/libs/evoral/src/Note.cpp index 51aee00958..d86f7b8948 100644 --- a/libs/evoral/src/Note.cpp +++ b/libs/evoral/src/Note.cpp @@ -39,7 +39,7 @@ Note