move a bunch of MIDI editing into EditingContext

This commit is contained in:
Paul Davis 2024-01-30 12:40:30 -07:00
parent a2f04a3104
commit bde4768734
10 changed files with 462 additions and 358 deletions

View file

@ -21,19 +21,28 @@
#include "pbd/error.h" #include "pbd/error.h"
#include "pbd/stacktrace.h" #include "pbd/stacktrace.h"
#include "ardour/legatize.h"
#include "ardour/midi_region.h"
#include "ardour/midi_source.h"
#include "ardour/rc_configuration.h" #include "ardour/rc_configuration.h"
#include "ardour/transpose.h"
#include "ardour/quantize.h" #include "ardour/quantize.h"
#include "gtkmm2ext/bindings.h" #include "gtkmm2ext/bindings.h"
#include "actions.h" #include "actions.h"
#include "ardour_ui.h"
#include "edit_note_dialog.h"
#include "editing_context.h" #include "editing_context.h"
#include "editor_drag.h" #include "editor_drag.h"
#include "keyboard.h" #include "keyboard.h"
#include "midi_region_view.h" #include "midi_region_view.h"
#include "note_base.h"
#include "quantize_dialog.h" #include "quantize_dialog.h"
#include "selection.h" #include "selection.h"
#include "selection_memento.h" #include "selection_memento.h"
#include "transform_dialog.h"
#include "transpose_dialog.h"
#include "verbose_cursor.h" #include "verbose_cursor.h"
#include "pbd/i18n.h" #include "pbd/i18n.h"
@ -48,6 +57,7 @@ using namespace Temporal;
using std::string; using std::string;
sigc::signal<void> EditingContext::DropDownKeys; sigc::signal<void> EditingContext::DropDownKeys;
Gtkmm2ext::Bindings* EditingContext::button_bindings = nullptr;
static const gchar *_grid_type_strings[] = { static const gchar *_grid_type_strings[] = {
N_("No Grid"), N_("No Grid"),
@ -104,6 +114,17 @@ EditingContext::EditingContext ()
, _visible_canvas_height (0) , _visible_canvas_height (0)
, quantize_dialog (nullptr) , quantize_dialog (nullptr)
{ {
if (!button_bindings) {
button_bindings = new Bindings ("editor-mouse");
XMLNode* node = button_settings();
if (node) {
for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
button_bindings->load_operation (**i);
}
}
}
grid_type_strings = I18N (_grid_type_strings); grid_type_strings = I18N (_grid_type_strings);
} }
@ -260,7 +281,7 @@ EditingContext::register_midi_actions (Bindings* midi_bindings)
} }
void void
EditingContext::midi_action (void (MidiRegionView::*method)()) EditingContext::midi_action (void (MidiView::*method)())
{ {
MidiRegionSelection ms = get_selection().midi_regions(); MidiRegionSelection ms = get_selection().midi_regions();
@ -1638,3 +1659,260 @@ EditingContext::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType
return ret; return ret;
} }
void
EditingContext::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
{
using namespace Menu_Helpers;
NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
if (!note) {
return;
}
/* We need to get the selection here and pass it to the operations, since
popping up the menu will cause a region leave event which clears
entered_regionview. */
MidiView& mrv = note->region_view();
const RegionSelection rs = region_selection ();
const uint32_t sel_size = mrv.selection_size ();
MenuList& items = _note_context_menu.items();
items.clear();
if (sel_size > 0) {
items.push_back (MenuElem(_("Delete"), sigc::mem_fun(mrv, &MidiView::delete_selection)));
}
items.push_back(MenuElem(_("Edit..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::edit_notes), &mrv)));
items.push_back(MenuElem(_("Transpose..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transpose_regions), rs)));
items.push_back(MenuElem(_("Legatize"), sigc::bind(sigc::mem_fun(*this, &EditingContext::legatize_regions), rs, false)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Quantize..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::quantize_regions), rs)));
items.push_back(MenuElem(_("Remove Overlap"), sigc::bind(sigc::mem_fun(*this, &EditingContext::legatize_regions), rs, true)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Transform..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transform_regions), rs)));
_note_context_menu.popup (event->button.button, event->button.time);
}
XMLNode*
EditingContext::button_settings () const
{
XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
XMLNode* node = find_named_node (*settings, X_("Buttons"));
if (!node) {
node = new XMLNode (X_("Buttons"));
}
return node;
}
std::vector<MidiView*>
EditingContext::filter_to_unique_midi_region_views (RegionSelection const & rs) const
{
typedef std::pair<std::shared_ptr<MidiSource>,timepos_t> MapEntry;
std::set<MapEntry> single_region_set;
std::vector<MidiView*> views;
/* build a list of regions that are unique with respect to their source
* and start position. Note: this is non-exhaustive... if someone has a
* non-forked copy of a MIDI region and then suitably modifies it, this
* will still put both regions into the list of things to be acted
* upon.
*
* Solution: user should not select both regions, or should fork one of them.
*/
for (auto const & rv : rs) {
MidiView* mrv = dynamic_cast<MidiView*> (rv);
if (!mrv) {
continue;
}
MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->midi_region()->start());
if (single_region_set.insert (entry).second) {
views.push_back (mrv);
}
}
return views;
}
void
EditingContext::quantize_region ()
{
if (_session) {
quantize_regions(region_selection ());
}
}
void
EditingContext::quantize_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
Quantize* quant = get_quantize_op ();
if (!quant) {
return;
}
if (!quant->empty()) {
apply_midi_note_edit_op (*quant, rs);
}
delete quant;
}
void
EditingContext::legatize_region (bool shrink_only)
{
if (_session) {
legatize_regions(region_selection (), shrink_only);
}
}
void
EditingContext::legatize_regions (const RegionSelection& rs, bool shrink_only)
{
if (rs.n_midi_regions() == 0) {
return;
}
Legatize legatize(shrink_only);
apply_midi_note_edit_op (legatize, rs);
}
void
EditingContext::transform_region ()
{
if (_session) {
transform_regions(region_selection ());
}
}
void
EditingContext::transform_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransformDialog td;
td.present();
const int r = td.run();
td.hide();
if (r == Gtk::RESPONSE_OK) {
Transform transform(td.get());
apply_midi_note_edit_op(transform, rs);
}
}
void
EditingContext::transpose_region ()
{
if (_session) {
transpose_regions(region_selection ());
}
}
void
EditingContext::transpose_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransposeDialog d;
int const r = d.run ();
if (r == RESPONSE_ACCEPT) {
Transpose transpose(d.semitones ());
apply_midi_note_edit_op (transpose, rs);
}
}
void
EditingContext::edit_notes (MidiView* mrv)
{
MidiView::Selection const & s = mrv->selection();
if (s.empty ()) {
return;
}
EditNoteDialog* d = new EditNoteDialog (mrv, s);
d->show_all ();
d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &EditingContext::note_edit_done), d));
}
void
EditingContext::note_edit_done (int r, EditNoteDialog* d)
{
d->done (r);
delete d;
}
PBD::Command*
EditingContext::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiView& mrv)
{
Evoral::Sequence<Temporal::Beats>::Notes selected;
mrv.selection_as_notelist (selected, true);
if (selected.empty()) {
return 0;
}
std::vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
v.push_back (selected);
timepos_t pos = mrv.midi_region()->source_position();
return op (mrv.midi_region()->model(), pos.beats(), v);
}
void
EditingContext::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
{
if (rs.empty()) {
return;
}
bool in_command = false;
std::vector<MidiView*> views = filter_to_unique_midi_region_views (rs);
for (auto & mv : views) {
Command* cmd = apply_midi_note_edit_op_to_region (op, *mv);
if (cmd) {
if (!in_command) {
begin_reversible_command (op.name ());
in_command = true;
}
(*cmd)();
_session->add_command (cmd);
}
}
if (in_command) {
commit_reversible_command ();
_session->set_dirty ();
}
}

View file

@ -60,7 +60,9 @@ class XMLNode;
class CursorContext; class CursorContext;
class DragManager; class DragManager;
class EditorCursor; class EditorCursor;
class EditNoteDialog;
class MidiRegionView; class MidiRegionView;
class MidiView;
class MouseCursors; class MouseCursors;
class VerboseCursor; class VerboseCursor;
class TrackViewList; class TrackViewList;
@ -308,9 +310,14 @@ public:
/* MIDI actions, proxied to selected MidiRegionView(s) */ /* MIDI actions, proxied to selected MidiRegionView(s) */
ARDOUR::Quantize* get_quantize_op (); ARDOUR::Quantize* get_quantize_op ();
virtual void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs) = 0; void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs);
void midi_action (void (MidiRegionView::*method)()); void midi_action (void (MidiView::*method)());
virtual std::vector<MidiRegionView*> filter_to_unique_midi_region_views (RegionSelection const & ms) const = 0; std::vector<MidiView*> filter_to_unique_midi_region_views (RegionSelection const & ms) const;
void quantize_region ();
void transform_region ();
void legatize_region (bool shrink_only);
void transpose_region ();
void register_midi_actions (Gtkmm2ext::Bindings*); void register_midi_actions (Gtkmm2ext::Bindings*);
@ -470,6 +477,23 @@ public:
virtual bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0; virtual bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0;
virtual bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0; virtual bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0;
void popup_note_context_menu (ArdourCanvas::Item*, GdkEvent*);
Gtk::Menu _note_context_menu;
static Gtkmm2ext::Bindings* button_bindings;
XMLNode* button_settings () const;
virtual RegionSelection region_selection() = 0;
void edit_notes (MidiView*);
void note_edit_done (int, EditNoteDialog*);
PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiView& mrv);
void quantize_regions (const RegionSelection& rs);
void legatize_regions (const RegionSelection& rs, bool shrink_only);
void transform_regions (const RegionSelection& rs);
void transpose_regions (const RegionSelection& rs);
}; };

View file

@ -320,7 +320,6 @@ Editor::Editor ()
, _err_screen_engine (0) , _err_screen_engine (0)
, cut_buffer_start (0) , cut_buffer_start (0)
, cut_buffer_length (0) , cut_buffer_length (0)
, button_bindings (0)
, last_paste_pos (timepos_t::max (Temporal::AudioTime)) /* XXX NUTEMPO how to choose time domain */ , last_paste_pos (timepos_t::max (Temporal::AudioTime)) /* XXX NUTEMPO how to choose time domain */
, paste_count (0) , paste_count (0)
, sfbrowser (0) , sfbrowser (0)
@ -794,17 +793,6 @@ Editor::Editor ()
_show_marker_lines = false; _show_marker_lines = false;
/* Button bindings */
button_bindings = new Bindings ("editor-mouse");
XMLNode* node = button_settings();
if (node) {
for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
button_bindings->load_operation (**i);
}
}
constructed = true; constructed = true;
/* grab current parameter state */ /* grab current parameter state */
@ -858,19 +846,6 @@ Editor::~Editor()
} }
} }
XMLNode*
Editor::button_settings () const
{
XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
XMLNode* node = find_named_node (*settings, X_("Buttons"));
if (!node) {
node = new XMLNode (X_("Buttons"));
}
return node;
}
bool bool
Editor::get_smart_mode () const Editor::get_smart_mode () const
{ {
@ -6268,60 +6243,6 @@ Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* ev
_control_point_context_menu.popup (event->button.button, event->button.time); _control_point_context_menu.popup (event->button.button, event->button.time);
} }
void
Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
{
using namespace Menu_Helpers;
NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
if (!note) {
return;
}
/* We need to get the selection here and pass it to the operations, since
popping up the menu will cause a region leave event which clears
entered_regionview. */
MidiView& mrv = note->region_view();
const RegionSelection rs = get_regions_from_selection_and_entered ();
const uint32_t sel_size = mrv.selection_size ();
MenuList& items = _note_context_menu.items();
items.clear();
if (sel_size > 0) {
items.push_back(MenuElem(_("Delete"),
sigc::mem_fun(mrv, &MidiView::delete_selection)));
}
items.push_back(MenuElem(_("Edit..."),
sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
items.push_back(MenuElem(_("Transpose..."),
sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
items.push_back(MenuElem(_("Legatize"),
sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Quantize..."),
sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
items.push_back(MenuElem(_("Remove Overlap"),
sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Transform..."),
sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
_note_context_menu.popup (event->button.button, event->button.time);
}
void void
Editor::zoom_vertical_modifier_released() Editor::zoom_vertical_modifier_released()
{ {
@ -6595,5 +6516,5 @@ Editor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapP
ArdourCanvas::Duple ArdourCanvas::Duple
Editor::upper_left() const Editor::upper_left() const
{ {
get_trackview_group ()->canvas_origin ().y; return get_trackview_group ()->canvas_origin ();
} }

View file

@ -533,6 +533,8 @@ protected:
void suspend_route_redisplay (); void suspend_route_redisplay ();
void resume_route_redisplay (); void resume_route_redisplay ();
RegionSelection region_selection();
private: private:
void color_handler (); void color_handler ();
@ -769,9 +771,6 @@ private:
void popup_control_point_context_menu (ArdourCanvas::Item*, GdkEvent*); void popup_control_point_context_menu (ArdourCanvas::Item*, GdkEvent*);
Gtk::Menu _control_point_context_menu; Gtk::Menu _control_point_context_menu;
void popup_note_context_menu (ArdourCanvas::Item*, GdkEvent*);
Gtk::Menu _note_context_menu;
void initial_display (); void initial_display ();
void add_stripables (ARDOUR::StripableList&); void add_stripables (ARDOUR::StripableList&);
void add_routes (ARDOUR::RouteList&); void add_routes (ARDOUR::RouteList&);
@ -1162,9 +1161,6 @@ private:
bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType);
bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType);
Gtkmm2ext::Bindings* button_bindings;
XMLNode* button_settings () const;
/* KEYMAP HANDLING */ /* KEYMAP HANDLING */
void register_actions (); void register_actions ();
@ -1245,18 +1241,9 @@ private:
void normalize_region (); void normalize_region ();
void adjust_region_gain (bool up); void adjust_region_gain (bool up);
void reset_region_gain (); void reset_region_gain ();
void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs);
void set_tempo_curve_range (double& max, double& min) const;
void quantize_region ();
void quantize_regions (const RegionSelection& rs);
void legatize_region (bool shrink_only);
void legatize_regions (const RegionSelection& rs, bool shrink_only);
void deinterlace_midi_regions (const RegionSelection& rs); void deinterlace_midi_regions (const RegionSelection& rs);
void deinterlace_selected_midi_regions (); void deinterlace_selected_midi_regions ();
void transform_region (); void set_tempo_curve_range (double& max, double& min) const;
void transform_regions (const RegionSelection& rs);
void transpose_region ();
void transpose_regions (const RegionSelection& rs);
void insert_patch_change (bool from_context); void insert_patch_change (bool from_context);
void fork_selected_regions (); void fork_selected_regions ();
void fork_regions_from_unselected (); void fork_regions_from_unselected ();
@ -1726,7 +1713,6 @@ private:
void edit_meter_marker (MeterMarker&); void edit_meter_marker (MeterMarker&);
void edit_bbt_marker (BBTMarker&); void edit_bbt_marker (BBTMarker&);
void edit_control_point (ArdourCanvas::Item*); void edit_control_point (ArdourCanvas::Item*);
void edit_notes (MidiView*);
void edit_region (RegionView*); void edit_region (RegionView*);
void edit_current_meter (); void edit_current_meter ();
@ -2139,8 +2125,6 @@ private:
void apply_filter (ARDOUR::Filter&, std::string cmd, ProgressReporter* progress = 0); void apply_filter (ARDOUR::Filter&, std::string cmd, ProgressReporter* progress = 0);
PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv);
/* plugin setup */ /* plugin setup */
int plugin_setup (std::shared_ptr<ARDOUR::Route>, std::shared_ptr<ARDOUR::PluginInsert>, ARDOUR::Route::PluginSetupOptions); int plugin_setup (std::shared_ptr<ARDOUR::Route>, std::shared_ptr<ARDOUR::PluginInsert>, ARDOUR::Route::PluginSetupOptions);
@ -2327,7 +2311,6 @@ private:
bool _show_touched_automation; bool _show_touched_automation;
int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching, bool fixed_end); int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching, bool fixed_end);
void note_edit_done (int, EditNoteDialog*);
void toggle_sound_midi_notes (); void toggle_sound_midi_notes ();
/** Flag for a bit of a hack wrt control point selection; see set_selected_control_point_from_click */ /** Flag for a bit of a hack wrt control point selection; see set_selected_control_point_from_click */
@ -2351,8 +2334,6 @@ private:
MainMenuDisabler* _main_menu_disabler; MainMenuDisabler* _main_menu_disabler;
std::vector<MidiRegionView*> filter_to_unique_midi_region_views (RegionSelection const & ms) const;
/* private helper functions to help with registering region actions */ /* private helper functions to help with registering region actions */
Glib::RefPtr<Gtk::Action> register_region_action (Glib::RefPtr<Gtk::ActionGroup> group, Editing::RegionActionTarget, char const* name, char const* label, sigc::slot<void> slot); Glib::RefPtr<Gtk::Action> register_region_action (Glib::RefPtr<Gtk::ActionGroup> group, Editing::RegionActionTarget, char const* name, char const* label, sigc::slot<void> slot);

View file

@ -2323,28 +2323,6 @@ Editor::edit_control_point (ArdourCanvas::Item* item)
} }
} }
void
Editor::edit_notes (MidiView* mrv)
{
MidiView::Selection const & s = mrv->selection();
if (s.empty ()) {
return;
}
EditNoteDialog* d = new EditNoteDialog (mrv, s);
d->show_all ();
d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
}
void
Editor::note_edit_done (int r, EditNoteDialog* d)
{
d->done (r);
delete d;
}
void void
Editor::edit_region (RegionView* rv) Editor::edit_region (RegionView* rv)
{ {

View file

@ -6130,54 +6130,6 @@ Editor::strip_region_silence ()
} }
} }
PBD::Command*
Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
{
Evoral::Sequence<Temporal::Beats>::Notes selected;
mrv.selection_as_notelist (selected, true);
if (selected.empty()) {
return 0;
}
vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
v.push_back (selected);
timepos_t pos = mrv.midi_region()->source_position();
return op (mrv.midi_region()->model(), pos.beats(), v);
}
void
Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
{
if (rs.empty()) {
return;
}
bool in_command = false;
vector<MidiRegionView*> views = filter_to_unique_midi_region_views (rs);
for (vector<MidiRegionView*>::iterator mrv = views.begin(); mrv != views.end(); ++mrv) {
Command* cmd = apply_midi_note_edit_op_to_region (op, **mrv);
if (cmd) {
if (!in_command) {
begin_reversible_command (op.name ());
in_command = true;
}
(*cmd)();
_session->add_command (cmd);
}
}
if (in_command) {
commit_reversible_command ();
_session->set_dirty ();
}
}
#include "ardour/midi_source.h" // MidiSource::name() #include "ardour/midi_source.h" // MidiSource::name()
void void
@ -6307,140 +6259,6 @@ Editor::fork_selected_regions ()
} }
} }
void
Editor::quantize_region ()
{
if (_session) {
quantize_regions(get_regions_from_selection_and_entered ());
}
}
void
Editor::quantize_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
Quantize* quant = get_quantize_op ();
if (!quant) {
return;
}
if (!quant->empty()) {
apply_midi_note_edit_op (*quant, rs);
}
delete quant;
}
void
Editor::legatize_region (bool shrink_only)
{
if (_session) {
legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
}
}
void
Editor::deinterlace_midi_regions (const RegionSelection& rs)
{
begin_reversible_command (_("de-interlace midi"));
RegionSelection rcopy = rs;
if (_session) {
for (RegionSelection::iterator i = rcopy.begin (); i != rcopy.end(); i++) {
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
if (mrv) {
XMLNode& before (mrv->region()->playlist()->get_state());
/* pass the regions to deinterlace_midi_region*/
_session->deinterlace_midi_region(mrv->midi_region());
XMLNode& after (mrv->region()->playlist()->get_state());
_session->add_command (new MementoCommand<Playlist>(*(mrv->region()->playlist()), &before, &after));
}
}
}
/* Remove the original region(s) safely, without rippling, as part of this command */
remove_regions(rs, false /*can_ripple*/, true /*as_part_of_other_command*/);
commit_reversible_command ();
}
void
Editor::deinterlace_selected_midi_regions ()
{
if (_session) {
RegionSelection rs = get_regions_from_selection_and_entered ();
deinterlace_midi_regions(rs);
}
}
void
Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
{
if (rs.n_midi_regions() == 0) {
return;
}
Legatize legatize(shrink_only);
apply_midi_note_edit_op (legatize, rs);
}
void
Editor::transform_region ()
{
if (_session) {
transform_regions(get_regions_from_selection_and_entered ());
}
}
void
Editor::transform_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransformDialog td;
td.present();
const int r = td.run();
td.hide();
if (r == Gtk::RESPONSE_OK) {
Transform transform(td.get());
apply_midi_note_edit_op(transform, rs);
}
}
void
Editor::transpose_region ()
{
if (_session) {
transpose_regions(get_regions_from_selection_and_entered ());
}
}
void
Editor::transpose_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransposeDialog d;
int const r = d.run ();
if (r == RESPONSE_ACCEPT) {
Transpose transpose(d.semitones ());
apply_midi_note_edit_op (transpose, rs);
}
}
void void
Editor::insert_patch_change (bool from_context) Editor::insert_patch_change (bool from_context)
@ -6563,6 +6381,43 @@ Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress
} }
} }
void
Editor::deinterlace_midi_regions (const RegionSelection& rs)
{
begin_reversible_command (_("de-interlace midi"));
RegionSelection rcopy = rs;
if (_session) {
for (RegionSelection::iterator i = rcopy.begin (); i != rcopy.end(); i++) {
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
if (mrv) {
XMLNode& before (mrv->region()->playlist()->get_state());
/* pass the regions to deinterlace_midi_region*/
_session->deinterlace_midi_region(mrv->midi_region());
XMLNode& after (mrv->region()->playlist()->get_state());
_session->add_command (new MementoCommand<Playlist>(*(mrv->region()->playlist()), &before, &after));
}
}
}
/* Remove the original region(s) safely, without rippling, as part of this command */
remove_regions(rs, false /*can_ripple*/, true /*as_part_of_other_command*/);
commit_reversible_command ();
}
void
Editor::deinterlace_selected_midi_regions ()
{
if (_session) {
RegionSelection rs = region_selection ();
deinterlace_midi_regions(rs);
}
}
void void
Editor::external_edit_region () Editor::external_edit_region ()
{ {
@ -9361,41 +9216,6 @@ Editor::launch_playlist_selector ()
} }
} }
vector<MidiRegionView*>
Editor::filter_to_unique_midi_region_views (RegionSelection const & ms) const
{
typedef std::pair<std::shared_ptr<MidiSource>,timepos_t> MapEntry;
std::set<MapEntry> single_region_set;
vector<MidiRegionView*> views;
/* build a list of regions that are unique with respect to their source
* and start position. Note: this is non-exhaustive... if someone has a
* non-forked copy of a MIDI region and then suitably modifies it, this
* will still put both regions into the list of things to be acted
* upon.
*
* Solution: user should not select both regions, or should fork one of them.
*/
for (MidiRegionSelection::const_iterator i = ms.begin(); i != ms.end(); ++i) {
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
if (!mrv) {
continue;
}
MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->region()->start());
if (single_region_set.insert (entry).second) {
views.push_back (mrv);
}
}
return views;
}
void void
Editor::add_region_marker () Editor::add_region_marker ()
{ {

View file

@ -2475,3 +2475,9 @@ Editor::move_selected_tracks (bool up)
ensure_time_axis_view_is_visible (*scroll_to, false); ensure_time_axis_view_is_visible (*scroll_to, false);
} }
} }
RegionSelection
Editor::region_selection()
{
return get_regions_from_selection_and_entered ();
}

View file

@ -26,9 +26,12 @@
#include "canvas/rectangle.h" #include "canvas/rectangle.h"
#include "editor_cursors.h" #include "editor_cursors.h"
#include "editor_drag.h"
#include "keyboard.h"
#include "midi_cue_background.h" #include "midi_cue_background.h"
#include "midi_cue_editor.h" #include "midi_cue_editor.h"
#include "midi_cue_view.h" #include "midi_cue_view.h"
#include "note_base.h"
#include "ui_config.h" #include "ui_config.h"
#include "verbose_cursor.h" #include "verbose_cursor.h"
@ -36,12 +39,14 @@
using namespace ARDOUR; using namespace ARDOUR;
using namespace ArdourCanvas; using namespace ArdourCanvas;
using namespace Gtkmm2ext;
using namespace Temporal; using namespace Temporal;
MidiCueEditor::MidiCueEditor() MidiCueEditor::MidiCueEditor()
: vertical_adjustment (0.0, 0.0, 10.0, 400.0) : vertical_adjustment (0.0, 0.0, 10.0, 400.0)
, horizontal_adjustment (0.0, 0.0, 1e16) , horizontal_adjustment (0.0, 0.0, 1e16)
, view (nullptr) , view (nullptr)
, mouse_mode (Editing::MouseDraw)
{ {
build_canvas (); build_canvas ();
@ -250,14 +255,51 @@ MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, std::shared_ptr
} }
bool bool
MidiCueEditor::button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) MidiCueEditor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{ {
switch (event->button.button) {
case 1:
return button_press_handler_1 (item, event, item_type);
break;
case 2:
return button_press_handler_2 (item, event, item_type);
break;
case 3:
break;
default:
return button_press_dispatch (&event->button);
break;
}
return true; return true;
} }
bool bool
MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item*, GdkEvent*, ItemType) MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{ {
NoteBase* note = nullptr;
if (mouse_mode == Editing::MouseContent) {
switch (item_type) {
case NoteItem:
/* Existing note: allow trimming/motion */
if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
if (note->big_enough_to_trim() && note->mouse_near_ends()) {
_drags->set (new NoteResizeDrag (*this, item), event, get_canvas_cursor());
} else {
_drags->set (new NoteDrag (*this, item), event);
}
}
return true;
default:
break;
}
}
return true; return true;
} }
@ -268,21 +310,39 @@ MidiCueEditor::button_press_handler_2 (ArdourCanvas::Item*, GdkEvent*, ItemType)
} }
bool bool
MidiCueEditor::button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) MidiCueEditor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{ {
if (Keyboard::is_context_menu_event (&event->button)) {
switch (item_type) {
case NoteItem:
if (internal_editing()) {
popup_note_context_menu (item, event);
}
break;
default:
break;
}
}
return true; return true;
} }
bool bool
MidiCueEditor::button_press_dispatch (GdkEventButton*) MidiCueEditor::button_press_dispatch (GdkEventButton* ev)
{ {
return true; /* this function is intended only for buttons 4 and above. */
Gtkmm2ext::MouseButton b (ev->state, ev->button);
return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
} }
bool bool
MidiCueEditor::button_release_dispatch (GdkEventButton*) MidiCueEditor::button_release_dispatch (GdkEventButton* ev)
{ {
return true; /* this function is intended only for buttons 4 and above. */
Gtkmm2ext::MouseButton b (ev->state, ev->button);
return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
} }
bool bool
@ -315,3 +375,36 @@ MidiCueEditor::key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
return true; return true;
} }
void
MidiCueEditor::set_mouse_mode (Editing::MouseMode m, bool force)
{
if (m != Editing::MouseDraw && m != Editing::MouseContent) {
return;
}
mouse_mode = m;
}
void
MidiCueEditor::step_mouse_mode (bool next)
{
}
Editing::MouseMode
MidiCueEditor::current_mouse_mode () const
{
return mouse_mode;
}
bool
MidiCueEditor::internal_editing() const
{
return true;
}
RegionSelection
MidiCueEditor::region_selection()
{
RegionSelection rs;
return rs;
}

View file

@ -69,6 +69,11 @@ class MidiCueEditor : public CueEditor
void reset_zoom (samplecnt_t); void reset_zoom (samplecnt_t);
void set_mouse_mode (Editing::MouseMode, bool force = false);
void step_mouse_mode (bool next);
Editing::MouseMode current_mouse_mode () const;
bool internal_editing() const;
protected: protected:
Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start,
Temporal::RoundMode direction, Temporal::RoundMode direction,
@ -125,6 +130,10 @@ class MidiCueEditor : public CueEditor
void build_canvas (); void build_canvas ();
void canvas_allocate (Gtk::Allocation); void canvas_allocate (Gtk::Allocation);
Editing::MouseMode mouse_mode;
RegionSelection region_selection();
}; };

View file

@ -175,10 +175,6 @@ public:
virtual void reverse_region () = 0; virtual void reverse_region () = 0;
virtual void normalize_region () = 0; virtual void normalize_region () = 0;
virtual void quantize_region () = 0;
virtual void legatize_region (bool shrink_only) = 0;
virtual void transform_region () = 0;
virtual void transpose_region () = 0;
virtual void pitch_shift_region () = 0; virtual void pitch_shift_region () = 0;
virtual void transition_to_rolling (bool fwd) = 0; virtual void transition_to_rolling (bool fwd) = 0;
@ -330,8 +326,6 @@ public:
virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0; virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0;
virtual Temporal::timecnt_t get_paste_offset (Temporal::timepos_t const & pos, unsigned paste_count, Temporal::timecnt_t const & duration) = 0; virtual Temporal::timecnt_t get_paste_offset (Temporal::timepos_t const & pos, unsigned paste_count, Temporal::timecnt_t const & duration) = 0;
virtual void edit_notes (MidiView*) = 0;
virtual void queue_visual_videotimeline_update () = 0; virtual void queue_visual_videotimeline_update () = 0;
virtual void set_close_video_sensitive (bool) = 0; virtual void set_close_video_sensitive (bool) = 0;
virtual void toggle_ruler_video (bool) = 0; virtual void toggle_ruler_video (bool) = 0;