X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=8533cefdc07cbcdb24a34310672b79448703bbc9;hb=5fef65538040fbac1b9edd1847a269aa925a49c9;hp=0d17bdbf6a33b071d12140c89cb2c12e3a7033f7;hpb=8c9749e42faf7808034ed8b7afce4a2fe6dc6f33;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 0d17bdbf6a..8533cefdc0 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -27,31 +27,35 @@ #include +#include "midi++/midnam_patch.h" + #include "pbd/memento_command.h" #include "pbd/stateful_diff_command.h" #include "ardour/midi_model.h" -#include "ardour/midi_patch_manager.h" #include "ardour/midi_region.h" #include "ardour/midi_source.h" #include "ardour/midi_track.h" +#include "ardour/operations.h" #include "ardour/session.h" #include "evoral/Parameter.hpp" -#include "evoral/MIDIParameters.hpp" #include "evoral/MIDIEvent.hpp" #include "evoral/Control.hpp" #include "evoral/midi_util.h" #include "canvas/debug.h" +#include "canvas/text.h" #include "automation_region_view.h" #include "automation_time_axis.h" +#include "control_point.h" #include "debug.h" #include "editor.h" #include "editor_drag.h" #include "ghostregion.h" #include "gui_thread.h" +#include "item_counts.h" #include "keyboard.h" #include "midi_channel_dialog.h" #include "midi_cut_buffer.h" @@ -63,12 +67,12 @@ #include "midi_velocity_dialog.h" #include "mouse_cursors.h" #include "note_player.h" +#include "paste_context.h" #include "public_editor.h" #include "route_time_axis.h" #include "rgb_macros.h" #include "selection.h" #include "streamview.h" -#include "utils.h" #include "patch_change_dialog.h" #include "verbose_cursor.h" #include "ardour_ui.h" @@ -82,19 +86,25 @@ using namespace ARDOUR; using namespace PBD; using namespace Editing; +using namespace std; using Gtkmm2ext::Keyboard; PBD::Signal1 MidiRegionView::SelectionCleared; #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1) -MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, - boost::shared_ptr r, double spu, Gdk::Color const & basic_color) +MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, + RouteTimeAxisView& tv, + boost::shared_ptr r, + double spu, + uint32_t basic_color) : RegionView (parent, tv, r, spu, basic_color) , _current_range_min(0) , _current_range_max(0) + , _region_relative_time_converter(r->session().tempo_map(), r->position()) + , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start()) , _active_notes(0) - , _note_group (new ArdourCanvas::Group (group)) + , _note_group (new ArdourCanvas::Container (group)) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -110,8 +120,11 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , _no_sound_notes (false) , _last_event_x (0) , _last_event_y (0) + , _grabbed_keyboard (false) + , _entered (false) , pre_enter_cursor (0) , pre_press_cursor (0) + , pre_note_enter_cursor (0) , _note_player (0) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -124,14 +137,20 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); } -MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, - boost::shared_ptr r, double spu, Gdk::Color& basic_color, - TimeAxisViewItem::Visibility visibility) - : RegionView (parent, tv, r, spu, basic_color, false, visibility) +MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, + RouteTimeAxisView& tv, + boost::shared_ptr r, + double spu, + uint32_t basic_color, + bool recording, + TimeAxisViewItem::Visibility visibility) + : RegionView (parent, tv, r, spu, basic_color, recording, visibility) , _current_range_min(0) , _current_range_max(0) + , _region_relative_time_converter(r->session().tempo_map(), r->position()) + , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start()) , _active_notes(0) - , _note_group (new ArdourCanvas::Group (parent)) + , _note_group (new ArdourCanvas::Container (parent)) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -147,8 +166,11 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , _no_sound_notes (false) , _last_event_x (0) , _last_event_y (0) + , _grabbed_keyboard (false) + , _entered (false) , pre_enter_cursor (0) , pre_press_cursor (0) + , pre_note_enter_cursor (0) , _note_player (0) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -176,8 +198,10 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , RegionView (other) , _current_range_min(0) , _current_range_max(0) + , _region_relative_time_converter(other.region_relative_time_converter()) + , _source_relative_time_converter(other.source_relative_time_converter()) , _active_notes(0) - , _note_group (new ArdourCanvas::Group (get_canvas_group())) + , _note_group (new ArdourCanvas::Container (get_canvas_group())) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -193,25 +217,24 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , _no_sound_notes (false) , _last_event_x (0) , _last_event_y (0) + , _grabbed_keyboard (false) + , _entered (false) , pre_enter_cursor (0) , pre_press_cursor (0) + , pre_note_enter_cursor (0) , _note_player (0) { - Gdk::Color c; - int r,g,b,a; - - UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a); - c.set_rgb_p (r/255.0, g/255.0, b/255.0); - - init (c, false); + init (false); } MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr region) : RegionView (other, boost::shared_ptr (region)) , _current_range_min(0) , _current_range_max(0) + , _region_relative_time_converter(other.region_relative_time_converter()) + , _source_relative_time_converter(other.source_relative_time_converter()) , _active_notes(0) - , _note_group (new ArdourCanvas::Group (get_canvas_group())) + , _note_group (new ArdourCanvas::Container (get_canvas_group())) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -227,21 +250,18 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptrmidi_source(0)->load_model(); + Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex()); + midi_region()->midi_source(0)->load_model(lm); } _model = midi_region()->midi_source(0)->model(); _enable_display = false; + _fill_color_name = "midi frame base"; - RegionView::init (basic_color, false); - - compute_colors (basic_color); + RegionView::init (false); set_height (trackview.current_height()); @@ -322,41 +342,37 @@ MidiRegionView::connect_to_diskstream () bool MidiRegionView::canvas_group_event(GdkEvent* ev) { + if (in_destructor || _recregion) { + return false; + } + + if (!trackview.editor().internal_editing()) { + // not in internal edit mode, so just act like a normal region + return RegionView::canvas_group_event (ev); + } + bool r; switch (ev->type) { case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: _last_event_x = ev->crossing.x; _last_event_y = ev->crossing.y; - break; - case GDK_MOTION_NOTIFY: - _last_event_x = ev->motion.x; - _last_event_y = ev->motion.y; - break; - default: - break; - } - - if (ev->type == GDK_2BUTTON_PRESS) { - // cannot use double-click to exit internal mode if single-click is being used - MouseMode m = trackview.editor().current_mouse_mode(); - - if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) { - return trackview.editor().toggle_internal_editing_from_double_click (ev); - } - } + enter_notify(&ev->crossing); + // set entered_regionview (among other things) + return RegionView::canvas_group_event (ev); - if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) || - (trackview.editor().current_mouse_mode() == MouseTimeFX) || - (trackview.editor().current_mouse_mode() == MouseZoom)) { - // handle non-internal-edit/non-draw modes elsewhere + case GDK_LEAVE_NOTIFY: + _last_event_x = ev->crossing.x; + _last_event_y = ev->crossing.y; + leave_notify(&ev->crossing); + // reset entered_regionview (among other things) return RegionView::canvas_group_event (ev); - } - switch (ev->type) { case GDK_SCROLL: - return scroll (&ev->scroll); + if (scroll (&ev->scroll)) { + return true; + } + break; case GDK_KEY_PRESS: return key_press (&ev->key); @@ -373,20 +389,16 @@ MidiRegionView::canvas_group_event(GdkEvent* ev) _note_player = 0; return r; - case GDK_ENTER_NOTIFY: - return enter_notify (&ev->crossing); - - case GDK_LEAVE_NOTIFY: - return leave_notify (&ev->crossing); - case GDK_MOTION_NOTIFY: + _last_event_x = ev->motion.x; + _last_event_y = ev->motion.y; return motion (&ev->motion); default: break; } - return trackview.editor().canvas_region_view_event (ev, group, this); + return RegionView::canvas_group_event (ev); } bool @@ -396,24 +408,9 @@ MidiRegionView::enter_notify (GdkEventCrossing* ev) _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context () ); - if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) { - create_ghost_note (ev->x, ev->y); - } - - if (!trackview.editor().internal_editing()) { - Keyboard::magic_widget_drop_focus(); - } else { - Keyboard::magic_widget_grab_focus(); - group->grab_focus(); - } - - // if current operation is non-operational in a midi region, change the cursor to so indicate - if (trackview.editor().current_mouse_mode() == MouseGain) { - Editor* editor = dynamic_cast (&trackview.editor()); - pre_enter_cursor = editor->get_canvas_cursor(); - editor->set_canvas_cursor(editor->cursors()->timebar); - } + enter_internal(); + _entered = true; return false; } @@ -422,32 +419,48 @@ MidiRegionView::leave_notify (GdkEventCrossing*) { _mouse_mode_connection.disconnect (); - trackview.editor().verbose_cursor()->hide (); - remove_ghost_note (); - - if (pre_enter_cursor) { - Editor* editor = dynamic_cast (&trackview.editor()); - editor->set_canvas_cursor(pre_enter_cursor); - } + leave_internal(); + _entered = false; return false; } void MidiRegionView::mouse_mode_changed () { - if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) { - create_ghost_note (_last_event_x, _last_event_y); + if (trackview.editor().internal_editing()) { + // Switched in to internal editing mode while entered + enter_internal(); } else { - remove_ghost_note (); - trackview.editor().verbose_cursor()->hide (); + // Switched out of internal editing mode while entered + leave_internal(); } +} - if (!trackview.editor().internal_editing()) { - Keyboard::magic_widget_drop_focus(); - } else { +void +MidiRegionView::enter_internal() +{ + if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) { + // Show ghost note under pencil + create_ghost_note(_last_event_x, _last_event_y); + } + + if (!_selection.empty()) { + // Grab keyboard for moving selected notes with arrow keys Keyboard::magic_widget_grab_focus(); - group->grab_focus(); + _grabbed_keyboard = true; + } +} + +void +MidiRegionView::leave_internal() +{ + trackview.editor().verbose_cursor()->hide (); + remove_ghost_note (); + + if (_grabbed_keyboard) { + Keyboard::magic_widget_drop_focus(); + _grabbed_keyboard = false; } } @@ -461,10 +474,10 @@ MidiRegionView::button_press (GdkEventButton* ev) Editor* editor = dynamic_cast (&trackview.editor()); MouseMode m = editor->current_mouse_mode(); - if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { + if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { pre_press_cursor = editor->get_canvas_cursor (); editor->set_canvas_cursor (editor->cursors()->midi_pencil); - } + } if (_mouse_state != SelectTouchDragging) { @@ -510,7 +523,7 @@ MidiRegionView::button_release (GdkEventButton* ev) clear_selection (); break; - case MouseObject: + case MouseContent: case MouseTimeFX: { clear_selection(); @@ -523,17 +536,12 @@ MidiRegionView::button_release (GdkEventButton* ev) event_y = ev->y; group->canvas_to_item (event_x, event_y); - bool success; - Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x)); - - if (!success) { - beats = 1; - } + Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x)); /* Shorten the length by 1 tick so that we can add a new note at the next grid snap without it overlapping this one. */ - beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat; + beats -= Evoral::MusicalTime::tick(); create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); } @@ -542,17 +550,12 @@ MidiRegionView::button_release (GdkEventButton* ev) } case MouseDraw: { - bool success; - Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x)); - - if (!success) { - beats = 1; - } + Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x)); /* Shorten the length by 1 tick so that we can add a new note at the next grid snap without it overlapping this one. */ - beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat; + beats -= Evoral::MusicalTime::tick(); create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); @@ -585,18 +588,18 @@ MidiRegionView::motion (GdkEventMotion* ev) { PublicEditor& editor = trackview.editor (); - if (!_ghost_note && editor.current_mouse_mode() == MouseObject && + if (!_ghost_note && editor.current_mouse_mode() == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) && _mouse_state != AddDragging) { create_ghost_note (ev->x, ev->y); - } else if (_ghost_note && editor.current_mouse_mode() == MouseObject && + } else if (_ghost_note && editor.current_mouse_mode() == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { update_ghost_note (ev->x, ev->y); - } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) { + } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) { remove_ghost_note (); editor.verbose_cursor()->hide (); @@ -619,15 +622,17 @@ MidiRegionView::motion (GdkEventMotion* ev) MouseMode m = editor.current_mouse_mode(); - if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) { + if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) { editor.drags()->set (new NoteCreateDrag (dynamic_cast (&editor), group, this), (GdkEvent *) ev); _mouse_state = AddDragging; remove_ghost_note (); editor.verbose_cursor()->hide (); return true; - } else if (m == MouseObject) { + } else if (m == MouseContent) { editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); - clear_selection (); + if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + clear_selection (); + } _mouse_state = SelectRectDragging; return true; } else if (m == MouseRange) { @@ -716,7 +721,7 @@ MidiRegionView::key_press (GdkEventKey* ev) bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier); bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); - change_note_lengths (fine, shorter, 0.0, start, end); + change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end); return true; @@ -777,14 +782,16 @@ MidiRegionView::key_press (GdkEventKey* ev) } return true; - } else if (ev->keyval == GDK_Left && unmodified) { + } else if (ev->keyval == GDK_Left) { - nudge_notes (false); + bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); + nudge_notes (false, fine); return true; - } else if (ev->keyval == GDK_Right && unmodified) { + } else if (ev->keyval == GDK_Right) { - nudge_notes (true); + bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); + nudge_notes (true, fine); return true; } else if (ev->keyval == GDK_c && unmodified) { @@ -897,7 +904,7 @@ MidiRegionView::show_list_editor () * \param snap_t true to snap t to the grid, otherwise false. */ void -MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t) +MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t) { if (length < 2 * DBL_EPSILON) { return; @@ -1345,11 +1352,17 @@ MidiRegionView::region_resized (const PropertyChange& what_changed) RegionView::region_resized(what_changed); if (what_changed.contains (ARDOUR::Properties::position)) { + _region_relative_time_converter.set_origin_b(_region->position()); set_duration(_region->length(), 0); if (_enable_display) { redisplay_model(); } } + + if (what_changed.contains (ARDOUR::Properties::start) || + what_changed.contains (ARDOUR::Properties::position)) { + _source_relative_time_converter.set_origin_b (_region->position() - _region->start()); + } } void @@ -1427,8 +1440,8 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) if (Note* cnote = dynamic_cast(event)) { - const double y0 = midi_stream_view()->note_to_y(note->note()); - const double y1 = y0 + floor(midi_stream_view()->note_height()); + const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note())); + const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.); cnote->set_y0 (y0); cnote->set_y1 (y1); @@ -1503,7 +1516,7 @@ MidiRegionView::end_write() /** Resolve an active MIDI note (while recording). */ void -MidiRegionView::resolve_note(uint8_t note, double end_time) +MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time) { if (midi_view()->note_mode() != Sustained) { return; @@ -1536,7 +1549,8 @@ MidiRegionView::extend_active_notes() for (unsigned i=0; i < 128; ++i) { if (_active_notes[i]) { - _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length())); + _active_notes[i]->set_x1( + trackview.editor().sample_to_pixel(_region->position() + _region->length())); } } } @@ -1626,7 +1640,7 @@ MidiRegionView::update_note (Note* ev, bool update_ghost_regions) { boost::shared_ptr note = ev->note(); const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time())); - const double y0 = midi_stream_view()->note_to_y(note->note()); + const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note())); ev->set_x0 (x); ev->set_y0 (y0); @@ -1640,9 +1654,9 @@ MidiRegionView::update_note (Note* ev, bool update_ghost_regions) ev->set_x1 (trackview.editor().sample_to_pixel (_region->length())); } - ev->set_y1 (y0 + floor(midi_stream_view()->note_height())); + ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1)); - if (note->length() == 0) { + if (!note->length()) { if (_active_notes && note->note() < 128) { // If this note is already active there's a stuck note, // finish the old note rectangle @@ -1681,8 +1695,8 @@ MidiRegionView::update_hit (Hit* ev) const framepos_t note_start_frames = source_beats_to_region_frames(note->time()); const double x = trackview.editor().sample_to_pixel(note_start_frames); - const double diamond_size = midi_stream_view()->note_height(); - const double y = midi_stream_view()->note_to_y(note->note()) + (diamond_size/2.0); + const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.); + const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5; ev->set_position (ArdourCanvas::Duple (x, y)); ev->set_height (diamond_size); @@ -1717,7 +1731,7 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) } else if (midi_view()->note_mode() == Percussive) { - const double diamond_size = midi_stream_view()->note_height() / 2.0; + const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.); Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); @@ -1838,13 +1852,13 @@ MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p) /// Return true iff @p pc applies to the given time on the given channel. static bool -patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel) +patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel) { return pc->time() <= time && pc->channel() == channel; } void -MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const +MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const { // The earliest event not before time MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time); @@ -1857,15 +1871,11 @@ MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::Patc } if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) { - key.bank_number = (*i)->bank(); - key.program_number = (*i)->program (); + key.set_bank((*i)->bank()); + key.set_program((*i)->program ()); } else { - key.bank_number = key.program_number = 0; - } - - if (!key.is_sane()) { - error << string_compose(_("insane MIDI patch key %1:%2"), - key.bank_number, key.program_number) << endmsg; + key.set_bank(0); + key.set_program(0); } } @@ -1874,11 +1884,11 @@ MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPri { MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change")); - if (pc.patch()->program() != new_patch.program_number) { - c->change_program (pc.patch (), new_patch.program_number); + if (pc.patch()->program() != new_patch.program()) { + c->change_program (pc.patch (), new_patch.program()); } - int const new_bank = new_patch.bank_number; + int const new_bank = new_patch.bank(); if (pc.patch()->bank() != new_bank) { c->change_bank (pc.patch (), new_bank); } @@ -1964,47 +1974,15 @@ MidiRegionView::delete_patch_change (PatchChange* pc) } void -MidiRegionView::previous_patch (PatchChange& patch) -{ - if (patch.patch()->program() < 127) { - MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); - key.program_number++; - change_patch_change (patch, key); - } -} - -void -MidiRegionView::next_patch (PatchChange& patch) -{ - if (patch.patch()->program() > 0) { - MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); - key.program_number--; - change_patch_change (patch, key); - } -} - -void -MidiRegionView::next_bank (PatchChange& patch) -{ - if (patch.patch()->program() < 127) { - MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); - if (key.bank_number > 0) { - key.bank_number--; - change_patch_change (patch, key); - } - } -} - -void -MidiRegionView::previous_bank (PatchChange& patch) +MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta) { - if (patch.patch()->program() > 0) { - MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); - if (key.bank_number < 127) { - key.bank_number++; - change_patch_change (patch, key); - } + MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch()); + if (bank) { + key.set_bank(key.bank() + delta); + } else { + key.set_program(key.program() + delta); } + change_patch_change(patch, key); } void @@ -2065,6 +2043,12 @@ MidiRegionView::clear_selection_except (NoteBase* ev, bool signal) } } + if (!ev && _entered) { + // Clearing selection entirely, ungrab keyboard + Keyboard::magic_widget_drop_focus(); + _grabbed_keyboard = false; + } + /* this does not change the status of this regionview w.r.t the editor selection. */ @@ -2077,6 +2061,8 @@ MidiRegionView::clear_selection_except (NoteBase* ev, bool signal) void MidiRegionView::unique_select(NoteBase* ev) { + const bool selection_was_empty = _selection.empty(); + clear_selection_except (ev); /* don't bother with checking to see if we should remove this @@ -2087,6 +2073,11 @@ MidiRegionView::unique_select(NoteBase* ev) if (!ev->selected()) { add_to_selection (ev); + if (selection_was_empty && _entered) { + // Grab keyboard for moving notes with arrow keys + Keyboard::magic_widget_grab_focus(); + _grabbed_keyboard = true; + } } } @@ -2242,7 +2233,7 @@ MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend) /* find end of latest note selected, select all between that and the start of "ev" */ Evoral::MusicalTime earliest = Evoral::MaxMusicalTime; - Evoral::MusicalTime latest = 0; + Evoral::MusicalTime latest = Evoral::MusicalTime(); for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { if ((*i)->note()->end_time() > latest) { @@ -2281,8 +2272,18 @@ MidiRegionView::note_deselected(NoteBase* ev) } void -MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend) +MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend) { + PublicEditor& editor = trackview.editor(); + + // Convert to local coordinates + const framepos_t p = _region->position(); + const double y = midi_view()->y_position(); + const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p)); + const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p)); + const double y0 = max(0.0, gy0 - y); + const double y1 = max(0.0, gy1 - y); + // TODO: Make this faster by storing the last updated selection rect, and only // adjusting things that are in the area that appears/disappeared. // We probably need a tree to be able to find events in O(log(n)) time. @@ -2298,6 +2299,24 @@ MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1 remove_from_selection (*i); } } + + typedef RouteTimeAxisView::AutomationTracks ATracks; + typedef std::list Selectables; + + /* Add control points to selection. */ + const ATracks& atracks = midi_view()->automation_tracks(); + Selectables selectables; + editor.get_selection().clear_points(); + for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) { + a->second->get_selectables(start, end, gy0, gy1, selectables); + for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) { + ControlPoint* cp = dynamic_cast(*s); + if (cp) { + editor.get_selection().add(cp); + } + } + a->second->set_selected_points(editor.get_selection().points); + } } void @@ -2330,6 +2349,11 @@ MidiRegionView::remove_from_selection (NoteBase* ev) if (i != _selection.end()) { _selection.erase (i); + if (_selection.empty() && _grabbed_keyboard) { + // Ungrab keyboard + Keyboard::magic_widget_drop_focus(); + _grabbed_keyboard = false; + } } ev->set_selected (false); @@ -2344,18 +2368,19 @@ MidiRegionView::remove_from_selection (NoteBase* ev) void MidiRegionView::add_to_selection (NoteBase* ev) { - bool add_mrv_selection = false; - - if (_selection.empty()) { - add_mrv_selection = true; - } + const bool selection_was_empty = _selection.empty(); if (_selection.insert (ev).second) { ev->set_selected (true); start_playing_midi_note ((ev)->note()); + if (selection_was_empty && _entered) { + // Grab keyboard for moving notes with arrow keys + Keyboard::magic_widget_grab_focus(); + _grabbed_keyboard = true; + } } - if (add_mrv_selection) { + if (selection_was_empty) { PublicEditor& editor (trackview.editor()); editor.get_selection().add (this); } @@ -2375,7 +2400,7 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) } for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) { + if ((*i)->note()->time() == earliest) { to_play.push_back ((*i)->note()); } (*i)->move_event(dx, dy); @@ -2502,7 +2527,7 @@ MidiRegionView::get_end_position_pixels() } framepos_t -MidiRegionView::source_beats_to_absolute_frames(double beats) const +MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const { /* the time converter will return the frame corresponding to `beats' relative to the start of the source. The start of the source @@ -2512,7 +2537,7 @@ MidiRegionView::source_beats_to_absolute_frames(double beats) const return source_start + _source_relative_time_converter.to (beats); } -double +Evoral::MusicalTime MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const { /* the `frames' argument needs to be converted into a frame count @@ -2524,12 +2549,12 @@ MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const } framepos_t -MidiRegionView::region_beats_to_region_frames(double beats) const +MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const { return _region_relative_time_converter.to(beats); } -double +Evoral::MusicalTime MidiRegionView::region_frames_to_region_beats(framepos_t frames) const { return _region_relative_time_converter.from(frames); @@ -2554,7 +2579,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/) // calculate the colors: get the color settings uint32_t fill_color = UINT_RGBA_CHANGE_A( - ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), + ARDOUR_UI::config()->color ("midi note selected"), 128); // make the resize preview notes more transparent and bright @@ -2567,7 +2592,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/) 0.85)); resize_rect->set_outline_color (NoteBase::calculate_outline ( - ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected())); + ARDOUR_UI::config()->color ("midi note selected"))); resize_data->resize_rect = resize_rect; _resize_data.push_back(resize_data); @@ -2608,6 +2633,15 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_ } } + if (current_x < 0) { + // This works even with snapping because RegionView::snap_frame_to_frame() + // snaps forward if the snapped sample is before the beginning of the region + current_x = 0; + } + if (current_x > trackview.editor().sample_to_pixel(_region->length())) { + current_x = trackview.editor().sample_to_pixel(_region->length()); + } + if (at_front) { resize_rect->set_x0 (snap_to_pixel(current_x)); resize_rect->set_x1 (canvas_note->x1()); @@ -2617,30 +2651,23 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_ } if (!cursor_set) { - double beats; - - beats = snap_pixel_to_sample (current_x); - beats = region_frames_to_region_beats (beats); - - double len; + const double snapped_x = snap_pixel_to_sample (current_x); + Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x); + Evoral::MusicalTime len = Evoral::MusicalTime(); if (at_front) { if (beats < canvas_note->note()->end_time()) { len = canvas_note->note()->time() - beats; len += canvas_note->note()->length(); - } else { - len = 0; } } else { if (beats >= canvas_note->note()->time()) { len = beats - canvas_note->note()->time(); - } else { - len = 0; } } char buf[16]; - snprintf (buf, sizeof (buf), "%.3g beats", len); + snprintf (buf, sizeof (buf), "%.3g beats", len.to_double()); show_verbose_cursor (buf, 0, 0); cursor_set = true; @@ -2682,28 +2709,34 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ } } + if (current_x < 0) { + current_x = 0; + } + if (current_x > trackview.editor().sample_to_pixel(_region->length())) { + current_x = trackview.editor().sample_to_pixel(_region->length()); + } + /* Convert that to a frame within the source */ current_x = snap_pixel_to_sample (current_x) + _region->start (); /* and then to beats */ - current_x = region_frames_to_region_beats (current_x); + const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x); - if (at_front && current_x < canvas_note->note()->end_time()) { - note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x); + if (at_front && x_beats < canvas_note->note()->end_time()) { + note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats); - double len = canvas_note->note()->time() - current_x; + Evoral::MusicalTime len = canvas_note->note()->time() - x_beats; len += canvas_note->note()->length(); - if (len > 0) { - /* XXX convert to beats */ + if (!!len) { note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len); } } if (!at_front) { - double len = current_x - canvas_note->note()->time(); + const Evoral::MusicalTime len = x_beats - canvas_note->note()->time(); - if (len > 0) { + if (!!len) { /* XXX convert to beats */ note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len); } @@ -2765,8 +2798,8 @@ MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evo { bool change_start = false; bool change_length = false; - Evoral::MusicalTime new_start = 0; - Evoral::MusicalTime new_length = 0; + Evoral::MusicalTime new_start; + Evoral::MusicalTime new_length; /* NOTE: the semantics of the two delta arguments are slightly subtle: @@ -2777,11 +2810,11 @@ MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evo if negative - move the end of the note earlier in time (shortening it) */ - if (front_delta) { + if (!!front_delta) { if (front_delta < 0) { if (event->note()->time() < -front_delta) { - new_start = 0; + new_start = Evoral::MusicalTime(); } else { new_start = event->note()->time() + front_delta; // moves earlier } @@ -2809,7 +2842,7 @@ MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evo } - if (end_delta) { + if (!!end_delta) { bool can_change = true; if (end_delta < 0) { if (event->note()->length() < -end_delta) { @@ -2862,7 +2895,7 @@ MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bo if (relative) { if (delta < 0.0) { if (event->note()->time() < -delta) { - new_time = 0; + new_time = Evoral::MusicalTime(); } else { new_time = event->note()->time() + delta; } @@ -2991,18 +3024,12 @@ MidiRegionView::transpose (bool up, bool fine, bool allow_smush) void MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end) { - if (delta == 0.0) { + if (!delta) { if (fine) { - delta = 1.0/128.0; + delta = Evoral::MusicalTime(1.0/128.0); } else { /* grab the current grid distance */ - bool success; - delta = trackview.editor().get_grid_type_as_beats (success, _region->position()); - if (!success) { - /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */ - error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg; - return; - } + delta = get_grid_beats(_region->position()); } } @@ -3018,7 +3045,9 @@ MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTim /* note the negation of the delta for start */ - trim_note (*i, (start ? -delta : 0), (end ? delta : 0)); + trim_note (*i, + (start ? -delta : Evoral::MusicalTime()), + (end ? delta : Evoral::MusicalTime())); i = next; } @@ -3027,7 +3056,7 @@ MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTim } void -MidiRegionView::nudge_notes (bool forward) +MidiRegionView::nudge_notes (bool forward, bool fine) { if (_selection.empty()) { return; @@ -3038,15 +3067,21 @@ MidiRegionView::nudge_notes (bool forward) into a vector and sort before using the first one. */ - framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time()); - framepos_t unused; - framecnt_t distance; + const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time()); + Evoral::MusicalTime delta; - if (trackview.editor().snap_mode() == Editing::SnapOff) { + if (!fine) { + + /* non-fine, move by 1 bar regardless of snap */ + delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar()); + + } else if (trackview.editor().snap_mode() == Editing::SnapOff) { /* grid is off - use nudge distance */ - distance = trackview.editor().get_nudge_distance (ref_point, unused); + framepos_t unused; + const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused); + delta = region_frames_to_region_beats (fabs ((double)distance)); } else { @@ -3065,16 +3100,15 @@ MidiRegionView::nudge_notes (bool forward) next_pos -= 1; } - trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false); - distance = ref_point - next_pos; + trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false); + const framecnt_t distance = ref_point - next_pos; + delta = region_frames_to_region_beats (fabs ((double)distance)); } - if (distance == 0) { + if (!delta) { return; } - Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance)); - if (!forward) { delta = -delta; } @@ -3108,7 +3142,7 @@ MidiRegionView::note_entered(NoteBase* ev) { Editor* editor = dynamic_cast(&trackview.editor()); - pre_enter_cursor = editor->get_canvas_cursor (); + pre_note_enter_cursor = editor->get_canvas_cursor (); if (_mouse_state == SelectTouchDragging) { note_selected (ev, true); @@ -3128,9 +3162,9 @@ MidiRegionView::note_left (NoteBase*) editor->verbose_cursor()->hide (); - if (pre_enter_cursor) { - editor->set_canvas_cursor (pre_enter_cursor); - pre_enter_cursor = 0; + if (pre_note_enter_cursor) { + editor->set_canvas_cursor (pre_note_enter_cursor); + pre_note_enter_cursor = 0; } } @@ -3180,15 +3214,15 @@ MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, boo { Editor* editor = dynamic_cast(&trackview.editor()); Editing::MouseMode mm = editor->current_mouse_mode(); - bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw); + bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw); - if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) { - editor->set_canvas_cursor (editor->cursors()->left_side_trim); - } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) { - editor->set_canvas_cursor (editor->cursors()->right_side_trim); - } else { - if (pre_enter_cursor && can_set_cursor) { - editor->set_canvas_cursor (pre_enter_cursor); + if (can_set_cursor) { + if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) { + editor->set_canvas_cursor (editor->cursors()->left_side_trim); + } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) { + editor->set_canvas_cursor (editor->cursors()->right_side_trim); + } else if (pre_note_enter_cursor) { + editor->set_canvas_cursor (pre_note_enter_cursor); } } } @@ -3205,15 +3239,15 @@ MidiRegionView::set_frame_color() } if (_selected) { - f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase(); - } else if (high_enough_for_name) { - f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase(); + f = ARDOUR_UI::config()->color ("selected region base"); + } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) { + f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base"); } else { f = fill_color; } if (!rect_visible) { - f = UINT_RGBA_CHANGE_A (f, 0); + f = UINT_RGBA_CHANGE_A (f, 80); } frame->set_fill_color (f); @@ -3303,34 +3337,54 @@ MidiRegionView::selection_as_cut_buffer () const } /** This method handles undo */ -void -MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb) +bool +MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx) { - if (mcb.empty()) { - return; + trackview.editor().begin_reversible_command (Operations::paste); + + // Paste notes, if available + MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes()); + if (m != selection.midi_notes.end()) { + ctx.counts.increase_n_notes(); + paste_internal(pos, ctx.count, ctx.times, **m); } - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times)); + // Paste control points to automation children, if available + typedef RouteTimeAxisView::AutomationTracks ATracks; + const ATracks& atracks = midi_view()->automation_tracks(); + for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) { + a->second->paste(pos, selection, ctx); + } - trackview.session()->begin_reversible_command (_("paste")); + trackview.editor().commit_reversible_command (); - start_note_diff_command (_("paste")); + return true; +} - Evoral::MusicalTime beat_delta; - Evoral::MusicalTime paste_pos_beats; - Evoral::MusicalTime duration; - Evoral::MusicalTime end_point = 0; +/** This method handles undo */ +void +MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb) +{ + if (mcb.empty()) { + return; + } - duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time(); - paste_pos_beats = absolute_frames_to_source_beats (pos); - beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats; - paste_pos_beats = 0; + start_note_diff_command (_("paste")); - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n", - (*mcb.notes().begin())->time(), - (*mcb.notes().rbegin())->end_time(), + const Evoral::MusicalTime snap_beats = get_grid_beats(pos); + const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time(); + const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time(); + const Evoral::MusicalTime duration = last_time - first_time; + const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats); + const Evoral::MusicalTime paste_offset = snap_duration * paste_count; + const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset; + Evoral::MusicalTime end_point = Evoral::MusicalTime(); + + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n", + first_time, + last_time, duration, pos, _region->position(), - paste_pos_beats, beat_delta)); + pos_beats)); clear_selection (); @@ -3339,15 +3393,13 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb) for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) { boost::shared_ptr copied_note (new NoteType (*((*i).get()))); - copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta); + copied_note->set_time (pos_beats + copied_note->time() - first_time); /* make all newly added notes selected */ note_diff_add_note (copied_note, true); end_point = copied_note->end_time(); } - - paste_pos_beats += duration; } /* if we pasted past the current end of the region, extend the region */ @@ -3365,8 +3417,6 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb) } apply_diff (true); - - trackview.session()->commit_reversible_command (); } struct EventNoteTimeEarlyFirstComparator { @@ -3486,6 +3536,8 @@ MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_s void MidiRegionView::update_ghost_note (double x, double y) { + x = std::max(0.0, x); + MidiTimeAxisView* const mtv = dynamic_cast(&trackview); _last_ghost_x = x; @@ -3502,17 +3554,14 @@ MidiRegionView::update_ghost_note (double x, double y) /* use region_frames... because we are converting a delta within the region */ - bool success; - double length = editor.get_grid_type_as_beats (success, unsnapped_frame); - - if (!success) { - length = 1; - } + const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame); /* note that this sets the time of the ghost note in beats relative to the start of the source; that is how all note times are stored. */ - _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ())); + _ghost_note->note()->set_time ( + std::max(Evoral::MusicalTime(), + absolute_frames_to_source_beats (f + _region->position ()))); _ghost_note->note()->set_length (length); _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y)); _ghost_note->note()->set_channel (mtv->get_channel_for_add ()); @@ -3535,9 +3584,6 @@ MidiRegionView::create_ghost_note (double x, double y) update_ghost_note (x, y); _ghost_note->show (); - _last_ghost_x = x; - _last_ghost_y = y; - show_verbose_cursor (_ghost_note->note ()); } @@ -3567,6 +3613,8 @@ MidiRegionView::drop_down_keys () void MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y) { + /* XXX: This is dead code. What was it for? */ + double note = midi_stream_view()->y_to_note(y); Events e; MidiTimeAxisView* const mtv = dynamic_cast(&trackview); @@ -3624,7 +3672,7 @@ void MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos) { if (_step_edit_cursor == 0) { - ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); + ArdourCanvas::Item* const group = get_canvas_group(); _step_edit_cursor = new ArdourCanvas::Rectangle (group); _step_edit_cursor->set_y0 (0); @@ -3688,8 +3736,6 @@ MidiRegionView::data_recorded (boost::weak_ptr w) boost::shared_ptr buf = mtv->midi_track()->get_gui_feed_buffer (); - BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0)); - framepos_t back = max_framepos; for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) { @@ -3703,16 +3749,12 @@ MidiRegionView::data_recorded (boost::weak_ptr w) } } - /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is - frames from the start of the source, and so time_beats is in terms of the - source. - */ - - Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ()); + /* convert from session frames to source beats */ + Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time()); if (ev.type() == MIDI_CMD_NOTE_ON) { boost::shared_ptr note ( - new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())); + new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity())); add_note (note, true); @@ -3739,7 +3781,7 @@ MidiRegionView::trim_front_starting () /* Reparent the note group to the region view's parent, so that it doesn't change when the region view is trimmed. */ - _temporary_note_group = new ArdourCanvas::Group (group->parent ()); + _temporary_note_group = new ArdourCanvas::Container (group->parent ()); _temporary_note_group->move (group->position ()); _note_group->reparent (_temporary_note_group); } @@ -3805,8 +3847,8 @@ MidiRegionView::show_verbose_cursor (boost::shared_ptr n) const get_patch_key_at(n->time(), n->channel(), patch_key); name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")), n->channel(), - patch_key.bank_number, - patch_key.program_number, + patch_key.bank(), + patch_key.program(), n->note()); } } @@ -3824,28 +3866,9 @@ MidiRegionView::show_verbose_cursor (boost::shared_ptr n) const void MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const { - double wx, wy; - - trackview.editor().verbose_cursor()->set_text (text); - trackview.editor().get_pointer_position (wx, wy); - - wx += xoffset; - wy += yoffset; - - /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */ - - boost::optional bbo = trackview.editor().verbose_cursor()->item().bounding_box(); - - assert (bbo); - - ArdourCanvas::Rect bb = bbo.get(); - - if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) { - wy -= (bb.y1 - bb.y0) + 2 * yoffset; - } - - trackview.editor().verbose_cursor()->set_position (wx, wy); + trackview.editor().verbose_cursor()->set (text); trackview.editor().verbose_cursor()->show (); + trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset)); } /** @param p A session framepos. @@ -3857,13 +3880,8 @@ MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_fr { PublicEditor& editor = trackview.editor (); - bool success; - Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p); + const Evoral::MusicalTime grid_beats = get_grid_beats(p); - if (!success) { - grid_beats = 1; - } - grid_frames = region_beats_to_region_frames (grid_beats); /* Hack so that we always snap to the note that we are over, instead of snapping @@ -3911,3 +3929,15 @@ MidiRegionView::get_selected_channels () const return rtav->midi_track()->get_playback_channel_mask(); } + +Evoral::MusicalTime +MidiRegionView::get_grid_beats(framepos_t pos) const +{ + PublicEditor& editor = trackview.editor(); + bool success = false; + Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos); + if (!success) { + beats = Evoral::MusicalTime(1); + } + return beats; +}