#include "ardour/session.h"
#include "evoral/Parameter.hpp"
-#include "evoral/MIDIEvent.hpp"
+#include "evoral/Event.hpp"
#include "evoral/Control.hpp"
#include "evoral/midi_util.h"
, _channel_selection_scoped_note (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _optimization_iterator (_events.end())
, _list_editor (0)
, _no_sound_notes (false)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
- , _note_entered (false)
+ , _entered_note (0)
, _mouse_changed_selection (false)
{
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
+
+ _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
+ _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
+
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
, _channel_selection_scoped_note (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _optimization_iterator (_events.end())
, _list_editor (0)
, _no_sound_notes (false)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
- , _note_entered (false)
+ , _entered_note (0)
, _mouse_changed_selection (false)
{
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
+
+ _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
+ _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
+
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
if (_enable_display) {
redisplay_model();
}
+ } else if (p == "color-regions-using-track-color") {
+ set_colors ();
}
}
, _channel_selection_scoped_note (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _optimization_iterator (_events.end())
, _list_editor (0)
, _no_sound_notes (false)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
- , _note_entered (false)
+ , _entered_note (0)
, _mouse_changed_selection (false)
{
init (false);
, _channel_selection_scoped_note (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _optimization_iterator (_events.end())
, _list_editor (0)
, _no_sound_notes (false)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
- , _note_entered (false)
+ , _entered_note (0)
, _mouse_changed_selection (false)
{
init (true);
gui_context ());
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
+ UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
connect_to_diskstream ();
}
remove_ghost_note ();
/* XXX This is problematic as the function is executed for every region
- and only for one region _note_entered can be true. Still it's
+ and only for one region _entered_note can be true. Still it's
necessary as to hide the verbose cursor when we're changing from
draw mode to internal edit mode. These lines are the reason why
in some situations no verbose cursor is shown when we enter internal
edit mode over a note. */
- if (!_note_entered) {
+ if (!_entered_note) {
hide_verbose_cursor ();
}
}
{
hide_verbose_cursor ();
remove_ghost_note ();
- _note_entered = false;
+ _entered_note = 0;
if (_grabbed_keyboard) {
Keyboard::magic_widget_drop_focus();
if (_mouse_state != SelectTouchDragging) {
_pressed_button = ev->button;
- _mouse_state = Pressed;
+
+ if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
+
+ if (midi_view()->note_mode() == Percussive) {
+ editor->drags()->set (new HitCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
+ } else {
+ editor->drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
+ }
+
+ _mouse_state = AddDragging;
+ remove_ghost_note ();
+ hide_verbose_cursor ();
+ } else {
+ _mouse_state = Pressed;
+ }
return true;
}
case MouseTimeFX:
{
_mouse_changed_selection = true;
-
- if (Keyboard::is_insert_note_event(ev)) {
-
- double event_x, event_y;
-
- event_x = ev->x;
- event_y = ev->y;
- group->canvas_to_item (event_x, event_y);
-
- Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x) + _region->position());
- create_note_at (editor.pixel_to_sample (event_x), event_y, beats, ev->state, true);
- } else {
- clear_editor_note_selection ();
- }
+ clear_editor_note_selection ();
break;
}
case MouseDraw:
- {
- Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x) + _region->position());
- create_note_at (editor.pixel_to_sample (event_x), event_y, beats, ev->state, true);
- break;
- }
+ break;
+
default:
break;
}
break;
case AddDragging:
- /* Only create a ghost note when we added a note, not when we were drag-selecting. */
- create_ghost_note (ev->x, ev->y, ev->state);
+ /* Don't a ghost note when we added a note - wait until motion to avoid visual confusion.
+ we don't want one when we were drag-selecting either. */
case SelectRectDragging:
editor.drags()->end_grab ((GdkEvent *) ev);
_mouse_state = None;
{
PublicEditor& editor = trackview.editor ();
- if (!_note_entered) {
+ if (!_entered_note) {
- if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
+ if (_mouse_state == AddDragging) {
+ if (_ghost_note) {
+ remove_ghost_note ();
+ }
+
+ } else if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
_mouse_state != AddDragging) {
MouseMode m = editor.current_mouse_mode();
- if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
- editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
- _mouse_state = AddDragging;
- remove_ghost_note ();
- hide_verbose_cursor ();
- return true;
- } else if (m == MouseContent) {
+ if (m == MouseContent && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
clear_editor_note_selection ();
detectable auto-repeat is the name of the game and only sends
repeated presses, carry out key actions at key press, not release.
*/
-
bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
- _mouse_state = SelectTouchDragging;
+
+ if (_mouse_state != AddDragging) {
+ _mouse_state = SelectTouchDragging;
+ }
+
return true;
} else if (ev->keyval == GDK_Escape && unmodified) {
}
}
- for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- delete *i;
- }
+ _note_group->clear (true);
_events.clear();
_patch_changes.clear();
_sys_exes.clear();
content_connection.disconnect ();
_model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
/* Don't signal as nobody else needs to know until selection has been altered. */
- clear_events ();
+ clear_events();
if (_enable_display) {
redisplay_model();
}
void
-MidiRegionView::apply_diff (bool as_subcommand)
+MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
{
bool add_or_remove;
bool commit = false;
return;
}
- if ((add_or_remove = _note_diff_command->adds_or_removes())) {
+ if (!was_copy && (add_or_remove = _note_diff_command->adds_or_removes())) {
// 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());
}
}
- midi_view()->midi_track()->midi_playlist()->region_edited(
- _region, _note_diff_command);
+ midi_view()->midi_track()->midi_playlist()->region_edited (_region, _note_diff_command);
if (as_subcommand) {
_model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
NoteBase*
MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
{
+
if (_optimization_iterator != _events.end()) {
++_optimization_iterator;
}
- if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
- return *_optimization_iterator;
+ if (_optimization_iterator != _events.end() && _optimization_iterator->first == note) {
+ return _optimization_iterator->second;
}
- for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
- if ((*_optimization_iterator)->note() == note) {
- return *_optimization_iterator;
- }
+ _optimization_iterator = _events.find (note);
+ if (_optimization_iterator != _events.end()) {
+ return _optimization_iterator->second;
}
return 0;
Events::iterator it;
for (it = _events.begin(); it != _events.end(); ++it) {
- if ((*it)->note()->id() == id) {
- return *it;
+ if (it->first->id() == id) {
+ return it->second;
}
}
return 0;
}
+boost::shared_ptr<PatchChange>
+MidiRegionView::find_canvas_patch_change (MidiModel::PatchChangePtr p)
+{
+ PatchChanges::const_iterator f = _patch_changes.find (p);
+
+ if (f != _patch_changes.end()) {
+ return f->second;
+ }
+
+ return boost::shared_ptr<PatchChange>();
+}
+
void
MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
{
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
NoteBase* cne = find_canvas_note (*n);
if (cne) {
- e.push_back (cne);
+ e.insert (make_pair (*n, cne));
}
}
}
touching model. Leave active notes (with length 0) alone since
they are being extended. */
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- if ((*i)->note()->length() > 0) {
- update_note(*i);
+ if (i->second->note()->length() > 0) {
+ update_note(i->second);
}
}
_last_display_zoom = zoom;
return;
}
- for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- (*i)->invalidate ();
+ for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
+ _optimization_iterator->second->invalidate();
}
- MidiModel::ReadLock lock(_model->read_lock());
+ bool empty_when_starting = _events.empty();
+ _optimization_iterator = _events.begin();
+ MidiModel::Notes missing_notes;
+ Note* sus = NULL;
+ Hit* hit = NULL;
+ MidiModel::ReadLock lock(_model->read_lock());
MidiModel::Notes& notes (_model->notes());
- _optimization_iterator = _events.begin();
- bool empty_when_starting = _events.empty();
NoteBase* cne;
-
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
boost::shared_ptr<NoteType> note (*n);
bool visible;
if (note_in_region_range (note, visible)) {
-
if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
-
- if (visible) {
- cne->validate ();
- update_note (cne);
- cne->show ();
+ cne->validate ();
+ bool update = false;
+
+ if (note_in_region_range (note, visible)) {
+ if (visible) {
+ update = true;
+ cne->show ();
+ } else {
+ cne->hide ();
+ }
} else {
cne->hide ();
}
+ if ((sus = dynamic_cast<Note*>(cne))) {
- } else {
-
- cne = add_note (note, visible);
- }
+ if (update) {
+ update_sustained (sus);
+ }
- set<Evoral::event_id_t>::iterator it;
- for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
- if ((*it) == note->id()) {
- add_to_selection (cne);
- }
- }
+ for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
+ if (gr && !gr->trackview.hidden()) {
+ gr->update_note (sus, !update);
+ }
+ }
+ } else if ((hit = dynamic_cast<Hit*>(cne))) {
- } else {
+ if (update) {
+ update_hit (hit);
+ }
- if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
- cne->validate ();
- cne->hide ();
+ for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+ MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
+ if (gr && !gr->trackview.hidden()) {
+ gr->update_hit (hit, !update);
+ }
+ }
+ }
+ } else {
+ missing_notes.insert (note);
}
}
}
- /* remove note items that are no longer valid */
-
if (!empty_when_starting) {
+ MidiModel::Notes::iterator f;
for (Events::iterator i = _events.begin(); i != _events.end(); ) {
- if (!(*i)->valid ()) {
+
+ NoteBase* cne = i->second;
+
+ /* remove note items that are no longer valid */
+ if (!cne->valid()) {
for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
if (gr) {
- gr->remove_note (*i);
+ gr->remove_note (cne);
}
}
- delete *i;
+ delete cne;
i = _events.erase (i);
} else {
}
}
- _patch_changes.clear();
+ for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
+ boost::shared_ptr<NoteType> note (*n);
+ NoteBase* cne;
+ bool visible;
+
+ if (note_in_region_range (note, visible)) {
+ if (visible) {
+ cne = add_note (note, true);
+ } else {
+ cne = add_note (note, false);
+ }
+ } else {
+ cne = add_note (note, false);
+ }
+
+ for (set<Evoral::event_id_t>::iterator it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
+ if ((*it) == note->id()) {
+ add_to_selection (cne);
+ }
+ }
+ }
+
_sys_exes.clear();
display_sysexes();
_marked_for_velocity.clear ();
_pending_note_selection.clear ();
- /* we may have caused _events to contain things out of order (e.g. if a note
- moved earlier or later). we don't generally need them in time order, but
- make a note that a sort is required for those cases that require it.
- */
-
- _sort_needed = true;
}
void
MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
{
for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
+ boost::shared_ptr<PatchChange> p;
if ((*i)->channel() != channel) {
continue;
}
- const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
- add_canvas_patch_change (*i, patch_name, active_channel);
+ if ((p = find_canvas_patch_change (*i)) != 0) {
+
+ const framecnt_t region_frames = source_beats_to_region_frames ((*i)->time());
+
+ if (region_frames < 0 || region_frames >= _region->length()) {
+ p->hide();
+ } else {
+ const double x = trackview.editor().sample_to_pixel (region_frames);
+ const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
+ p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
+ p->flag()->set_text (patch_name);
+
+ p->show();
+ }
+
+ } else {
+ const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
+ add_canvas_patch_change (*i, patch_name, active_channel);
+ }
}
}
if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
- const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
- boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
-
- if (mev) {
- if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
- have_periodic_system_messages = true;
- break;
- }
+ if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
+ have_periodic_system_messages = true;
+ break;
}
}
}
for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
-
- const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
- boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
-
Evoral::Beats time = (*i)->time();
- if (mev) {
- if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
- if (!display_periodic_messages) {
- continue;
- }
+ if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
+ if (!display_periodic_messages) {
+ continue;
}
}
if (_active_notes) {
end_write();
}
-
- _selection.clear();
+ _entered_note = 0;
clear_events ();
delete _note_group;
redisplay_model();
}
- for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
- if ((*x)->canvas_item()->width() >= _pixel_width) {
- (*x)->hide();
- } else {
- (*x)->show();
+ bool hide_all = false;
+ PatchChanges::iterator x = _patch_changes.begin();
+ if (x != _patch_changes.end()) {
+ hide_all = x->second->flag()->width() >= _pixel_width;
+ }
+
+ if (hide_all) {
+ for (; x != _patch_changes.end(); ++x) {
+ x->second->hide();
}
}
}
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
- (*x)->set_height (midi_stream_view()->contents_height());
+ (*x).second->set_height (midi_stream_view()->contents_height());
}
if (_step_edit_cursor) {
_current_range_min = min;
_current_range_max = max;
- for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
- NoteBase* event = *i;
- boost::shared_ptr<NoteType> note (event->note());
-
- if (note->note() < _current_range_min ||
- note->note() > _current_range_max) {
- event->hide();
- } else {
- event->show();
- }
-
- if (Note* cnote = dynamic_cast<Note*>(event)) {
-
- const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
- const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
-
- if (y0 < 0 || y1 >= _height) {
- /* During DnD, the region uses the 'old/current'
- * midi_stream_view()'s range and its position/height calculation.
- *
- * Ideally DnD would decouple the midi_stream_view() for the
- * region(s) being dragged and set it to the target's range
- * (or in case of the drop-zone, FullRange).
- * but I don't see how this can be done without major rework.
- *
- * For now, just prevent visual bleeding of events in case
- * the target-track is smaller.
- */
- event->hide();
- continue;
- }
- cnote->set_y0 (y0);
- cnote->set_y1 (y1);
-
- } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
- update_hit (chit);
- }
- }
+ redisplay_model ();
}
GhostRegion*
ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
}
- for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- ghost->add_note(*i);
- }
-
ghost->set_colors ();
ghost->set_height ();
ghost->set_duration (_region->length() / samples_per_pixel);
- ghosts.push_back (ghost);
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
+ ghost->add_note(i->second);
+ }
+
+ ghosts.push_back (ghost);
+ enable_display (true);
return ghost;
}
/* must compare double explicitly as Beats::operator< rounds to ppqn */
const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
- note->time().to_double() > midi_reg->start_beats() + midi_reg->length_beats());
+ note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
- visible = (note->note() >= midi_stream_view()->lowest_note()) &&
- (note->note() <= midi_stream_view()->highest_note());
+ visible = (note->note() >= _current_range_min) &&
+ (note->note() <= _current_range_max);
return !outside;
}
const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
boost::shared_ptr<NoteType> note = ev->note();
- const double session_source_start = (_region->pulse() * 4.0) - mr->start_beats();
+ const double session_source_start = _region->quarter_note() - mr->start_beats();
const framepos_t note_start_frames = map.frame_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
const double x0 = trackview.editor().sample_to_pixel (note_start_frames);
double x1;
- const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
+ const double y0 = 1 + floor(note_to_y(note->note()));
double y1;
/* trim note display to not overlap the end of its region */
- if (note->length() > 0) {
+ if (note->length().to_double() > 0.0) {
double note_end_time = note->end_time().to_double();
if (note_end_time > mr->start_beats() + mr->length_beats()) {
x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
}
- y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1);
+ y1 = y0 + std::max(1., floor(note_height()) - 1);
ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
ev->set_fill_color(base_col);
ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
- if (update_ghost_regions) {
- for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
- MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
- if (gr) {
- gr->update_note (ev);
- }
- }
- }
}
void
{
boost::shared_ptr<NoteType> note = ev->note();
- const double note_time_qn = note->time().to_double() + ((_region->pulse() * 4.0) - midi_region()->start_beats());
+ const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_quarter_note (note_time_qn) - _region->position();
const double x = trackview.editor().sample_to_pixel(note_start_frames);
- const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
- const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
+ const double diamond_size = std::max(1., floor(note_height()) - 2.);
+ const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
// see DnD note in MidiRegionView::apply_note_range() above
if (y <= 0 || y >= _height) {
ev->set_fill_color(base_col);
ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
- if (update_ghost_regions) {
- for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
- MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
- if (gr) {
- gr->update_hit (ev);
- }
- }
- }
}
/** Add a MIDI note to the view (with length).
if (midi_view()->note_mode() == Sustained) {
- Note* ev_rect = new Note (*this, _note_group, note);
+ Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
update_sustained (ev_rect);
} else if (midi_view()->note_mode() == Percussive) {
- const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
+ const double diamond_size = std::max(1., floor(note_height()) - 2.);
- Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
+ Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
update_hit (ev_diamond);
}
event->on_channel_selection_change (get_selected_channels());
- _events.push_back(event);
+ _events.insert (make_pair (event->note(), event));
if (visible) {
event->show();
// so we need to do something more sophisticated to keep its color
// appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
// up to date.
-
boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
new PatchChange(*this, group,
displaytext,
height,
x, 1.0,
instrument_info(),
- patch));
+ patch,
+ _patch_change_outline,
+ _patch_change_fill)
+ );
if (patch_change->item().width() < _pixel_width) {
// Show unless patch change is beyond the region bounds
patch_change->hide ();
}
- _patch_changes.push_back (patch_change);
+ _patch_changes.insert (make_pair (patch, patch_change));
+}
+
+void
+MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
+{
+ /* remove the canvas item */
+ for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
+ if (x->second->patch() == pc->patch()) {
+ _patch_changes.erase (x);
+ break;
+ }
+ }
}
MIDI::Name::PatchPrimaryKey
{
string name = _("alter patch change");
trackview.editor().begin_reversible_command (name);
+
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
if (pc.patch()->program() != new_patch.program()) {
_model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
- _patch_changes.clear ();
+ remove_canvas_patch_change (&pc);
display_patch_changes ();
}
_model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
- _patch_changes.clear ();
+ for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
+ if (x->second->patch() == old_change) {
+ _patch_changes.erase (x);
+ break;
+ }
+ }
+
display_patch_changes ();
}
_model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
- _patch_changes.clear ();
display_patch_changes ();
}
_model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
- _patch_changes.clear ();
display_patch_changes ();
}
MidiRegionView::delete_patch_change (PatchChange* pc)
{
trackview.editor().begin_reversible_command (_("delete patch change"));
+
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
c->remove (pc->patch ());
_model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
- _patch_changes.clear ();
+ remove_canvas_patch_change (pc);
display_patch_changes ();
}
void
MidiRegionView::note_deleted (NoteBase* cne)
{
+ if (_entered_note && cne == _entered_note) {
+ _entered_note = 0;
+ }
+
if (_selection.empty()) {
return;
}
_selection.clear();
apply_diff ();
+
hide_verbose_cursor ();
}
clear_editor_note_selection ();
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- add_to_selection (*i);
+ add_to_selection (i->second);
}
}
clear_editor_note_selection ();
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
+ framepos_t t = source_beats_to_absolute_frames(i->first->time());
if (t >= start && t <= end) {
- add_to_selection (*i);
+ add_to_selection (i->second);
}
}
}
MidiRegionView::invert_selection ()
{
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- if ((*i)->selected()) {
- remove_from_selection(*i);
+ if (i->second->selected()) {
+ remove_from_selection(i->second);
} else {
- add_to_selection (*i);
+ add_to_selection (i->second);
}
}
}
/* find notes entirely within OR spanning the earliest..latest range */
- if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
- ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
- add_to_selection (*i);
+ if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
+ (i->first->time() <= earliest && i->first->end_time() >= latest)) {
+ add_to_selection (i->second);
}
-
}
}
}
// 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)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
+ if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
// Rectangles intersect
- if (!(*i)->selected()) {
- add_to_selection (*i);
+ if (!i->second->selected()) {
+ add_to_selection (i->second);
}
- } else if ((*i)->selected() && !extend) {
+ } else if (i->second->selected() && !extend) {
// Rectangles do not intersect
- remove_from_selection (*i);
+ remove_from_selection (i->second);
}
}
// 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)->y1() >= y1 && (*i)->y1() <= y2)) {
+ if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
// within y- (note-) range
- if (!(*i)->selected()) {
- add_to_selection (*i);
+ if (!i->second->selected()) {
+ add_to_selection (i->second);
}
- } else if ((*i)->selected() && !extend) {
- remove_from_selection (*i);
+ } else if (i->second->selected() && !extend) {
+ remove_from_selection (i->second);
}
}
}
}
}
+NoteBase*
+MidiRegionView::copy_selection ()
+{
+ NoteBase* note;
+ _copy_drag_events.clear ();
+
+ if (_selection.empty()) {
+ return 0;
+ }
+
+ for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+ boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
+ if (midi_view()->note_mode() == Sustained) {
+ Note* n = new Note (*this, _note_group, g);
+ update_sustained (n, false);
+ note = n;
+ } else {
+ Hit* h = new Hit (*this, _note_group, 10, g);
+ update_hit (h, false);
+ note = h;
+ }
+
+ _copy_drag_events.push_back (note);
+ }
+
+ return _copy_drag_events.front ();
+}
+
+void
+MidiRegionView::move_copies (double dx, double dy, double cumulative_dy)
+{
+ typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
+ PossibleChord to_play;
+ Evoral::Beats earliest = Evoral::MaxBeats;
+
+ for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
+ if ((*i)->note()->time() < earliest) {
+ earliest = (*i)->note()->time();
+ }
+ }
+
+ for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
+ if ((*i)->note()->time() == earliest) {
+ to_play.push_back ((*i)->note());
+ }
+ (*i)->move_event(dx, dy);
+ }
+
+ if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
+
+ if (to_play.size() > 1) {
+
+ PossibleChord shifted;
+
+ for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
+ boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
+ moved_note->set_note (moved_note->note() + cumulative_dy);
+ shifted.push_back (moved_note);
+ }
+
+ start_playing_midi_chord (shifted);
+
+ } else if (!to_play.empty()) {
+
+ boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
+ moved_note->set_note (moved_note->note() + cumulative_dy);
+ start_playing_midi_note (moved_note);
+ }
+ }
+}
+
void
-MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
+MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool copy)
{
uint8_t lowest_note_in_selection = 127;
uint8_t highest_note_in_selection = 0;
uint8_t highest_note_difference = 0;
- // find highest and lowest notes first
+ if (!copy) {
+ // find highest and lowest notes first
- 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);
- }
-
- /*
- cerr << "dnote: " << (int) dnote << endl;
- cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
- << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
- cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
- << int(highest_note_in_selection) << endl;
- cerr << "selection size: " << _selection.size() << endl;
- cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
- */
+ 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);
+ }
- // Make sure the note pitch does not exceed the MIDI standard range
- if (highest_note_in_selection + dnote > 127) {
- highest_note_difference = highest_note_in_selection - 127;
- }
- TempoMap& map (trackview.session()->tempo_map());
+ /*
+ cerr << "dnote: " << (int) dnote << endl;
+ cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
+ << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
+ cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
+ << int(highest_note_in_selection) << endl;
+ cerr << "selection size: " << _selection.size() << endl;
+ cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
+ */
- start_note_diff_command (_("move notes"));
+ // Make sure the note pitch does not exceed the MIDI standard range
+ if (highest_note_in_selection + dnote > 127) {
+ highest_note_difference = highest_note_in_selection - 127;
+ }
+ TempoMap& map (trackview.session()->tempo_map());
- for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
+ start_note_diff_command (_("move notes"));
- double const start_qn = (_region->pulse() * 4.0) - midi_region()->start_beats();
- framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
- Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
- if (new_time < 0) {
- continue;
+ for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
+
+ double const start_qn = _region->quarter_note() - midi_region()->start_beats();
+ framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
+ Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
+ if (new_time < 0) {
+ continue;
+ }
+
+ note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
+
+ uint8_t original_pitch = (*i)->note()->note();
+ uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+
+ // keep notes in standard midi range
+ clamp_to_0_127(new_pitch);
+
+ lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
+ highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
+
+ note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
+ }
+ } else {
+
+ clear_editor_note_selection ();
+
+ for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.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);
}
- note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
+ // Make sure the note pitch does not exceed the MIDI standard range
+ if (highest_note_in_selection + dnote > 127) {
+ highest_note_difference = highest_note_in_selection - 127;
+ }
+
+ TempoMap& map (trackview.session()->tempo_map());
+ start_note_diff_command (_("copy notes"));
+
+ for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
+
+ /* update time */
+
+ double const start_qn = _region->quarter_note() - midi_region()->start_beats();
+ framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
+ Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
+
+ if (new_time < 0) {
+ continue;
+ }
+
+ (*i)->note()->set_time (new_time);
+
+ /* update pitch */
+
+ uint8_t original_pitch = (*i)->note()->note();
+ uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
- uint8_t original_pitch = (*i)->note()->note();
- uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+ (*i)->note()->set_note (new_pitch);
- // keep notes in standard midi range
- clamp_to_0_127(new_pitch);
+ // keep notes in standard midi range
+ clamp_to_0_127(new_pitch);
- lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
- highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
+ lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
+ highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
- note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
+ note_diff_add_note ((*i)->note(), true);
+
+ delete *i;
+ }
+
+ _copy_drag_events.clear ();
}
- apply_diff();
+ apply_diff (false, copy);
// 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()) {
- midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
+ midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
}
}
/* and then to beats */
const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions);
- const double quarter_note_start = (_region->pulse() * 4.0) - midi_region()->start_beats();
+ const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
const Evoral::Beats x_beats = Evoral::Beats (e_qaf - quarter_note_start);
if (at_front && x_beats < canvas_note->note()->end_time()) {
void
MidiRegionView::note_entered(NoteBase* ev)
{
- _note_entered = true;
+ _entered_note = ev;
Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
void
MidiRegionView::note_left (NoteBase*)
{
- _note_entered = false;
+ _entered_note = 0;
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
(*i)->hide_velocity ();
void
MidiRegionView::sysex_entered (SysEx* p)
{
- ostringstream s;
+ // ostringstream s;
// CAIROCANVAS
// need a way to extract text from p->_flag->_text
// s << p->text();
// Update notes for selection
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- (*i)->on_channel_selection_change (mask);
+ i->second->on_channel_selection_change (mask);
}
_patch_changes.clear ();
const Evoral::Beats duration = last_time - first_time;
const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
const Evoral::Beats paste_offset = snap_duration * paste_count;
- const Evoral::Beats pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
+ const Evoral::Beats quarter_note = absolute_frames_to_source_beats(pos) + paste_offset;
Evoral::Beats end_point = Evoral::Beats();
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
first_time,
last_time,
duration, pos, _region->position(),
- pos_beats));
+ quarter_note));
clear_editor_note_selection ();
for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
- copied_note->set_time (pos_beats + copied_note->time() - first_time);
+ copied_note->set_time (quarter_note + copied_note->time() - first_time);
copied_note->set_id (Evoral::next_event_id());
/* make all newly added notes selected */
}
};
-void
-MidiRegionView::time_sort_events ()
-{
- if (!_sort_needed) {
- return;
- }
-
- EventNoteTimeEarlyFirstComparator cmp;
- _events.sort (cmp);
-
- _sort_needed = false;
-}
-
void
MidiRegionView::goto_next_note (bool add_to_selection)
{
bool use_next = false;
- if (_events.back()->selected()) {
- return;
- }
-
- time_sort_events ();
-
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
+ NoteBase* first_note = 0;
- for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- if ((*i)->selected()) {
- use_next = true;
- continue;
- } else if (use_next) {
- if (channel_mask & (1 << (*i)->note()->channel())) {
- if (!add_to_selection) {
- unique_select (*i);
- } else {
- note_selected (*i, true, false);
+ MidiModel::ReadLock lock(_model->read_lock());
+ MidiModel::Notes& notes (_model->notes());
+
+ for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
+ NoteBase* cne = 0;
+ if ((cne = find_canvas_note (*n))) {
+
+ if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
+ first_note = cne;
+ }
+
+ if (cne->selected()) {
+ use_next = true;
+ continue;
+ } else if (use_next) {
+ if (channel_mask & (1 << (*n)->channel())) {
+ if (!add_to_selection) {
+ unique_select (cne);
+ } else {
+ note_selected (cne, true, false);
+ }
+
+ return;
}
- return;
}
}
}
/* use the first one */
- if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
- unique_select (_events.front());
+ if (!_events.empty() && first_note) {
+ unique_select (first_note);
}
}
{
bool use_next = false;
- if (_events.front()->selected()) {
- return;
- }
-
- time_sort_events ();
-
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
+ NoteBase* last_note = 0;
- for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
- if ((*i)->selected()) {
- use_next = true;
- continue;
- } else if (use_next) {
- if (channel_mask & (1 << (*i)->note()->channel())) {
- if (!add_to_selection) {
- unique_select (*i);
- } else {
- note_selected (*i, true, false);
+ MidiModel::ReadLock lock(_model->read_lock());
+ MidiModel::Notes& notes (_model->notes());
+
+ for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
+ NoteBase* cne = 0;
+ if ((cne = find_canvas_note (*n))) {
+
+ if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
+ last_note = cne;
+ }
+
+ if (cne->selected()) {
+ use_next = true;
+ continue;
+
+ } else if (use_next) {
+ if (channel_mask & (1 << (*n)->channel())) {
+ if (!add_to_selection) {
+ unique_select (cne);
+ } else {
+ note_selected (cne, true, false);
+ }
+
+ return;
}
- return;
}
}
}
/* use the last one */
- if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
- unique_select (*(_events.rbegin()));
+ if (!_events.empty() && last_note) {
+ unique_select (last_note);
}
}
{
bool had_selected = false;
- time_sort_events ();
+ /* we previously time sorted events here, but Notes is a multiset sorted by time */
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- if ((*i)->selected()) {
- selected.insert ((*i)->note());
+ if (i->second->selected()) {
+ selected.insert (i->first);
had_selected = true;
}
}
if (allow_all_if_none_selected && !had_selected) {
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- selected.insert ((*i)->note());
+ selected.insert (i->first);
}
}
}
framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
const int32_t divisions = editor.get_grid_music_divisions (state);
- const double snapped_region_qn = snap_frame_to_grid_underneath (unsnapped_frame, divisions, true).to_double();
+ const bool shift_snap = midi_view()->note_mode() != Percussive;
+ const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap);
+
+ /* prevent Percussive mode from displaying a ghost hit at region end */
+ if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
+ _ghost_note->hide();
+ hide_verbose_cursor ();
+ return;
+ }
+
+ /* ghost note may have been snapped before region */
+ if (_ghost_note && snapped_beats.to_double() < 0.0) {
+ _ghost_note->hide();
+ return;
+
+ } else if (_ghost_note) {
+ _ghost_note->show();
+ }
- Evoral::Beats snapped_beats = Evoral::Beats (snapped_region_qn);
/* calculate time in beats relative to start of source */
const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
_ghost_note->note()->set_time (snapped_beats);
_ghost_note->note()->set_length (length);
- _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
+ _ghost_note->note()->set_note (y_to_note (y));
_ghost_note->note()->set_channel (mtv->get_channel_for_add ());
_ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
/* the ghost note does not appear in ghost regions, so pass false in here */
{
/* XXX: This is dead code. What was it for? */
- double note = midi_stream_view()->y_to_note(y);
+ double note = y_to_note(y);
Events e;
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
}
for (Events::iterator i = e.begin(); i != e.end(); ++i) {
- if (_selection.insert (*i).second) {
- (*i)->set_selected (true);
+ if (_selection.insert (i->second).second) {
+ i->second->set_selected (true);
}
}
{
RegionView::color_handler ();
+ _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
+ _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
+
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- (*i)->set_selected ((*i)->selected()); // will change color
+ i->second->set_selected (i->second->selected()); // will change color
}
/* XXX probably more to do here */
framepos_t back = max_framepos;
for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
- Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
+ const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
if (ev.is_channel_event()) {
if (get_channel_mode() == FilterChannels) {
*/
const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
const double rem = eqaf - qaf;
- if (rem >= 0.0 && eqaf - grid_beats.to_double() > _region->pulse() * 4.0) {
+ if (rem >= 0.0) {
eqaf -= grid_beats.to_double();
}
}
- const double session_start_off = (_region->pulse() * 4.0) - midi_region()->start_beats();
+ const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
return Evoral::Beats (eqaf - session_start_off);
}
}
return beats;
}
+uint8_t
+MidiRegionView::y_to_note (double y) const
+{
+ int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
+ + _current_range_min;
+
+ if (n < 0) {
+ return 0;
+ } else if (n > 127) {
+ return 127;
+ }
+
+ /* min due to rounding and/or off-by-one errors */
+ return min ((uint8_t) n, _current_range_max);
+}
+
+double
+MidiRegionView::note_to_y(uint8_t note) const
+{
+ return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
+}