X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=c0d8a3c59968892b545cbc6b98a8fda0198cc5ec;hb=47d186d1a301c8cbc8ed8c3b6cbe0c84bb6b681f;hp=6270dbf8fcc516b314a00eb287e72d80b6a21a90;hpb=fb9f4636e7995b8d79b83101108a2d05a23684d7;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 6270dbf8fc..c0d8a3c599 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); - } 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); - 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,9 +595,14 @@ MidiRegionView::motion (GdkEventMotion* ev) { PublicEditor& editor = trackview.editor (); - if (!_note_entered) { + if (!_entered_note) { + + if (_mouse_state == AddDragging) { + if (_ghost_note) { + remove_ghost_note (); + } - if (!_ghost_note && editor.current_mouse_mode() == MouseContent && + } else if (!_ghost_note && editor.current_mouse_mode() == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) && _mouse_state != AddDragging) { @@ -639,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 (); @@ -723,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) { @@ -916,7 +916,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, Evoral::Beats length, uint32_t state) +MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap) { if (length < 2 * DBL_EPSILON) { return; @@ -932,8 +932,7 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, ui // Start of note in frames relative to region start const int32_t divisions = trackview.editor().get_grid_music_divisions (state); - const double snapped_qn = snap_frame_to_grid_underneath (t, divisions).to_double(); - Evoral::Beats beat_time = Evoral::Beats (snapped_qn); + Evoral::Beats beat_time = snap_frame_to_grid_underneath (t, divisions, shift_snap); const double note = view->y_to_note(y); const uint8_t chan = mtv->get_channel_for_add(); @@ -1116,12 +1115,12 @@ MidiRegionView::find_canvas_note (boost::shared_ptr note) /** This version finds any canvas note matching the supplied note. */ NoteBase* -MidiRegionView::find_canvas_note (NoteType note) +MidiRegionView::find_canvas_note (Evoral::event_id_t id) { Events::iterator it; for (it = _events.begin(); it != _events.end(); ++it) { - if (*((*it)->note()) == note) { + if ((*it)->note()->id() == id) { return *it; } } @@ -1201,9 +1200,9 @@ MidiRegionView::redisplay_model() cne = add_note (note, visible); } - set >::iterator it; + set::iterator it; for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) { - if (*(*it) == *note) { + if ((*it) == note->id()) { add_to_selection (cne); } } @@ -1384,7 +1383,7 @@ MidiRegionView::~MidiRegionView () if (_active_notes) { end_write(); } - + _entered_note = 0; _selection.clear(); clear_events (); @@ -1541,6 +1540,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv) ghost->add_note(*i); } + ghost->set_colors (); ghost->set_height (); ghost->set_duration (_region->length() / samples_per_pixel); ghosts.push_back (ghost); @@ -1672,8 +1672,10 @@ bool MidiRegionView::note_in_region_range (const boost::shared_ptr note, bool& visible) const { const boost::shared_ptr midi_reg = midi_region(); - const bool outside = (note->time() < midi_reg->start_pulse() * 4.0 || - note->time() > (midi_reg->start_pulse() + midi_reg->length_pulse()) * 4.0); + + /* 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()); visible = (note->note() >= midi_stream_view()->lowest_note()) && (note->note() <= midi_stream_view()->highest_note()); @@ -1703,24 +1705,25 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) TempoMap& map (trackview.session()->tempo_map()); const boost::shared_ptr mr = midi_region(); boost::shared_ptr note = ev->note(); - const double qn_note_time = note->time().to_double() + ((_region->pulse() - mr->start_pulse()) * 4.0); - const framepos_t note_start_frames = map.frame_at_quarter_note (qn_note_time) - _region->position(); + + 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())); double y1; /* trim note display to not overlap the end of its region */ - if (note->length() > 0) { - Evoral::Beats note_end_time = note->end_time(); + if (note->length().to_double() > 0.0) { + double note_end_time = note->end_time().to_double(); - if (note->end_time().to_double() > (mr->start_pulse() + mr->length_pulse()) * 4.0) { - note_end_time = Evoral::Beats ((mr->start_pulse() + mr->length_pulse()) * 4.0); + if (note_end_time > mr->start_beats() + mr->length_beats()) { + note_end_time = mr->start_beats() + mr->length_beats(); } - const double session_qn_start = (_region->pulse() - mr->start_pulse()) * 4.0; - const double quarter_note_end_time = session_qn_start + note_end_time.to_double(); - const framepos_t note_end_frames = map.frame_at_quarter_note (quarter_note_end_time) - _region->position(); + const framepos_t note_end_frames = map.frame_at_quarter_note (session_source_start + note_end_time) - _region->position(); + x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1; } else { x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1; @@ -1728,8 +1731,7 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1); - ArdourCanvas::Rect rect (x0, y0, x1, y1); - ev->set (rect); + ev->set (ArdourCanvas::Rect (x0, y0, x1, y1)); if (!note->length()) { if (_active_notes && note->note() < 128) { @@ -1753,9 +1755,9 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) } // Update color in case velocity has changed - //const uint32_t base_col = ev->base_color(); - //ev->set_fill_color(base_col); - //ev->set_outline_color(ev->calculate_outline(base_col, ev->selected())); + const uint32_t base_col = ev->base_color(); + ev->set_fill_color(base_col); + ev->set_outline_color(ev->calculate_outline(base_col, ev->selected())); if (update_ghost_regions) { for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { @@ -1772,7 +1774,7 @@ 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->pulse() - midi_region()->start_pulse()) * 4.0); + 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); @@ -1790,14 +1792,15 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions) ev->set_height (diamond_size); // Update color in case velocity has changed - ev->set_fill_color(ev->base_color()); - ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected())); + const uint32_t base_col = ev->base_color(); + ev->set_fill_color(base_col); + ev->set_outline_color(ev->calculate_outline(base_col, ev->selected())); if (update_ghost_regions) { for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { MidiGhostRegion* gr = dynamic_cast (*i); if (gr) { - gr->update_note (ev); + gr->update_hit (ev); } } } @@ -2106,6 +2109,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; } @@ -2120,6 +2127,10 @@ MidiRegionView::delete_selection() return; } + if (trackview.editor().drags()->active()) { + return; + } + start_note_diff_command (_("delete selection")); for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { @@ -2131,6 +2142,7 @@ MidiRegionView::delete_selection() _selection.clear(); apply_diff (); + hide_verbose_cursor (); } @@ -2224,13 +2236,13 @@ MidiRegionView::invert_selection () The requested notes most likely won't exist in the view until the next model redisplay. */ void -MidiRegionView::select_notes (list > notes) +MidiRegionView::select_notes (list notes) { NoteBase* cne; - list >::iterator n; + list::iterator n; for (n = notes.begin(); n != notes.end(); ++n) { - if ((cne = find_canvas_note(*(*n))) != 0) { + if ((cne = find_canvas_note(*n)) != 0) { add_to_selection (cne); } else { _pending_note_selection.insert(*n); @@ -2583,7 +2595,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->pulse() - midi_region()->start_pulse()) * 4.0; + 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) { @@ -2909,9 +2921,9 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ } /* and then to beats */ - const double e_baf = tmap.exact_beat_at_frame (current_fr + midi_region()->position(), divisions); - const double quarter_note_start_beat = tmap.quarter_note_at_beat (_region->beat() - midi_region()->start_beats().to_double()); - const Evoral::Beats x_beats = Evoral::Beats (tmap.quarter_note_at_beat (e_baf) - quarter_note_start_beat); + const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions); + 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()) { note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats)); @@ -3327,7 +3339,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()); @@ -3350,7 +3362,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 (); @@ -3382,7 +3394,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(); @@ -3565,14 +3577,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 (); @@ -3581,7 +3593,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 */ @@ -3740,9 +3752,18 @@ 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 double snapped_region_qn = snap_frame_to_grid_underneath (unsnapped_frame, divisions).to_double(); + const bool shift_snap = midi_view()->note_mode() != Percussive; + const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap); + + /* ghost note may have been snapped before region */ + if (_ghost_note && snapped_beats.to_double() < 0.0) { + _ghost_note->hide(); + return; + + } else if (_ghost_note) { + _ghost_note->show(); + } - Evoral::Beats snapped_beats = Evoral::Beats (snapped_region_qn); /* calculate time in beats relative to start of source */ const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position()); @@ -4113,27 +4134,25 @@ MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const * @return beat duration of p snapped to the grid subdivision underneath it. */ Evoral::Beats -MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions) const +MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const { TempoMap& map (trackview.session()->tempo_map()); + double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions); - const double qaf = map.quarter_note_at_frame (p + _region->position()); - double eqaf; - if (divisions != 0) { - eqaf = map.exact_qn_at_frame (p + _region->position(), divisions); + if (divisions != 0 && shift_snap) { + const double qaf = map.quarter_note_at_frame (p + _region->position()); /* Hack so that we always snap to the note that we are over, instead of snapping to the next one if we're more than halfway through the one we're over. */ const Evoral::Beats grid_beats = get_grid_beats (p + _region->position()); - const double rem = fmod (qaf, grid_beats.to_double()); - if (rem >= grid_beats.to_double() / 2.0) { + const double rem = eqaf - qaf; + if (rem >= 0.0) { eqaf -= grid_beats.to_double(); } - } else { - eqaf = qaf; } + const double session_start_off = _region->quarter_note() - midi_region()->start_beats(); - return Evoral::Beats (eqaf - ((_region->pulse() - midi_region()->start_pulse()) * 4.0)); + return Evoral::Beats (eqaf - session_start_off); } ChannelMode