X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=9925a52c5c0b58cfd816488c119e64db6e1d8318;hb=8cd71108c1d153f84f25c3b9e5153a110826c130;hp=bab797c8c7d1b132f33074d94b8ba456ed974e7f;hpb=355183f1abea75d8fab0926cd7e7130796574cb0;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index bab797c8c7..9925a52c5c 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -18,25 +18,23 @@ */ #include -#include #include #include #include -#include +#include "gtkmm2ext/gtk_ui.h" #include #include "pbd/memento_command.h" #include "pbd/stateful_diff_command.h" -#include "ardour/playlist.h" -#include "ardour/tempo.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" @@ -50,6 +48,7 @@ #include "canvas-hit.h" #include "canvas-note.h" #include "canvas_patch_change.h" +#include "canvas-sysex.h" #include "debug.h" #include "editor.h" #include "editor_drag.h" @@ -63,14 +62,16 @@ #include "midi_streamview.h" #include "midi_time_axis.h" #include "midi_util.h" +#include "midi_velocity_dialog.h" +#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 "mouse_cursors.h" #include "patch_change_dialog.h" #include "verbose_cursor.h" @@ -89,8 +90,6 @@ 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) - , _force_channel(-1) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -110,23 +109,23 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , _no_sound_notes (false) , _last_event_x (0) , _last_event_y (0) - , _pre_enter_cursor (0) + , pre_enter_cursor (0) + , pre_press_cursor (0) + , _note_player (0) { _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); + SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); } MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, false, visibility) - , _force_channel(-1) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -146,14 +145,16 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , _no_sound_notes (false) , _last_event_x (0) , _last_event_y (0) - , _pre_enter_cursor (0) + , pre_enter_cursor (0) + , pre_press_cursor (0) + , _note_player (0) { _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); connect_to_diskstream (); - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); + SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); } void @@ -169,8 +170,6 @@ MidiRegionView::parameter_changed (std::string const & p) MidiRegionView::MidiRegionView (const MidiRegionView& other) : sigc::trackable(other) , RegionView (other) - , _force_channel(-1) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -190,7 +189,9 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , _no_sound_notes (false) , _last_event_x (0) , _last_event_y (0) - , _pre_enter_cursor (0) + , pre_enter_cursor (0) + , pre_press_cursor (0) + , _note_player (0) { Gdk::Color c; int r,g,b,a; @@ -203,8 +204,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr region) : RegionView (other, boost::shared_ptr (region)) - , _force_channel(-1) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -224,7 +223,9 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptrraise_to_top(); - group->signal_event().connect( - sigc::mem_fun(this, &MidiRegionView::canvas_event), false); + 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()->signal_midi_patch_settings_changed().connect( - sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_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()); trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this), - ui_bind(&MidiRegionView::snap_changed, this), + boost::bind (&MidiRegionView::snap_changed, this), gui_context()); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); + SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); +} + +InstrumentInfo& +MidiRegionView::instrument_info () const +{ + RouteUI* route_ui = dynamic_cast (&trackview); + return route_ui->route()->instrument_info(); } const boost::shared_ptr @@ -304,13 +313,15 @@ MidiRegionView::connect_to_diskstream () { midi_view()->midi_track()->DataRecorded.connect( *this, invalidator(*this), - ui_bind(&MidiRegionView::data_recorded, this, _1), + boost::bind (&MidiRegionView::data_recorded, this, _1), gui_context()); } bool MidiRegionView::canvas_event(GdkEvent* ev) { + bool r; + switch (ev->type) { case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: @@ -325,7 +336,19 @@ MidiRegionView::canvas_event(GdkEvent* ev) break; } - if (!trackview.editor().internal_editing()) { + if (ev->type == GDK_2BUTTON_PRESS) { + // cannot use double-click to exit internal mode if single-click is being used + MouseMode m = trackview.editor().current_mouse_mode(); + + if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) { + return trackview.editor().toggle_internal_editing_from_double_click (ev); + } + } + + if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) || + (trackview.editor().current_mouse_mode() == MouseTimeFX) || + (trackview.editor().current_mouse_mode() == MouseZoom)) { + // handle non-draw modes elsewhere return false; } @@ -342,11 +365,11 @@ MidiRegionView::canvas_event(GdkEvent* ev) case GDK_BUTTON_PRESS: return button_press (&ev->button); - case GDK_2BUTTON_PRESS: - return true; - case GDK_BUTTON_RELEASE: - return button_release (&ev->button); + r = button_release (&ev->button); + delete _note_player; + _note_player = 0; + return r; case GDK_ENTER_NOTIFY: return enter_notify (&ev->crossing); @@ -375,10 +398,10 @@ bool MidiRegionView::enter_notify (GdkEventCrossing* ev) { trackview.editor().MouseModeChanged.connect ( - _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context () + _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context () ); - if (trackview.editor().current_mouse_mode() == MouseRange && _mouse_state != AddDragging) { + if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) { create_ghost_note (ev->x, ev->y); } @@ -389,6 +412,13 @@ MidiRegionView::enter_notify (GdkEventCrossing* ev) group->grab_focus(); } + // if current operation is non-operational in a midi region, change the cursor to so indicate + if (trackview.editor().current_mouse_mode() == MouseGain) { + Editor* editor = dynamic_cast (&trackview.editor()); + pre_enter_cursor = editor->get_canvas_cursor(); + editor->set_canvas_cursor(editor->cursors()->timebar); + } + return false; } @@ -400,13 +430,18 @@ MidiRegionView::leave_notify (GdkEventCrossing*) trackview.editor().verbose_cursor()->hide (); remove_ghost_note (); + if (pre_enter_cursor) { + Editor* editor = dynamic_cast (&trackview.editor()); + editor->set_canvas_cursor(pre_enter_cursor); + } + return false; } void MidiRegionView::mouse_mode_changed () { - if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) { + if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) { create_ghost_note (_last_event_x, _last_event_y); } else { remove_ghost_note (); @@ -428,11 +463,19 @@ MidiRegionView::button_press (GdkEventButton* ev) return false; } - if (_mouse_state != SelectTouchDragging) { + Editor* editor = dynamic_cast (&trackview.editor()); + MouseMode m = editor->current_mouse_mode(); + + if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { + pre_press_cursor = editor->get_canvas_cursor (); + editor->set_canvas_cursor (editor->cursors()->midi_pencil); + } + if (_mouse_state != SelectTouchDragging) { + _pressed_button = ev->button; _mouse_state = Pressed; - + return true; } @@ -458,10 +501,20 @@ MidiRegionView::button_release (GdkEventButton* ev) PublicEditor& editor = trackview.editor (); + if (pre_press_cursor) { + dynamic_cast(&editor)->set_canvas_cursor (pre_press_cursor, false); + pre_press_cursor = 0; + } + switch (_mouse_state) { case Pressed: // Clicked switch (editor.current_mouse_mode()) { + case MouseRange: + /* no motion occured - simple click */ + clear_selection (); + break; + case MouseObject: case MouseTimeFX: { @@ -492,7 +545,7 @@ MidiRegionView::button_release (GdkEventButton* ev) break; } - case MouseRange: + case MouseDraw: { bool success; Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x)); @@ -537,21 +590,24 @@ MidiRegionView::motion (GdkEventMotion* ev) { PublicEditor& editor = trackview.editor (); - if (!_ghost_note && editor.current_mouse_mode() != MouseRange - && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) - && _mouse_state != AddDragging) { + if (!_ghost_note && editor.current_mouse_mode() == MouseObject && + Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) && + _mouse_state != AddDragging) { create_ghost_note (ev->x, ev->y); - } else if (_ghost_note && editor.current_mouse_mode() != MouseRange - && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { + + } else if (_ghost_note && editor.current_mouse_mode() == MouseObject && + Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { update_ghost_note (ev->x, ev->y); - } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) { - remove_ghost_note (); + } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) { + remove_ghost_note (); editor.verbose_cursor()->hide (); - } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) { + + } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) { + update_ghost_note (ev->x, ev->y); } @@ -564,28 +620,33 @@ MidiRegionView::motion (GdkEventMotion* ev) switch (_mouse_state) { case Pressed: - if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject - && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { - - editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); - _mouse_state = SelectRectDragging; - return true; - - } else if (editor.internal_editing()) { - - editor.drags()->set (new NoteCreateDrag (dynamic_cast (&editor), group, this), (GdkEvent *) ev); - _mouse_state = AddDragging; - - remove_ghost_note (); - - editor.verbose_cursor()->hide (); - - return true; + if (_pressed_button == 1) { + + 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 (); + editor.verbose_cursor()->hide (); + return true; + } else if (m == MouseObject) { + editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); + clear_selection (); + _mouse_state = SelectRectDragging; + return true; + } else if (m == MouseRange) { + editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); + _mouse_state = SelectVerticalDragging; + return true; + } } return false; case SelectRectDragging: + case SelectVerticalDragging: case AddDragging: editor.drags()->motion_handler ((GdkEvent *) ev, false); break; @@ -617,13 +678,18 @@ MidiRegionView::scroll (GdkEventScroll* ev) trackview.editor().verbose_cursor()->hide (); - bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier); + bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); + bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier); if (ev->direction == GDK_SCROLL_UP) { - change_velocities (true, fine, false); + change_velocities (true, fine, false, together); } else if (ev->direction == GDK_SCROLL_DOWN) { - change_velocities (false, fine, false); + change_velocities (false, fine, false, together); + } else { + /* left, right: we don't use them */ + return false; } + return true; } @@ -656,7 +722,11 @@ MidiRegionView::key_press (GdkEventKey* ev) return true; - } else if (ev->keyval == GDK_Delete && unmodified) { + } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) { + + if (_selection.empty()) { + return false; + } delete_selection(); return true; @@ -687,9 +757,10 @@ MidiRegionView::key_press (GdkEventKey* ev) bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier); bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); + bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier); if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) { - change_velocities (true, fine, allow_smush); + change_velocities (true, fine, allow_smush, together); } else { transpose (true, fine, allow_smush); } @@ -699,9 +770,10 @@ MidiRegionView::key_press (GdkEventKey* ev) bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier); bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); + bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier); if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) { - change_velocities (false, fine, allow_smush); + change_velocities (false, fine, allow_smush, together); } else { transpose (false, fine, allow_smush); } @@ -720,6 +792,10 @@ MidiRegionView::key_press (GdkEventKey* ev) } else if (ev->keyval == GDK_c && unmodified) { channel_edit (); return true; + + } else if (ev->keyval == GDK_v && unmodified) { + velocity_edit (); + return true; } return false; @@ -738,20 +814,15 @@ MidiRegionView::key_release (GdkEventKey* ev) void MidiRegionView::channel_edit () { - bool first = true; - uint8_t current_channel = 0; - if (_selection.empty()) { return; } - - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - if (first) { - current_channel = (*i)->note()->channel (); - first = false; - } - } + /* pick a note somewhat at random (since Selection is a set<>) to + * provide the "current" channel for the dialog. + */ + + uint8_t current_channel = (*_selection.begin())->note()->channel (); MidiChannelDialog channel_dialog (current_channel); int ret = channel_dialog.run (); @@ -776,11 +847,47 @@ MidiRegionView::channel_edit () apply_diff (); } +void +MidiRegionView::velocity_edit () +{ + if (_selection.empty()) { + return; + } + + /* pick a note somewhat at random (since Selection is a set<>) to + * provide the "current" velocity for the dialog. + */ + + uint8_t current_velocity = (*_selection.begin())->note()->velocity (); + MidiVelocityDialog velocity_dialog (current_velocity); + int ret = velocity_dialog.run (); + + switch (ret) { + case Gtk::RESPONSE_OK: + break; + default: + return; + } + + uint8_t new_velocity = velocity_dialog.velocity (); + + start_note_diff_command (_("velocity edit")); + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + Selection::iterator next = i; + ++next; + change_note_velocity (*i, new_velocity, false); + i = next; + } + + apply_diff (); +} + void MidiRegionView::show_list_editor () { if (!_list_editor) { - _list_editor = new MidiListEditor (trackview.session(), midi_region()); + _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track()); } _list_editor->present (); } @@ -794,13 +901,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) { @@ -808,13 +916,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; @@ -822,7 +928,7 @@ MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap view->update_note_range(new_note->note()); - MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note"); + MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note")); cmd->add (new_note); _model->apply_command(*trackview.session(), cmd); @@ -830,9 +936,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) { @@ -1094,18 +1200,18 @@ 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) { - if (chn_mask & (1<patch_changes().begin(); i != _model->patch_changes().end(); ++i) { @@ -1113,20 +1219,8 @@ MidiRegionView::display_patch_changes_on_channel (uint8_t channel) continue; } - MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ()); - - boost::shared_ptr patch = - MIDI::Name::MidiPatchManager::instance().find_patch( - _model_name, _custom_device_mode, channel, patch_key); - - if (patch != 0) { - add_canvas_patch_change (*i, patch->name()); - } else { - char buf[16]; - /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */ - snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO); - add_canvas_patch_change (*i, buf); - } + const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel); + add_canvas_patch_change (*i, patch_name, active_channel); } } @@ -1176,7 +1270,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()) { @@ -1196,12 +1289,12 @@ 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().frame_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)); + new CanvasSysEx(*this, *_note_group, text, height, x, 1.0, (*i))); // Show unless message is beyond the region bounds if (time - _region->start() >= _region->length() || time < _region->start()) { @@ -1230,8 +1323,10 @@ MidiRegionView::~MidiRegionView () end_write(); } + _selection_cleared_connection.disconnect (); + _selection.clear(); - clear_events(); + clear_events (false); delete _note_group; delete _note_diff_command; @@ -1256,12 +1351,19 @@ 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) { + (*x)->hide(); + } else { + (*x)->show(); + } + } + move_step_edit_cursor (_step_edit_cursor_position); set_step_edit_cursor_width (_step_edit_cursor_width); } @@ -1365,7 +1467,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv) ghost->set_duration (_region->length() / samples_per_unit); ghosts.push_back (ghost); - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context()); + GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); return ghost; } @@ -1376,9 +1478,11 @@ MidiRegionView::add_ghost (TimeAxisView& tv) void MidiRegionView::begin_write() { - assert(!_active_notes); + if (_active_notes) { + delete[] _active_notes; + } _active_notes = new CanvasNote*[128]; - for (unsigned i=0; i < 128; ++i) { + for (unsigned i = 0; i < 128; ++i) { _active_notes[i] = 0; } } @@ -1450,13 +1554,34 @@ MidiRegionView::play_midi_note(boost::shared_ptr note) return; } - NotePlayer* np = new NotePlayer (route_ui->midi_track()); + NotePlayer* np = new NotePlayer (route_ui->midi_track ()); np->add (note); np->play (); + + /* NotePlayer deletes itself */ +} + +void +MidiRegionView::start_playing_midi_note(boost::shared_ptr note) +{ + if (_no_sound_notes || !Config->get_sound_midi_notes()) { + return; + } + + RouteUI* route_ui = dynamic_cast (&trackview); + + if (!route_ui || !route_ui->midi_track()) { + return; + } + + delete _note_player; + _note_player = new NotePlayer (route_ui->midi_track ()); + _note_player->add (note); + _note_player->on (); } void -MidiRegionView::play_midi_chord (vector > notes) +MidiRegionView::start_playing_midi_chord (vector > notes) { if (_no_sound_notes || !Config->get_sound_midi_notes()) { return; @@ -1468,13 +1593,14 @@ MidiRegionView::play_midi_chord (vector > notes) return; } - NotePlayer* np = new NotePlayer (route_ui->midi_track()); + delete _note_player; + _note_player = new NotePlayer (route_ui->midi_track()); for (vector >::iterator n = notes.begin(); n != notes.end(); ++n) { - np->add (*n); + _note_player->add (*n); } - np->play (); + _note_player->on (); } @@ -1516,8 +1642,7 @@ MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions) ev->property_y2() = y1 + 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()]) { @@ -1534,7 +1659,7 @@ MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions) /* outline all edges */ ev->property_outline_what() = (guint32) 0xF; } - + if (update_ghost_regions) { for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { MidiGhostRegion* gr = dynamic_cast (*i); @@ -1571,9 +1696,6 @@ 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); - //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group(); if (midi_view()->note_mode() == Sustained) { @@ -1615,7 +1737,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) { @@ -1667,56 +1789,81 @@ MidiRegionView::step_sustain (Evoral::MusicalTime beats) change_note_lengths (false, false, beats, false, true); } +/** Add a new patch change flag to the canvas. + * @param patch the patch change to add + * @param the text to display in the flag + * @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) +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); double const height = midi_stream_view()->contents_height(); boost::shared_ptr patch_change = boost::shared_ptr( - new CanvasPatchChange(*this, *_note_group, + new CanvasPatchChange(*this, *group, displaytext, height, x, 1.0, - _model_name, - _custom_device_mode, - patch) - ); - - // Show unless patch change is beyond the region bounds - if (region_frames < 0 || region_frames >= _region->length()) { - patch_change->hide(); + instrument_info(), + patch, + active_channel)); + + if (patch_change->width() < _pixel_width) { + // Show unless patch change is beyond the region bounds + if (region_frames < 0 || region_frames >= _region->length()) { + patch_change->hide(); + } else { + patch_change->show(); + } } else { - patch_change->show(); + patch_change->hide (); } _patch_changes.push_back (patch_change); } -void -MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) +MIDI::Name::PatchPrimaryKey +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) 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.msb = (*i)->bank_msb (); - key.lsb = (*i)->bank_lsb (); + if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) { + key.bank_number = (*i)->bank(); key.program_number = (*i)->program (); } else { - key.msb = key.lsb = key.program_number = 0; + 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) { @@ -1726,7 +1873,7 @@ MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::Pa c->change_program (pc.patch (), new_patch.program_number); } - int const new_bank = (new_patch.msb << 7) | new_patch.lsb; + int const new_bank = new_patch.bank_number; if (pc.patch()->bank() != new_bank) { c->change_bank (pc.patch (), new_bank); } @@ -1815,8 +1962,7 @@ void MidiRegionView::previous_patch (CanvasPatchChange& patch) { if (patch.patch()->program() < 127) { - MIDI::Name::PatchPrimaryKey key; - get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key); + MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); key.program_number++; change_patch_change (patch, key); } @@ -1826,47 +1972,32 @@ void MidiRegionView::next_patch (CanvasPatchChange& patch) { if (patch.patch()->program() > 0) { - MIDI::Name::PatchPrimaryKey key; - get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key); + MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); key.program_number--; change_patch_change (patch, key); } } void -MidiRegionView::previous_bank (CanvasPatchChange& patch) +MidiRegionView::next_bank (CanvasPatchChange& patch) { if (patch.patch()->program() < 127) { - MIDI::Name::PatchPrimaryKey key; - get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key); - if (key.lsb > 0) { - key.lsb--; + MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); + if (key.bank_number > 0) { + key.bank_number--; change_patch_change (patch, key); - } else { - if (key.msb > 0) { - key.lsb = 127; - key.msb--; - change_patch_change (patch, key); - } } } } void -MidiRegionView::next_bank (CanvasPatchChange& patch) +MidiRegionView::previous_bank (CanvasPatchChange& patch) { if (patch.patch()->program() > 0) { - MIDI::Name::PatchPrimaryKey key; - get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key); - if (key.lsb < 127) { - key.lsb++; + MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch()); + if (key.bank_number < 127) { + key.bank_number++; change_patch_change (patch, key); - } else { - if (key.msb < 127) { - key.lsb = 0; - key.msb++; - change_patch_change (patch, key); - } } } } @@ -1914,9 +2045,7 @@ MidiRegionView::delete_note (boost::shared_ptr n) void MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal) { - bool changed = false; - - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { if ((*i) != ev) { Selection::iterator tmp = i; ++tmp; @@ -1924,7 +2053,6 @@ MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool (*i)->set_selected (false); (*i)->hide_velocity (); _selection.erase (i); - changed = true; i = tmp; } else { @@ -1936,7 +2064,7 @@ MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool selection. */ - if (changed && signal) { + if (signal) { SelectionCleared (this); /* EMIT SIGNAL */ } } @@ -2143,10 +2271,26 @@ MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev) void MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend) { - if (x1 > x2) { - swap (x1, x2); + // 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) { + if ((*i)->x1() < x2 && (*i)->x2() > x1 && (*i)->y1() < y2 && (*i)->y2() > y1) { + // Rectangles intersect + if (!(*i)->selected()) { + add_to_selection (*i); + } + } else if ((*i)->selected() && !extend) { + // Rectangles do not intersect + remove_from_selection (*i); + } } +} +void +MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend) +{ if (y1 > y2) { swap (y1, y2); } @@ -2156,30 +2300,12 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2 // 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)->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); } } @@ -2214,7 +2340,7 @@ MidiRegionView::add_to_selection (CanvasNoteEvent* ev) if (_selection.insert (ev).second) { ev->set_selected (true); - play_midi_note ((ev)->note()); + start_playing_midi_note ((ev)->note()); } if (add_mrv_selection) { @@ -2255,13 +2381,13 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) shifted.push_back (moved_note); } - play_midi_chord (shifted); + start_playing_midi_chord (shifted); } else if (!to_play.empty()) { boost::shared_ptr moved_note (new NoteType (*to_play.front())); moved_note->set_note (moved_note->note() + cumulative_dy); - play_midi_note (moved_note); + start_playing_midi_note (moved_note); } } } @@ -2269,11 +2395,9 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) void MidiRegionView::note_dropped(CanvasNoteEvent *, 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 @@ -2747,9 +2871,10 @@ MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime } void -MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush) +MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together) { int8_t delta; + int8_t value = 0; if (_selection.empty()) { return; @@ -2767,8 +2892,8 @@ MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush) 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; } } } @@ -2778,12 +2903,25 @@ MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush) for (Selection::iterator i = _selection.begin(); i != _selection.end();) { Selection::iterator next = i; ++next; - change_note_velocity (*i, delta, true); + + if (all_together) { + if (i == _selection.begin()) { + change_note_velocity (*i, delta, true); + value = (*i)->note()->velocity() + delta; + } else { + change_note_velocity (*i, value, false); + } + + } else { + change_note_velocity (*i, delta, true); + } + i = next; } apply_diff(); + cursor_label: if (!_selection.empty()) { char buf[24]; snprintf (buf, sizeof (buf), "Vel %d", @@ -2850,7 +2988,7 @@ MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTim delta = trackview.editor().get_grid_type_as_beats (success, _region->position()); if (!success) { /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */ - cerr << "Grid type not available as beats - TO BE FIXED\n"; + error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg; return; } } @@ -2890,7 +3028,7 @@ MidiRegionView::nudge_notes (bool forward) framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time()); framepos_t unused; - framepos_t distance; + framecnt_t distance; if (trackview.editor().snap_mode() == Editing::SnapOff) { @@ -2958,7 +3096,7 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev) { Editor* editor = dynamic_cast(&trackview.editor()); - _pre_enter_cursor = editor->get_canvas_cursor (); + pre_enter_cursor = editor->get_canvas_cursor (); if (_mouse_state == SelectTouchDragging) { note_selected (ev, true); @@ -2978,39 +3116,65 @@ MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*) editor->verbose_cursor()->hide (); - if (_pre_enter_cursor) { - editor->set_canvas_cursor (_pre_enter_cursor); - _pre_enter_cursor = 0; + if (pre_enter_cursor) { + editor->set_canvas_cursor (pre_enter_cursor); + pre_enter_cursor = 0; } } void -MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev) +MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p) { ostringstream s; /* XXX should get patch name if we can */ - s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->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(); } void MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *) { trackview.editor().verbose_cursor()->hide (); + /* focus will transfer back via the enter-notify event sent to this + * midi region view. + */ +} + +void +MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p) +{ + ostringstream s; + s << p->text(); + show_verbose_cursor (s.str(), 10, 20); + p->grab_focus(); +} + +void +MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *) +{ + trackview.editor().verbose_cursor()->hide (); + /* focus will transfer back via the enter-notify event sent to this + * midi region view. + */ } void MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor) { Editor* editor = dynamic_cast(&trackview.editor()); + Editing::MouseMode mm = editor->current_mouse_mode(); + bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw); - if (x_fraction > 0.0 && x_fraction < 0.2) { + if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) { editor->set_canvas_cursor (editor->cursors()->left_side_trim); - } else if (x_fraction >= 0.8 && x_fraction < 1.0) { + } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) { editor->set_canvas_cursor (editor->cursors()->right_side_trim); } else { - if (_pre_enter_cursor && can_set_cursor) { - editor->set_canvas_cursor (_pre_enter_cursor); + if (pre_enter_cursor && can_set_cursor) { + editor->set_canvas_cursor (pre_enter_cursor); } } } @@ -3042,34 +3206,28 @@ MidiRegionView::set_frame_color() } void -MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask) +MidiRegionView::midi_channel_mode_changed () { - switch (mode) { - case AllChannels: - case FilterChannels: - _force_channel = -1; - break; - case ForceChannel: - _force_channel = mask; + 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 (); } void -MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode) +MidiRegionView::instrument_settings_changed () { - _model_name = model; - _custom_device_mode = custom_device_mode; redisplay_model(); } @@ -3150,7 +3308,7 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb) Evoral::MusicalTime end_point = 0; duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time(); - paste_pos_beats = region_frames_to_region_beats (pos - _region->position()); + paste_pos_beats = absolute_frames_to_source_beats (pos); beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats; paste_pos_beats = 0; @@ -3188,7 +3346,7 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb) DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame)); _region->clear_changes (); - _region->set_length (end_frame); + _region->set_length (end_frame - _region->position()); trackview.session()->add_command (new StatefulDiffCommand (_region)); } @@ -3228,7 +3386,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()) { @@ -3265,7 +3423,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()) { @@ -3391,7 +3549,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); @@ -3514,7 +3672,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 @@ -3524,10 +3689,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); @@ -3575,26 +3738,63 @@ MidiRegionView::trim_front_ending () void MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc) { - PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), 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 (CanvasSysEx* sysex) +{ + 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 @@ -3661,3 +3861,25 @@ MidiRegionView::selection_cleared (MidiRegionView* rv) /* Clear our selection in sympathy; but don't signal the fact */ clear_selection (false); } + +void +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(); +} +