X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=afae96bf2215d485a8206472177e4a25bc5437b2;hb=658bb3ccd43658de18fbd43cd91a8e66650e27a7;hp=99a7bf38f75bfefa6ab95dc5ff2338e1cdb87217;hpb=2863640a52ddaea45ebe5bdd8aceb5567fe8989c;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 99a7bf38f7..afae96bf22 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -31,10 +30,11 @@ #include "pbd/memento_command.h" #include "pbd/stateful_diff_command.h" -#include "ardour/midi_region.h" -#include "ardour/midi_source.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/session.h" #include "evoral/Parameter.hpp" @@ -43,11 +43,10 @@ #include "evoral/Control.hpp" #include "evoral/midi_util.h" +#include "canvas/debug.h" + #include "automation_region_view.h" #include "automation_time_axis.h" -#include "canvas-hit.h" -#include "canvas-note.h" -#include "canvas_patch_change.h" #include "debug.h" #include "editor.h" #include "editor_drag.h" @@ -65,20 +64,24 @@ #include "mouse_cursors.h" #include "note_player.h" #include "public_editor.h" +#include "route_time_axis.h" #include "rgb_macros.h" #include "selection.h" -#include "simpleline.h" #include "streamview.h" #include "utils.h" #include "patch_change_dialog.h" #include "verbose_cursor.h" +#include "ardour_ui.h" +#include "note.h" +#include "hit.h" +#include "patch_change.h" +#include "sys_ex.h" #include "i18n.h" using namespace ARDOUR; using namespace PBD; using namespace Editing; -using namespace ArdourCanvas; using Gtkmm2ext::Keyboard; PBD::Signal1 MidiRegionView::SelectionCleared; @@ -88,11 +91,10 @@ PBD::Signal1 MidiRegionView::SelectionCleared; MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color const & basic_color) : RegionView (parent, tv, r, spu, basic_color) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) - , _note_group(new ArdourCanvas::Group(*group)) + , _note_group (new ArdourCanvas::Group (group)) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -112,6 +114,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , pre_press_cursor (0) , _note_player (0) { + CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); @@ -125,11 +128,10 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & boost::shared_ptr r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, false, visibility) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) - , _note_group(new ArdourCanvas::Group(*parent)) + , _note_group (new ArdourCanvas::Group (parent)) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -149,7 +151,9 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , pre_press_cursor (0) , _note_player (0) { + CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); _note_group->raise_to_top(); + PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); connect_to_diskstream (); @@ -170,11 +174,10 @@ MidiRegionView::parameter_changed (std::string const & p) MidiRegionView::MidiRegionView (const MidiRegionView& other) : sigc::trackable(other) , RegionView (other) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) - , _note_group(new ArdourCanvas::Group(*get_canvas_group())) + , _note_group (new ArdourCanvas::Group (get_canvas_group())) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -205,11 +208,10 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr region) : RegionView (other, boost::shared_ptr (region)) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) - , _note_group(new ArdourCanvas::Group(*get_canvas_group())) + , _note_group (new ArdourCanvas::Group (get_canvas_group())) , _note_diff_command (0) , _ghost_note(0) , _step_edit_cursor (0) @@ -243,10 +245,10 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd) { PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); - CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR, - boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1), - gui_context()); - + NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR, + boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1), + gui_context()); + if (wfd) { midi_region()->midi_source(0)->load_model(); } @@ -277,10 +279,10 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd) reset_width_dependent_items (_pixel_width); group->raise_to_top(); - group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false); - midi_view()->signal_channel_mode_changed().connect( - sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed)); + midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this), + boost::bind (&MidiRegionView::midi_channel_mode_changed, this), + gui_context ()); instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this), boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context()); @@ -318,7 +320,7 @@ MidiRegionView::connect_to_diskstream () } bool -MidiRegionView::canvas_event(GdkEvent* ev) +MidiRegionView::canvas_group_event(GdkEvent* ev) { bool r; @@ -337,7 +339,12 @@ MidiRegionView::canvas_event(GdkEvent* ev) } if (ev->type == GDK_2BUTTON_PRESS) { - return trackview.editor().toggle_internal_editing_from_double_click (ev); + // 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); + } } if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) || @@ -379,7 +386,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) break; } - return false; + return trackview.editor().canvas_region_view_event (ev, group, this); } void @@ -491,8 +498,8 @@ MidiRegionView::button_release (GdkEventButton* ev) event_x = ev->x; event_y = ev->y; - group->w2i(event_x, event_y); - group->ungrab(ev->time); + group->canvas_to_item (event_x, event_y); + group->ungrab (); PublicEditor& editor = trackview.editor (); @@ -521,10 +528,10 @@ MidiRegionView::button_release (GdkEventButton* ev) event_x = ev->x; event_y = ev->y; - group->w2i(event_x, event_y); + group->canvas_to_item (event_x, event_y); bool success; - Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x)); + Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x)); if (!success) { beats = 1; @@ -535,7 +542,7 @@ MidiRegionView::button_release (GdkEventButton* ev) */ beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat; - create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true); + create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); } break; @@ -543,7 +550,7 @@ MidiRegionView::button_release (GdkEventButton* ev) case MouseDraw: { bool success; - Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x)); + Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x)); if (!success) { beats = 1; @@ -554,7 +561,7 @@ MidiRegionView::button_release (GdkEventButton* ev) */ beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat; - create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true); + create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); break; } @@ -620,7 +627,6 @@ 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()))) { - editor.drags()->set (new NoteCreateDrag (dynamic_cast (&editor), group, this), (GdkEvent *) ev); _mouse_state = AddDragging; remove_ghost_note (); @@ -680,7 +686,11 @@ MidiRegionView::scroll (GdkEventScroll* ev) change_velocities (true, fine, false, together); } else if (ev->direction == GDK_SCROLL_DOWN) { change_velocities (false, fine, false, together); + } else { + /* left, right: we don't use them */ + return false; } + return true; } @@ -892,13 +902,14 @@ MidiRegionView::show_list_editor () void MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t) { + if (length < 2 * DBL_EPSILON) { + return; + } + MidiTimeAxisView* const mtv = dynamic_cast(&trackview); MidiStreamView* const view = mtv->midi_view(); - double note = view->y_to_note(y); - - assert(note >= 0.0); - assert(note <= 127.0); + const double note = view->y_to_note(y); // Start of note in frames relative to region start if (snap_t) { @@ -906,13 +917,11 @@ MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap t = snap_frame_to_grid_underneath (t, grid_frames); } - assert (t >= 0); - assert (length != 0); - - const boost::shared_ptr new_note (new NoteType (mtv->get_channel_for_add (), - region_frames_to_region_beats(t + _region->start()), - length, - (uint8_t)note, 0x40)); + const boost::shared_ptr new_note ( + new NoteType (mtv->get_channel_for_add (), + region_frames_to_region_beats(t + _region->start()), + length, + (uint8_t)note, 0x40)); if (_model->contains (new_note)) { return; @@ -928,9 +937,9 @@ MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap } void -MidiRegionView::clear_events() +MidiRegionView::clear_events (bool with_selection_signal) { - clear_selection(); + clear_selection (with_selection_signal); MidiGhostRegion* gr; for (std::vector::iterator g = ghosts.begin(); g != ghosts.end(); ++g) { @@ -987,7 +996,7 @@ MidiRegionView::note_diff_add_note (const boost::shared_ptr note, bool } void -MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev) +MidiRegionView::note_diff_remove_note (NoteBase* ev) { if (_note_diff_command && ev->note()) { _note_diff_command->remove(ev->note()); @@ -995,7 +1004,7 @@ MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev) } void -MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev, +MidiRegionView::note_diff_add_change (NoteBase* ev, MidiModel::NoteDiffCommand::Property property, uint8_t val) { @@ -1005,7 +1014,7 @@ MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev, } void -MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev, +MidiRegionView::note_diff_add_change (NoteBase* ev, MidiModel::NoteDiffCommand::Property property, Evoral::MusicalTime val) { @@ -1054,7 +1063,7 @@ MidiRegionView::abort_command() clear_selection(); } -CanvasNoteEvent* +NoteBase* MidiRegionView::find_canvas_note (boost::shared_ptr note) { if (_optimization_iterator != _events.end()) { @@ -1081,7 +1090,7 @@ MidiRegionView::get_events (Events& e, Evoral::Sequence::No _model->get_notes (notes, op, val, chan_mask); for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) { - CanvasNoteEvent* cne = find_canvas_note (*n); + NoteBase* cne = find_canvas_note (*n); if (cne) { e.push_back (cne); } @@ -1109,24 +1118,26 @@ MidiRegionView::redisplay_model() MidiModel::Notes& notes (_model->notes()); _optimization_iterator = _events.begin(); + bool empty_when_starting = !_events.empty(); + for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) { boost::shared_ptr note (*n); - CanvasNoteEvent* cne; + NoteBase* cne; bool visible; if (note_in_region_range (note, visible)) { - if ((cne = find_canvas_note (note)) != 0) { + if (empty_when_starting && (cne = find_canvas_note (note)) != 0) { cne->validate (); - CanvasNote* cn; - CanvasHit* ch; + Note* cn; + Hit* ch; - if ((cn = dynamic_cast(cne)) != 0) { + if ((cn = dynamic_cast(cne)) != 0) { update_note (cn); - } else if ((ch = dynamic_cast(cne)) != 0) { + } else if ((ch = dynamic_cast(cne)) != 0) { update_hit (ch); } @@ -1143,7 +1154,7 @@ MidiRegionView::redisplay_model() } else { - if ((cne = find_canvas_note (note)) != 0) { + if (empty_when_starting && (cne = find_canvas_note (note)) != 0) { cne->validate (); cne->hide (); } @@ -1153,21 +1164,23 @@ MidiRegionView::redisplay_model() /* remove note items that are no longer valid */ - for (Events::iterator i = _events.begin(); i != _events.end(); ) { - if (!(*i)->valid ()) { - - for (vector::iterator j = ghosts.begin(); j != ghosts.end(); ++j) { - MidiGhostRegion* gr = dynamic_cast (*j); - if (gr) { - gr->remove_note (*i); + if (empty_when_starting) { + for (Events::iterator i = _events.begin(); i != _events.end(); ) { + if (!(*i)->valid ()) { + + for (vector::iterator j = ghosts.begin(); j != ghosts.end(); ++j) { + MidiGhostRegion* gr = dynamic_cast (*j); + if (gr) { + gr->remove_note (*i); + } } + + delete *i; + i = _events.erase (i); + + } else { + ++i; } - - delete *i; - i = _events.erase (i); - - } else { - ++i; } } @@ -1192,7 +1205,7 @@ void MidiRegionView::display_patch_changes () { MidiTimeAxisView* const mtv = dynamic_cast(&trackview); - uint16_t chn_mask = mtv->channel_selector().get_selected_channels(); + uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask(); for (uint8_t i = 0; i < 16; ++i) { display_patch_changes_on_channel (i, chn_mask & (1 << i)); @@ -1211,7 +1224,7 @@ MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_c continue; } - string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel); + const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel); add_canvas_patch_change (*i, patch_name, active_channel); } } @@ -1262,7 +1275,6 @@ MidiRegionView::display_sysexes() boost::static_pointer_cast > (*i); Evoral::MusicalTime time = (*i)->time(); - assert (time >= 0); if (mev) { if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) { @@ -1282,12 +1294,15 @@ MidiRegionView::display_sysexes() } string text = str.str(); - const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time)); + const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time)); double height = midi_stream_view()->contents_height(); - boost::shared_ptr sysex = boost::shared_ptr( - new CanvasSysEx(*this, *_note_group, text, height, x, 1.0)); + // CAIROCANVAS: no longer passing *i (the sysex event) to the + // SysEx canvas object!!! + + boost::shared_ptr sysex = boost::shared_ptr( + new SysEx (*this, _note_group, text, height, x, 1.0)); // Show unless message is beyond the region bounds if (time - _region->start() >= _region->length() || time < _region->start()) { @@ -1319,7 +1334,7 @@ MidiRegionView::~MidiRegionView () _selection_cleared_connection.disconnect (); _selection.clear(); - clear_events(); + clear_events (false); delete _note_group; delete _note_diff_command; @@ -1344,14 +1359,13 @@ void MidiRegionView::reset_width_dependent_items (double pixel_width) { RegionView::reset_width_dependent_items(pixel_width); - assert(_pixel_width == pixel_width); if (_enable_display) { redisplay_model(); } for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { - if ((*x)->width() >= _pixel_width) { + if ((*x)->canvas_item()->width() >= _pixel_width) { (*x)->hide(); } else { (*x)->show(); @@ -1374,8 +1388,8 @@ MidiRegionView::set_height (double height) midi_stream_view()->highest_note(), height != old_height + FUDGE); - if (name_pixbuf) { - name_pixbuf->raise_to_top(); + if (name_text) { + name_text->raise_to_top(); } for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { @@ -1383,7 +1397,7 @@ MidiRegionView::set_height (double height) } if (_step_edit_cursor) { - _step_edit_cursor->property_y2() = midi_stream_view()->contents_height(); + _step_edit_cursor->set_y1 (midi_stream_view()->contents_height()); } } @@ -1406,7 +1420,7 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) _current_range_max = max; for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) { - CanvasNoteEvent* event = *i; + NoteBase* event = *i; boost::shared_ptr note (event->note()); if (note->note() < _current_range_min || @@ -1416,15 +1430,15 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) event->show(); } - if (CanvasNote* cnote = dynamic_cast(event)) { + if (Note* cnote = dynamic_cast(event)) { - const double y1 = midi_stream_view()->note_to_y(note->note()); - const double y2 = y1 + floor(midi_stream_view()->note_height()); + const double y0 = midi_stream_view()->note_to_y(note->note()); + const double y1 = y0 + floor(midi_stream_view()->note_height()); - cnote->property_y1() = y1; - cnote->property_y2() = y2; + cnote->set_y0 (y0); + cnote->set_y1 (y1); - } else if (CanvasHit* chit = dynamic_cast(event)) { + } else if (Hit* chit = dynamic_cast(event)) { const double diamond_size = update_hit (chit); @@ -1436,9 +1450,9 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) GhostRegion* MidiRegionView::add_ghost (TimeAxisView& tv) { - CanvasNote* note; + Note* note; - double unit_position = _region->position () / samples_per_unit; + double unit_position = _region->position () / samples_per_pixel; MidiTimeAxisView* mtv = dynamic_cast(&tv); MidiGhostRegion* ghost; @@ -1452,13 +1466,13 @@ MidiRegionView::add_ghost (TimeAxisView& tv) } for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - if ((note = dynamic_cast(*i)) != 0) { + if ((note = dynamic_cast(*i)) != 0) { ghost->add_note(note); } } ghost->set_height (); - ghost->set_duration (_region->length() / samples_per_unit); + ghost->set_duration (_region->length() / samples_per_pixel); ghosts.push_back (ghost); GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); @@ -1472,9 +1486,11 @@ MidiRegionView::add_ghost (TimeAxisView& tv) void MidiRegionView::begin_write() { - assert(!_active_notes); - _active_notes = new CanvasNote*[128]; - for (unsigned i=0; i < 128; ++i) { + if (_active_notes) { + delete[] _active_notes; + } + _active_notes = new Note*[128]; + for (unsigned i = 0; i < 128; ++i) { _active_notes[i] = 0; } } @@ -1509,8 +1525,8 @@ MidiRegionView::resolve_note(uint8_t note, double end_time) */ const framepos_t end_time_frames = region_beats_to_region_frames(end_time); - _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames); - _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges + _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames)); + _active_notes[note]->set_outline_what (0xf); _active_notes[note] = 0; } } @@ -1527,7 +1543,7 @@ MidiRegionView::extend_active_notes() for (unsigned i=0; i < 128; ++i) { if (_active_notes[i]) { - _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length()); + _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length())); } } } @@ -1613,46 +1629,45 @@ MidiRegionView::note_in_region_range (const boost::shared_ptr note, bo * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false. */ void -MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions) +MidiRegionView::update_note (Note* ev, bool update_ghost_regions) { boost::shared_ptr note = ev->note(); - const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time())); - const double y1 = midi_stream_view()->note_to_y(note->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()); - ev->property_x1() = x; - ev->property_y1() = y1; + ev->set_x0 (x); + ev->set_y0 (y0); /* 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()); - ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames); + ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames)); } else { - ev->property_x2() = trackview.editor().frame_to_pixel (_region->length()); + ev->set_x1 (trackview.editor().sample_to_pixel (_region->length())); } - ev->property_y2() = y1 + floor(midi_stream_view()->note_height()); + ev->set_y1 (y0 + floor(midi_stream_view()->note_height())); if (note->length() == 0) { - if (_active_notes) { - assert(note->note() < 128); + if (_active_notes && note->note() < 128) { // If this note is already active there's a stuck note, // finish the old note rectangle if (_active_notes[note->note()]) { - CanvasNote* const old_rect = _active_notes[note->note()]; + Note* const old_rect = _active_notes[note->note()]; boost::shared_ptr old_note = old_rect->note(); - old_rect->property_x2() = x; - old_rect->property_outline_what() = (guint32) 0xF; + old_rect->set_x1 (x); + old_rect->set_outline_what (0xF); } _active_notes[note->note()] = ev; } /* outline all but right edge */ - ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); + ev->set_outline_what (0x1 & 0x4 & 0x8); } else { /* outline all edges */ - ev->property_outline_what() = (guint32) 0xF; + ev->set_outline_what (0xF); } - + if (update_ghost_regions) { for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { MidiGhostRegion* gr = dynamic_cast (*i); @@ -1664,16 +1679,16 @@ MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions) } double -MidiRegionView::update_hit (CanvasHit* ev) +MidiRegionView::update_hit (Hit* ev) { boost::shared_ptr note = ev->note(); const framepos_t note_start_frames = source_beats_to_region_frames(note->time()); - const double x = trackview.editor().frame_to_pixel(note_start_frames); + const double x = trackview.editor().sample_to_pixel(note_start_frames); const double diamond_size = midi_stream_view()->note_height() / 2.0; const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0); - ev->move_to (x, y); + ev->set_position (ArdourCanvas::Duple (x, y)); return diamond_size; } @@ -1687,16 +1702,13 @@ MidiRegionView::update_hit (CanvasHit* ev) void MidiRegionView::add_note(const boost::shared_ptr note, bool visible) { - CanvasNoteEvent* event = 0; - - assert(note->time() >= 0); - assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive); + NoteBase* event = 0; //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group(); if (midi_view()->note_mode() == Sustained) { - CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note); + Note* ev_rect = new Note (*this, _note_group, note); update_note (ev_rect); @@ -1714,7 +1726,7 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) const double diamond_size = midi_stream_view()->note_height() / 2.0; - CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note); + Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); update_hit (ev_diamond); @@ -1733,7 +1745,7 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) event->show_velocity(); } - event->on_channel_selection_change(_last_channel_selection); + event->on_channel_selection_change (get_selected_channels()); _events.push_back(event); if (visible) { @@ -1791,26 +1803,27 @@ MidiRegionView::step_sustain (Evoral::MusicalTime beats) * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel. */ void -MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel) +MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/) { - assert (patch->time() >= 0); - framecnt_t region_frames = source_beats_to_region_frames (patch->time()); - const double x = trackview.editor().frame_to_pixel (region_frames); + const double x = trackview.editor().sample_to_pixel (region_frames); double const height = midi_stream_view()->contents_height(); - boost::shared_ptr patch_change = boost::shared_ptr( - new CanvasPatchChange(*this, *group, - displaytext, - height, - x, 1.0, - instrument_info(), - patch, - active_channel) - ); - - if (patch_change->width() < _pixel_width) { + // CAIROCANVAS: active_channel info removed from PatcChange constructor + // so we need to do something more sophisticated to keep its color + // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill) + // up to date. + + boost::shared_ptr patch_change = boost::shared_ptr( + new PatchChange(*this, group, + displaytext, + height, + x, 1.0, + instrument_info(), + patch)); + + if (patch_change->item().width() < _pixel_width) { // Show unless patch change is beyond the region bounds if (region_frames < 0 || region_frames >= _region->length()) { patch_change->hide(); @@ -1830,26 +1843,41 @@ MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p) return MIDI::Name::PatchPrimaryKey (p->program(), p->bank()); } +/// 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) +{ + return pc->time() <= time && pc->channel() == channel; +} + void -MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) +MidiRegionView::get_patch_key_at (double 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); - while (i != _model->patch_changes().end() && (*i)->channel() != channel) { - ++i; + + // Go backwards until we find the latest PC for this channel, or the start + while (i != _model->patch_changes().begin() && + (i == _model->patch_changes().end() || + !patch_applies(*i, time, channel))) { + --i; } - if (i != _model->patch_changes().end()) { - key.bank_number = (*i)->bank(); + if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) { + key.bank_number = (*i)->bank(); key.program_number = (*i)->program (); } else { key.bank_number = key.program_number = 0; } - assert (key.is_sane()); + if (!key.is_sane()) { + error << string_compose(_("insane MIDI patch key %1:%2"), + key.bank_number, key.program_number) << endmsg; + } } void -MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch) +MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch) { MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change")); @@ -1921,7 +1949,7 @@ MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChangenew_patch_change_diff_command (_("move patch change")); c->change_time (pc.patch (), t); @@ -1932,7 +1960,7 @@ MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t) } void -MidiRegionView::delete_patch_change (CanvasPatchChange* pc) +MidiRegionView::delete_patch_change (PatchChange* pc) { MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change")); c->remove (pc->patch ()); @@ -1943,7 +1971,7 @@ MidiRegionView::delete_patch_change (CanvasPatchChange* pc) } void -MidiRegionView::previous_patch (CanvasPatchChange& patch) +MidiRegionView::previous_patch (PatchChange& patch) { if (patch.patch()->program() < 127) { MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); @@ -1953,7 +1981,7 @@ MidiRegionView::previous_patch (CanvasPatchChange& patch) } void -MidiRegionView::next_patch (CanvasPatchChange& patch) +MidiRegionView::next_patch (PatchChange& patch) { if (patch.patch()->program() > 0) { MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); @@ -1963,7 +1991,7 @@ MidiRegionView::next_patch (CanvasPatchChange& patch) } void -MidiRegionView::previous_bank (CanvasPatchChange& patch) +MidiRegionView::next_bank (PatchChange& patch) { if (patch.patch()->program() < 127) { MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); @@ -1975,7 +2003,7 @@ MidiRegionView::previous_bank (CanvasPatchChange& patch) } void -MidiRegionView::next_bank (CanvasPatchChange& patch) +MidiRegionView::previous_bank (PatchChange& patch) { if (patch.patch()->program() > 0) { MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); @@ -1987,7 +2015,7 @@ MidiRegionView::next_bank (CanvasPatchChange& patch) } void -MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne) +MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne) { if (_selection.empty()) { return; @@ -2027,7 +2055,7 @@ MidiRegionView::delete_note (boost::shared_ptr n) } void -MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal) +MidiRegionView::clear_selection_except (NoteBase* ev, bool signal) { for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { if ((*i) != ev) { @@ -2054,7 +2082,7 @@ MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool } void -MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev) +MidiRegionView::unique_select(NoteBase* ev) { clear_selection_except (ev); @@ -2107,32 +2135,39 @@ MidiRegionView::invert_selection () void MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend) { + bool have_selection = !_selection.empty(); uint8_t low_note = 127; uint8_t high_note = 0; MidiModel::Notes& notes (_model->notes()); _optimization_iterator = _events.begin(); + + if (extend && !have_selection) { + extend = false; + } + /* scan existing selection to get note range */ + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + if ((*i)->note()->note() < low_note) { + low_note = (*i)->note()->note(); + } + if ((*i)->note()->note() > high_note) { + high_note = (*i)->note()->note(); + } + } + if (!add) { clear_selection (); - } - if (extend && _selection.empty()) { - extend = false; + if (!extend && (low_note == high_note) && (high_note == notenum)) { + /* only note previously selected is the one we are + * reselecting. treat this as cancelling the selection. + */ + return; + } } if (extend) { - - /* scan existing selection to get note range */ - - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - if ((*i)->note()->note() < low_note) { - low_note = (*i)->note()->note(); - } - if ((*i)->note()->note() > high_note) { - high_note = (*i)->note()->note(); - } - } - low_note = min (low_note, notenum); high_note = max (high_note, notenum); } @@ -2142,7 +2177,7 @@ MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, b for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) { boost::shared_ptr note (*n); - CanvasNoteEvent* cne; + NoteBase* cne; bool select = false; if (((1 << note->channel()) & channel_mask) != 0) { @@ -2179,7 +2214,7 @@ MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask) for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) { boost::shared_ptr note (*n); - CanvasNoteEvent* cne; + NoteBase* cne; if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) { if ((cne = find_canvas_note (note)) != 0) { @@ -2194,7 +2229,7 @@ MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask) } void -MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend) +MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend) { if (!add) { clear_selection_except (ev); @@ -2247,51 +2282,26 @@ MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool } void -MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev) +MidiRegionView::note_deselected(NoteBase* ev) { remove_from_selection (ev); } void -MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend) +MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend) { - if (x1 > x2) { - swap (x1, x2); - } - - if (y1 > y2) { - swap (y1, y2); - } - // 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. for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - - /* check if any corner of the note is inside the rect - - Notes: - 1) this is computing "touched by", not "contained by" the rect. - 2) this does not require that events be sorted in time. - */ - - const double ix1 = (*i)->x1(); - const double ix2 = (*i)->x2(); - const double iy1 = (*i)->y1(); - const double iy2 = (*i)->y2(); - - if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) || - (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) || - (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) || - (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) { - - // Inside rectangle + if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) { + // Rectangles intersect if (!(*i)->selected()) { add_to_selection (*i); } } else if ((*i)->selected() && !extend) { - // Not inside rectangle + // Rectangles do not intersect remove_from_selection (*i); } } @@ -2309,28 +2319,19 @@ MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool exten // We probably need a tree to be able to find events in O(log(n)) time. for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - - /* check if any corner of the note is inside the rect - - Notes: - 1) this is computing "touched by", not "contained by" the rect. - 2) this does not require that events be sorted in time. - */ - if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) { // within y- (note-) range if (!(*i)->selected()) { add_to_selection (*i); } } else if ((*i)->selected() && !extend) { - // Not inside rectangle remove_from_selection (*i); } } } void -MidiRegionView::remove_from_selection (CanvasNoteEvent* ev) +MidiRegionView::remove_from_selection (NoteBase* ev) { Selection::iterator i = _selection.find (ev); @@ -2348,7 +2349,7 @@ MidiRegionView::remove_from_selection (CanvasNoteEvent* ev) } void -MidiRegionView::add_to_selection (CanvasNoteEvent* ev) +MidiRegionView::add_to_selection (NoteBase* ev) { bool add_mrv_selection = false; @@ -2411,13 +2412,11 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) } void -MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote) +MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote) { - assert (!_selection.empty()); - uint8_t lowest_note_in_selection = 127; uint8_t highest_note_in_selection = 0; - uint8_t highest_note_difference = 0; + uint8_t highest_note_difference = 0; // find highest and lowest notes first @@ -2480,10 +2479,10 @@ MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote) * @return Snapped frame relative to the region position. */ framepos_t -MidiRegionView::snap_pixel_to_frame(double x) +MidiRegionView::snap_pixel_to_sample(double x) { PublicEditor& editor (trackview.editor()); - return snap_frame_to_frame (editor.pixel_to_frame (x)); + return snap_frame_to_frame (editor.pixel_to_sample (x)); } /** @param x Pixel relative to the region position. @@ -2492,21 +2491,21 @@ MidiRegionView::snap_pixel_to_frame(double x) double MidiRegionView::snap_to_pixel(double x) { - return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x)); + return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x)); } double MidiRegionView::get_position_pixels() { framepos_t region_frame = get_position(); - return trackview.editor().frame_to_pixel(region_frame); + return trackview.editor().sample_to_pixel(region_frame); } double MidiRegionView::get_end_position_pixels() { framepos_t frame = get_position() + get_duration (); - return trackview.editor().frame_to_pixel(frame); + return trackview.editor().sample_to_pixel(frame); } framepos_t @@ -2549,33 +2548,33 @@ MidiRegionView::begin_resizing (bool /*at_front*/) _resize_data.clear(); for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - CanvasNote *note = dynamic_cast (*i); + Note *note = dynamic_cast (*i); // only insert CanvasNotes into the map if (note) { NoteResizeData *resize_data = new NoteResizeData(); - resize_data->canvas_note = note; + resize_data->note = note; // create a new SimpleRect from the note which will be the resize preview - SimpleRect *resize_rect = new SimpleRect( - *_note_group, note->x1(), note->y1(), note->x2(), note->y2()); + ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group, + ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1())); // calculate the colors: get the color settings uint32_t fill_color = UINT_RGBA_CHANGE_A( - ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(), + ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), 128); // make the resize preview notes more transparent and bright fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5); // calculate color based on note velocity - resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE( - CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()), + resize_rect->set_fill_color (UINT_INTERPOLATE( + NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()), fill_color, - 0.85); + 0.85)); - resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline( - ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get()); + resize_rect->set_outline_color (NoteBase::calculate_outline ( + ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected())); resize_data->resize_rect = resize_rect; _resize_data.push_back(resize_data); @@ -2593,41 +2592,41 @@ MidiRegionView::begin_resizing (bool /*at_front*/) * as the \a primary note. */ void -MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative) +MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative) { bool cursor_set = false; for (std::vector::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) { - SimpleRect* resize_rect = (*i)->resize_rect; - CanvasNote* canvas_note = (*i)->canvas_note; + ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect; + Note* canvas_note = (*i)->note; double current_x; if (at_front) { if (relative) { - current_x = canvas_note->x1() + delta_x; + current_x = canvas_note->x0() + delta_x; } else { - current_x = primary->x1() + delta_x; + current_x = primary->x0() + delta_x; } } else { if (relative) { - current_x = canvas_note->x2() + delta_x; + current_x = canvas_note->x1() + delta_x; } else { - current_x = primary->x2() + delta_x; + current_x = primary->x1() + delta_x; } } if (at_front) { - resize_rect->property_x1() = snap_to_pixel(current_x); - resize_rect->property_x2() = canvas_note->x2(); + resize_rect->set_x0 (snap_to_pixel(current_x)); + resize_rect->set_x1 (canvas_note->x1()); } else { - resize_rect->property_x2() = snap_to_pixel(current_x); - resize_rect->property_x1() = canvas_note->x1(); + resize_rect->set_x1 (snap_to_pixel(current_x)); + resize_rect->set_x0 (canvas_note->x0()); } if (!cursor_set) { double beats; - beats = snap_pixel_to_frame (current_x); + beats = snap_pixel_to_sample (current_x); beats = region_frames_to_region_beats (beats); double len; @@ -2662,13 +2661,13 @@ MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at * Parameters the same as for \a update_resizing(). */ void -MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative) +MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative) { start_note_diff_command (_("resize notes")); for (std::vector::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) { - CanvasNote* canvas_note = (*i)->canvas_note; - SimpleRect* resize_rect = (*i)->resize_rect; + Note* canvas_note = (*i)->note; + ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect; /* Get the new x position for this resize, which is in pixels relative * to the region position. @@ -2678,20 +2677,20 @@ MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at if (at_front) { if (relative) { - current_x = canvas_note->x1() + delta_x; + current_x = canvas_note->x0() + delta_x; } else { - current_x = primary->x1() + delta_x; + current_x = primary->x0() + delta_x; } } else { if (relative) { - current_x = canvas_note->x2() + delta_x; + current_x = canvas_note->x1() + delta_x; } else { - current_x = primary->x2() + delta_x; + current_x = primary->x1() + delta_x; } } /* Convert that to a frame within the source */ - current_x = snap_pixel_to_frame (current_x) + _region->start (); + current_x = snap_pixel_to_sample (current_x) + _region->start (); /* and then to beats */ current_x = region_frames_to_region_beats (current_x); @@ -2737,7 +2736,7 @@ MidiRegionView::abort_resizing () } void -MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative) +MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative) { uint8_t new_velocity; @@ -2754,7 +2753,7 @@ MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bo } void -MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative) +MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative) { uint8_t new_note; @@ -2769,7 +2768,7 @@ MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool rela } void -MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta) +MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta) { bool change_start = false; bool change_length = false; @@ -2841,7 +2840,7 @@ MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_del } void -MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative) +MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative) { uint8_t new_channel; @@ -2863,7 +2862,7 @@ MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool re } void -MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative) +MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative) { Evoral::MusicalTime new_time; @@ -2885,7 +2884,7 @@ MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime de } void -MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t) +MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t) { note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t); } @@ -2912,8 +2911,8 @@ MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool al if (!allow_smush) { for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) { - return; + if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) { + goto cursor_label; } } } @@ -2941,6 +2940,7 @@ MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool al apply_diff(); + cursor_label: if (!_selection.empty()) { char buf[24]; snprintf (buf, sizeof (buf), "Vel %d", @@ -3080,7 +3080,7 @@ MidiRegionView::nudge_notes (bool forward) return; } - Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance)); + Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance)); if (!forward) { delta = -delta; @@ -3111,7 +3111,7 @@ MidiRegionView::change_channel(uint8_t channel) void -MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev) +MidiRegionView::note_entered(NoteBase* ev) { Editor* editor = dynamic_cast(&trackview.editor()); @@ -3125,7 +3125,7 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev) } void -MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*) +MidiRegionView::note_left (NoteBase*) { Editor* editor = dynamic_cast(&trackview.editor()); @@ -3142,19 +3142,39 @@ MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*) } void -MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p) +MidiRegionView::patch_entered (PatchChange* p) { ostringstream s; /* XXX should get patch name if we can */ - s << _("Bank:") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' - << _("Program:") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' - << _("Channel:") << ((int) p->patch()->channel() + 1); + s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' + << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' + << _("Channel ") << ((int) p->patch()->channel() + 1); show_verbose_cursor (s.str(), 10, 20); - p->grab_focus(); + p->item().grab_focus(); } void -MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *) +MidiRegionView::patch_left (PatchChange *) +{ + trackview.editor().verbose_cursor()->hide (); + /* focus will transfer back via the enter-notify event sent to this + * midi region view. + */ +} + +void +MidiRegionView::sysex_entered (SysEx* p) +{ + ostringstream s; + // CAIROCANVAS + // need a way to extract text from p->_flag->_text + // s << p->text(); + // show_verbose_cursor (s.str(), 10, 20); + p->item().grab_focus(); +} + +void +MidiRegionView::sysex_left (SysEx *) { trackview.editor().verbose_cursor()->hide (); /* focus will transfer back via the enter-notify event sent to this @@ -3192,9 +3212,9 @@ MidiRegionView::set_frame_color() } if (_selected) { - f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get(); + f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase(); } else if (high_enough_for_name) { - f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get(); + f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase(); } else { f = fill_color; } @@ -3203,23 +3223,25 @@ MidiRegionView::set_frame_color() f = UINT_RGBA_CHANGE_A (f, 0); } - frame->property_fill_color_rgba() = f; + frame->set_fill_color (f); } void -MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask) +MidiRegionView::midi_channel_mode_changed () { + MidiTimeAxisView* const mtv = dynamic_cast(&trackview); + uint16_t mask = mtv->midi_track()->get_playback_channel_mask(); + ChannelMode mode = mtv->midi_track()->get_playback_channel_mode (); + if (mode == ForceChannel) { mask = 0xFFFF; // Show all notes as active (below) } // Update notes for selection for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - (*i)->on_channel_selection_change(mask); + (*i)->on_channel_selection_change (mask); } - _last_channel_selection = mask; - _patch_changes.clear (); display_patch_changes (); } @@ -3276,7 +3298,7 @@ MidiRegionView::selection_as_cut_buffer () const { Notes notes; - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) { NoteType* n = (*i)->note().get(); notes.insert (boost::shared_ptr (new NoteType (*n))); } @@ -3355,7 +3377,7 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb) } struct EventNoteTimeEarlyFirstComparator { - bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) { + bool operator() (NoteBase* a, NoteBase* b) { return a->note()->time() < b->note()->time(); } }; @@ -3385,7 +3407,7 @@ MidiRegionView::goto_next_note (bool add_to_selection) time_sort_events (); MidiTimeAxisView* const mtv = dynamic_cast(&trackview); - uint16_t const channel_mask = mtv->channel_selector().get_selected_channels (); + uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask(); for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { if ((*i)->selected()) { @@ -3422,7 +3444,7 @@ MidiRegionView::goto_previous_note (bool add_to_selection) time_sort_events (); MidiTimeAxisView* const mtv = dynamic_cast(&trackview); - uint16_t const channel_mask = mtv->channel_selector().get_selected_channels (); + uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask (); for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) { if ((*i)->selected()) { @@ -3476,11 +3498,11 @@ MidiRegionView::update_ghost_note (double x, double y) _last_ghost_x = x; _last_ghost_y = y; - _note_group->w2i (x, y); + _note_group->canvas_to_item (x, y); PublicEditor& editor = trackview.editor (); - framepos_t const unsnapped_frame = editor.pixel_to_frame (x); + 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); @@ -3514,8 +3536,9 @@ MidiRegionView::create_ghost_note (double x, double y) remove_ghost_note (); boost::shared_ptr g (new NoteType); - _ghost_note = new NoEventCanvasNote (*this, *_note_group, g); - _ghost_note->property_outline_color_rgba() = 0x000000aa; + _ghost_note = new Note (*this, _note_group, g); + _ghost_note->set_ignore_events (true); + _ghost_note->set_outline_color (0x000000aa); update_ghost_note (x, y); _ghost_note->show (); @@ -3548,7 +3571,7 @@ MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, doub Events e; MidiTimeAxisView* const mtv = dynamic_cast(&trackview); - uint16_t chn_mask = mtv->channel_selector().get_selected_channels(); + uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask(); if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { get_events (e, Evoral::Sequence::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask); @@ -3603,11 +3626,11 @@ MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos) if (_step_edit_cursor == 0) { ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); - _step_edit_cursor = new ArdourCanvas::SimpleRect (*group); - _step_edit_cursor->property_y1() = 0; - _step_edit_cursor->property_y2() = midi_stream_view()->contents_height(); - _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90); - _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90); + _step_edit_cursor = new ArdourCanvas::Rectangle (group); + _step_edit_cursor->set_y0 (0); + _step_edit_cursor->set_y1 (midi_stream_view()->contents_height()); + _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90)); + _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90)); } move_step_edit_cursor (pos); @@ -3620,8 +3643,8 @@ MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos) _step_edit_cursor_position = pos; if (_step_edit_cursor) { - double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos)); - _step_edit_cursor->property_x1() = pixel; + double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos)); + _step_edit_cursor->set_x0 (pixel); set_step_edit_cursor_width (_step_edit_cursor_width); } } @@ -3640,7 +3663,7 @@ MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats) _step_edit_cursor_width = beats; if (_step_edit_cursor) { - _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats)); + _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats))); } } @@ -3671,7 +3694,14 @@ MidiRegionView::data_recorded (boost::weak_ptr w) for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) { Evoral::MIDIEvent const ev (*i, false); - assert (ev.buffer ()); + + if (ev.is_channel_event()) { + if (get_channel_mode() == FilterChannels) { + if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) { + continue; + } + } + } /* 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 @@ -3681,10 +3711,8 @@ MidiRegionView::data_recorded (boost::weak_ptr w) Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ()); 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, 0, ev.note(), ev.velocity())); add_note (note, true); @@ -3711,15 +3739,15 @@ 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->property_parent ()); - _temporary_note_group->move (group->property_x(), group->property_y()); - _note_group->reparent (*_temporary_note_group); + _temporary_note_group = new ArdourCanvas::Group (group->parent ()); + _temporary_note_group->move (group->position ()); + _note_group->reparent (_temporary_note_group); } void MidiRegionView::trim_front_ending () { - _note_group->reparent (*group); + _note_group->reparent (group); delete _temporary_note_group; _temporary_note_group = 0; @@ -3730,28 +3758,67 @@ MidiRegionView::trim_front_ending () } void -MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc) +MidiRegionView::edit_patch_change (PatchChange* pc) { - PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY); - if (d.run () != Gtk::RESPONSE_ACCEPT) { + PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true); + + int response = d.run(); + + switch (response) { + case Gtk::RESPONSE_ACCEPT: + break; + case Gtk::RESPONSE_REJECT: + delete_patch_change (pc); + return; + default: return; } change_patch_change (pc->patch(), d.patch ()); } +void +MidiRegionView::delete_sysex (SysEx* /*sysex*/) +{ + // CAIROCANVAS + // sysyex object doesn't have a pointer to a sysex event + // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex")); + // c->remove (sysex->sysex()); + // _model->apply_command (*trackview.session(), c); + + //_sys_exes.clear (); + // display_sysexes(); +} void MidiRegionView::show_verbose_cursor (boost::shared_ptr n) const { - char buf[24]; - snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d", - Evoral::midi_note_name (n->note()).c_str(), + using namespace MIDI::Name; + + std::string name; + + MidiTimeAxisView* const mtv = dynamic_cast(&trackview); + if (mtv) { + boost::shared_ptr device_names(mtv->get_device_names()); + if (device_names) { + MIDI::Name::PatchPrimaryKey patch_key; + 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, + n->note()); + } + } + + char buf[128]; + snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d", (int) n->note (), + name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(), (int) n->channel() + 1, (int) n->velocity()); - show_verbose_cursor (buf, 10, 20); + show_verbose_cursor(buf, 10, 20); } void @@ -3759,6 +3826,7 @@ MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double { double wx, wy; + trackview.editor().verbose_cursor()->set_text (text); trackview.editor().get_pointer_position (wx, wy); wx += xoffset; @@ -3766,14 +3834,17 @@ MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */ - double x1, y1, x2, y2; - trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2); + boost::optional bbo = trackview.editor().verbose_cursor()->item().bounding_box(); - if ((wy + y2 - y1) > trackview.editor().canvas_height()) { - wy -= (y2 - y1) + 2 * yoffset; + 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 (text, wx, wy); + trackview.editor().verbose_cursor()->set_position (wx, wy); trackview.editor().verbose_cursor()->show (); } @@ -3825,3 +3896,18 @@ MidiRegionView::note_button_release () delete _note_player; _note_player = 0; } + +ChannelMode +MidiRegionView::get_channel_mode () const +{ + RouteTimeAxisView* rtav = dynamic_cast (&trackview); + return rtav->midi_track()->get_playback_channel_mode(); +} + +uint16_t +MidiRegionView::get_selected_channels () const +{ + RouteTimeAxisView* rtav = dynamic_cast (&trackview); + return rtav->midi_track()->get_playback_channel_mask(); +} +