#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"
#include "sys_ex.h"
#include "ui_config.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
using Gtkmm2ext::Keyboard;
-PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
-
#define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _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));
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
connect_to_diskstream ();
-
- SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
-
- PublicEditor& editor (trackview.editor());
- editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
}
MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _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));
connect_to_diskstream ();
-
- SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
-
- PublicEditor& editor (trackview.editor());
- editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
}
void
if (_enable_display) {
redisplay_model();
}
+ } else if (p == "color-regions-using-track-color") {
+ set_colors ();
}
}
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _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);
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
- , _sort_needed (true)
, _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);
RegionView::init (false);
- set_height (trackview.current_height());
+ //set_height (trackview.current_height());
region_muted ();
region_sync_changed ();
region_resized (ARDOUR::bounds_change);
- region_locked ();
+ //region_locked ();
set_colors ();
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 ();
-
- SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
-
- PublicEditor& editor (trackview.editor());
- editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
}
InstrumentInfo&
bool
MidiRegionView::enter_notify (GdkEventCrossing* ev)
{
- enter_internal();
+ enter_internal (ev->state);
_entered = true;
return false;
set_frame_color();
if (_entered) {
- if (trackview.editor().internal_editing()) {
- // Switched in to internal editing mode while entered
- enter_internal();
- } else {
- // Switched out of internal editing mode while entered
+ if (!trackview.editor().internal_editing()) {
+ /* Switched out of internal editing mode while entered.
+ Only necessary for leave as a mouse_mode_change over a region
+ automatically triggers an enter event. */
leave_internal();
}
+ else if (trackview.editor().current_mouse_mode() == MouseContent) {
+ // hide cursor and ghost note after changing to internal edit mode
+ remove_ghost_note ();
+
+ /* XXX This is problematic as the function is executed for every region
+ 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 (!_entered_note) {
+ hide_verbose_cursor ();
+ }
+ }
}
}
void
-MidiRegionView::enter_internal()
+MidiRegionView::enter_internal (uint32_t state)
{
if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
// Show ghost note under pencil
- create_ghost_note(_last_event_x, _last_event_y);
- }
- else {
- remove_ghost_note ();
- hide_verbose_cursor ();
+ create_ghost_note(_last_event_x, _last_event_y, state);
}
if (!_selection.empty()) {
{
hide_verbose_cursor ();
remove_ghost_note ();
+ _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;
}
switch (editor.current_mouse_mode()) {
case MouseRange:
- /* no motion occured - simple click */
- clear_selection ();
+ /* no motion occurred - simple click */
+ clear_editor_note_selection ();
_mouse_changed_selection = true;
break;
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));
- create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
- } else {
- clear_selection ();
- }
+ clear_editor_note_selection ();
break;
}
case MouseDraw:
- {
- Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
- create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
- 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);
+ /* 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) {
- remove_ghost_note ();
+ 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) {
+ } else if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
+ Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
+ _mouse_state != AddDragging) {
- create_ghost_note (ev->x, ev->y);
+ create_ghost_note (ev->x, ev->y, ev->state);
- } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
- Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
+ } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
+ Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
- update_ghost_note (ev->x, ev->y);
+ update_ghost_note (ev->x, ev->y, ev->state);
- } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
+ } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
- remove_ghost_note ();
- hide_verbose_cursor ();
+ remove_ghost_note ();
+ hide_verbose_cursor ();
- } else if (editor.current_mouse_mode() == MouseDraw) {
+ } else if (editor.current_mouse_mode() == MouseDraw) {
- if (_ghost_note) {
- update_ghost_note (ev->x, ev->y);
- }
- else {
- create_ghost_note (ev->x, ev->y);
+ if (_ghost_note) {
+ update_ghost_note (ev->x, ev->y, ev->state);
+ }
+ else {
+ create_ghost_note (ev->x, ev->y, ev->state);
+ }
}
}
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_selection ();
+ clear_editor_note_selection ();
_mouse_changed_selection = true;
}
_mouse_state = SelectRectDragging;
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) {
- clear_selection();
+ clear_editor_note_selection ();
_mouse_state = None;
} else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
* \param snap_t true to snap t to the grid, otherwise false.
*/
void
-MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bool snap_t)
+MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap)
{
if (length < 2 * DBL_EPSILON) {
return;
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
MidiStreamView* const view = mtv->midi_view();
+ boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion> (_region);
- // Start of note in frames relative to region start
- if (snap_t) {
- framecnt_t grid_frames;
- t = snap_frame_to_grid_underneath (t, grid_frames);
+ if (!mr) {
+ return;
}
- const MidiModel::TimeType beat_time = region_frames_to_region_beats(
- t + _region->start());
+ // Start of note in frames relative to region start
+ const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
+ Evoral::Beats beat_time = snap_frame_to_grid_underneath (t, divisions, shift_snap);
const double note = view->y_to_note(y);
const uint8_t chan = mtv->get_channel_for_add();
start_note_diff_command(_("add note"));
- clear_selection ();
+ clear_editor_note_selection ();
note_diff_add_note (new_note, true, false);
apply_diff();
}
void
-MidiRegionView::clear_events (bool with_selection_signal)
+MidiRegionView::clear_events ()
{
- clear_selection (with_selection_signal);
+ // clear selection without signaling
+ clear_selection_internal ();
MidiGhostRegion* gr;
for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
}
}
- 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 (false);
+ 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);
{
delete _note_diff_command;
_note_diff_command = 0;
- clear_selection();
+ clear_editor_note_selection();
}
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;
/** This version finds any canvas note matching the supplied note. */
NoteBase*
-MidiRegionView::find_canvas_note (NoteType note)
+MidiRegionView::find_canvas_note (Evoral::event_id_t id)
{
Events::iterator it;
for (it = _events.begin(); it != _events.end(); ++it) {
- if (*((*it)->note()) == note) {
- 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());
-
- MidiModel::Notes& notes (_model->notes());
+ bool empty_when_starting = _events.empty();
_optimization_iterator = _events.begin();
+ MidiModel::Notes missing_notes;
+ Note* sus = NULL;
+ Hit* hit = NULL;
- bool empty_when_starting = _events.empty();
+ MidiModel::ReadLock lock(_model->read_lock());
+ MidiModel::Notes& notes (_model->notes());
+ NoteBase* cne;
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
boost::shared_ptr<NoteType> note (*n);
- NoteBase* cne;
bool visible;
if (note_in_region_range (note, visible)) {
-
if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
-
cne->validate ();
- update_note (cne);
-
- if (visible) {
- cne->show ();
+ 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<boost::shared_ptr<NoteType> >::iterator it;
- for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
- if (*(*it) == *note) {
- 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_cleared_connection.disconnect ();
-
- _selection.clear();
- clear_events (false);
+ _entered_note = 0;
+ clear_events ();
delete _note_group;
delete _note_diff_command;
delete _step_edit_cursor;
- delete _temporary_note_group;
}
void
MidiRegionView::region_resized (const PropertyChange& what_changed)
{
- RegionView::region_resized(what_changed);
+ RegionView::region_resized(what_changed); // calls RegionView::set_duration()
if (what_changed.contains (ARDOUR::Properties::position)) {
_region_relative_time_converter.set_origin_b(_region->position());
_region_relative_time_converter_double.set_origin_b(_region->position());
- set_duration(_region->length(), 0);
- if (_enable_display) {
- redisplay_model();
- }
+ /* reset_width dependent_items() redisplays model */
+
}
if (what_changed.contains (ARDOUR::Properties::start) ||
what_changed.contains (ARDOUR::Properties::position)) {
_source_relative_time_converter.set_origin_b (_region->position() - _region->start());
}
+ /* catch end and start trim so we can update the view*/
+ if (!what_changed.contains (ARDOUR::Properties::start) &&
+ what_changed.contains (ARDOUR::Properties::length)) {
+ enable_display (true);
+ } else if (what_changed.contains (ARDOUR::Properties::start) &&
+ what_changed.contains (ARDOUR::Properties::length)) {
+ enable_display (true);
+ }
}
void
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);
}
+ ghost->set_colors ();
+ ghost->set_height ();
+ ghost->set_duration (_region->length() / samples_per_pixel);
+
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- ghost->add_note(*i);
+ ghost->add_note(i->second);
}
- ghost->set_height ();
- ghost->set_duration (_region->length() / samples_per_pixel);
ghosts.push_back (ghost);
-
+ enable_display (true);
return ghost;
}
bool
MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
{
- /* This is imprecise due to all the conversion conversion involved, so only
- hide notes if they seem to start more than one tick before the start. */
- const framecnt_t tick_frames = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
- const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
- const bool outside = ((note_start_frames <= -tick_frames) ||
- (note_start_frames >= _region->length()));
+ const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
- visible = (note->note() >= midi_stream_view()->lowest_note()) &&
- (note->note() <= midi_stream_view()->highest_note());
+ /* 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());
+
+ visible = (note->note() >= _current_range_min) &&
+ (note->note() <= _current_range_max);
return !outside;
}
void
MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
{
+ TempoMap& map (trackview.session()->tempo_map());
+ const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
boost::shared_ptr<NoteType> note = ev->note();
- const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
- const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
- ev->set_x0 (x);
- ev->set_y0 (y0);
+ 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(note_to_y(note->note()));
+ double y1;
/* trim note display to not overlap the end of its region */
+ if (note->length().to_double() > 0.0) {
+ double note_end_time = note->end_time().to_double();
- if (note->length() > 0) {
- const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
- ev->set_x1 (std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1);
+ if (note_end_time > mr->start_beats() + mr->length_beats()) {
+ note_end_time = mr->start_beats() + mr->length_beats();
+ }
+
+ const framepos_t note_end_frames = map.frame_at_quarter_note (session_source_start + note_end_time) - _region->position();
+
+ x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
} else {
- ev->set_x1 (std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1);
+ x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
}
- ev->set_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));
if (!note->length()) {
if (_active_notes && note->note() < 128) {
if (old_rect) {
/* There is an active note on this key, so we have a stuck
note. Finish the old rectangle here. */
- old_rect->set_x1 (x);
+ old_rect->set_x1 (x1);
old_rect->set_outline_all ();
}
_active_notes[note->note()] = ev;
}
// Update color in case velocity has changed
- ev->set_fill_color(ev->base_color());
- ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
-
- 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);
- }
- }
- }
+ const uint32_t base_col = ev->base_color();
+ ev->set_fill_color(base_col);
+ ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
+
}
void
{
boost::shared_ptr<NoteType> note = ev->note();
- const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
+ const double note_time_qn = note->time().to_double() + (_region->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_height (diamond_size);
// Update color in case velocity has changed
- ev->set_fill_color(ev->base_color());
- ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
-
- 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);
- }
- }
- }
+ const uint32_t base_col = ev->base_color();
+ ev->set_fill_color(base_col);
+ ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
+
}
/** 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();
framepos_t region_end = _region->last_frame();
if (end_frame > region_end) {
- _region->set_length (end_frame - _region->position());
+ /* XX sets length in beats from audio space. make musical */
+ _region->set_length (end_frame - _region->position(), 0);
}
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
start_note_diff_command (_("step add"));
- clear_selection ();
+ clear_editor_note_selection ();
note_diff_add_note (new_note, true, false);
apply_diff();
// 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;
}
return;
}
+ if (trackview.editor().drags()->active()) {
+ return;
+ }
+
start_note_diff_command (_("delete selection"));
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
_selection.clear();
apply_diff ();
+
hide_verbose_cursor ();
}
}
void
-MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
+MidiRegionView::clear_editor_note_selection ()
{
- for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
- if ((*i) != ev) {
- Selection::iterator tmp = i;
- ++tmp;
+ DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
+ PublicEditor& editor(trackview.editor());
+ editor.get_selection().clear_midi_notes();
+}
- (*i)->set_selected (false);
- (*i)->hide_velocity ();
- _selection.erase (i);
+void
+MidiRegionView::clear_selection ()
+{
+ clear_selection_internal();
+ PublicEditor& editor(trackview.editor());
+ editor.get_selection().remove(this);
+}
- i = tmp;
- } else {
- ++i;
- }
+void
+MidiRegionView::clear_selection_internal ()
+{
+ DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
+
+ for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+ (*i)->set_selected(false);
+ (*i)->hide_velocity();
}
+ _selection.clear();
- if (!ev && _entered) {
+ if (_entered) {
// Clearing selection entirely, ungrab keyboard
Keyboard::magic_widget_drop_focus();
_grabbed_keyboard = false;
}
-
- /* this does not change the status of this regionview w.r.t the editor
- selection.
- */
-
- if (signal) {
- SelectionCleared (this); /* EMIT SIGNAL */
- }
}
void
MidiRegionView::unique_select(NoteBase* ev)
{
- const bool selection_was_empty = _selection.empty();
-
- clear_selection_except (ev);
-
- /* don't bother with checking to see if we should remove this
- regionview from the editor selection, since we're about to add
- another note, and thus put/keep this regionview in the editor
- selection anyway.
- */
-
- if (!ev->selected()) {
- add_to_selection (ev);
- if (selection_was_empty && _entered) {
- // Grab keyboard for moving notes with arrow keys
- Keyboard::magic_widget_grab_focus();
- _grabbed_keyboard = true;
- }
- }
+ clear_editor_note_selection();
+ add_to_selection(ev);
}
void
MidiRegionView::select_all_notes ()
{
- clear_selection ();
+ clear_editor_note_selection ();
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- add_to_selection (*i);
+ add_to_selection (i->second);
}
}
void
MidiRegionView::select_range (framepos_t start, framepos_t end)
{
- clear_selection ();
+ 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);
}
}
}
The requested notes most likely won't exist in the view until the next model redisplay.
*/
void
-MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
+MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
{
NoteBase* cne;
- list<boost::shared_ptr<NoteType> >::iterator n;
+ list<Evoral::event_id_t>::iterator n;
for (n = notes.begin(); n != notes.end(); ++n) {
- if ((cne = find_canvas_note(*(*n))) != 0) {
+ if ((cne = find_canvas_note(*n)) != 0) {
add_to_selection (cne);
} else {
_pending_note_selection.insert(*n);
}
if (!add) {
- clear_selection ();
+ clear_editor_note_selection ();
if (!extend && (low_note == high_note) && (high_note == notenum)) {
/* only note previously selected is the one we are
MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
{
if (!add) {
- clear_selection_except (ev);
- if (!_selection.empty()) {
- PublicEditor& editor (trackview.editor());
- editor.get_selection().add (this);
- }
+ clear_editor_note_selection();
+ add_to_selection (ev);
}
if (!extend) {
/* 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;
- }
+ /*
+ 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;
+ */
+
+ // 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 (_("move notes"));
+ start_note_diff_command (_("move notes"));
- for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
+ for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
- framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
- Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
+ double const start_qn = _region->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;
+ }
- 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);
+ }
+
+ // 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;
}
- note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
+ 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);
- uint8_t original_pitch = (*i)->note()->note();
- uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+ /* update pitch */
- // keep notes in standard midi range
- clamp_to_0_127(new_pitch);
+ uint8_t original_pitch = (*i)->note()->note();
+ uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
- lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
- highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
+ (*i)->note()->set_note (new_pitch);
- note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, 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);
+
+ 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);
}
}
void
MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
+ TempoMap& tmap (trackview.session()->tempo_map());
bool cursor_set = false;
bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
sign = -1;
}
- const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x, ensure_snap) : trackview.editor ().pixel_to_sample (current_x));
- Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
- Evoral::Beats len = Evoral::Beats();
+ double snapped_x;
+ int32_t divisions = 0;
+
+ if (with_snap) {
+ snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
+ divisions = trackview.editor().get_grid_music_divisions (0);
+ } else {
+ snapped_x = trackview.editor ().pixel_to_sample (current_x);
+ }
+ const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions)
+ - midi_region()->beat()) + midi_region()->start_beats();
+
+ Evoral::Beats len = Evoral::Beats();
if (at_front) {
if (beats < canvas_note->note()->end_time()) {
MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
_note_diff_command = _model->new_note_diff_command (_("resize notes"));
+ TempoMap& tmap (trackview.session()->tempo_map());
/* XX why doesn't snap_pixel_to_sample() handle this properly? */
bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
sign = -1;
}
+ uint32_t divisions = 0;
/* Convert the new x position to a frame within the source */
framepos_t current_fr;
if (with_snap) {
- current_fr = snap_pixel_to_sample (current_x, ensure_snap) + _region->start ();
+ current_fr = snap_pixel_to_sample (current_x, ensure_snap);
+ divisions = trackview.editor().get_grid_music_divisions (0);
} else {
- current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start ();
+ current_fr = trackview.editor().pixel_to_sample (current_x);
}
/* and then to beats */
- const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr);
+ const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions);
+ 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()) {
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
if (!fine) {
/* non-fine, move by 1 bar regardless of snap */
- delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
+ delta = Evoral::Beats(trackview.session()->tempo_map().meter_at_frame (ref_point).divisions_per_bar());
} else if (trackview.editor().snap_mode() == Editing::SnapOff) {
void
MidiRegionView::note_entered(NoteBase* ev)
{
- _note_entered = true;
+ _entered_note = ev;
Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
if (_mouse_state == SelectTouchDragging) {
+
note_selected (ev, true);
+
} else if (editor->current_mouse_mode() == MouseContent) {
+
+ remove_ghost_note ();
show_verbose_cursor (ev->note ());
+
} else if (editor->current_mouse_mode() == MouseDraw) {
+
+ remove_ghost_note ();
show_verbose_cursor (ev->note ());
}
}
void
MidiRegionView::note_left (NoteBase*)
{
- _note_entered = false;
+ _entered_note = 0;
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
(*i)->hide_velocity ();
MidiRegionView::patch_entered (PatchChange* p)
{
ostringstream s;
- /* XXX should get patch name if we can */
s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
- << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
+ << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
<< _("Channel ") << ((int) p->patch()->channel() + 1);
show_verbose_cursor (s.str(), 10, 20);
p->item().grab_focus();
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 ();
/** This method handles undo */
bool
-MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
+MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
{
bool commit = false;
// Paste notes, if available
MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
if (m != selection.midi_notes.end()) {
ctx.counts.increase_n_notes();
- if (!(*m)->empty()) { commit = true; }
+ if (!(*m)->empty()) {
+ commit = true;
+ }
paste_internal(pos, ctx.count, ctx.times, **m);
}
typedef RouteTimeAxisView::AutomationTracks ATracks;
const ATracks& atracks = midi_view()->automation_tracks();
for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
- if (a->second->paste(pos, selection, ctx)) {
+ if (a->second->paste(pos, selection, ctx, sub_num)) {
+ if(!commit) {
+ trackview.editor().begin_reversible_command (Operations::paste);
+ }
commit = true;
}
}
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_selection ();
+ clear_editor_note_selection ();
for (int n = 0; n < (int) times; ++n) {
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 */
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
_region->clear_changes ();
- _region->set_length (end_frame - _region->position());
+ /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
+ _region->set_length (end_frame - _region->position(), trackview.editor().get_grid_music_divisions (0));
trackview.session()->add_command (new StatefulDiffCommand (_region));
}
}
};
-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);
}
}
}
void
-MidiRegionView::update_ghost_note (double x, double y)
+MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
{
x = std::max(0.0, x);
PublicEditor& editor = trackview.editor ();
framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
- framecnt_t grid_frames;
- framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
+
+ const int32_t divisions = editor.get_grid_music_divisions (state);
+ const 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();
+ }
/* calculate time in beats relative to start of source */
- const Evoral::Beats length = get_grid_beats(unsnapped_frame);
- const Evoral::Beats time = std::max(
- Evoral::Beats(),
- absolute_frames_to_source_beats (f + _region->position ()));
+ const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
- _ghost_note->note()->set_time (time);
+ _ghost_note->note()->set_time (snapped_beats);
_ghost_note->note()->set_length (length);
- _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
+ _ghost_note->note()->set_note (y_to_note (y));
_ghost_note->note()->set_channel (mtv->get_channel_for_add ());
- _ghost_note->note()->set_velocity (get_velocity_for_add (time));
-
+ _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
/* the ghost note does not appear in ghost regions, so pass false in here */
update_note (_ghost_note, false);
}
void
-MidiRegionView::create_ghost_note (double x, double y)
+MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
{
remove_ghost_note ();
}
_ghost_note->set_ignore_events (true);
_ghost_note->set_outline_color (0x000000aa);
- update_ghost_note (x, y);
+ update_ghost_note (x, y, state);
_ghost_note->show ();
show_verbose_cursor (_ghost_note->note ());
return;
}
- create_ghost_note (_last_ghost_x, _last_ghost_y);
+ create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
}
void
{
/* 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 */
MidiRegionView::enable_display (bool yn)
{
RegionView::enable_display (yn);
- if (yn) {
- redisplay_model ();
- }
}
void
_step_edit_cursor_width = beats;
if (_step_edit_cursor) {
- _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
+ _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (
+ region_beats_to_region_frames (_step_edit_cursor_position + beats)
+ - region_beats_to_region_frames (_step_edit_cursor_position)));
}
}
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) {
void
MidiRegionView::trim_front_starting ()
{
- /* Reparent the note group to the region view's parent, so that it doesn't change
- when the region view is trimmed.
+ /* We used to eparent the note group to the region view's parent, so that it didn't change.
+ now we update it.
*/
- _temporary_note_group = new ArdourCanvas::Container (group->parent ());
- _temporary_note_group->move (group->position ());
- _note_group->reparent (_temporary_note_group);
}
void
MidiRegionView::trim_front_ending ()
{
- _note_group->reparent (group);
- delete _temporary_note_group;
- _temporary_note_group = 0;
-
if (_region->start() < 0) {
/* Trim drag made start time -ve; fix this */
midi_region()->fix_negative_start ();
// display_sysexes();
}
-void
-MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
+std::string
+MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
{
using namespace MIDI::Name;
-
std::string name;
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
n->channel(),
patch_key.bank(),
patch_key.program(),
- n->note());
+ note_value);
}
- mtv->set_note_highlight (n->note());
}
char buf[128];
snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
- (int) n->note (),
- name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
+ (int) note_value,
+ name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
(int) n->channel() + 1,
(int) n->velocity());
- show_verbose_cursor(buf, 10, 20);
+ return buf;
+}
+
+void
+MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
+ uint8_t new_value) const
+{
+ MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ if (mtv) {
+ mtv->set_note_highlight (new_value);
+ }
+
+ show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
+}
+
+void
+MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
+{
+ show_verbose_cursor_for_new_note_value(n, n->note());
}
void
}
/** @param p A session framepos.
- * @param grid_frames Filled in with the number of frames that a grid interval is at p.
- * @return p snapped to the grid subdivision underneath it.
+ * @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
+ * bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
+ * @return beat duration of p snapped to the grid subdivision underneath it.
*/
-framepos_t
-MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
+Evoral::Beats
+MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const
{
- PublicEditor& editor = trackview.editor ();
-
- const Evoral::Beats grid_beats = get_grid_beats(p);
-
- grid_frames = region_beats_to_region_frames (grid_beats);
-
- /* Hack so that we always snap to the note that we are over, instead of snapping
- to the next one if we're more than halfway through the one we're over.
- */
- if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
- p -= grid_frames / 2;
- }
-
- return snap_frame_to_frame (p);
-}
+ TempoMap& map (trackview.session()->tempo_map());
+ double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
-/** Called when the selection has been cleared in any MidiRegionView.
- * @param rv MidiRegionView that the selection was cleared in.
- */
-void
-MidiRegionView::selection_cleared (MidiRegionView* rv)
-{
- if (rv == this) {
- return;
+ if (divisions != 0 && shift_snap) {
+ const double qaf = map.quarter_note_at_frame (p + _region->position());
+ /* Hack so that we always snap to the note that we are over, instead of snapping
+ to the next one if we're more than halfway through the one we're over.
+ */
+ const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
+ const double rem = eqaf - qaf;
+ if (rem >= 0.0) {
+ eqaf -= grid_beats.to_double();
+ }
}
+ const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
- /* Clear our selection in sympathy; but don't signal the fact */
- clear_selection (false);
+ return Evoral::Beats (eqaf - session_start_off);
}
ChannelMode
{
PublicEditor& editor = trackview.editor();
bool success = false;
- Evoral::Beats beats = editor.get_grid_type_as_beats(success, pos);
+ Evoral::Beats beats = editor.get_grid_type_as_beats (success, pos);
if (!success) {
beats = Evoral::Beats(1);
}
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;
+}