summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/editor_drag.cc25
-rw-r--r--gtk2_ardour/editor_drag.h1
-rw-r--r--gtk2_ardour/midi_region_view.cc207
-rw-r--r--gtk2_ardour/midi_region_view.h8
4 files changed, 192 insertions, 49 deletions
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index 91a1b52348..cdbdf7fce8 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -5209,7 +5209,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
//( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
TrackViewList tracks_to_add;
TrackViewList tracks_to_remove;
- for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i)
+ for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i)
if ( !_editor->selection->tracks.contains ( *i ) )
tracks_to_add.push_back ( *i );
_editor->selection->add(tracks_to_add);
@@ -5611,6 +5611,7 @@ NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
, _cumulative_dx (0)
, _cumulative_dy (0)
, _was_selected (false)
+ , _copy (false)
{
DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
@@ -5624,6 +5625,13 @@ void
NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
{
Drag::start_grab (event);
+
+ if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
+ _copy = true;
+ } else {
+ _copy = false;
+ }
+
setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
if (!(_was_selected = _primary->selected())) {
@@ -5720,8 +5728,13 @@ NoteDrag::total_dy () const
}
void
-NoteDrag::motion (GdkEvent * event, bool)
+NoteDrag::motion (GdkEvent * event, bool first_move)
{
+ if (_copy && first_move) {
+ /* make copies of all the selected notes */
+ _primary = _region->copy_selection ();
+ }
+
/* Total change in x and y since the start of the drag */
frameoffset_t const dx = total_dx (event->button.state);
int8_t const dy = total_dy ();
@@ -5737,7 +5750,11 @@ NoteDrag::motion (GdkEvent * event, bool)
int8_t note_delta = total_dy();
if (tdx || tdy) {
- _region->move_selection (tdx, tdy, note_delta);
+ if (_copy) {
+ _region->move_copies (tdx, tdy, note_delta);
+ } else {
+ _region->move_selection (tdx, tdy, note_delta);
+ }
/* the new note value may be the same as the old one, but we
* don't know what that means because the selection may have
@@ -5797,7 +5814,7 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
}
}
} else {
- _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
+ _region->note_dropped (_primary, total_dx (ev->button.state), total_dy(), _copy);
}
}
diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h
index f235881d03..91fb37b82e 100644
--- a/gtk2_ardour/editor_drag.h
+++ b/gtk2_ardour/editor_drag.h
@@ -567,6 +567,7 @@ class NoteDrag : public Drag
double _cumulative_dy;
bool _was_selected;
double _note_height;
+ bool _copy;
};
class NoteCreateDrag : public Drag
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index 5a4a5d6063..b0250f2986 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -1052,7 +1052,7 @@ MidiRegionView::note_diff_add_change (NoteBase* ev,
}
void
-MidiRegionView::apply_diff (bool as_subcommand)
+MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
{
bool add_or_remove;
bool commit = false;
@@ -1061,15 +1061,14 @@ MidiRegionView::apply_diff (bool as_subcommand)
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);
@@ -2587,68 +2586,190 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
}
}
+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->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;
+ 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);
+ }
+
+ // 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"));
- note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
+ for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
- uint8_t original_pitch = (*i)->note()->note();
- uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+ /* update time */
- // keep notes in standard midi range
- clamp_to_0_127(new_pitch);
+ 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);
- lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
- highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
+ 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;
+
+ // keep notes in standard midi range
+ clamp_to_0_127(new_pitch);
- note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, 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);
}
}
diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h
index e1af49abce..fd62312693 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -180,7 +180,7 @@ public:
void note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity = false);
void note_diff_remove_note (NoteBase* ev);
- void apply_diff (bool as_subcommand = false);
+ void apply_diff (bool as_subcommand = false, bool was_copy = false);
void abort_command();
void note_entered(NoteBase* ev);
@@ -201,7 +201,9 @@ public:
void invert_selection ();
void move_selection(double dx, double dy, double cumulative_dy);
- void note_dropped (NoteBase* ev, ARDOUR::frameoffset_t, int8_t d_note);
+ void note_dropped (NoteBase* ev, ARDOUR::frameoffset_t, int8_t d_note, bool copy);
+ NoteBase* copy_selection ();
+ void move_copies(double dx, double dy, double cumulative_dy);
void select_notes (std::list<Evoral::event_id_t>);
void select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend);
@@ -412,6 +414,7 @@ private:
typedef boost::unordered_map<boost::shared_ptr<NoteType>, NoteBase*> Events;
typedef boost::unordered_map<ARDOUR::MidiModel::PatchChangePtr, boost::shared_ptr<PatchChange> > PatchChanges;
typedef std::vector< boost::shared_ptr<SysEx> > SysExes;
+ typedef std::vector<NoteBase*> CopyDragEvents;
ARDOUR::BeatsFramesConverter _region_relative_time_converter;
ARDOUR::BeatsFramesConverter _source_relative_time_converter;
@@ -419,6 +422,7 @@ private:
boost::shared_ptr<ARDOUR::MidiModel> _model;
Events _events;
+ CopyDragEvents _copy_drag_events;
PatchChanges _patch_changes;
SysExes _sys_exes;
Note** _active_notes;