From a9e7ce15dbd0f7e92b030f44ab0adb92649c880c Mon Sep 17 00:00:00 2001 From: chousemp3 Date: Fri, 12 Sep 2025 19:02:07 +0200 Subject: [PATCH] Add MIDI note strumming feature --- gtk2_ardour/editing_context.cc | 6 ++++ gtk2_ardour/midi_view.cc | 59 ++++++++++++++++++++++++++++++++++ gtk2_ardour/midi_view.h | 4 +++ 3 files changed, 69 insertions(+) diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index c5182fe2d4..95015838b5 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -632,6 +632,9 @@ EditingContext::register_midi_actions (Bindings* midi_bindings, std::string cons ActionManager::register_action (_midi_actions, X_("split-notes-less"), _("Split Selected Notes into less pieces"), sigc::bind (sigc::mem_fun (*this, &EditingContext::midi_action), &MidiView::split_notes_less)); ActionManager::register_action (_midi_actions, X_("join-notes"), _("Join Selected Notes"), sigc::bind (sigc::mem_fun (*this, &EditingContext::midi_action), &MidiView::join_notes)); + ActionManager::register_action (_midi_actions, X_("strum-forward"), _("Strum notes forward"), sigc::bind (sigc::mem_fun (*this, &EditingContext::midi_action), &MidiView::strum_notes_forward)); + ActionManager::register_action (_midi_actions, X_("strum-backward"), _("Strum notes backward"), sigc::bind (sigc::mem_fun (*this, &EditingContext::midi_action), &MidiView::strum_notes_backward)); + ActionManager::register_action (_midi_actions, X_("edit-channels"), _("Edit Note Channels"), sigc::bind (sigc::mem_fun (*this, &EditingContext::midi_action), &MidiView::channel_edit)); ActionManager::register_action (_midi_actions, X_("edit-velocities"), _("Edit Note Velocities"), sigc::bind (sigc::mem_fun (*this, &EditingContext::midi_action), &MidiView::velocity_edit)); @@ -1940,6 +1943,9 @@ EditingContext::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* eve items.back().set_sensitive (false); } items.push_back(MenuElem(_("Transform..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transform_regions), mvs))); + items.push_back (SeparatorElem()); + items.push_back(MenuElem(_("Strum forward"), sigc::mem_fun(mrv, &MidiView::strum_notes_forward))); + items.push_back(MenuElem(_("Strum backward"), sigc::mem_fun(mrv, &MidiView::strum_notes_backward))); _note_context_menu.popup (event->button.button, event->button.time); } diff --git a/gtk2_ardour/midi_view.cc b/gtk2_ardour/midi_view.cc index 79680298cd..a4176800bf 100644 --- a/gtk2_ardour/midi_view.cc +++ b/gtk2_ardour/midi_view.cc @@ -5367,3 +5367,62 @@ MidiView::set_visible_channel (int chn, bool clear_selection) } } +void +MidiView::strum_notes (bool forward, bool fine) +{ + if (_selection.empty()) { + return; + } + + start_note_diff_command (_("Strum")); + + Notes notes; + selection_as_notelist (notes, false); + + if (notes.size() < 2) { + abort_note_diff(); + return; + } + + Temporal::Beats total_offset; + Temporal::Beats offset; + + if (fine) { + offset = Temporal::Beats::ticks (Temporal::ticks_per_beat / 128); + } else { + offset = Temporal::Beats::ticks (Temporal::ticks_per_beat / 32); + } + + if (forward) { + for (auto const & n : notes) { + NoteBase* cne = find_canvas_note (n); + if (cne) { + change_note_time (cne, total_offset, true); + total_offset += offset; + } + } + } else { // backward + for (auto it = notes.rbegin(); it != notes.rend(); ++it) { + NoteBase* cne = find_canvas_note (*it); + if (cne) { + change_note_time (cne, total_offset, true); + total_offset += offset; + } + } + } + + apply_note_diff (); +} + +void +MidiView::strum_notes_forward () +{ + strum_notes (true, false); +} + +void +MidiView::strum_notes_backward () +{ + strum_notes (false, false); +} + diff --git a/gtk2_ardour/midi_view.h b/gtk2_ardour/midi_view.h index b3befd5da0..0b4dcd5756 100644 --- a/gtk2_ardour/midi_view.h +++ b/gtk2_ardour/midi_view.h @@ -431,7 +431,11 @@ class MidiView : public virtual sigc::trackable, public LineMerger void quantize_selected_notes (); + void strum_notes_forward (); + void strum_notes_backward (); + protected: + void strum_notes (bool forward, bool end); friend class MidiRubberbandSelectDrag; friend class MidiVerticalSelectDrag; friend class NoteDrag;