#include "sys_ex.h"
#include "ui_config.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace PBD;
, _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)
, _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)
, _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)
, _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)
bool
MidiRegionView::enter_notify (GdkEventCrossing* ev)
{
- enter_internal();
+ enter_internal (ev->state);
_entered = true;
return false;
}
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);
+ create_ghost_note(_last_event_x, _last_event_y, state);
}
if (!_selection.empty()) {
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);
+ 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 ();
}
}
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);
+ 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;
}
default:
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);
+ create_ghost_note (ev->x, ev->y, ev->state);
case SelectRectDragging:
editor.drags()->end_grab ((GdkEvent *) ev);
_mouse_state = None;
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())) {
- 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 (editor.current_mouse_mode() == MouseDraw) {
if (_ghost_note) {
- update_ghost_note (ev->x, ev->y);
+ update_ghost_note (ev->x, ev->y, ev->state);
}
else {
- create_ghost_note (ev->x, ev->y);
+ create_ghost_note (ev->x, ev->y, ev->state);
}
}
}
* \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;
}
// 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);
- }
+ 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 MidiModel::TimeType beat_time = Evoral::Beats (trackview.session()->tempo_map().beat_at_frame (_region->position() + t)
- - (mr->beat() - mr->start_beats().to_double()));
const double note = view->y_to_note(y);
const uint8_t chan = mtv->get_channel_for_add();
const uint8_t velocity = get_velocity_for_add(beat_time);
_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);
- NoteBase* cne;
bool visible;
if (note_in_region_range (note, visible)) {
delete _note_group;
delete _note_diff_command;
delete _step_edit_cursor;
- delete _temporary_note_group;
}
void
what_changed.contains (ARDOUR::Properties::position)) {
_source_relative_time_converter.set_origin_b (_region->position() - _region->start());
}
- if (what_changed.contains (ARDOUR::Properties::length)) {
+ /* 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
if (_enable_display) {
redisplay_model();
- }
+ }
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
if ((*x)->canvas_item()->width() >= _pixel_width) {
TempoMap& map (trackview.session()->tempo_map());
const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
boost::shared_ptr<NoteType> note = ev->note();
- const framepos_t note_start_frames = map.frame_at_beat (_region->beat() - mr->start_beats().to_double()
- + note->time().to_double()) - _region->position();
-
+ const double qn_note_time = note->time().to_double() + ((_region->pulse() * 4.0) - mr->start_beats().to_double());
+ const framepos_t note_start_frames = map.frame_at_quarter_note (qn_note_time) - _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()));
- double y1;/* trim note display to not overlap the end of its region */
+ double y1;
+ /* trim note display to not overlap the end of its region */
if (note->length() > 0) {
Evoral::Beats note_end_time = note->end_time();
if (note->end_time() > mr->start_beats() + mr->length_beats()) {
- note_end_time = mr->length_beats();
+ note_end_time = mr->start_beats() + mr->length_beats();
}
+ const double session_qn_start = (_region->pulse() * 4.0) - mr->start_beats().to_double();
+ const double quarter_note_end_time = session_qn_start + note_end_time.to_double();
- const framepos_t note_end_frames = map.frame_at_beat (_region->beat() - mr->start_beats().to_double()
- + note_end_time.to_double()) - _region->position();
-
+ const framepos_t note_end_frames = map.frame_at_quarter_note (quarter_note_end_time) - _region->position();
x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
} else {
x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1);
- ev->set_x0 (x0);
- ev->set_x1 (x1);
- ev->set_y0 (y0);
- ev->set_y1 (y1);
+ ArdourCanvas::Rect rect (x0, y0, x1, y1);
+ ev->set (rect);
if (!note->length()) {
if (_active_notes && note->note() < 128) {
}
// Update color in case velocity has changed
- //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()));
+ 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()));
if (update_ghost_regions) {
for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
{
boost::shared_ptr<NoteType> note = ev->note();
- const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_beat (_region->beat() - midi_region()->start_beats().to_double()
- + note->time().to_double()) - _region->position();
+ const double note_time_qn = note->time().to_double() + ((_region->pulse() * 4.0) - midi_region()->start_beats().to_double());
+ 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;
return;
}
+ if (trackview.editor().drags()->active()) {
+ return;
+ }
+
start_note_diff_command (_("delete selection"));
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
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"));
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->pulse() * 4.0) - midi_region()->start_beats().to_double();
+ 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;
}
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);
+ divisions = trackview.editor().get_grid_music_divisions (0);
} else {
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 + _region->start());
+ const double e_baf = tmap.exact_beat_at_frame (current_fr + midi_region()->position(), divisions);
+ const double quarter_note_start_beat = tmap.quarter_note_at_beat (_region->beat() - midi_region()->start_beats().to_double());
+ const Evoral::Beats x_beats = Evoral::Beats (tmap.quarter_note_at_beat (e_baf) - quarter_note_start_beat);
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));
/** This method handles undo */
bool
-MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t& sub_num)
+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);
}
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, sub_num)) {
+ if(!commit) {
+ trackview.editor().begin_reversible_command (Operations::paste);
+ }
commit = true;
}
}
}
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 double snapped_region_qn = snap_frame_to_grid_underneath (unsnapped_frame, divisions, true).to_double();
+
+ 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);
- 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_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
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 ();
char buf[128];
snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
(int) note_value,
- name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(),
+ name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
(int) n->channel() + 1,
(int) n->velocity());
}
/** @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 p_beat = region_frames_to_region_beats (p);
- const Evoral::Beats grid_beats = get_grid_beats(p);
-
- grid_frames = region_beats_to_region_frames (p_beat + grid_beats) - region_beats_to_region_frames (p_beat);
+ TempoMap& map (trackview.session()->tempo_map());
+ double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
- /* 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;
+ 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() > _region->pulse() * 4.0) {
+ eqaf -= grid_beats.to_double();
+ }
}
+ const double session_start_off = (_region->pulse() * 4.0) - midi_region()->start_beats().to_double();
- return snap_frame_to_frame (p);
+ return Evoral::Beats (eqaf - session_start_off);
}
ChannelMode