From 946fe23e6099533c60088fd8fbb78381eb3a0aef Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sat, 25 Nov 2023 15:25:29 -0700 Subject: [PATCH] further steps towards MidiRegionView outside the Editor --- gtk2_ardour/editing_context.cc | 31 +++- gtk2_ardour/editing_context.h | 5 + gtk2_ardour/editor.cc | 1 - gtk2_ardour/editor.h | 2 - gtk2_ardour/editor_canvas.cc | 2 + gtk2_ardour/editor_ops.cc | 26 --- gtk2_ardour/midi_clip_editor.cc | 1 + gtk2_ardour/midi_cue_editor.cc | 35 +++- gtk2_ardour/midi_cue_editor.h | 3 + gtk2_ardour/midi_region_view.cc | 104 ++++++----- gtk2_ardour/midi_region_view.h | 1 + gtk2_ardour/midi_streamview.cc | 232 +----------------------- gtk2_ardour/midi_streamview.h | 55 +----- gtk2_ardour/midi_view_background.cc | 272 ++++++++++++++++++++++++++++ gtk2_ardour/midi_view_background.h | 96 ++++++++++ gtk2_ardour/public_editor.h | 3 - gtk2_ardour/quantize_dialog.cc | 4 +- gtk2_ardour/quantize_dialog.h | 6 +- gtk2_ardour/trigger_page.cc | 11 +- gtk2_ardour/trigger_page.h | 2 +- gtk2_ardour/wscript | 1 + libs/ardour/ardour/midi_operator.h | 2 + 22 files changed, 530 insertions(+), 365 deletions(-) create mode 100644 gtk2_ardour/midi_view_background.cc create mode 100644 gtk2_ardour/midi_view_background.h diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index 3d0bf03c87..da0d4ed6cc 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -22,6 +22,7 @@ #include "pbd/stacktrace.h" #include "ardour/rc_configuration.h" +#include "ardour/quantize.h" #include "gtkmm2ext/bindings.h" @@ -30,6 +31,7 @@ #include "editor_drag.h" #include "keyboard.h" #include "midi_region_view.h" +#include "quantize_dialog.h" #include "selection.h" #include "selection_memento.h" #include "verbose_cursor.h" @@ -100,10 +102,9 @@ EditingContext::EditingContext () , bbt_bar_helper_on (0) , _visible_canvas_width (0) , _visible_canvas_height (0) + , quantize_dialog (nullptr) { grid_type_strings = I18N (_grid_type_strings); - - _verbose_cursor = new VerboseCursor (*this); } EditingContext::~EditingContext() @@ -1528,3 +1529,29 @@ EditingContext::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper) bbt_ruler_scale = (EditingContext::BBTRulerScale) suggested_scale; } + +Quantize* +EditingContext::get_quantize_op () +{ + if (!quantize_dialog) { + quantize_dialog = new QuantizeDialog (*this); + } + + quantize_dialog->present (); + int r = quantize_dialog->run (); + quantize_dialog->hide (); + + + if (r != Gtk::RESPONSE_OK) { + return nullptr; + } + + return new Quantize (quantize_dialog->snap_start(), + quantize_dialog->snap_end(), + quantize_dialog->start_grid_size(), + quantize_dialog->end_grid_size(), + quantize_dialog->strength(), + quantize_dialog->swing(), + quantize_dialog->threshold()); +} + diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index c4ad3f16d8..4ee757f542 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -37,6 +37,7 @@ #include "temporal/timeline.h" +#include "ardour/midi_operator.h" #include "ardour/session_handle.h" #include "ardour/types.h" @@ -278,6 +279,8 @@ public: PBD::Signal0 MouseModeChanged; /* MIDI actions, proxied to selected MidiRegionView(s) */ + ARDOUR::Quantize* get_quantize_op (); + virtual void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs) = 0; void midi_action (void (MidiRegionView::*method)()); virtual std::vector filter_to_unique_midi_region_views (RegionSelection const & ms) const = 0; @@ -415,6 +418,8 @@ public: double _visible_canvas_width; double _visible_canvas_height; ///< height of the visible area of the track canvas + + QuantizeDialog* quantize_dialog; }; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index e6f204a7f6..6834ae77a5 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -418,7 +418,6 @@ Editor::Editor () , _show_touched_automation (false) , _control_point_toggled_on_press (false) , _stepping_axis_view (0) - , quantize_dialog (0) , _main_menu_disabler (0) , domain_bounce_info (nullptr) { diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index b859c778d3..541c1c3788 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1247,7 +1247,6 @@ private: void normalize_region (); void adjust_region_gain (bool up); void reset_region_gain (); - ARDOUR::Quantize* get_quantize_op (); 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 (); @@ -2327,7 +2326,6 @@ private: void update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, std::string name); void bring_all_sources_into_session (); - QuantizeDialog* quantize_dialog; MainMenuDisabler* _main_menu_disabler; std::vector filter_to_unique_midi_region_views (RegionSelection const & ms) const; diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index 9621bd38f8..7ecb4f9e74 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -89,6 +89,8 @@ Editor::initialize_canvas () */ no_scroll_group = new ArdourCanvas::Container (_track_canvas->root()); + _verbose_cursor = new VerboseCursor (*this); + ArdourCanvas::ScrollGroup* hsg; ArdourCanvas::ScrollGroup* hg; ArdourCanvas::ScrollGroup* cg; diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 9e42481a3d..60fc21d047 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -111,7 +111,6 @@ #include "note.h" #include "paste_context.h" #include "patch_change_dialog.h" -#include "quantize_dialog.h" #include "region_gain_line.h" #include "route_time_axis.h" #include "selection.h" @@ -6243,31 +6242,6 @@ Editor::quantize_regions (const RegionSelection& rs) delete quant; } -Quantize* -Editor::get_quantize_op () -{ - if (!quantize_dialog) { - quantize_dialog = new QuantizeDialog (*this); - } - - quantize_dialog->present (); - int r = quantize_dialog->run (); - quantize_dialog->hide (); - - - if (r != Gtk::RESPONSE_OK) { - return nullptr; - } - - return new Quantize (quantize_dialog->snap_start(), - quantize_dialog->snap_end(), - quantize_dialog->start_grid_size(), - quantize_dialog->end_grid_size(), - quantize_dialog->strength(), - quantize_dialog->swing(), - quantize_dialog->threshold()); -} - void Editor::legatize_region (bool shrink_only) { diff --git a/gtk2_ardour/midi_clip_editor.cc b/gtk2_ardour/midi_clip_editor.cc index 813bfb6ce2..5e7fc7663b 100644 --- a/gtk2_ardour/midi_clip_editor.cc +++ b/gtk2_ardour/midi_clip_editor.cc @@ -75,6 +75,7 @@ void MidiClipEditorBox::set_session (Session* s) { SessionHandlePtr::set_session (s); + editor->set_session (s); } void diff --git a/gtk2_ardour/midi_cue_editor.cc b/gtk2_ardour/midi_cue_editor.cc index c735ebe26f..431f9019a2 100644 --- a/gtk2_ardour/midi_cue_editor.cc +++ b/gtk2_ardour/midi_cue_editor.cc @@ -24,6 +24,7 @@ #include "midi_cue_editor.h" #include "ui_config.h" +#include "verbose_cursor.h" using namespace ARDOUR; using namespace ArdourCanvas; @@ -34,6 +35,8 @@ MidiCueEditor::MidiCueEditor() , horizontal_adjustment (0.0, 0.0, 1e16) { build_canvas (); + + _verbose_cursor = new VerboseCursor (*this); } MidiCueEditor::~MidiCueEditor () @@ -44,10 +47,10 @@ void MidiCueEditor::build_canvas () { _canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment); - _canvas = _canvas_viewport->canvas (); - _canvas->set_background_color (UIConfiguration::instance().color ("arrange base")); - // _canvas->use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes); + _canvas = _canvas_viewport->canvas (); + _canvas->set_background_color (0xff00000a); // UIConfiguration::instance().color ("arrange base")); + dynamic_cast(_canvas)->use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes); /* scroll group for items that should not automatically scroll * (e.g verbose cursor). It shares the canvas coordinate space. @@ -139,3 +142,29 @@ MidiCueEditor::current_page_samples() const return (samplecnt_t) _visible_canvas_width* samples_per_pixel; } +void +MidiCueEditor::apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs) +{ +} + +PBD::Command* +MidiCueEditor::apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv) +{ +#if 0 + Evoral::Sequence::Notes selected; + mrv.selection_as_notelist (selected, true); + + if (selected.empty()) { + return 0; + } + + vector::Notes> v; + v.push_back (selected); + + timepos_t pos = mrv.midi_region()->source_position(); + + return op (mrv.midi_region()->model(), pos.beats(), v); +#endif + + return nullptr; +} diff --git a/gtk2_ardour/midi_cue_editor.h b/gtk2_ardour/midi_cue_editor.h index eea75501ad..d63e9a4cd5 100644 --- a/gtk2_ardour/midi_cue_editor.h +++ b/gtk2_ardour/midi_cue_editor.h @@ -54,6 +54,9 @@ class MidiCueEditor : public CueEditor int32_t get_grid_beat_divisions (Editing::GridType gt) { return 1; } int32_t get_grid_music_divisions (Editing::GridType gt, uint32_t event_state) { return 1; } + void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs); + PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv); + protected: Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, Temporal::RoundMode direction, diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 33f834de8e..4e53fd7793 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -326,6 +326,59 @@ MidiRegionView::connect_to_diskstream () gui_context()); } +std::string +MidiRegionView::get_modifier_name () const +{ + const bool opaque = _region->opaque() || trackview.layer_display () == Stacked; + + std::string mod_name; + + if (_dragging) { + mod_name = "dragging region"; + } else if (editing_context.internal_editing()) { + if (!opaque || _region->muted ()) { + mod_name = "editable region"; + } + } else { + if (!opaque || _region->muted ()) { + mod_name = "transparent region base"; + } + } + + return mod_name; +} + +GhostRegion* +MidiRegionView::add_ghost (TimeAxisView& tv) +{ + double unit_position = editing_context.time_to_pixel (_region->position ()); + MidiTimeAxisView* mtv = dynamic_cast(&tv); + MidiGhostRegion* ghost; + + if (mtv && mtv->midi_view()) { + return 0; + } else { + AutomationTimeAxisView* atv = dynamic_cast(&tv); + if (atv && atv->parameter() == Evoral::Parameter (MidiVelocityAutomation)) { + ghost = new VelocityGhostRegion (*this, tv, trackview, unit_position); + } else { + ghost = new MidiGhostRegion (*this, tv, trackview, unit_position); + } + } + + ghost->set_colors (); + ghost->set_height (); + ghost->set_duration (_region->length().samples() / samples_per_pixel); + + for (auto const & i : _events) { + ghost->add_note (i.second); + } + + ghosts.push_back (ghost); + return ghost; +} + + bool MidiRegionView::canvas_group_event(GdkEvent* ev) { @@ -1625,36 +1678,6 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) view_changed (); } -GhostRegion* -MidiRegionView::add_ghost (TimeAxisView& tv) -{ - double unit_position = editing_context.time_to_pixel (_region->position ()); - MidiTimeAxisView* mtv = dynamic_cast(&tv); - MidiGhostRegion* ghost; - - if (mtv && mtv->midi_view()) { - return 0; - } else { - AutomationTimeAxisView* atv = dynamic_cast(&tv); - if (atv && atv->parameter() == Evoral::Parameter (MidiVelocityAutomation)) { - ghost = new VelocityGhostRegion (*this, tv, trackview, unit_position); - } else { - ghost = new MidiGhostRegion (*this, tv, trackview, unit_position); - } - } - - ghost->set_colors (); - ghost->set_height (); - ghost->set_duration (_region->length().samples() / samples_per_pixel); - - for (auto const & i : _events) { - ghost->add_note (i.second); - } - - ghosts.push_back (ghost); - return ghost; -} - /** Begin tracking note state for successive calls to add_event */ @@ -3805,25 +3828,10 @@ MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, boo } } + uint32_t MidiRegionView::get_fill_color() const { - const bool opaque = _region->opaque() || trackview.layer_display () == Stacked; - - std::string mod_name; - - if (_dragging) { - mod_name = "dragging region"; - } else if (editing_context.internal_editing()) { - if (!opaque || _region->muted ()) { - mod_name = "editable region"; - } - } else { - if (!opaque || _region->muted ()) { - mod_name = "transparent region base"; - } - } - Gtkmm2ext::Color c; if (_selected) { c = UIConfiguration::instance().color ("selected region base"); @@ -3833,6 +3841,8 @@ MidiRegionView::get_fill_color() const c = fill_color; } + string mod_name = get_modifier_name(); + if (mod_name.empty ()) { return c; } else { @@ -4742,7 +4752,7 @@ MidiRegionView::quantize_selected_notes () return; } - trackview.editor().apply_midi_note_edit_op (*quant, rs); + editing_context.apply_midi_note_edit_op (*quant, rs); delete quant; } diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 16f78b1d2b..2e053e55df 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -118,6 +118,7 @@ public: inline ARDOUR::ColorMode color_mode() const { return midi_view()->color_mode(); } + std::string get_modifier_name() const; uint32_t get_fill_color() const; void color_handler (); diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 929ea1eb3c..6edc545d18 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -29,8 +29,8 @@ #include -#include "canvas/line_set.h" #include "canvas/rectangle.h" +#include "canvas/line_set.h" #include "ardour/midi_region.h" #include "ardour/midi_source.h" @@ -64,14 +64,7 @@ using namespace Editing; MidiStreamView::MidiStreamView (MidiTimeAxisView& tv) : StreamView (tv) - , note_range_adjustment(0.0f, 0.0f, 0.0f) - , _range_dirty(false) - , _range_sum_cache(-1.0) - , _lowest_note(UIConfiguration::instance().get_default_lower_midi_note()) - , _highest_note(UIConfiguration::instance().get_default_upper_midi_note()) - , _data_note_min(60) - , _data_note_max(71) - , _note_lines (0) + , MidiViewBackground (_canvas_group) , _updates_suspended (false) { /* use a dedicated group for MIDI regions (on top of the grid and lines) */ @@ -82,25 +75,16 @@ MidiStreamView::MidiStreamView (MidiTimeAxisView& tv) /* put the note lines in the timeaxisview's group, so it can be put below ghost regions from MIDI underlays */ - _note_lines = new ArdourCanvas::LineSet (_canvas_group, ArdourCanvas::LineSet::Horizontal); - _note_lines->Event.connect( sigc::bind(sigc::mem_fun(_trackview.editor(), &PublicEditor::canvas_stream_view_event), _note_lines, &_trackview)); - _note_lines->lower_to_bottom(); color_handler (); UIConfiguration::instance().ColorsChanged.connect(sigc::mem_fun(*this, &MidiStreamView::color_handler)); UIConfiguration::instance().ParameterChanged.connect(sigc::mem_fun(*this, &MidiStreamView::parameter_changed)); - - note_range_adjustment.set_page_size(_highest_note - _lowest_note); - note_range_adjustment.set_value(_lowest_note); - - note_range_adjustment.signal_value_changed().connect( - sigc::mem_fun(*this, &MidiStreamView::note_range_adjustment_changed)); } MidiStreamView::~MidiStreamView () @@ -110,11 +94,7 @@ MidiStreamView::~MidiStreamView () void MidiStreamView::parameter_changed (string const & param) { - if (param == X_("max-note-height")) { - apply_note_range (_lowest_note, _highest_note, true); - } else { - StreamView::parameter_changed (param); - } + StreamView::parameter_changed (param); } RegionView* @@ -243,21 +223,6 @@ MidiStreamView::update_contents_metrics(std::shared_ptr r) } } -bool -MidiStreamView::update_data_note_range(uint8_t min, uint8_t max) -{ - bool dirty = false; - if (min < _data_note_min) { - _data_note_min = min; - dirty = true; - } - if (max > _data_note_max) { - _data_note_max = max; - dirty = true; - } - return dirty; -} - void MidiStreamView::set_layer_display (LayerDisplay d) { @@ -314,140 +279,7 @@ MidiStreamView::redisplay_track () } void -MidiStreamView::update_contents_height () -{ - StreamView::update_contents_height(); - - _note_lines->set_extent (ArdourCanvas::COORD_MAX); - - apply_note_range (lowest_note(), highest_note(), true); -} - -void -MidiStreamView::draw_note_lines() -{ - if (!_note_lines || _updates_suspended) { - return; - } - - double y; - double prev_y = 0.; - Gtkmm2ext::Color black = UIConfiguration::instance().color_mod ("piano roll black", "piano roll black"); - Gtkmm2ext::Color white = UIConfiguration::instance().color_mod ("piano roll white", "piano roll white"); - Gtkmm2ext::Color outline = UIConfiguration::instance().color ("piano roll black outline"); - Gtkmm2ext::Color color; - - ArdourCanvas::LineSet::ResetRAII lr (*_note_lines); - - if (child_height() < 140 || note_height() < 3) { - /* track is too small for note lines, or there are too many */ - return; - } - - /* do this is order of highest ... lowest since that matches the - * coordinate system in which y=0 is at the top - */ - - for (int i = highest_note() + 1; i >= lowest_note(); --i) { - - y = floor (note_to_y (i)); - - /* add a thicker line/bar which covers the entire vertical height of this note. */ - - switch (i % 12) { - case 1: - case 3: - case 6: - case 8: - case 10: - color = black; - break; - case 4: - case 11: - /* this is the line actually corresponding to the division between B & C and E & F */ - _note_lines->add_coord (y, 1.0, outline); - /* fallthrough */ - default: - color = white; - break; - } - - double h = y - prev_y; - double middle = y + (h/2.0); - - if (!fmod (h, 2.) && !fmod (middle, 1.)) { - middle += 0.5; - } - - if (middle >= 0 && h > 1.0) { - _note_lines->add_coord (middle, h, color); - } - - prev_y = y; - } -} - -void -MidiStreamView::set_note_range(VisibleNoteRange r) -{ - if (r == FullRange) { - _lowest_note = 0; - _highest_note = 127; - } else { - _lowest_note = _data_note_min; - _highest_note = _data_note_max; - } - - apply_note_range(_lowest_note, _highest_note, true); -} - -void -MidiStreamView::apply_note_range(uint8_t lowest, uint8_t highest, bool to_region_views) -{ - _highest_note = highest; - _lowest_note = lowest; - - float uiscale = UIConfiguration::instance().get_ui_scale(); - uiscale = expf (uiscale) / expf (1.f); - - const int mnh = UIConfiguration::instance().get_max_note_height(); - int const max_note_height = std::max (mnh, mnh * uiscale); - int const range = _highest_note - _lowest_note; - - int const available_note_range = floor (child_height() / max_note_height); - int additional_notes = available_note_range - range; - - /* distribute additional notes to higher and lower ranges, clamp at 0 and 127 */ - for (int i = 0; i < additional_notes; i++){ - - if (i % 2 && _highest_note < 127){ - _highest_note++; - } - else if (i % 2) { - _lowest_note--; - } - else if (_lowest_note > 0){ - _lowest_note--; - } - else { - _highest_note++; - } - } - - note_range_adjustment.set_page_size (_highest_note - _lowest_note); - note_range_adjustment.set_value (_lowest_note); - - draw_note_lines(); - - if (to_region_views) { - apply_note_range_to_regions (); - } - - NoteRangeChanged(); /* EMIT SIGNAL*/ -} - -void -MidiStreamView::apply_note_range_to_regions () +MidiStreamView::apply_note_range_to_children () { if (!_updates_suspended) { for (list::iterator i = region_views.begin(); i != region_views.end(); ++i) { @@ -456,13 +288,6 @@ MidiStreamView::apply_note_range_to_regions () } } -void -MidiStreamView::update_note_range(uint8_t note_num) -{ - _data_note_min = min(_data_note_min, note_num); - _data_note_max = max(_data_note_max, note_num); -} - void MidiStreamView::setup_rec_box () { @@ -559,8 +384,9 @@ MidiStreamView::setup_rec_box () void MidiStreamView::color_handler () { + MidiViewBackground::color_handler (); + _region_group->set_render_with_alpha (UIConfiguration::instance().modifier ("region alpha").a()); - draw_note_lines (); if (_trackview.is_midi_track()) { canvas_rect->set_fill_color (UIConfiguration::instance().color_mod ("midi track base", "midi track base")); @@ -573,34 +399,6 @@ MidiStreamView::color_handler () } } -void -MidiStreamView::note_range_adjustment_changed() -{ - double sum = note_range_adjustment.get_value() + note_range_adjustment.get_page_size(); - int lowest = (int) floor(note_range_adjustment.get_value()); - int highest; - - if (sum == _range_sum_cache) { - //cerr << "cached" << endl; - highest = (int) floor(sum); - } else { - //cerr << "recalc" << endl; - highest = lowest + (int) floor(note_range_adjustment.get_page_size()); - _range_sum_cache = sum; - } - - if (lowest == _lowest_note && highest == _highest_note) { - return; - } - - //cerr << "note range adjustment changed: " << lowest << " " << highest << endl; - //cerr << " val=" << v_zoom_adjustment.get_value() << " page=" << v_zoom_adjustment.get_page_size() << " sum=" << v_zoom_adjustment.get_value() + v_zoom_adjustment.get_page_size() << endl; - - _lowest_note = lowest; - _highest_note = highest; - apply_note_range(lowest, highest, true); -} - void MidiStreamView::update_rec_box () { @@ -618,21 +416,6 @@ MidiStreamView::update_rec_box () mrv->extend_active_notes (); } -uint8_t -MidiStreamView::y_to_note (double y) const -{ - int const n = ((contents_height() - y) / contents_height() * (double)contents_note_range()) - + lowest_note(); - - if (n < 0) { - return 0; - } else if (n > 127) { - return 127; - } - - /* min due to rounding and/or off-by-one errors */ - return min ((uint8_t) n, highest_note()); -} /** Suspend updates to the regions' note ranges and our * note lines until resume_updates() is called. @@ -652,7 +435,7 @@ MidiStreamView::resume_updates () _updates_suspended = false; draw_note_lines (); - apply_note_range_to_regions (); + apply_note_range_to_children (); _canvas_group->redraw (); } @@ -710,3 +493,4 @@ MidiStreamView::get_regions_with_selected_data (RegionSelection& rs) } } } + diff --git a/gtk2_ardour/midi_streamview.h b/gtk2_ardour/midi_streamview.h index 6151bd06b5..fc5dec061b 100644 --- a/gtk2_ardour/midi_streamview.h +++ b/gtk2_ardour/midi_streamview.h @@ -28,6 +28,7 @@ #include "ardour/location.h" #include "enums.h" +#include "midi_view_background.h" #include "streamview.h" #include "time_axis_view_item.h" #include "route_time_axis.h" @@ -57,7 +58,7 @@ class RegionSelection; class CrossfadeView; class Selection; -class MidiStreamView : public StreamView +class MidiStreamView : public StreamView, public MidiViewBackground { public: MidiStreamView (MidiTimeAxisView&); @@ -66,50 +67,18 @@ public: void get_inverted_selectables (Selection&, std::list& results); void get_regions_with_selected_data (RegionSelection&); - enum VisibleNoteRange { - FullRange, - ContentsRange - }; - - Gtk::Adjustment note_range_adjustment; - - void set_note_range(VisibleNoteRange r); - - inline uint8_t lowest_note() const { return _lowest_note; } - inline uint8_t highest_note() const { return _highest_note; } - - void update_note_range(uint8_t note_num); - void set_layer_display (LayerDisplay); //bool can_change_layer_display() const { return false; } // revert this change for now. Although stacked view is weirdly implemented wrt the "scroomer", it is still necessary to be able to manage layered regions. void redisplay_track (); - inline double contents_height() const { + double contents_height() const { return (child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 2); } - inline double note_to_y(uint8_t note) const { - return contents_height() - (note + 1 - lowest_note()) * note_height() + 1; - } - - uint8_t y_to_note(double y) const; - - inline double note_height() const { - return contents_height() / (double)contents_note_range(); - } - - inline uint8_t contents_note_range() const { - return highest_note() - lowest_note() + 1; - } - - sigc::signal NoteRangeChanged; - RegionView* create_region_view (std::shared_ptr, bool, bool); bool paste (Temporal::timepos_t const & pos, const Selection& selection, PasteContext& ctx); - void apply_note_range(uint8_t lowest, uint8_t highest, bool to_region_views); - void suspend_updates (); void resume_updates (); @@ -120,11 +89,11 @@ public: protected: void setup_rec_box (); void update_rec_box (); + bool updates_suspended() const { return _updates_suspended; } ArdourCanvas::Container* _region_group; private: - RegionView* add_region_view_internal ( std::shared_ptr, bool wait_for_waves, @@ -133,24 +102,12 @@ private: void display_region(MidiRegionView* region_view, bool load_model); void display_track (std::shared_ptr tr); - void update_contents_height (); - - void draw_note_lines(); - bool update_data_note_range(uint8_t min, uint8_t max); - void update_contents_metrics(std::shared_ptr r); + void update_contents_metrics (std::shared_ptr r); void color_handler (); - void note_range_adjustment_changed(); - void apply_note_range_to_regions (); + void apply_note_range_to_children (); - bool _range_dirty; - double _range_sum_cache; - uint8_t _lowest_note; ///< currently visible - uint8_t _highest_note; ///< currently visible - uint8_t _data_note_min; ///< in data - uint8_t _data_note_max; ///< in data - ArdourCanvas::LineSet* _note_lines; /** true if updates to the note lines and regions are currently suspended */ bool _updates_suspended; }; diff --git a/gtk2_ardour/midi_view_background.cc b/gtk2_ardour/midi_view_background.cc new file mode 100644 index 0000000000..a1394409ba --- /dev/null +++ b/gtk2_ardour/midi_view_background.cc @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2006-2014 David Robillard + * Copyright (C) 2007 Doug McLain + * Copyright (C) 2008-2017 Paul Davis + * Copyright (C) 2009-2012 Carl Hetherington + * Copyright (C) 2014-2019 Robin Gareus + * Copyright (C) 2015 Tim Mayberry + * Copyright (C) 2016 Nick Mainsbridge + * + * 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 "canvas/line_set.h" + +#include "midi_view_background.h" +#include "ui_config.h" + +#include "pbd/i18n.h" + +using namespace std; + +MidiViewBackground::MidiViewBackground (ArdourCanvas::Item* parent) + : note_range_adjustment (0.0f, 0.0f, 0.0f) + , _range_dirty (false) + , _range_sum_cache (-1.0) + , _lowest_note (UIConfiguration::instance().get_default_lower_midi_note()) + , _highest_note (UIConfiguration::instance().get_default_upper_midi_note()) + , _data_note_min (60) + , _data_note_max (71) + , _note_lines (new ArdourCanvas::LineSet (parent, ArdourCanvas::LineSet::Horizontal)) +{ + _note_lines->lower_to_bottom(); + + color_handler (); + + UIConfiguration::instance().ColorsChanged.connect(sigc::mem_fun(*this, &MidiViewBackground::color_handler)); + UIConfiguration::instance().ParameterChanged.connect(sigc::mem_fun(*this, &MidiViewBackground::parameter_changed)); + + note_range_adjustment.set_page_size(_highest_note - _lowest_note); + note_range_adjustment.set_value(_lowest_note); + + note_range_adjustment.signal_value_changed().connect(sigc::mem_fun(*this, &MidiViewBackground::note_range_adjustment_changed)); +} + +MidiViewBackground::~MidiViewBackground() +{ +} + +void +MidiViewBackground::parameter_changed (std::string const & param) +{ + if (param == X_("max-note-height")) { + apply_note_range (_lowest_note, _highest_note, true); + } +} + +void +MidiViewBackground::color_handler () +{ + draw_note_lines (); +} + +void +MidiViewBackground::note_range_adjustment_changed() +{ + double sum = note_range_adjustment.get_value() + note_range_adjustment.get_page_size(); + int lowest = (int) floor(note_range_adjustment.get_value()); + int highest; + + if (sum == _range_sum_cache) { + //cerr << "cached" << endl; + highest = (int) floor(sum); + } else { + //cerr << "recalc" << endl; + highest = lowest + (int) floor(note_range_adjustment.get_page_size()); + _range_sum_cache = sum; + } + + if (lowest == _lowest_note && highest == _highest_note) { + return; + } + + //cerr << "note range adjustment changed: " << lowest << " " << highest << endl; + //cerr << " val=" << v_zoom_adjustment.get_value() << " page=" << v_zoom_adjustment.get_page_size() << " sum=" << v_zoom_adjustment.get_value() + v_zoom_adjustment.get_page_size() << endl; + + _lowest_note = lowest; + _highest_note = highest; + apply_note_range (lowest, highest, true); +} + +uint8_t +MidiViewBackground::y_to_note (double y) const +{ + int const n = ((contents_height() - y) / contents_height() * (double)contents_note_range()) + + lowest_note(); + + if (n < 0) { + return 0; + } else if (n > 127) { + return 127; + } + + /* min due to rounding and/or off-by-one errors */ + return min ((uint8_t) n, highest_note()); +} + + +void +MidiViewBackground::update_note_range(uint8_t note_num) +{ + _data_note_min = min(_data_note_min, note_num); + _data_note_max = max(_data_note_max, note_num); +} + +void +MidiViewBackground::update_contents_height () +{ + _note_lines->set_extent (ArdourCanvas::COORD_MAX); + apply_note_range (lowest_note(), highest_note(), true); +} + +void +MidiViewBackground::draw_note_lines() +{ + if (updates_suspended()) { + return; + } + + double y; + double prev_y = 0.; + Gtkmm2ext::Color black = UIConfiguration::instance().color_mod ("piano roll black", "piano roll black"); + Gtkmm2ext::Color white = UIConfiguration::instance().color_mod ("piano roll white", "piano roll white"); + Gtkmm2ext::Color outline = UIConfiguration::instance().color ("piano roll black outline"); + Gtkmm2ext::Color color; + + ArdourCanvas::LineSet::ResetRAII lr (*_note_lines); + + if (contents_height() < 140 || note_height() < 3) { + /* context is too small for note lines, or there are too many */ + return; + } + + /* do this is order of highest ... lowest since that matches the + * coordinate system in which y=0 is at the top + */ + + for (int i = highest_note() + 1; i >= lowest_note(); --i) { + + y = floor (note_to_y (i)); + + /* add a thicker line/bar which covers the entire vertical height of this note. */ + + switch (i % 12) { + case 1: + case 3: + case 6: + case 8: + case 10: + color = black; + break; + case 4: + case 11: + /* this is the line actually corresponding to the division between B & C and E & F */ + _note_lines->add_coord (y, 1.0, outline); + /* fallthrough */ + default: + color = white; + break; + } + + double h = y - prev_y; + double middle = y + (h/2.0); + + if (!fmod (h, 2.) && !fmod (middle, 1.)) { + middle += 0.5; + } + + if (middle >= 0 && h > 1.0) { + _note_lines->add_coord (middle, h, color); + } + + prev_y = y; + } +} + +void +MidiViewBackground::set_note_range(VisibleNoteRange r) +{ + if (r == FullRange) { + _lowest_note = 0; + _highest_note = 127; + } else { + _lowest_note = _data_note_min; + _highest_note = _data_note_max; + } + + apply_note_range(_lowest_note, _highest_note, true); +} + +void +MidiViewBackground::apply_note_range(uint8_t lowest, uint8_t highest, bool to_children) +{ + _highest_note = highest; + _lowest_note = lowest; + + float uiscale = UIConfiguration::instance().get_ui_scale(); + uiscale = expf (uiscale) / expf (1.f); + + const int mnh = UIConfiguration::instance().get_max_note_height(); + int const max_note_height = std::max (mnh, mnh * uiscale); + int const range = _highest_note - _lowest_note; + + int const available_note_range = floor (contents_height() / max_note_height); + int additional_notes = available_note_range - range; + + /* distribute additional notes to higher and lower ranges, clamp at 0 and 127 */ + for (int i = 0; i < additional_notes; i++){ + + if (i % 2 && _highest_note < 127){ + _highest_note++; + } + else if (i % 2) { + _lowest_note--; + } + else if (_lowest_note > 0){ + _lowest_note--; + } + else { + _highest_note++; + } + } + + note_range_adjustment.set_page_size (_highest_note - _lowest_note); + note_range_adjustment.set_value (_lowest_note); + + draw_note_lines(); + + if (to_children) { + apply_note_range_to_children (); + } + + NoteRangeChanged(); /* EMIT SIGNAL*/ +} + + + +bool +MidiViewBackground::update_data_note_range (uint8_t min, uint8_t max) +{ + bool dirty = false; + if (min < _data_note_min) { + _data_note_min = min; + dirty = true; + } + if (max > _data_note_max) { + _data_note_max = max; + dirty = true; + } + return dirty; +} diff --git a/gtk2_ardour/midi_view_background.h b/gtk2_ardour/midi_view_background.h new file mode 100644 index 0000000000..15314a4948 --- /dev/null +++ b/gtk2_ardour/midi_view_background.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2006-2014 David Robillard + * Copyright (C) 2007 Doug McLain + * Copyright (C) 2008-2017 Paul Davis + * Copyright (C) 2009-2012 Carl Hetherington + * Copyright (C) 2016-2017 Robin Gareus + * + * 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. + */ + +#ifndef __gtk2_ardour_midi_view_background_h__ +#define __gtk2_ardour_midi_view_background_h__ + +#include + +#include + +namespace ArdourCanvas { +class Item; +class LineSet; +} + + + +class MidiViewBackground +{ + public: + MidiViewBackground (ArdourCanvas::Item* parent); + virtual ~MidiViewBackground (); + + Gtk::Adjustment note_range_adjustment; + + enum VisibleNoteRange { + FullRange, + ContentsRange + }; + + void set_note_range (VisibleNoteRange r); + + inline uint8_t lowest_note() const { return _lowest_note; } + inline uint8_t highest_note() const { return _highest_note; } + + void update_note_range(uint8_t note_num); + + virtual double contents_height() const = 0; + + double note_to_y (uint8_t note) const { + return contents_height() - (note + 1 - lowest_note()) * note_height() + 1; + } + + uint8_t y_to_note(double y) const; + + uint8_t contents_note_range() const { + return highest_note() - lowest_note() + 1; + } + + double note_height() const { + return contents_height() / (double)contents_note_range(); + } + + sigc::signal NoteRangeChanged; + void apply_note_range (uint8_t lowest, uint8_t highest, bool to_children); + + protected: + bool _range_dirty; + double _range_sum_cache; + uint8_t _lowest_note; ///< currently visible + uint8_t _highest_note; ///< currently visible + uint8_t _data_note_min; ///< in data + uint8_t _data_note_max; ///< in data + ArdourCanvas::LineSet* _note_lines; + + void color_handler (); + void parameter_changed (std::string const &); + void note_range_adjustment_changed(); + void update_contents_height (); + void draw_note_lines(); + bool update_data_note_range(uint8_t min, uint8_t max); + virtual void apply_note_range_to_children () = 0; + virtual bool updates_suspended() const { return false; } +}; + + +#endif /* __gtk2_ardour_midi_view_background_h__ */ diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 932c01f160..08ec511073 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -460,9 +460,6 @@ public: virtual void queue_redisplay_track_views () = 0; - virtual ARDOUR::Quantize* get_quantize_op () = 0; - virtual void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs) = 0; - virtual void set_tempo_curve_range (double& max, double& min) const = 0; /// Singleton instance, set up by Editor::Editor() diff --git a/gtk2_ardour/quantize_dialog.cc b/gtk2_ardour/quantize_dialog.cc index 0b723893c0..6dba31e0d9 100644 --- a/gtk2_ardour/quantize_dialog.cc +++ b/gtk2_ardour/quantize_dialog.cc @@ -26,7 +26,7 @@ #include "pbd/convert.h" #include "quantize_dialog.h" -#include "public_editor.h" +#include "editing_context.h" #include "pbd/i18n.h" #include "pbd/integer_division.h" @@ -71,7 +71,7 @@ static const int _grid_beats[] = { std::vector QuantizeDialog::grid_strings; -QuantizeDialog::QuantizeDialog (PublicEditor& e) +QuantizeDialog::QuantizeDialog (EditingContext& e) : ArdourDialog (_("Quantize"), false, false) , editor (e) , strength_adjustment (100.0, 0.0, 100.0, 1.0, 10.0) diff --git a/gtk2_ardour/quantize_dialog.h b/gtk2_ardour/quantize_dialog.h index d012dd0085..da2bb947ba 100644 --- a/gtk2_ardour/quantize_dialog.h +++ b/gtk2_ardour/quantize_dialog.h @@ -38,12 +38,12 @@ namespace ARDOUR { class MidiModel; }; -class PublicEditor; +class EditingContext; class QuantizeDialog : public ArdourDialog { public: - QuantizeDialog (PublicEditor&); + QuantizeDialog (EditingContext&); ~QuantizeDialog (); Temporal::Beats start_grid_size() const; @@ -55,7 +55,7 @@ public: float swing () const; private: - PublicEditor& editor; + EditingContext& editor; Gtk::ComboBoxText start_grid_combo; Gtk::ComboBoxText end_grid_combo; diff --git a/gtk2_ardour/trigger_page.cc b/gtk2_ardour/trigger_page.cc index db7ba152de..3a3cd41b62 100644 --- a/gtk2_ardour/trigger_page.cc +++ b/gtk2_ardour/trigger_page.cc @@ -147,11 +147,13 @@ TriggerPage::TriggerPage () table->attach (_audio_trig_box, col, col + 1, 0, 1, Gtk::FILL, Gtk::SHRINK | Gtk::FILL); ++col; -#ifdef MIDI_PROPERTIES_BOX_IMPLEMENTED col = 2; table->attach (_midi_trig_box, col, col + 1, 0, 1, Gtk::FILL, Gtk::SHRINK); ++col; -#endif + + col = 3; + table->attach (_midi_trim_box, col, col + 1, 0, 1, Gtk::EXPAND, Gtk::SHRINK); + ++col; _parameter_box.pack_start (*table); @@ -282,6 +284,7 @@ TriggerPage::set_session (Session* s) _audio_trig_box.set_session (s); _midi_trig_box.set_session (s); + _midi_trim_box.set_session (s); update_title (); start_updating (); @@ -386,6 +389,7 @@ TriggerPage::selection_changed () _audio_trig_box.hide (); _midi_trig_box.hide (); + _midi_trim_box.hide (); _parameter_box.hide (); @@ -404,6 +408,9 @@ TriggerPage::selection_changed () } else { _midi_trig_box.set_trigger (ref); _midi_trig_box.show (); + + // _midi_trim_box.set_trigger (ref); + _midi_trim_box.show (); } } _parameter_box.show (); diff --git a/gtk2_ardour/trigger_page.h b/gtk2_ardour/trigger_page.h index 57e8bb4897..86baaa1ec6 100644 --- a/gtk2_ardour/trigger_page.h +++ b/gtk2_ardour/trigger_page.h @@ -133,10 +133,10 @@ private: #if REGION_PROPERTIES_BOX_TODO AudioRegionOperationsBox _audio_ops_box; AudioClipEditorBox _audio_trim_box; +#endif MidiRegionOperationsBox _midi_ops_box; MidiClipEditorBox _midi_trim_box; -#endif RouteProcessorSelection _selection; std::list _strips; diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index d4796a437a..1256c18de7 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -173,6 +173,7 @@ gtk2_ardour_sources = [ 'midi_time_axis.cc', 'midi_tracer.cc', 'midi_velocity_dialog.cc', + 'midi_view_background.cc', 'mini_timeline.cc', 'missing_file_dialog.cc', 'missing_filesource_dialog.cc', diff --git a/libs/ardour/ardour/midi_operator.h b/libs/ardour/ardour/midi_operator.h index 9970416d10..214db360d8 100644 --- a/libs/ardour/ardour/midi_operator.h +++ b/libs/ardour/ardour/midi_operator.h @@ -26,6 +26,8 @@ #include "temporal/beats.h" #include "evoral/Sequence.h" +#include "ardour/libardour_visibility.h" + namespace PBD { class Command; }