X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=a2aa46f3ff607c354ba200dc2672d3535cd8916f;hb=7701207123891b400c144db880b77dc698a202ea;hp=fea3857a905410cc0189edf1ae92e3ca22888a63;hpb=0e63fa65b5bf96040e752386083f80403042a6e6;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index fea3857a90..a2aa46f3ff 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -82,7 +82,7 @@ #include "sys_ex.h" #include "ui_config.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace ARDOUR; using namespace PBD; @@ -111,7 +111,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _step_edit_cursor_width (1.0) , _step_edit_cursor_position (0.0) , _channel_selection_scoped_note (0) - , _temporary_note_group (0) , _mouse_state(None) , _pressed_button(0) , _sort_needed (true) @@ -155,7 +154,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _step_edit_cursor_width (1.0) , _step_edit_cursor_position (0.0) , _channel_selection_scoped_note (0) - , _temporary_note_group (0) , _mouse_state(None) , _pressed_button(0) , _sort_needed (true) @@ -204,7 +202,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , _step_edit_cursor_width (1.0) , _step_edit_cursor_position (0.0) , _channel_selection_scoped_note (0) - , _temporary_note_group (0) , _mouse_state(None) , _pressed_button(0) , _sort_needed (true) @@ -237,7 +234,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptrstate); _entered = true; return false; @@ -441,11 +437,11 @@ MidiRegionView::mouse_mode_changed () } void -MidiRegionView::enter_internal() +MidiRegionView::enter_internal (uint32_t state) { if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) { // Show ghost note under pencil - create_ghost_note(_last_event_x, _last_event_y); + create_ghost_note(_last_event_x, _last_event_y, state); } if (!_selection.empty()) { @@ -554,8 +550,8 @@ MidiRegionView::button_release (GdkEventButton* ev) event_y = ev->y; group->canvas_to_item (event_x, event_y); - Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x)); - create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); + 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 (); } @@ -564,8 +560,8 @@ MidiRegionView::button_release (GdkEventButton* ev) } case MouseDraw: { - Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x)); - create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); + 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; } default: @@ -577,7 +573,7 @@ MidiRegionView::button_release (GdkEventButton* ev) 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); + create_ghost_note (ev->x, ev->y, ev->state); case SelectRectDragging: editor.drags()->end_grab ((GdkEvent *) ev); _mouse_state = None; @@ -607,12 +603,12 @@ MidiRegionView::motion (GdkEventMotion* ev) Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) && _mouse_state != AddDragging) { - create_ghost_note (ev->x, ev->y); + create_ghost_note (ev->x, ev->y, ev->state); } 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); + update_ghost_note (ev->x, ev->y, ev->state); } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) { @@ -622,10 +618,10 @@ MidiRegionView::motion (GdkEventMotion* ev) } else if (editor.current_mouse_mode() == MouseDraw) { if (_ghost_note) { - update_ghost_note (ev->x, ev->y); + update_ghost_note (ev->x, ev->y, ev->state); } else { - create_ghost_note (ev->x, ev->y); + create_ghost_note (ev->x, ev->y, ev->state); } } } @@ -920,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, bool snap_t) +MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap) { if (length < 2 * DBL_EPSILON) { return; @@ -935,13 +931,9 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bo } // Start of note in frames relative to region start - if (snap_t) { - framecnt_t grid_frames; - t = snap_frame_to_grid_underneath (t, grid_frames); - } + const int32_t divisions = trackview.editor().get_grid_music_divisions (state); + Evoral::Beats beat_time = snap_frame_to_grid_underneath (t, divisions, shift_snap); - const MidiModel::TimeType beat_time = Evoral::Beats (trackview.session()->tempo_map().beat_at_frame (_region->position() + t) - - (mr->beat() - mr->start_beats().to_double())); const double note = view->y_to_note(y); const uint8_t chan = mtv->get_channel_for_add(); const uint8_t velocity = get_velocity_for_add(beat_time); @@ -1184,11 +1176,11 @@ MidiRegionView::redisplay_model() _optimization_iterator = _events.begin(); bool empty_when_starting = _events.empty(); + NoteBase* cne; for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) { boost::shared_ptr note (*n); - NoteBase* cne; bool visible; if (note_in_region_range (note, visible)) { @@ -1398,7 +1390,6 @@ MidiRegionView::~MidiRegionView () delete _note_group; delete _note_diff_command; delete _step_edit_cursor; - delete _temporary_note_group; } void @@ -1410,12 +1401,21 @@ MidiRegionView::region_resized (const PropertyChange& what_changed) _region_relative_time_converter.set_origin_b(_region->position()); _region_relative_time_converter_double.set_origin_b(_region->position()); /* reset_width dependent_items() redisplays 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()); } + /* catch end and start trim so we can update the view*/ + if (!what_changed.contains (ARDOUR::Properties::start) && + what_changed.contains (ARDOUR::Properties::length)) { + enable_display (true); + } else if (what_changed.contains (ARDOUR::Properties::start) && + what_changed.contains (ARDOUR::Properties::length)) { + enable_display (true); + } } void @@ -1699,16 +1699,27 @@ MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions) void 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 x0 = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time())); + const double qn_note_time = note->time().to_double() + ((_region->pulse() * 4.0) - mr->start_beats().to_double()); + const framepos_t note_start_frames = map.frame_at_quarter_note (qn_note_time) - _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) { - const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length()); + Evoral::Beats note_end_time = note->end_time(); + + 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() * 4.0) - mr->start_beats().to_double(); + 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(); 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; @@ -1716,10 +1727,8 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1); - ev->set_x0 (x0); - ev->set_x1 (x1); - ev->set_y0 (y0); - ev->set_y1 (y1); + ArdourCanvas::Rect rect (x0, y0, x1, y1); + ev->set (rect); if (!note->length()) { if (_active_notes && note->note() < 128) { @@ -1743,8 +1752,9 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) } // 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) { @@ -1761,7 +1771,9 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions) { boost::shared_ptr note = ev->note(); - const framepos_t note_start_frames = source_beats_to_region_frames(note->time()); + const double note_time_qn = note->time().to_double() + ((_region->pulse() * 4.0) - midi_region()->start_beats().to_double()); + 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; @@ -2107,6 +2119,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) { @@ -2564,14 +2580,15 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote) if (highest_note_in_selection + dnote > 127) { highest_note_difference = highest_note_in_selection - 127; } + TempoMap& map (trackview.session()->tempo_map()); start_note_diff_command (_("move notes")); for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) { - framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt; - Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames); - + double const start_qn = (_region->pulse() * 4.0) - midi_region()->start_beats().to_double(); + 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) { continue; } @@ -2729,6 +2746,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/) void MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap) { + TempoMap& tmap (trackview.session()->tempo_map()); bool cursor_set = false; bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic; @@ -2790,9 +2808,19 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_ sign = -1; } - const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x, ensure_snap) : trackview.editor ().pixel_to_sample (current_x)); - Evoral::Beats beats = region_frames_to_region_beats (snapped_x); - Evoral::Beats len = Evoral::Beats(); + double snapped_x; + int32_t divisions = 0; + + if (with_snap) { + snapped_x = snap_pixel_to_sample (current_x, ensure_snap); + divisions = trackview.editor().get_grid_music_divisions (0); + } else { + snapped_x = trackview.editor ().pixel_to_sample (current_x); + } + const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions) + - midi_region()->beat()) + midi_region()->start_beats(); + + Evoral::Beats len = Evoral::Beats(); if (at_front) { if (beats < canvas_note->note()->end_time()) { @@ -2825,6 +2853,7 @@ void MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap) { _note_diff_command = _model->new_note_diff_command (_("resize notes")); + TempoMap& tmap (trackview.session()->tempo_map()); /* XX why doesn't snap_pixel_to_sample() handle this properly? */ bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic; @@ -2872,16 +2901,20 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ sign = -1; } + uint32_t divisions = 0; /* Convert the new x position to a frame within the source */ framepos_t current_fr; if (with_snap) { current_fr = snap_pixel_to_sample (current_x, ensure_snap); + divisions = trackview.editor().get_grid_music_divisions (0); } else { current_fr = trackview.editor().pixel_to_sample (current_x); } /* and then to beats */ - const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr + _region->start()); + 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); 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)); @@ -3488,14 +3521,16 @@ MidiRegionView::selection_as_cut_buffer () const /** This method handles undo */ bool -MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t& sub_num) +MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num) { bool commit = false; // 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(); - if (!(*m)->empty()) { commit = true; } + if (!(*m)->empty()) { + commit = true; + } paste_internal(pos, ctx.count, ctx.times, **m); } @@ -3504,6 +3539,9 @@ MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContex const ATracks& atracks = midi_view()->automation_tracks(); for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) { if (a->second->paste(pos, selection, ctx, sub_num)) { + if(!commit) { + trackview.editor().begin_reversible_command (Operations::paste); + } commit = true; } } @@ -3689,7 +3727,7 @@ MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_s } void -MidiRegionView::update_ghost_note (double x, double y) +MidiRegionView::update_ghost_note (double x, double y, uint32_t state) { x = std::max(0.0, x); @@ -3703,21 +3741,19 @@ MidiRegionView::update_ghost_note (double x, double y) PublicEditor& editor = trackview.editor (); framepos_t const unsnapped_frame = editor.pixel_to_sample (x); - framecnt_t grid_frames; - framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames); + const int32_t divisions = editor.get_grid_music_divisions (state); + const double snapped_region_qn = snap_frame_to_grid_underneath (unsnapped_frame, divisions, true).to_double(); + + 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); - const Evoral::Beats time = std::max( - Evoral::Beats(), - absolute_frames_to_source_beats (f + _region->position ())); + const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position()); - _ghost_note->note()->set_time (time); + _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_channel (mtv->get_channel_for_add ()); - _ghost_note->note()->set_velocity (get_velocity_for_add (time)); - + _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 */ update_note (_ghost_note, false); @@ -3725,7 +3761,7 @@ MidiRegionView::update_ghost_note (double x, double y) } void -MidiRegionView::create_ghost_note (double x, double y) +MidiRegionView::create_ghost_note (double x, double y, uint32_t state) { remove_ghost_note (); @@ -3737,7 +3773,7 @@ MidiRegionView::create_ghost_note (double x, double y) } _ghost_note->set_ignore_events (true); _ghost_note->set_outline_color (0x000000aa); - update_ghost_note (x, y); + update_ghost_note (x, y, state); _ghost_note->show (); show_verbose_cursor (_ghost_note->note ()); @@ -3767,7 +3803,7 @@ MidiRegionView::snap_changed () return; } - create_ghost_note (_last_ghost_x, _last_ghost_y); + create_ghost_note (_last_ghost_x, _last_ghost_y, 0); } void @@ -3828,11 +3864,7 @@ MidiRegionView::color_handler () void MidiRegionView::enable_display (bool yn) { - RegionView::enable_display (yn); - if (yn) { - redisplay_model (); - } } void @@ -3948,21 +3980,14 @@ MidiRegionView::data_recorded (boost::weak_ptr w) void 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. + /* We used to eparent the note group to the region view's parent, so that it didn't change. + now we update it. */ - _temporary_note_group = new ArdourCanvas::Container (group->parent ()); - _temporary_note_group->move (group->position ()); - _note_group->reparent (_temporary_note_group); } void MidiRegionView::trim_front_ending () { - _note_group->reparent (group); - delete _temporary_note_group; - _temporary_note_group = 0; - if (_region->start() < 0) { /* Trim drag made start time -ve; fix this */ midi_region()->fix_negative_start (); @@ -4025,7 +4050,7 @@ MidiRegionView::get_note_name (boost::shared_ptr n, uint8_t note_value char buf[128]; snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d", (int) note_value, - name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(), + name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(), (int) n->channel() + 1, (int) n->velocity()); @@ -4086,26 +4111,30 @@ MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const } /** @param p A session framepos. - * @param grid_frames Filled in with the number of frames that a grid interval is at p. - * @return p snapped to the grid subdivision underneath it. + * @param divisions beat division to snap given by Editor::get_grid_music_divisions() where + * bar is -1, 0 is audio samples and a positive integer is beat subdivisions. + * @return beat duration of p snapped to the grid subdivision underneath it. */ -framepos_t -MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const +Evoral::Beats +MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const { - PublicEditor& editor = trackview.editor (); - const Evoral::Beats p_beat = region_frames_to_region_beats (p); - const Evoral::Beats grid_beats = get_grid_beats(p); + TempoMap& map (trackview.session()->tempo_map()); + double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions); - grid_frames = region_beats_to_region_frames (p_beat + grid_beats) - region_beats_to_region_frames (p_beat); - - /* 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. - */ - if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) { - p -= grid_frames / 2; + 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 = eqaf - qaf; + if (rem >= 0.0 && eqaf - grid_beats.to_double() > _region->pulse() * 4.0) { + eqaf -= grid_beats.to_double(); + } } + const double session_start_off = (_region->pulse() * 4.0) - midi_region()->start_beats().to_double(); - return snap_frame_to_frame (p); + return Evoral::Beats (eqaf - session_start_off); } ChannelMode