using namespace ArdourCanvas;
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::Group *parent, RouteTimeAxisView &tv,
boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
: RegionView (parent, tv, r, spu, basic_color)
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
+ Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
connect_to_diskstream ();
+
+ SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
}
MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
connect_to_diskstream ();
+
+ SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
+}
+
+void
+MidiRegionView::parameter_changed (std::string const & p)
+{
+ if (p == "diplay-first-midi-bank-as-zero") {
+ if (_enable_display) {
+ redisplay_model();
+ }
+ }
}
MidiRegionView::MidiRegionView (const MidiRegionView& other)
ui_bind(&MidiRegionView::snap_changed, this),
gui_context());
+ Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
connect_to_diskstream ();
+
+ SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
+}
+
+const boost::shared_ptr<ARDOUR::MidiRegion>
+MidiRegionView::midi_region() const
+{
+ return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
}
void
{
trackview.editor().MouseModeChanged.connect (
_mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
- );
-
- Keyboard::magic_widget_grab_focus();
- group->grab_focus();
+ );
if (trackview.editor().current_mouse_mode() == MouseRange) {
create_ghost_note (ev->x, ev->y);
}
+ if (!trackview.editor().internal_editing()) {
+ Keyboard::magic_widget_drop_focus();
+ } else {
+ Keyboard::magic_widget_grab_focus();
+ group->grab_focus();
+ }
+
return false;
}
trackview.editor().verbose_cursor()->hide ();
remove_ghost_note ();
+
return false;
}
remove_ghost_note ();
trackview.editor().verbose_cursor()->hide ();
}
+
+ if (!trackview.editor().internal_editing()) {
+ Keyboard::magic_widget_drop_focus();
+ } else {
+ Keyboard::magic_widget_grab_focus();
+ group->grab_focus();
+ }
}
bool
beats = 1;
}
- create_note_at (event_x, event_y, beats, true);
+ create_note_at (event_x, event_y, beats, true, true);
}
break;
beats = 1;
}
- create_note_at (event_x, event_y, beats, true);
+ create_note_at (event_x, event_y, beats, true, true);
break;
}
const double x = _drag_rect->property_x1();
const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
- create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
+ create_note_at (x, _drag_rect->property_y1(), region_frames_to_region_beats(length), true, false);
}
}
event_y = ev->y;
group->w2i(event_x, event_y);
+ PublicEditor& editor = trackview.editor ();
+
// convert event_x to global frame
- event_frame = snap_pixel_to_frame (event_x);
+ framecnt_t grid_frames;
+ event_frame = snap_frame_to_grid_underneath (editor.pixel_to_frame (event_x), grid_frames);
- if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
+ if (!_ghost_note && editor.current_mouse_mode() != MouseRange
&& Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
&& _mouse_state != AddDragging) {
create_ghost_note (ev->x, ev->y);
- } else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
+ } else if (_ghost_note && editor.current_mouse_mode() != MouseRange
&& Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
update_ghost_note (ev->x, ev->y);
- } else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange) {
+ } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) {
delete _ghost_note;
_ghost_note = 0;
- trackview.editor().verbose_cursor()->hide ();
- } else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
+ editor.verbose_cursor()->hide ();
+ } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) {
update_ghost_note (ev->x, ev->y);
}
return false;
}
- if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
+ if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject
&& !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
// Select drag start
_mouse_state = SelectRectDragging;
return true;
- } else if (trackview.editor().internal_editing()) {
+ } else if (editor.internal_editing()) {
// Add note drag start
- delete _ghost_note;
- _ghost_note = 0;
-
group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
Gdk::Cursor(Gdk::FLEUR), ev->time);
_drag_start_y = event_y;
_drag_rect = new ArdourCanvas::SimpleRect(*group);
- _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
+ _drag_rect->property_x1() = editor.frame_to_pixel(event_frame);
_drag_rect->property_y1() = midi_stream_view()->note_to_y(
midi_stream_view()->y_to_note(event_y));
- _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
+ _drag_rect->property_x2() = editor.frame_to_pixel(event_frame);
+
_drag_rect->property_y2() = _drag_rect->property_y1()
+ floor(midi_stream_view()->note_height());
_drag_rect->property_outline_what() = 0xFF;
_drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
_mouse_state = AddDragging;
+
+ delete _ghost_note;
+ _ghost_note = 0;
- if (_ghost_note) {
-
- delete _ghost_note;
- _ghost_note = 0;
-
- trackview.editor().verbose_cursor()->hide ();
- }
+ editor.verbose_cursor()->hide ();
return true;
}
}
if (_mouse_state == AddDragging) {
- event_x = trackview.editor().frame_to_pixel(event_frame);
+ event_x = editor.frame_to_pixel(event_frame);
+
+ if (editor.snap_mode() == SnapNormal) {
+ /* event_frame will have been snapped to the start of the note we are under;
+ it's more intuitive if we use the end of that note here
+ */
+ event_x = editor.frame_to_pixel (event_frame + grid_frames);
+ } else {
+ event_x = editor.frame_to_pixel (event_frame);
+ }
+
}
if (_drag_rect) {
_drag_rect->property_y1() = event_y;
}
- update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
+ update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y, Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
}
_last_x = event_x;
return false;
}
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+ /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
+ it still works for zoom.
+ */
+ return false;
+ }
+
trackview.editor().verbose_cursor()->hide ();
bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
repeated presses, carry out key actions at key press, not release.
*/
- if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
+ bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
+
+ if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
_mouse_state = SelectTouchDragging;
return true;
- } else if (ev->keyval == GDK_Escape) {
+ } else if (ev->keyval == GDK_Escape && unmodified) {
clear_selection();
_mouse_state = None;
- } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
+ } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
bool start = (ev->keyval == GDK_comma);
bool end = (ev->keyval == GDK_period);
return true;
- } else if (ev->keyval == GDK_Delete) {
+ } else if (ev->keyval == GDK_Delete && unmodified) {
delete_selection();
return true;
}
return true;
- } else if (ev->keyval == GDK_Left) {
+ } else if (ev->keyval == GDK_Left && unmodified) {
nudge_notes (false);
return true;
- } else if (ev->keyval == GDK_Right) {
+ } else if (ev->keyval == GDK_Right && unmodified) {
nudge_notes (true);
return true;
- } else if (ev->keyval == GDK_Control_L) {
- return true;
-
- } else if (ev->keyval == GDK_c) {
+ } else if (ev->keyval == GDK_c && unmodified) {
channel_edit ();
return true;
}
bool
MidiRegionView::key_release (GdkEventKey* ev)
{
- if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
+ if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
_mouse_state = None;
return true;
}
MidiRegionView::channel_edit ()
{
bool first = true;
- uint8_t current_channel;
+ uint8_t current_channel = 0;
if (_selection.empty()) {
return;
* \param y vertical position in pixels
* \param length duration of the note in beats, which will be snapped to the grid
* \param sh true to make the note 1 frame shorter than the snapped version of \a length.
+ * \param snap_x true to snap x to the grid, otherwise false.
*/
void
-MidiRegionView::create_note_at(double x, double y, double length, bool sh)
+MidiRegionView::create_note_at (double x, double y, double length, bool sh, bool snap_x)
{
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
MidiStreamView* const view = mtv->midi_view();
assert(note <= 127.0);
// Start of note in frames relative to region start
- framepos_t const start_frames = snap_pixel_to_frame (x);
+ framepos_t start_frames = trackview.editor().pixel_to_frame (x);
+ if (snap_x) {
+ framecnt_t grid_frames;
+ start_frames = snap_frame_to_grid_underneath (start_frames, grid_frames);
+ }
assert(start_frames >= 0);
// Snap length
- length = frames_to_beats(
- snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
+ length = region_frames_to_region_beats(
+ snap_frame_to_frame (start_frames + region_beats_to_region_frames(length)) - start_frames);
assert (length != 0);
if (sh) {
- length = frames_to_beats (beats_to_frames (length) - 1);
+ length = region_frames_to_region_beats (region_beats_to_region_frames (length) - 1);
}
const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
- frames_to_beats(start_frames + _region->start()), length,
+ region_frames_to_region_beats(start_frames + _region->start()), length,
(uint8_t)note, 0x40));
if (_model->contains (new_note)) {
if (chn_mask & (1<<i)) {
display_patch_changes_on_channel (i);
}
+ /* TODO gray-out patch instad of not displaying it */
}
}
add_canvas_patch_change (*i, patch->name());
} else {
char buf[16];
- /* program and bank numbers are zero-based: convert to one-based */
- snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
+ /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
+ snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
add_canvas_patch_change (*i, buf);
}
}
}
string text = str.str();
- const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
+ const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
double height = midi_stream_view()->contents_height();
if (_active_notes && _active_notes[note]) {
- const framepos_t end_time_frames = beats_to_frames(end_time);
+ /* XXX is end_time really region-centric? I think so, because
+ this is a new region that we're recording, so source zero is
+ the same as region zero
+ */
+ const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
_active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
_active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
bool
-MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
+MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
{
- const framepos_t note_start_frames = beats_to_frames(note->time());
-
- bool outside = (note_start_frames - _region->start() >= _region->length()) ||
- (note_start_frames < _region->start());
+ const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
+ bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
visible = (note->note() >= midi_stream_view()->lowest_note()) &&
(note->note() <= midi_stream_view()->highest_note());
{
boost::shared_ptr<NoteType> note = ev->note();
- const framepos_t note_start_frames = beats_to_frames(note->time());
-
- /* trim note display to not overlap the end of its region */
- const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
-
- const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
+ const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
const double y1 = midi_stream_view()->note_to_y(note->note());
- const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
ev->property_x1() = x;
ev->property_y1() = y1;
+ /* trim note display to not overlap the end of its region */
+
if (note->length() > 0) {
- ev->property_x2() = note_endpixel;
+ const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
+ ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
} else {
- ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
+ ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
}
ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
{
boost::shared_ptr<NoteType> note = ev->note();
- const framepos_t note_start_frames = beats_to_frames(note->time());
- const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
+ const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
+ const double x = trackview.editor().frame_to_pixel(note_start_frames);
const double diamond_size = midi_stream_view()->note_height() / 2.0;
const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
const double diamond_size = midi_stream_view()->note_height() / 2.0;
- CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
+ CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
update_hit (ev_diamond);
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
MidiStreamView* const view = mtv->midi_view();
- view->update_note_range(note->note());
+ view->update_note_range (note->note());
}
void
/* potentially extend region to hold new note */
- framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
- framepos_t region_end = _region->position() + _region->length() - 1;
+ framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
+ framepos_t region_end = _region->last_frame();
if (end_frame > region_end) {
_region->set_length (end_frame - _region->position());
{
assert (patch->time() >= 0);
- const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
+ const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
double const height = midi_stream_view()->contents_height();
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
c->add (MidiModel::PatchChangePtr (
new Evoral::PatchChange<Evoral::MusicalTime> (
- frames_to_beats (t + midi_region()->start()), mtv->get_channel_for_add(), patch.program(), patch.bank()
- )
- ));
+ absolute_frames_to_source_beats (_region->position() + t),
+ mtv->get_channel_for_add(), patch.program(), patch.bank()
+ )
+ )
+ );
_model->apply_command (*trackview.session(), c);
}
}
+void
+MidiRegionView::previous_bank (CanvasPatchChange& patch)
+{
+ if (patch.patch()->program() < 127) {
+ MIDI::Name::PatchPrimaryKey key;
+ get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
+ if (key.lsb > 0) {
+ key.lsb--;
+ change_patch_change (patch, key);
+ } else {
+ if (key.msb > 0) {
+ key.lsb = 127;
+ key.msb--;
+ change_patch_change (patch, key);
+ }
+ }
+ }
+}
+
+void
+MidiRegionView::next_bank (CanvasPatchChange& patch)
+{
+ if (patch.patch()->program() > 0) {
+ MIDI::Name::PatchPrimaryKey key;
+ get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
+ if (key.lsb < 127) {
+ key.lsb++;
+ change_patch_change (patch, key);
+ } else {
+ if (key.msb < 127) {
+ key.lsb = 0;
+ key.msb++;
+ change_patch_change (patch, key);
+ }
+ }
+ }
+}
+
void
MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
{
}
void
-MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
-{
- for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
- if ((*i)->selected() && (*i) != ev) {
- (*i)->set_selected(false);
- (*i)->hide_velocity();
- }
- }
-
- _selection.clear();
-}
-
-void
-MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
+MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
{
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
if ((*i) != ev) {
-
Selection::iterator tmp = i;
++tmp;
(*i)->set_selected (false);
+ (*i)->hide_velocity ();
_selection.erase (i);
-
+
i = tmp;
-
} else {
++i;
}
}
- /* don't bother with removing this regionview from the editor selection,
- since we're about to add another note, and thus put/keep this
- regionview in the editor selection.
+ /* 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(ArdourCanvas::CanvasNoteEvent* ev)
+{
+ 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()) {
}
}
+void
+MidiRegionView::select_range (framepos_t start, framepos_t end)
+{
+ clear_selection ();
+
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
+ framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
+ if (t >= start && t <= end) {
+ add_to_selection (*i);
+ }
+ }
+}
+
+void
+MidiRegionView::invert_selection ()
+{
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
+ if ((*i)->selected()) {
+ remove_from_selection(*i);
+ } else {
+ add_to_selection (*i);
+ }
+ }
+}
+
void
MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
{
}
void
-MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
+MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
{
if (!add) {
- clear_selection_except(ev);
+ clear_selection_except (ev);
+ if (!_selection.empty()) {
+ PublicEditor& editor (trackview.editor());
+ editor.get_selection().add (this);
+ }
}
if (!extend) {
}
void
-MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
+MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
{
if (x1 > x2) {
swap (x1, x2);
if (!(*i)->selected()) {
add_to_selection (*i);
}
- } else if ((*i)->selected()) {
+ } else if ((*i)->selected() && !extend) {
// Not inside rectangle
remove_from_selection (*i);
}
for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
- Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
+ Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
if (new_time < 0) {
continue;
// keep notes in standard midi range
clamp_to_0_127(new_pitch);
- // keep original pitch if note is dragged outside valid midi range
- if ((original_pitch != 0 && new_pitch == 0)
- || (original_pitch != 127 && new_pitch == 127)) {
- new_pitch = original_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);
}
}
+/** @param x Pixel relative to the region position.
+ * @return Snapped frame relative to the region position.
+ */
framepos_t
MidiRegionView::snap_pixel_to_frame(double x)
{
return snap_frame_to_frame (editor.pixel_to_frame (x));
}
-/** Snap a frame offset within our region using the current snap settings.
- * @param x Frame offset from this region's position.
- * @return Snapped frame offset from this region's position.
+/** @param x Pixel relative to the region position.
+ * @return Snapped pixel relative to the region position.
*/
-frameoffset_t
-MidiRegionView::snap_frame_to_frame (frameoffset_t x)
-{
- PublicEditor& editor = trackview.editor();
-
- /* x is region relative, convert it to global absolute frames */
- framepos_t const session_frame = x + _region->position();
-
- /* try a snap in either direction */
- framepos_t frame = session_frame;
- editor.snap_to (frame, 0);
-
- /* if we went off the beginning of the region, snap forwards */
- if (frame < _region->position ()) {
- frame = session_frame;
- editor.snap_to (frame, 1);
- }
-
- /* back to region relative */
- return frame - _region->position();
-}
-
double
MidiRegionView::snap_to_pixel(double x)
{
}
framepos_t
-MidiRegionView::beats_to_frames(double beats) const
+MidiRegionView::source_beats_to_absolute_frames(double beats) const
{
- return _time_converter.to(beats);
+ /* the time converter will return the frame corresponding to `beats'
+ relative to the start of the source. The start of the source
+ is an implied position given by region->position - region->start
+ */
+ const framepos_t source_start = _region->position() - _region->start();
+ return source_start + _source_relative_time_converter.to (beats);
}
double
-MidiRegionView::frames_to_beats(framepos_t frames) const
+MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
+{
+ /* the `frames' argument needs to be converted into a frame count
+ relative to the start of the source before being passed in to the
+ converter.
+ */
+ const framepos_t source_start = _region->position() - _region->start();
+ return _source_relative_time_converter.from (frames - source_start);
+}
+
+framepos_t
+MidiRegionView::region_beats_to_region_frames(double beats) const
{
- return _time_converter.from(frames);
+ return _region_relative_time_converter.to(beats);
+}
+
+double
+MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
+{
+ return _region_relative_time_converter.from(frames);
}
void
double beats;
beats = snap_pixel_to_frame (current_x);
- beats = frames_to_beats (beats);
+ beats = region_frames_to_region_beats (beats);
double len;
}
}
- /* Convert that to a frame within the region */
+ /* Convert that to a frame within the source */
current_x = snap_pixel_to_frame (current_x) + _region->start ();
/* and then to beats */
- current_x = frames_to_beats (current_x);
+ current_x = region_frames_to_region_beats (current_x);
if (at_front && current_x < canvas_note->note()->end_time()) {
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
into a vector and sort before using the first one.
*/
- framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
+ framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
framepos_t unused;
framepos_t distance;
return;
}
- Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
+ Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
if (!forward) {
delta = -delta;
MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
{
ostringstream s;
- s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
+ /* XXX should get patch name if we can */
+ s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
show_verbose_cursor (s.str(), 10, 20);
}
Evoral::MusicalTime end_point = 0;
duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
- paste_pos_beats = frames_to_beats (pos - _region->position());
+ paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
paste_pos_beats = 0;
/* if we pasted past the current end of the region, extend the region */
- framepos_t end_frame = _region->position() + beats_to_frames (end_point);
+ framepos_t end_frame = source_beats_to_absolute_frames (end_point);
framepos_t region_end = _region->position() + _region->length() - 1;
if (end_frame > region_end) {
_last_ghost_y = y;
_note_group->w2i (x, y);
- framepos_t const f = snap_pixel_to_frame (x);
- bool success;
- Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
-
- if (!success) {
- beats = 1;
- }
-
- double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
+ PublicEditor& editor = trackview.editor ();
+
+ framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
+ framecnt_t grid_frames;
+ framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
+
+ /* use region_frames... because we are converting a delta within the region
+ */
+
+ double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
- _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
+ /* note that this sets the time of the ghost note in beats relative to
+ the start of the source; that is how all note times are stored.
+ */
+ _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
_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 ());
_step_edit_cursor_position = pos;
if (_step_edit_cursor) {
- double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
+ double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
_step_edit_cursor->property_x1() = pixel;
set_step_edit_cursor_width (_step_edit_cursor_width);
}
_step_edit_cursor_width = beats;
if (_step_edit_cursor) {
- _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
+ _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
}
}
void
MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
{
- PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
+ PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
if (d.run () != Gtk::RESPONSE_ACCEPT) {
return;
}
trackview.editor().verbose_cursor()->set (text, wx, wy);
trackview.editor().verbose_cursor()->show ();
}
+
+/** @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.
+ */
+framepos_t
+MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
+{
+ PublicEditor& editor = trackview.editor ();
+
+ bool success;
+ Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
+
+ if (!success) {
+ grid_beats = 1;
+ }
+
+ 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);
+}
+
+/** 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;
+ }
+
+ /* Clear our selection in sympathy; but don't signal the fact */
+ clear_selection (false);
+}