X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=b00a4a5550eeef3db019abe921a57da4fa736904;hb=3705a2d6307cf443acbf8419b0e0f560591f2016;hp=d1b5f749900d158123f43b9d728736362eec87ee;hpb=38eb5f4539786e0edafc31e275ed598bd4e4164b;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index d1b5f74990..b00a4a5550 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -33,6 +33,10 @@ #include #include #include +#include + +#include +#include #include "streamview.h" #include "midi_region_view.h" @@ -62,26 +66,29 @@ using namespace ArdourCanvas; MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color) : RegionView (parent, tv, r, spu, basic_color) - , force_channel(-1) - , last_channel_selection(0xFFFF) + , _force_channel(-1) + , _last_channel_selection(0xFFFF) , _default_note_length(0.0) + , _current_range_min(0) + , _current_range_max(0) + , _model_name(string()) + , _custom_device_mode(string()) , _active_notes(0) , _note_group(new ArdourCanvas::Group(*parent)) , _delta_command(NULL) , _mouse_state(None) , _pressed_button(0) { - group->lower_to_bottom(); _note_group->raise_to_top(); - - frame->property_fill_color_rgba() = 0xff000033; } 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, visibility) - , force_channel(-1) - , last_channel_selection(0xFFFF) + : RegionView (parent, tv, r, spu, basic_color, false, visibility) + , _force_channel(-1) + , _last_channel_selection(0xFFFF) , _default_note_length(0.0) + , _model_name(string()) + , _custom_device_mode(string()) , _active_notes(0) , _note_group(new ArdourCanvas::Group(*parent)) , _delta_command(NULL) @@ -92,11 +99,57 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); } + +MidiRegionView::MidiRegionView (const MidiRegionView& other) + : RegionView (other) + , _force_channel(-1) + , _last_channel_selection(0xFFFF) + , _default_note_length(0.0) + , _model_name(string()) + , _custom_device_mode(string()) + , _active_notes(0) + , _note_group(new ArdourCanvas::Group(*get_canvas_group())) + , _delta_command(NULL) + , _mouse_state(None) + , _pressed_button(0) +{ + Gdk::Color c; + int r,g,b,a; + + UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a); + c.set_rgb_p (r/255.0, g/255.0, b/255.0); + + init (c, false); +} + +MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr other_region) + : RegionView (other, boost::shared_ptr (other_region)) + , _force_channel(-1) + , _last_channel_selection(0xFFFF) + , _default_note_length(0.0) + , _model_name(string()) + , _custom_device_mode(string()) + , _active_notes(0) + , _note_group(new ArdourCanvas::Group(*get_canvas_group())) + , _delta_command(NULL) + , _mouse_state(None) + , _pressed_button(0) +{ + Gdk::Color c; + int r,g,b,a; + + UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a); + c.set_rgb_p (r/255.0, g/255.0, b/255.0); + + init (c, true); +} + void MidiRegionView::init (Gdk::Color& basic_color, bool wfd) { - if (wfd) + if (wfd) { midi_region()->midi_source(0)->load_model(); + } const Meter& m = trackview.session().tempo_map().meter_at(_region->position()); const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position()); @@ -106,24 +159,23 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) _model = midi_region()->midi_source(0)->model(); _enable_display = false; - RegionView::init(basic_color, false); + RegionView::init (basic_color, false); compute_colors (basic_color); - reset_width_dependent_items ((double) _region->length() / samples_per_unit); - - set_y_position_and_height (0, trackview.height); + set_height (trackview.current_height()); region_muted (); + region_sync_changed (); region_resized (BoundsChanged); region_locked (); - - _region->StateChanged.connect (mem_fun(*this, &MidiRegionView::region_changed)); + + reset_width_dependent_items (_pixel_width); + //reset_width_dependent_items ((double) _region->length() / samples_per_unit); set_colors (); _enable_display = true; - if (_model) { if (wfd) { redisplay_model(); @@ -131,14 +183,14 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) _model->ContentsChanged.connect(sigc::mem_fun(this, &MidiRegionView::redisplay_model)); } - midi_region()->midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegionView::switch_source)); - + group->raise_to_top(); group->signal_event().connect (mem_fun (this, &MidiRegionView::canvas_event), false); - midi_view()->signal_force_channel_changed().connect( - mem_fun(this, &MidiRegionView::midi_force_channel_changed)); - midi_view()->signal_channel_selection_changed().connect( - mem_fun(this, &MidiRegionView::midi_channel_selection_changed)); + midi_view()->signal_channel_mode_changed().connect( + mem_fun(this, &MidiRegionView::midi_channel_mode_changed)); + + midi_view()->signal_midi_patch_settings_changed().connect( + mem_fun(this, &MidiRegionView::midi_patch_settings_changed)); } bool @@ -150,11 +202,11 @@ MidiRegionView::canvas_event(GdkEvent* ev) static double drag_start_x, drag_start_y; static double last_x, last_y; double event_x, event_y; - nframes_t event_frame = 0; + nframes64_t event_frame = 0; static ArdourCanvas::SimpleRect* drag_rect = NULL; - if (trackview.editor.current_mouse_mode() != MouseNote) + if (trackview.editor().current_mouse_mode() != MouseNote) return false; // Mmmm, spaghetti @@ -163,8 +215,8 @@ MidiRegionView::canvas_event(GdkEvent* ev) case GDK_KEY_PRESS: if (ev->key.keyval == GDK_Delete && !delete_mod) { delete_mod = true; - original_mode = trackview.editor.current_midi_edit_mode(); - trackview.editor.set_midi_edit_mode(MidiEditErase); + original_mode = trackview.editor().current_midi_edit_mode(); + trackview.editor().set_midi_edit_mode(MidiEditErase); start_delta_command(_("erase notes")); _mouse_state = EraseTouchDragging; return true; @@ -184,7 +236,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) apply_command(); } if (delete_mod) { - trackview.editor.set_midi_edit_mode(original_mode); + trackview.editor().set_midi_edit_mode(original_mode); _mouse_state = None; delete_mod = false; } @@ -198,13 +250,16 @@ MidiRegionView::canvas_event(GdkEvent* ev) case GDK_BUTTON_PRESS: if (_mouse_state != SelectTouchDragging && _mouse_state != EraseTouchDragging && - ev->button.button == 1 ) { + ev->button.button == 1) { _pressed_button = ev->button.button; _mouse_state = Pressed; return true; } _pressed_button = ev->button.button; - return false; + return true; + + case GDK_2BUTTON_PRESS: + return true; case GDK_ENTER_NOTIFY: /* FIXME: do this on switch to note tool, too, if the pointer is already in */ @@ -218,8 +273,8 @@ MidiRegionView::canvas_event(GdkEvent* ev) group->w2i(event_x, event_y); // convert event_x to global frame - event_frame = trackview.editor.pixel_to_frame(event_x) + _region->position(); - trackview.editor.snap_to(event_frame); + event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position(); + trackview.editor().snap_to(event_frame); // convert event_frame back to local coordinates relative to position event_frame -= _region->position(); @@ -227,7 +282,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) case Pressed: // Drag start // Select drag start - if (_pressed_button == 1 && trackview.editor.current_midi_edit_mode() == MidiEditSelect) { + if (_pressed_button == 1 && trackview.editor().current_midi_edit_mode() == MidiEditSelect) { group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, Gdk::Cursor(Gdk::FLEUR), ev->motion.time); last_x = event_x; @@ -250,7 +305,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) return true; // Add note drag start - } else if (trackview.editor.current_midi_edit_mode() == MidiEditPencil) { + } else if (trackview.editor().current_midi_edit_mode() == MidiEditPencil) { group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, Gdk::Cursor(Gdk::FLEUR), ev->motion.time); last_x = event_x; @@ -259,7 +314,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) drag_start_y = event_y; drag_rect = new ArdourCanvas::SimpleRect(*group); - drag_rect->property_x1() = trackview.editor.frame_to_pixel(event_frame); + drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame); drag_rect->property_y1() = midi_stream_view()->note_to_y(midi_stream_view()->y_to_note(event_y)); drag_rect->property_x2() = event_x; @@ -287,13 +342,14 @@ MidiRegionView::canvas_event(GdkEvent* ev) } if (_mouse_state == AddDragging) - event_x = trackview.editor.frame_to_pixel(event_frame); + event_x = trackview.editor().frame_to_pixel(event_frame); - if (drag_rect) + if (drag_rect) { if (event_x > drag_start_x) drag_rect->property_x2() = event_x; else drag_rect->property_x1() = event_x; + } if (drag_rect && _mouse_state == SelectRectDragging) { if (event_y > drag_start_y) @@ -321,36 +377,35 @@ MidiRegionView::canvas_event(GdkEvent* ev) event_y = ev->motion.y; group->w2i(event_x, event_y); group->ungrab(ev->button.time); - event_frame = trackview.editor.pixel_to_frame(event_x); + event_frame = trackview.editor().pixel_to_frame(event_x); - if(_pressed_button != 1) { + if (_pressed_button != 1) { return false; } switch (_mouse_state) { case Pressed: // Clicked - switch (trackview.editor.current_midi_edit_mode()) { + switch (trackview.editor().current_midi_edit_mode()) { case MidiEditSelect: case MidiEditResize: clear_selection(); break; case MidiEditPencil: create_note_at(event_x, event_y, _default_note_length); - default: - break; + default: break; } _mouse_state = None; - return true; + break; case SelectRectDragging: // Select drag done _mouse_state = None; delete drag_rect; drag_rect = NULL; - return true; + break; case AddDragging: // Add drag done _mouse_state = None; if (drag_rect->property_x2() > drag_rect->property_x1() + 2) { const double x = drag_rect->property_x1(); - const double length = trackview.editor.pixel_to_frame( + const double length = trackview.editor().pixel_to_frame( drag_rect->property_x2() - drag_rect->property_x1()); create_note_at(x, drag_rect->property_y1(), length); @@ -358,13 +413,10 @@ MidiRegionView::canvas_event(GdkEvent* ev) delete drag_rect; drag_rect = NULL; - return true; - default: - break; + default: break; } - default: - break; + default: break; } return false; @@ -373,7 +425,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) /** Add a note to the model, and the view, at a canvas (click) coordinate */ void -MidiRegionView::create_note_at(double x, double y, double duration) +MidiRegionView::create_note_at(double x, double y, double length) { MidiTimeAxisView* const mtv = dynamic_cast(&trackview); MidiStreamView* const view = mtv->midi_view(); @@ -383,35 +435,34 @@ MidiRegionView::create_note_at(double x, double y, double duration) assert(note >= 0.0); assert(note <= 127.0); - nframes_t new_note_time = trackview.editor.pixel_to_frame (x); + nframes64_t new_note_time = trackview.editor().pixel_to_frame (x); assert(new_note_time >= 0); new_note_time += _region->start(); /* const Meter& m = trackview.session().tempo_map().meter_at(new_note_time); const Tempo& t = trackview.session().tempo_map().tempo_at(new_note_time); - double duration = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); + double length = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); */ - // we need to snap here again in nframes_t in order to be sample accurate + // we need to snap here again in nframes64_t in order to be sample accurate // since note time is region-absolute but snap_to_frame expects position-relative // time we have to coordinate transform back and forth here. - nframes_t new_note_time_position_relative = new_note_time - _region->start(); + nframes64_t new_note_time_position_relative = new_note_time - _region->start(); new_note_time = snap_to_frame(new_note_time_position_relative) + _region->start(); - // we need to snap the duration too to be sample accurate - nframes_t new_note_duration = nframes_t(duration); - new_note_duration = snap_to_frame(new_note_time_position_relative + new_note_duration) + _region->start() + // we need to snap the length too to be sample accurate + nframes64_t new_note_length = nframes_t(length); + new_note_length = snap_to_frame(new_note_time_position_relative + new_note_length) + _region->start() - new_note_time; - const boost::shared_ptr new_note(new Note(0, new_note_time, new_note_duration, (uint8_t)note, 0x40)); - view->update_bounds(new_note->note()); + const boost::shared_ptr new_note(new Evoral::Note( + 0, new_note_time, new_note_length, (uint8_t)note, 0x40)); + view->update_note_range(new_note->note()); MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note"); cmd->add(new_note); - _model->apply_command(cmd); - - //add_note(new_note); + _model->apply_command(trackview.session(), cmd); } @@ -427,7 +478,7 @@ MidiRegionView::clear_events() } } - for (std::vector::iterator i = _events.begin(); i != _events.end(); ++i) + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) delete *i; _events.clear(); @@ -443,6 +494,60 @@ MidiRegionView::display_model(boost::shared_ptr model) if (_enable_display) redisplay_model(); } + + +void +MidiRegionView::start_delta_command(string name) +{ + if (!_delta_command) + _delta_command = _model->new_delta_command(name); +} + +void +MidiRegionView::command_add_note(const boost::shared_ptr note, bool selected) +{ + if (_delta_command) + _delta_command->add(note); + + if (selected) + _marked_for_selection.insert(note); +} + +void +MidiRegionView::command_remove_note(ArdourCanvas::CanvasNoteEvent* ev) +{ + if (_delta_command && ev->note()) { + _delta_command->remove(ev->note()); + } +} + +void +MidiRegionView::apply_command() +{ + if (!_delta_command) { + return; + } + + // Mark all selected notes for selection when model reloads + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + _marked_for_selection.insert((*i)->note()); + } + + _model->apply_command(trackview.session(), _delta_command); + _delta_command = NULL; + midi_view()->midi_track()->diskstream()->playlist_modified(); + + _marked_for_selection.clear(); +} + + +void +MidiRegionView::abort_command() +{ + delete _delta_command; + _delta_command = NULL; + clear_selection(); +} void @@ -455,33 +560,29 @@ MidiRegionView::redisplay_model() if (_model) { clear_events(); - begin_write(); - _model->read_lock(); - - /* + + MidiModel::Notes notes = _model->notes(); - cerr << endl << "Model contains " << notes.size() << " Notes:" << endl; - for(MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) { - Note note = *(*i).get(); - cerr << "MODEL: Note time: " << note.time() << " duration: " << note.duration() - << " end-time: " << note.end_time() - << " velocity: " << int(note.velocity()) - //<< " Note-on: " << note.on_event(). - //<< " Note-off: " << note.off_event() + /* + cerr << endl << _model->midi_source()->name() << " : redisplaying " << notes.size() << " notes:" << endl; + for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) { + cerr << "NOTE time: " << (*i)->time() + << " pitch: " << int((*i)->note()) + << " length: " << (*i)->length() + << " end-time: " << (*i)->end_time() + << " velocity: " << int((*i)->velocity()) << endl; - }*/ + } + */ - for (size_t i=0; i < _model->n_notes(); ++i) { + for (size_t i = 0; i < _model->n_notes(); ++i) { add_note(_model->note_at(i)); } + + find_and_insert_program_change_flags(); - MidiModel::PgmChanges& pgm_changes = _model->pgm_changes(); - for_each(pgm_changes.begin(), pgm_changes.end(), - sigc::mem_fun(this, &MidiRegionView::add_pgm_change)); - - end_write(); - + // Is this necessary? /*for (Automatable::Controls::const_iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) { @@ -510,7 +611,6 @@ MidiRegionView::redisplay_model() _automation_children.insert(std::make_pair(i->second->parameter(), arv)); }*/ - _model->read_unlock(); } else { @@ -518,6 +618,69 @@ MidiRegionView::redisplay_model() } } +void +MidiRegionView::find_and_insert_program_change_flags() +{ + // Draw program change 'flags' + for (Automatable::Controls::iterator control = _model->controls().begin(); + control != _model->controls().end(); ++control) { + if (control->first.type() == MidiPgmChangeAutomation) { + Glib::Mutex::Lock list_lock (control->second->list()->lock()); + + uint8_t channel = control->first.channel(); + + for (AutomationList::const_iterator event = control->second->list()->begin(); + event != control->second->list()->end(); ++event) { + double event_time = (*event)->when; + double program_number = floor((*event)->value + 0.5); + + //cerr << " got program change on channel " << int(channel) << " time: " << event_time << " number: " << program_number << endl; + + // find bank select msb and lsb for the program change + Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK); + boost::shared_ptr msb_control = _model->control(bank_select_msb); + uint8_t msb = 0; + if (msb_control != 0) { + msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5)); + } + + Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK); + boost::shared_ptr lsb_control = _model->control(bank_select_lsb); + uint8_t lsb = 0; + if (lsb_control != 0) { + lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5)); + } + + //cerr << " got msb " << int(msb) << " and lsb " << int(lsb) << " thread_id: " << pthread_self() << endl; + + MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number); + + boost::shared_ptr patch = + MIDI::Name::MidiPatchManager::instance().find_patch( + _model_name, + _custom_device_mode, + channel, + patch_key + ); + + ControlEvent program_change(nframes_t(event_time), uint8_t(program_number), channel); + + if (patch != 0) { + //cerr << " got patch with name " << patch->name() << " number " << patch->number() << endl; + add_pgm_change(program_change, patch->name()); + } else { + char buf[4]; + snprintf(buf, 4, "%d", int(program_number)); + add_pgm_change(program_change, buf); + } + } + break; + } else if (control->first.type() == MidiCCAutomation) { + //cerr << " found CC Automation of channel " << int(control->first.channel()) << " and id " << control->first.id() << endl; + } + } +} + MidiRegionView::~MidiRegionView () { @@ -525,8 +688,9 @@ MidiRegionView::~MidiRegionView () RegionViewGoingAway (this); /* EMIT_SIGNAL */ - if (_active_notes) + if (_active_notes) { end_write(); + } _selection.clear(); clear_events(); @@ -557,22 +721,43 @@ MidiRegionView::reset_width_dependent_items (double pixel_width) } void -MidiRegionView::set_y_position_and_height (double y, double h) +MidiRegionView::set_height (gdouble height) { - RegionView::set_y_position_and_height(y, h - 1); + static const double FUDGE = 2; + const double old_height = _height; + RegionView::set_height(height); + _height = height - FUDGE; + + apply_note_range(midi_stream_view()->lowest_note(), + midi_stream_view()->highest_note(), + height != old_height + FUDGE); + + if (name_text) { + name_text->raise_to_top(); + } +} - if (_enable_display) { - _model->read_lock(); +/** Apply the current note range from the stream view + * by repositioning/hiding notes as necessary + */ +void +MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force) +{ + if (_enable_display) { + if (!force && _current_range_min == min && _current_range_max == max) { + return; + } + + _current_range_min = min; + _current_range_max = max; - for (std::vector::const_iterator i = _events.begin(); i != _events.end(); ++i) { + for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) { CanvasNoteEvent* event = *i; Item* item = dynamic_cast(event); assert(item); if (event && event->note()) { - if (event->note()->note() < midi_stream_view()->lowest_note() || - event->note()->note() > midi_stream_view()->highest_note()) { - + if (event->note()->note() < _current_range_min || event->note()->note() > _current_range_max) { if (canvas_item_visible(item)) { item->hide(); } @@ -582,15 +767,14 @@ MidiRegionView::set_y_position_and_height (double y, double h) } event->hide_velocity(); - if(CanvasNote* note = dynamic_cast(event)) { + if (CanvasNote* note = dynamic_cast(event)) { const double y1 = midi_stream_view()->note_to_y(event->note()->note()); const double y2 = y1 + floor(midi_stream_view()->note_height()); note->property_y1() = y1; note->property_y2() = y2; - } - if(CanvasHit* hit = dynamic_cast(event)) { - double x = trackview.editor.frame_to_pixel((nframes_t) + } else if (CanvasHit* hit = dynamic_cast(event)) { + double x = trackview.editor().frame_to_pixel((nframes64_t) event->note()->time() - _region->start()); const double diamond_size = midi_stream_view()->note_height() / 2.0; double y = midi_stream_view()->note_to_y(event->note()->note()) @@ -600,18 +784,13 @@ MidiRegionView::set_y_position_and_height (double y, double h) hit->move(x-hit->x1(), y-hit->y1()); hit->show(); } - if(event->selected()) { + if (event->selected()) { event->show_velocity(); } } } } - _model->read_unlock(); - } - - if (name_text) { - name_text->raise_to_top(); } } @@ -641,7 +820,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv) ghost->set_duration (_region->length() / samples_per_unit); ghosts.push_back (ghost); - for (std::vector::iterator i = _events.begin(); i != _events.end(); ++i) { + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { if ((note = dynamic_cast(*i)) != 0) { ghost->add_note(note); } @@ -660,8 +839,9 @@ MidiRegionView::begin_write() { assert(!_active_notes); _active_notes = new CanvasNote*[128]; - for (unsigned i=0; i < 128; ++i) + for (unsigned i=0; i < 128; ++i) { _active_notes[i] = NULL; + } } @@ -685,7 +865,7 @@ MidiRegionView::resolve_note(uint8_t note, double end_time) return; if (_active_notes && _active_notes[note]) { - _active_notes[note]->property_x2() = trackview.editor.frame_to_pixel((nframes_t)end_time); + _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel((nframes64_t)end_time); _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges _active_notes[note] = NULL; } @@ -697,63 +877,105 @@ MidiRegionView::resolve_note(uint8_t note, double end_time) void MidiRegionView::extend_active_notes() { - if (!_active_notes) + if (!_active_notes) { return; + } - for (unsigned i=0; i < 128; ++i) - if (_active_notes[i]) - _active_notes[i]->property_x2() = trackview.editor.frame_to_pixel(_region->length()); + for (unsigned i=0; i < 128; ++i) { + if (_active_notes[i]) { + _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length()); + } + } } +void +MidiRegionView::play_midi_note(boost::shared_ptr note) +{ + if (!trackview.editor().sound_notes()) { + return; + } + + RouteUI* route_ui = dynamic_cast (&trackview); + assert(route_ui); + + route_ui->midi_track()->write_immediate_event(note->on_event().size(), note->on_event().buffer()); + + nframes_t note_length_ms = (note->off_event().time() - note->on_event().time()) + * (1000 / (double)route_ui->session().nominal_frame_rate()); + Glib::signal_timeout().connect(bind(mem_fun(this, &MidiRegionView::play_midi_note_off), note), + note_length_ms, G_PRIORITY_DEFAULT); +} -/** Add a MIDI note to the view (with duration). +bool +MidiRegionView::play_midi_note_off(boost::shared_ptr note) +{ + RouteUI* route_ui = dynamic_cast (&trackview); + assert(route_ui); + + route_ui->midi_track()->write_immediate_event(note->off_event().size(), note->off_event().buffer()); + + return false; +} + + +/** Add a MIDI note to the view (with length). * - * If in sustained mode, notes with duration 0 will be considered active + * If in sustained mode, notes with length 0 will be considered active * notes, and resolve_note should be called when the corresponding note off * event arrives, to properly display the note. */ void -MidiRegionView::add_note(const boost::shared_ptr note) +MidiRegionView::add_note(const boost::shared_ptr note) { assert(note->time() >= 0); assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive); // dont display notes beyond the region bounds - if( - note->time() - _region->start() >= _region->length() || - note->time() < _region->start() || - note->note() < midi_stream_view()->lowest_note() || - note->note() > midi_stream_view()->highest_note() - ) { + if ( note->time() - _region->start() >= _region->length() || + note->time() < _region->start() || + note->note() < midi_stream_view()->lowest_note() || + note->note() > midi_stream_view()->highest_note() ) { return; } ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); - CanvasNoteEvent *event = 0; + CanvasNoteEvent* event = 0; - const double x = trackview.editor.frame_to_pixel((nframes_t)note->time() - _region->start()); + const double x = trackview.editor().frame_to_pixel((nframes64_t)note->time() - _region->start()); if (midi_view()->note_mode() == Sustained) { - //cerr << "MRV::add_note sustained " << note->note() << " @ " << note->time() - // << " .. " << note->end_time() << endl; - const double y1 = midi_stream_view()->note_to_y(note->note()); const double note_endpixel = - trackview.editor.frame_to_pixel((nframes_t)note->end_time() - _region->start()); + trackview.editor().frame_to_pixel((nframes64_t)note->end_time() - _region->start()); CanvasNote* ev_rect = new CanvasNote(*this, *group, note); ev_rect->property_x1() = x; ev_rect->property_y1() = y1; - if (note->duration() > 0) + if (note->length() > 0) ev_rect->property_x2() = note_endpixel; else - ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length()); + ev_rect->property_x2() = trackview.editor().frame_to_pixel(_region->length()); ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height()); - if (note->duration() == 0) { - _active_notes[note->note()] = ev_rect; + if (note->length() == 0) { + + if (_active_notes) { + assert(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()]; + boost::shared_ptr old_note = old_rect->note(); + cerr << "MidiModel: WARNING: Note has length 0: chan " << old_note->channel() + << "note " << (int)old_note->note() << " @ " << old_note->time() << endl; + /* FIXME: How large to make it? Make it a diamond? */ + old_rect->property_x2() = old_rect->property_x1() + 2.0; + old_rect->property_outline_what() = (guint32) 0xF; + } + _active_notes[note->note()] = ev_rect; + } /* outline all but right edge */ ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); } else { @@ -790,34 +1012,152 @@ MidiRegionView::add_note(const boost::shared_ptr note) event = 0; } - if(event) { - if(_marked_for_selection.find(event->note()) != _marked_for_selection.end()) { - note_selected(event, true); + if (event) { + if (_marked_for_selection.find(note) != _marked_for_selection.end()) { + note_selected(event, true); } - event->on_channel_selection_change(last_channel_selection); + event->on_channel_selection_change(_last_channel_selection); } } void -MidiRegionView::add_pgm_change(boost::shared_ptr event) +MidiRegionView::add_pgm_change(ControlEvent& program, string displaytext) { - assert(event->time() >= 0); + assert(program.time >= 0); - // dont display notes beyond the region bounds - if( - event->time() - _region->start() >= _region->length() || - event->time() < _region->start() - ) { + // dont display program changes beyond the region bounds + if (program.time - _region->start() >= _region->length() || program.time < _region->start()) return; - } ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); - const double x = trackview.editor.frame_to_pixel((nframes_t)event->time() - _region->start()); + const double x = trackview.editor().frame_to_pixel((nframes64_t)program.time - _region->start()); double height = midi_stream_view()->contents_height(); - _pgm_changes.push_back( - boost::shared_ptr( - new CanvasProgramChange(*this, *group, event, height, x, 1.0))); + + boost::shared_ptr pgm_change = boost::shared_ptr( + new CanvasProgramChange( + *this, + *group, + displaytext, + height, + x, + 1.0, + _model_name, + _custom_device_mode, + program.time, + program.channel, + program.value)); + + _pgm_changes.push_back(pgm_change); +} + +void +MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) +{ + + cerr << "getting patch key at " << time << " for channel " << channel << endl; + Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK); + boost::shared_ptr msb_control = _model->control(bank_select_msb); + float msb = -1.0; + if (msb_control != 0) { + msb = int(msb_control->get_float(true, time)); + cerr << "got msb " << msb; + } + + Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK); + boost::shared_ptr lsb_control = _model->control(bank_select_lsb); + float lsb = -1.0; + if (lsb_control != 0) { + lsb = lsb_control->get_float(true, time); + cerr << " got lsb " << lsb; + } + + Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0); + boost::shared_ptr program_control = _model->control(program_change); + float program_number = -1.0; + if (program_control != 0) { + program_number = program_control->get_float(true, time); + cerr << " got program " << program_number << endl; + } + + key.msb = (int) floor(msb + 0.5); + key.lsb = (int) floor(lsb + 0.5); + key.program_number = (int) floor(program_number + 0.5); + assert(key.is_sane()); +} + + +void +MidiRegionView::alter_program_change(ControlEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch) +{ + + // TODO: Get the real event here and alter them at the original times + Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK); + boost::shared_ptr msb_control = _model->control(bank_select_msb); + if (msb_control != 0) { + msb_control->set_float(float(new_patch.msb), true, old_program.time); + } + + // TODO: Get the real event here and alter them at the original times + Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK); + boost::shared_ptr lsb_control = _model->control(bank_select_lsb); + if (lsb_control != 0) { + lsb_control->set_float(float(new_patch.lsb), true, old_program.time); + } + + Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0); + boost::shared_ptr program_control = _model->control(program_change); + + assert(program_control != 0); + program_control->set_float(float(new_patch.program_number), true, old_program.time); + + redisplay_model(); +} + +void +MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch) +{ + ControlEvent program_change_event(program.event_time(), program.program(), program.channel()); + alter_program_change(program_change_event, new_patch); +} + +void +MidiRegionView::previous_program(CanvasProgramChange& program) +{ + MIDI::Name::PatchPrimaryKey key; + get_patch_key_at(program.event_time(), program.channel(), key); + + boost::shared_ptr patch = + MIDI::Name::MidiPatchManager::instance().previous_patch( + _model_name, + _custom_device_mode, + program.channel(), + key + ); + + ControlEvent program_change_event(program.event_time(), program.program(), program.channel()); + if (patch) { + alter_program_change(program_change_event, patch->patch_primary_key()); + } +} + +void +MidiRegionView::next_program(CanvasProgramChange& program) +{ + MIDI::Name::PatchPrimaryKey key; + get_patch_key_at(program.event_time(), program.channel(), key); + + boost::shared_ptr patch = + MIDI::Name::MidiPatchManager::instance().next_patch( + _model_name, + _custom_device_mode, + program.channel(), + key + ); + ControlEvent program_change_event(program.event_time(), program.program(), program.channel()); + if (patch) { + alter_program_change(program_change_event, patch->patch_primary_key()); + } } void @@ -825,9 +1165,11 @@ MidiRegionView::delete_selection() { assert(_delta_command); - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) - if ((*i)->selected()) + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + if ((*i)->selected()) { _delta_command->remove((*i)->note()); + } + } _selection.clear(); } @@ -835,9 +1177,11 @@ MidiRegionView::delete_selection() void MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev) { - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) - if ((*i)->selected() && (*i) != ev) + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + if ((*i)->selected() && (*i) != ev) { (*i)->selected(false); + } + } _selection.clear(); } @@ -845,40 +1189,49 @@ MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev) void MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev) { - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) - if ((*i) != ev) + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + if ((*i) != ev) { (*i)->selected(false); + } + } _selection.clear(); _selection.insert(ev); - if ( ! ev->selected()) + if ( ! ev->selected()) { ev->selected(true); + } } void MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add) { - if ( ! add) + if ( ! add) { clear_selection_except(ev); + } - _selection.insert(ev); + if (_selection.insert(ev).second) { + play_midi_note(ev->note()); + } - if ( ! ev->selected()) + if ( ! ev->selected()) { ev->selected(true); + } } void MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev, bool add) { - if ( ! add) + if ( ! add) { clear_selection_except(ev); + } _selection.erase(ev); - if (ev->selected()) + if (ev->selected()) { ev->selected(false); + } } @@ -888,24 +1241,50 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2 const double last_y = std::min(y1, y2); const double y = std::max(y1, y2); - // FIXME: so, so, so much slower than this should be + // 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. + +#ifndef NDEBUG + double last_x1 = 0.0; +#endif if (x1 < x2) { - for (std::vector::iterator i = _events.begin(); i != _events.end(); ++i) { + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { +#ifndef NDEBUG + // Events should always be sorted by increasing x1() here + assert((*i)->x1() >= last_x1); + last_x1 = (*i)->x1(); +#endif + // Inside rectangle if ((*i)->x1() >= x1 && (*i)->x1() <= x2 && (*i)->y1() >= last_y && (*i)->y1() <= y) { - (*i)->selected(true); - _selection.insert(*i); - } else { + if (!(*i)->selected()) { + (*i)->selected(true); + _selection.insert(*i); + play_midi_note((*i)->note()); + } + // Not inside rectangle + } else if ((*i)->selected()) { (*i)->selected(false); _selection.erase(*i); } } } else { - for (std::vector::iterator i = _events.begin(); i != _events.end(); ++i) { + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { +#ifndef NDEBUG + // Events should always be sorted by increasing x1() here + assert((*i)->x1() >= last_x1); + last_x1 = (*i)->x1(); +#endif + // Inside rectangle if ((*i)->x2() <= x1 && (*i)->x2() >= x2 && (*i)->y1() >= last_y && (*i)->y1() <= y) { - (*i)->selected(true); - _selection.insert(*i); - } else { + if (!(*i)->selected()) { + (*i)->selected(true); + _selection.insert(*i); + play_midi_note((*i)->note()); + } + // Not inside rectangle + } else if ((*i)->selected()) { (*i)->selected(false); _selection.erase(*i); } @@ -932,15 +1311,10 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) uint8_t highest_note_difference = 0; // find highest and lowest notes first - for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ) { - Selection::iterator next = i; - ++next; - + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { uint8_t pitch = (*i)->note()->note(); lowest_note_in_selection = std::min(lowest_note_in_selection, pitch); highest_note_in_selection = std::max(highest_note_in_selection, pitch); - - i = next; } /* @@ -964,20 +1338,20 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) Selection::iterator next = i; ++next; - const boost::shared_ptr copy(new Note(*(*i)->note().get())); + const boost::shared_ptr copy(new Evoral::Note(*(*i)->note().get())); - // we need to snap here again in nframes_t in order to be sample accurate + // we need to snap here again in nframes64_t in order to be sample accurate double new_note_time = (*i)->note()->time(); new_note_time += dt; // keep notes inside region if dragged beyond left region bound - if(new_note_time < _region->start()) { + if (new_note_time < _region->start()) { new_note_time = _region->start(); } // since note time is region-absolute but snap_to_frame expects position-relative // time we have to coordinate transform back and forth here. - new_note_time = snap_to_frame(nframes_t(new_note_time) - _region->start()) + _region->start(); + new_note_time = snap_to_frame(nframes64_t(new_note_time) - _region->start()) + _region->start(); copy->set_time(new_note_time); @@ -988,7 +1362,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) clamp_0_to_127(new_pitch); //notes which are dragged beyond the standard midi range snap back to their original place - if((original_pitch != 0 && new_pitch == 0) || (original_pitch != 127 && new_pitch == 127)) { + if ((original_pitch != 0 && new_pitch == 0) || (original_pitch != 127 && new_pitch == 127)) { new_pitch = original_pitch; } @@ -998,42 +1372,41 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) copy->set_note(new_pitch); command_remove_note(*i); - command_add_note(copy); + command_add_note(copy, (*i)->selected()); - _marked_for_selection.insert(copy); i = next; } + apply_command(); // care about notes being moved beyond the upper/lower bounds on the canvas - if(lowest_note_in_selection < midi_stream_view()->lowest_note() || - highest_note_in_selection > midi_stream_view()->highest_note() - ) { + if (lowest_note_in_selection < midi_stream_view()->lowest_note() || + highest_note_in_selection > midi_stream_view()->highest_note()) { midi_stream_view()->set_note_range(MidiStreamView::ContentsRange); + } } } -} -nframes_t +nframes64_t MidiRegionView::snap_to_frame(double x) { - PublicEditor &editor = trackview.editor; + PublicEditor &editor = trackview.editor(); // x is region relative // convert x to global frame - nframes_t frame = editor.pixel_to_frame(x) + _region->position(); + nframes64_t frame = editor.pixel_to_frame(x) + _region->position(); editor.snap_to(frame); // convert event_frame back to local coordinates relative to position frame -= _region->position(); return frame; } -nframes_t -MidiRegionView::snap_to_frame(nframes_t x) +nframes64_t +MidiRegionView::snap_to_frame(nframes64_t x) { - PublicEditor &editor = trackview.editor; + PublicEditor &editor = trackview.editor(); // x is region relative // convert x to global frame - nframes_t frame = x + _region->position(); + nframes64_t frame = x + _region->position(); editor.snap_to(frame); // convert event_frame back to local coordinates relative to position frame -= _region->position(); @@ -1043,14 +1416,14 @@ MidiRegionView::snap_to_frame(nframes_t x) double MidiRegionView::snap_to_pixel(double x) { - return (double) trackview.editor.frame_to_pixel(snap_to_frame(x)); + return (double) trackview.editor().frame_to_pixel(snap_to_frame(x)); } double -MidiRegionView::get_position_pixels(void) +MidiRegionView::get_position_pixels() { - nframes_t region_frame = get_position(); - return trackview.editor.frame_to_pixel(region_frame); + nframes64_t region_frame = get_position(); + return trackview.editor().frame_to_pixel(region_frame); } void @@ -1062,7 +1435,7 @@ MidiRegionView::begin_resizing(CanvasNote::NoteEnd note_end) CanvasNote *note = dynamic_cast (*i); // only insert CanvasNotes into the map - if(note) { + if (note) { NoteResizeData *resize_data = new NoteResizeData(); resize_data->canvas_note = note; @@ -1076,9 +1449,9 @@ MidiRegionView::begin_resizing(CanvasNote::NoteEnd note_end) note->y2()); // calculate the colors: get the color settings - uint fill_color = + uint32_t fill_color = UINT_RGBA_CHANGE_A( - ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), + ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(), 128); // make the resize preview notes more transparent and bright @@ -1087,16 +1460,16 @@ MidiRegionView::begin_resizing(CanvasNote::NoteEnd note_end) // calculate color based on note velocity resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE( - note_fill_color(note->note()->velocity()), + CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()), fill_color, 0.85); resize_rect->property_outline_color_rgba() = - ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(); + CanvasNoteEvent::calculate_outline(ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get()); resize_data->resize_rect = resize_rect; - if(note_end == CanvasNote::NOTE_ON) { + if (note_end == CanvasNote::NOTE_ON) { resize_data->current_x = note->x1(); } else { // NOTE_OFF resize_data->current_x = note->x2(); @@ -1116,7 +1489,7 @@ MidiRegionView::update_resizing(CanvasNote::NoteEnd note_end, double x, bool rel const double region_start = get_position_pixels(); - if(relative) { + if (relative) { (*i)->current_x = (*i)->current_x + x; } else { // x is in track relative, transform it to region relative @@ -1125,7 +1498,7 @@ MidiRegionView::update_resizing(CanvasNote::NoteEnd note_end, double x, bool rel double current_x = (*i)->current_x; - if(note_end == CanvasNote::NOTE_ON) { + if (note_end == CanvasNote::NOTE_ON) { resize_rect->property_x1() = snap_to_pixel(current_x); resize_rect->property_x2() = canvas_note->x2(); } else { @@ -1141,38 +1514,35 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo start_delta_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; - double current_x = (*i)->current_x; - const double position = get_position_pixels(); + CanvasNote* canvas_note = (*i)->canvas_note; + SimpleRect* resize_rect = (*i)->resize_rect; + double current_x = (*i)->current_x; + const double position = get_position_pixels(); - if(!relative) { + if (!relative) { // event_x is in track relative, transform it to region relative current_x = event_x - position; } // because snapping works on world coordinates we have to transform current_x // to world coordinates before snapping and transform it back afterwards - nframes_t current_frame = snap_to_frame(current_x); + nframes64_t current_frame = snap_to_frame(current_x); // transform to region start relative current_frame += _region->start(); - const boost::shared_ptr copy(new Note(*(canvas_note->note().get()))); + const boost::shared_ptr copy(new Evoral::Note(*(canvas_note->note().get()))); // resize beginning of note if (note_end == CanvasNote::NOTE_ON && current_frame < copy->end_time()) { command_remove_note(canvas_note); copy->on_event().time() = current_frame; - command_add_note(copy); - _marked_for_selection.insert(copy); + command_add_note(copy, _selection.find(canvas_note) != _selection.end()); } // resize end of note if (note_end == CanvasNote::NOTE_OFF && current_frame > copy->time()) { - command_remove_note(canvas_note); command_remove_note(canvas_note); copy->off_event().time() = current_frame; - command_add_note(copy); - _marked_for_selection.insert(copy); + command_add_note(copy, _selection.find(canvas_note) != _selection.end()); } delete resize_rect; @@ -1183,39 +1553,39 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo apply_command(); } +void +MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative) +{ + const boost::shared_ptr copy(new Evoral::Note(*(event->note().get()))); + + if (relative) { + uint8_t new_velocity = copy->velocity() + velocity; + clamp_0_to_127(new_velocity); + copy->set_velocity(new_velocity); + } else { + copy->set_velocity(velocity); + } + + command_remove_note(event); + command_add_note(copy, event->selected()); +} void -MidiRegionView::change_velocity(uint8_t velocity, bool relative) +MidiRegionView::change_velocity(CanvasNoteEvent* ev, int8_t velocity, bool relative) { start_delta_command(_("change velocity")); + + change_note_velocity(ev, velocity, relative); + for (Selection::iterator i = _selection.begin(); i != _selection.end();) { Selection::iterator next = i; ++next; - - CanvasNoteEvent *event = *i; - const boost::shared_ptr copy(new Note(*(event->note().get()))); - - if(relative) { - uint8_t new_velocity = copy->velocity() + velocity; - clamp_0_to_127(new_velocity); - - copy->set_velocity(new_velocity); - } else { // absolute - copy->set_velocity(velocity); + if ( !(*((*i)->note()) == *(ev->note())) ) { + change_note_velocity(*i, velocity, relative); } - - command_remove_note(event); - command_add_note(copy); - - _marked_for_selection.insert(copy); i = next; } - // dont keep notes selected if tweaking a single note - if(_marked_for_selection.size() == 1) { - _marked_for_selection.clear(); - } - apply_command(); } @@ -1227,23 +1597,17 @@ MidiRegionView::change_channel(uint8_t channel) Selection::iterator next = i; ++next; - CanvasNoteEvent *event = *i; - const boost::shared_ptr copy(new Note(*(event->note().get()))); + CanvasNoteEvent* event = *i; + const boost::shared_ptr copy(new Evoral::Note(*(event->note().get()))); copy->set_channel(channel); command_remove_note(event); - command_add_note(copy); + command_add_note(copy, event->selected()); - _marked_for_selection.insert(copy); i = next; } - // dont keep notes selected if tweaking a single note - if(_marked_for_selection.size() == 1) { - _marked_for_selection.clear(); - } - apply_command(); } @@ -1282,22 +1646,31 @@ MidiRegionView::set_frame_color() } void -MidiRegionView::midi_force_channel_changed(int8_t channel) +MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask) { - force_channel = channel; + switch (mode) { + case AllChannels: + case FilterChannels: + _force_channel = -1; + break; + case ForceChannel: + _force_channel = mask; + 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); + } + + _last_channel_selection = mask; } void -MidiRegionView::midi_channel_selection_changed(uint16_t selection) +MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode) { - if(force_channel >= 0) { - selection = 0xFFFF; - } - - for(std::vector::iterator i = _events.begin(); - i != _events.end(); - ++i) { - (*i)->on_channel_selection_change(selection); - } - last_channel_selection = selection; + _model_name = model; + _custom_device_mode = custom_device_mode; + redisplay_model(); } +