X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=16569b6dce58f5a270cc36e024458a98f8de84d9;hb=bd107a28c6ab610a40b30dbd1a36c9b33b883784;hp=e0963b2d911d8a7c5d17937545730f58d62cd5a0;hpb=dca96d8b5d6737ec811cf5a46548d39a7912bec8;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index e0963b2d91..16569b6dce 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -122,7 +122,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _last_event_y (0) , _grabbed_keyboard (false) , _entered (false) - , _note_entered (false) + , _entered_note (0) , _mouse_changed_selection (false) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -165,7 +165,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _last_event_y (0) , _grabbed_keyboard (false) , _entered (false) - , _note_entered (false) + , _entered_note (0) , _mouse_changed_selection (false) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -213,7 +213,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , _last_event_y (0) , _grabbed_keyboard (false) , _entered (false) - , _note_entered (false) + , _entered_note (0) , _mouse_changed_selection (false) { init (false); @@ -245,7 +245,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptrbutton; - _mouse_state = Pressed; + + if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) { + + if (midi_view()->note_mode() == Percussive) { + editor->drags()->set (new HitCreateDrag (dynamic_cast (editor), group, this), (GdkEvent *) ev); + } else { + editor->drags()->set (new NoteCreateDrag (dynamic_cast (editor), group, this), (GdkEvent *) ev); + } + + _mouse_state = AddDragging; + remove_ghost_note (); + hide_verbose_cursor (); + } else { + _mouse_state = Pressed; + } return true; } @@ -541,29 +555,13 @@ MidiRegionView::button_release (GdkEventButton* ev) case MouseTimeFX: { _mouse_changed_selection = true; - - if (Keyboard::is_insert_note_event(ev)) { - - double event_x, event_y; - - event_x = ev->x; - event_y = ev->y; - group->canvas_to_item (event_x, event_y); - - Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x) + _region->position()); - create_note_at (editor.pixel_to_sample (event_x), event_y, beats, ev->state, true); - } else { - clear_editor_note_selection (); - } + clear_editor_note_selection (); break; } case MouseDraw: - { - Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x) + _region->position()); - create_note_at (editor.pixel_to_sample (event_x), event_y, beats, ev->state, true); - break; - } + break; + default: break; } @@ -572,8 +570,8 @@ MidiRegionView::button_release (GdkEventButton* ev) break; case AddDragging: - /* Only create a ghost note when we added a note, not when we were drag-selecting. */ - create_ghost_note (ev->x, ev->y, ev->state); + /* Don't a ghost note when we added a note - wait until motion to avoid visual confusion. + we don't want one when we were drag-selecting either. */ case SelectRectDragging: editor.drags()->end_grab ((GdkEvent *) ev); _mouse_state = None; @@ -597,7 +595,7 @@ MidiRegionView::motion (GdkEventMotion* ev) { PublicEditor& editor = trackview.editor (); - if (!_note_entered) { + if (!_entered_note) { if (_mouse_state == AddDragging) { if (_ghost_note) { @@ -644,13 +642,7 @@ MidiRegionView::motion (GdkEventMotion* ev) MouseMode m = editor.current_mouse_mode(); - 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 (); - hide_verbose_cursor (); - return true; - } else if (m == MouseContent) { + if (m == MouseContent && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { clear_editor_note_selection (); @@ -728,11 +720,14 @@ MidiRegionView::key_press (GdkEventKey* ev) detectable auto-repeat is the name of the game and only sends repeated presses, carry out key actions at key press, not release. */ - bool unmodified = Keyboard::no_modifier_keys_pressed (ev); if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) { - _mouse_state = SelectTouchDragging; + + if (_mouse_state != AddDragging) { + _mouse_state = SelectTouchDragging; + } + return true; } else if (ev->keyval == GDK_Escape && unmodified) { @@ -1388,8 +1383,7 @@ MidiRegionView::~MidiRegionView () if (_active_notes) { end_write(); } - - _selection.clear(); + _entered_note = 0; clear_events (); delete _note_group; @@ -1485,44 +1479,7 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) _current_range_min = min; _current_range_max = max; - for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) { - NoteBase* event = *i; - boost::shared_ptr note (event->note()); - - if (note->note() < _current_range_min || - note->note() > _current_range_max) { - event->hide(); - } else { - event->show(); - } - - if (Note* cnote = dynamic_cast(event)) { - - 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.); - - if (y0 < 0 || y1 >= _height) { - /* During DnD, the region uses the 'old/current' - * midi_stream_view()'s range and its position/height calculation. - * - * Ideally DnD would decouple the midi_stream_view() for the - * region(s) being dragged and set it to the target's range - * (or in case of the drop-zone, FullRange). - * but I don't see how this can be done without major rework. - * - * For now, just prevent visual bleeding of events in case - * the target-track is smaller. - */ - event->hide(); - continue; - } - cnote->set_y0 (y0); - cnote->set_y1 (y1); - - } else if (Hit* chit = dynamic_cast(event)) { - update_hit (chit); - } - } + redisplay_model (); } GhostRegion* @@ -1680,10 +1637,10 @@ MidiRegionView::note_in_region_range (const boost::shared_ptr note, bo /* must compare double explicitly as Beats::operator< rounds to ppqn */ const bool outside = (note->time().to_double() < midi_reg->start_beats() || - note->time().to_double() > midi_reg->start_beats() + midi_reg->length_beats()); + note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats()); - visible = (note->note() >= midi_stream_view()->lowest_note()) && - (note->note() <= midi_stream_view()->highest_note()); + visible = (note->note() >= _current_range_min) && + (note->note() <= _current_range_max); return !outside; } @@ -1711,16 +1668,16 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) const boost::shared_ptr mr = midi_region(); boost::shared_ptr note = ev->note(); - const double session_source_start = _region->pos_beats() - mr->start_beats(); + const double session_source_start = _region->quarter_note() - mr->start_beats(); const framepos_t note_start_frames = map.frame_at_quarter_note (note->time().to_double() + session_source_start) - _region->position(); const double x0 = trackview.editor().sample_to_pixel (note_start_frames); double x1; - const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note())); + const double y0 = 1 + floor(note_to_y(note->note())); double y1; /* trim note display to not overlap the end of its region */ - if (note->length() > 0) { + if (note->length().to_double() > 0.0) { double note_end_time = note->end_time().to_double(); if (note_end_time > mr->start_beats() + mr->length_beats()) { @@ -1734,7 +1691,7 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1; } - y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1); + y1 = y0 + std::max(1., floor(note_height()) - 1); ev->set (ArdourCanvas::Rect (x0, y0, x1, y1)); @@ -1779,12 +1736,12 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions) { boost::shared_ptr note = ev->note(); - const double note_time_qn = note->time().to_double() + (_region->pos_beats() - midi_region()->start_beats()); + const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats()); const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_quarter_note (note_time_qn) - _region->position(); const double x = trackview.editor().sample_to_pixel(note_start_frames); - 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; + const double diamond_size = std::max(1., floor(note_height()) - 2.); + const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5; // see DnD note in MidiRegionView::apply_note_range() above if (y <= 0 || y >= _height) { @@ -1832,7 +1789,7 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) } else if (midi_view()->note_mode() == Percussive) { - const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.); + const double diamond_size = std::max(1., floor(note_height()) - 2.); Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); @@ -2114,6 +2071,10 @@ MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta) void MidiRegionView::note_deleted (NoteBase* cne) { + if (_entered_note && cne == _entered_note) { + _entered_note = 0; + } + if (_selection.empty()) { return; } @@ -2143,6 +2104,7 @@ MidiRegionView::delete_selection() _selection.clear(); apply_diff (); + hide_verbose_cursor (); } @@ -2595,7 +2557,7 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote) for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) { - double const start_qn = _region->pos_beats() - midi_region()->start_beats(); + double const start_qn = _region->quarter_note() - midi_region()->start_beats(); framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt; Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn); if (new_time < 0) { @@ -2922,7 +2884,7 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ /* and then to beats */ const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions); - const double quarter_note_start = _region->pos_beats() - midi_region()->start_beats(); + const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats(); const Evoral::Beats x_beats = Evoral::Beats (e_qaf - quarter_note_start); if (at_front && x_beats < canvas_note->note()->end_time()) { @@ -3339,7 +3301,7 @@ MidiRegionView::change_channel(uint8_t channel) void MidiRegionView::note_entered(NoteBase* ev) { - _note_entered = true; + _entered_note = ev; Editor* editor = dynamic_cast(&trackview.editor()); @@ -3362,7 +3324,7 @@ MidiRegionView::note_entered(NoteBase* ev) void MidiRegionView::note_left (NoteBase*) { - _note_entered = false; + _entered_note = 0; for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { (*i)->hide_velocity (); @@ -3394,7 +3356,7 @@ MidiRegionView::patch_left (PatchChange *) void MidiRegionView::sysex_entered (SysEx* p) { - ostringstream s; + // ostringstream s; // CAIROCANVAS // need a way to extract text from p->_flag->_text // s << p->text(); @@ -3577,14 +3539,14 @@ MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float time const Evoral::Beats duration = last_time - first_time; const Evoral::Beats snap_duration = duration.snap_to(snap_beats); const Evoral::Beats paste_offset = snap_duration * paste_count; - const Evoral::Beats pos_beats = absolute_frames_to_source_beats(pos) + paste_offset; + const Evoral::Beats quarter_note = absolute_frames_to_source_beats(pos) + paste_offset; Evoral::Beats end_point = Evoral::Beats(); 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(), - pos_beats)); + quarter_note)); clear_editor_note_selection (); @@ -3593,7 +3555,7 @@ MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float time 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 (pos_beats + copied_note->time() - first_time); + copied_note->set_time (quarter_note + copied_note->time() - first_time); copied_note->set_id (Evoral::next_event_id()); /* make all newly added notes selected */ @@ -3752,7 +3714,15 @@ MidiRegionView::update_ghost_note (double x, double y, uint32_t state) framepos_t const unsnapped_frame = editor.pixel_to_sample (x); const int32_t divisions = editor.get_grid_music_divisions (state); - const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, true); + const bool shift_snap = midi_view()->note_mode() != Percussive; + const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap); + + /* prevent Percussive mode from displaying a ghost hit at region end */ + if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) { + _ghost_note->hide(); + hide_verbose_cursor (); + return; + } /* ghost note may have been snapped before region */ if (_ghost_note && snapped_beats.to_double() < 0.0) { @@ -3768,7 +3738,7 @@ MidiRegionView::update_ghost_note (double x, double y, uint32_t state) _ghost_note->note()->set_time (snapped_beats); _ghost_note->note()->set_length (length); - _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y)); + _ghost_note->note()->set_note (y_to_note (y)); _ghost_note->note()->set_channel (mtv->get_channel_for_add ()); _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats)); /* the ghost note does not appear in ghost regions, so pass false in here */ @@ -3834,7 +3804,7 @@ MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, doub { /* XXX: This is dead code. What was it for? */ - double note = midi_stream_view()->y_to_note(y); + double note = y_to_note(y); Events e; MidiTimeAxisView* const mtv = dynamic_cast(&trackview); @@ -4149,7 +4119,7 @@ MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, eqaf -= grid_beats.to_double(); } } - const double session_start_off = _region->pos_beats() - midi_region()->start_beats(); + const double session_start_off = _region->quarter_note() - midi_region()->start_beats(); return Evoral::Beats (eqaf - session_start_off); } @@ -4180,3 +4150,24 @@ MidiRegionView::get_grid_beats(framepos_t pos) const } return beats; } +uint8_t +MidiRegionView::y_to_note (double y) const +{ + int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1)) + + _current_range_min; + + 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, _current_range_max); +} + +double +MidiRegionView::note_to_y(uint8_t note) const +{ + return contents_height() - (note + 1 - _current_range_min) * note_height() + 1; +}