2006-08-10 23:24:57 -04:00
|
|
|
/*
|
2019-08-02 17:26:43 -04:00
|
|
|
* Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
|
|
|
|
* Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
|
|
|
|
* Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
|
|
|
|
* Copyright (C) 2008-2012 Hans Baier <hansfbaier@googlemail.com>
|
|
|
|
* Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
|
|
|
|
* Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
|
|
|
|
* Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
|
|
|
|
* Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
|
|
|
|
* Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
|
|
|
|
* Copyright (C) 2015-2017 André Nusser <andre.nusser@googlemail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2006-08-10 23:24:57 -04:00
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <algorithm>
|
2009-02-16 04:51:40 -05:00
|
|
|
#include <ostream>
|
2006-08-10 23:24:57 -04:00
|
|
|
|
|
|
|
#include <gtkmm.h>
|
|
|
|
|
2012-01-19 17:23:28 -05:00
|
|
|
#include "gtkmm2ext/gtk_ui.h"
|
2006-08-10 23:24:57 -04:00
|
|
|
|
2007-07-16 21:48:42 -04:00
|
|
|
#include <sigc++/signal.h>
|
|
|
|
|
2014-12-13 00:37:34 -05:00
|
|
|
#include "midi++/midnam_patch.h"
|
|
|
|
|
2009-08-13 23:00:41 -04:00
|
|
|
#include "pbd/memento_command.h"
|
2010-03-02 13:05:26 -05:00
|
|
|
#include "pbd/stateful_diff_command.h"
|
2009-08-13 23:00:41 -04:00
|
|
|
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/midi_model.h"
|
2015-03-01 13:33:25 -05:00
|
|
|
#include "ardour/midi_playlist.h"
|
2013-01-21 02:24:42 -05:00
|
|
|
#include "ardour/midi_region.h"
|
|
|
|
#include "ardour/midi_source.h"
|
|
|
|
#include "ardour/midi_track.h"
|
2014-11-15 16:32:08 -05:00
|
|
|
#include "ardour/operations.h"
|
2009-10-30 11:30:22 -04:00
|
|
|
#include "ardour/session.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
|
2019-10-25 15:13:51 -04:00
|
|
|
#include "evoral/Parameter.h"
|
|
|
|
#include "evoral/Event.h"
|
|
|
|
#include "evoral/Control.h"
|
2010-05-20 18:38:12 -04:00
|
|
|
#include "evoral/midi_util.h"
|
2006-08-10 23:24:57 -04:00
|
|
|
|
2013-04-25 16:06:12 -04:00
|
|
|
#include "canvas/debug.h"
|
2014-06-26 15:07:29 -04:00
|
|
|
#include "canvas/text.h"
|
2013-04-04 00:32:52 -04:00
|
|
|
|
2009-08-10 15:29:29 -04:00
|
|
|
#include "automation_region_view.h"
|
|
|
|
#include "automation_time_axis.h"
|
2014-11-16 22:35:37 -05:00
|
|
|
#include "control_point.h"
|
2011-02-20 20:52:15 -05:00
|
|
|
#include "debug.h"
|
2010-09-21 11:15:06 -04:00
|
|
|
#include "editor.h"
|
2011-11-16 15:11:33 -05:00
|
|
|
#include "editor_drag.h"
|
2006-08-10 23:24:57 -04:00
|
|
|
#include "ghostregion.h"
|
|
|
|
#include "gui_thread.h"
|
2014-11-16 22:35:37 -05:00
|
|
|
#include "item_counts.h"
|
2007-08-04 20:50:54 -04:00
|
|
|
#include "keyboard.h"
|
2011-06-13 10:48:48 -04:00
|
|
|
#include "midi_channel_dialog.h"
|
2009-08-12 21:57:03 -04:00
|
|
|
#include "midi_cut_buffer.h"
|
2009-08-26 23:09:30 -04:00
|
|
|
#include "midi_list_editor.h"
|
2009-08-10 15:29:29 -04:00
|
|
|
#include "midi_region_view.h"
|
|
|
|
#include "midi_streamview.h"
|
|
|
|
#include "midi_time_axis.h"
|
|
|
|
#include "midi_util.h"
|
2012-06-06 09:17:53 -04:00
|
|
|
#include "midi_velocity_dialog.h"
|
2012-01-19 17:23:28 -05:00
|
|
|
#include "mouse_cursors.h"
|
2010-09-15 14:54:04 -04:00
|
|
|
#include "note_player.h"
|
2014-12-06 12:20:52 -05:00
|
|
|
#include "paste_context.h"
|
2009-08-10 15:29:29 -04:00
|
|
|
#include "public_editor.h"
|
2013-03-27 21:50:18 -04:00
|
|
|
#include "route_time_axis.h"
|
2010-08-11 15:11:14 -04:00
|
|
|
#include "rgb_macros.h"
|
2009-08-10 15:29:29 -04:00
|
|
|
#include "selection.h"
|
|
|
|
#include "streamview.h"
|
2010-12-28 13:19:40 -05:00
|
|
|
#include "patch_change_dialog.h"
|
2011-05-02 09:38:16 -04:00
|
|
|
#include "verbose_cursor.h"
|
2013-04-04 00:32:52 -04:00
|
|
|
#include "note.h"
|
|
|
|
#include "hit.h"
|
|
|
|
#include "patch_change.h"
|
|
|
|
#include "sys_ex.h"
|
2015-01-02 09:44:54 -05:00
|
|
|
#include "ui_config.h"
|
2006-08-10 23:24:57 -04:00
|
|
|
|
2016-07-14 14:44:52 -04:00
|
|
|
#include "pbd/i18n.h"
|
2006-08-10 23:24:57 -04:00
|
|
|
|
|
|
|
using namespace ARDOUR;
|
|
|
|
using namespace PBD;
|
|
|
|
using namespace Editing;
|
2014-11-30 18:27:04 -05:00
|
|
|
using namespace std;
|
2009-12-04 17:51:32 -05:00
|
|
|
using Gtkmm2ext::Keyboard;
|
2006-08-10 23:24:57 -04:00
|
|
|
|
2011-08-03 07:15:01 -04:00
|
|
|
#define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
|
|
|
|
|
2014-11-20 17:36:09 -05:00
|
|
|
MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
|
|
|
|
RouteTimeAxisView& tv,
|
|
|
|
boost::shared_ptr<MidiRegion> r,
|
|
|
|
double spu,
|
|
|
|
uint32_t basic_color)
|
2006-08-10 23:24:57 -04:00
|
|
|
: RegionView (parent, tv, r, spu, basic_color)
|
2008-09-22 22:40:29 -04:00
|
|
|
, _current_range_min(0)
|
|
|
|
, _current_range_max(0)
|
2014-11-20 17:36:09 -05:00
|
|
|
, _region_relative_time_converter(r->session().tempo_map(), r->position())
|
|
|
|
, _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
|
2015-05-15 14:15:52 -04:00
|
|
|
, _region_relative_time_converter_double(r->session().tempo_map(), r->position())
|
2007-06-01 19:27:29 -04:00
|
|
|
, _active_notes(0)
|
2014-06-22 11:41:05 -04:00
|
|
|
, _note_group (new ArdourCanvas::Container (group))
|
2010-12-09 16:34:46 -05:00
|
|
|
, _note_diff_command (0)
|
2010-05-25 19:14:41 -04:00
|
|
|
, _ghost_note(0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _step_edit_cursor (0)
|
|
|
|
, _step_edit_cursor_width (1.0)
|
|
|
|
, _step_edit_cursor_position (0.0)
|
|
|
|
, _channel_selection_scoped_note (0)
|
2007-08-09 16:50:56 -04:00
|
|
|
, _mouse_state(None)
|
|
|
|
, _pressed_button(0)
|
2009-09-10 16:41:08 -04:00
|
|
|
, _optimization_iterator (_events.end())
|
2010-01-04 22:52:30 -05:00
|
|
|
, _list_editor (0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _no_sound_notes (false)
|
2014-12-30 19:14:29 -05:00
|
|
|
, _last_display_zoom (0)
|
2010-11-21 18:54:57 -05:00
|
|
|
, _last_event_x (0)
|
|
|
|
, _last_event_y (0)
|
2014-12-06 19:56:36 -05:00
|
|
|
, _grabbed_keyboard (false)
|
|
|
|
, _entered (false)
|
2016-11-19 05:40:41 -05:00
|
|
|
, _entered_note (0)
|
2015-01-10 12:07:31 -05:00
|
|
|
, _mouse_changed_selection (false)
|
2006-08-10 23:24:57 -04:00
|
|
|
{
|
2013-04-25 16:06:12 -04:00
|
|
|
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
|
2016-12-13 12:05:08 -05:00
|
|
|
|
|
|
|
_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");
|
|
|
|
|
2007-08-09 16:50:56 -04:00
|
|
|
_note_group->raise_to_top();
|
2011-05-19 17:11:21 -04:00
|
|
|
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
|
2010-10-08 10:54:16 -04:00
|
|
|
|
2012-04-25 08:58:19 -04:00
|
|
|
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
|
2018-07-03 18:33:11 -04:00
|
|
|
UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
|
|
|
|
|
2010-10-08 10:54:16 -04:00
|
|
|
connect_to_diskstream ();
|
2006-08-10 23:24:57 -04:00
|
|
|
}
|
|
|
|
|
2014-11-20 17:36:09 -05:00
|
|
|
MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
|
|
|
|
RouteTimeAxisView& tv,
|
|
|
|
boost::shared_ptr<MidiRegion> r,
|
|
|
|
double spu,
|
|
|
|
uint32_t basic_color,
|
2014-12-17 02:34:30 -05:00
|
|
|
bool recording,
|
2014-11-20 17:36:09 -05:00
|
|
|
TimeAxisViewItem::Visibility visibility)
|
2014-12-17 02:34:30 -05:00
|
|
|
: RegionView (parent, tv, r, spu, basic_color, recording, visibility)
|
2011-12-03 08:38:53 -05:00
|
|
|
, _current_range_min(0)
|
|
|
|
, _current_range_max(0)
|
2014-11-20 17:36:09 -05:00
|
|
|
, _region_relative_time_converter(r->session().tempo_map(), r->position())
|
|
|
|
, _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
|
2015-05-15 14:15:52 -04:00
|
|
|
, _region_relative_time_converter_double(r->session().tempo_map(), r->position())
|
2007-06-01 19:27:29 -04:00
|
|
|
, _active_notes(0)
|
2014-12-30 19:14:29 -05:00
|
|
|
, _note_group (new ArdourCanvas::Container (group))
|
2010-12-09 16:34:46 -05:00
|
|
|
, _note_diff_command (0)
|
2010-05-25 19:14:41 -04:00
|
|
|
, _ghost_note(0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _step_edit_cursor (0)
|
|
|
|
, _step_edit_cursor_width (1.0)
|
|
|
|
, _step_edit_cursor_position (0.0)
|
2010-12-29 11:34:51 -05:00
|
|
|
, _channel_selection_scoped_note (0)
|
2007-08-09 16:50:56 -04:00
|
|
|
, _mouse_state(None)
|
|
|
|
, _pressed_button(0)
|
2009-09-10 16:41:08 -04:00
|
|
|
, _optimization_iterator (_events.end())
|
2010-01-04 22:52:30 -05:00
|
|
|
, _list_editor (0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _no_sound_notes (false)
|
2014-12-30 19:14:29 -05:00
|
|
|
, _last_display_zoom (0)
|
2010-11-21 18:54:57 -05:00
|
|
|
, _last_event_x (0)
|
|
|
|
, _last_event_y (0)
|
2014-12-06 19:56:36 -05:00
|
|
|
, _grabbed_keyboard (false)
|
|
|
|
, _entered (false)
|
2016-11-19 05:40:41 -05:00
|
|
|
, _entered_note (0)
|
2015-01-10 12:07:31 -05:00
|
|
|
, _mouse_changed_selection (false)
|
2006-08-10 23:24:57 -04:00
|
|
|
{
|
2013-04-25 16:06:12 -04:00
|
|
|
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
|
2016-12-13 12:05:08 -05:00
|
|
|
|
|
|
|
_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");
|
|
|
|
|
2007-08-09 16:50:56 -04:00
|
|
|
_note_group->raise_to_top();
|
2013-04-25 16:06:12 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
|
2010-10-08 10:54:16 -04:00
|
|
|
|
|
|
|
connect_to_diskstream ();
|
2006-08-10 23:24:57 -04:00
|
|
|
}
|
|
|
|
|
2011-08-03 07:15:01 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::parameter_changed (std::string const & p)
|
|
|
|
{
|
2014-04-15 12:15:30 -04:00
|
|
|
if (p == "display-first-midi-bank-as-zero") {
|
2011-08-03 07:15:01 -04:00
|
|
|
if (_enable_display) {
|
|
|
|
redisplay_model();
|
|
|
|
}
|
2016-12-29 17:05:08 -05:00
|
|
|
} else if (p == "color-regions-using-track-color") {
|
|
|
|
set_colors ();
|
2018-07-03 18:33:11 -04:00
|
|
|
} else if (p == "use-note-color-for-velocity") {
|
|
|
|
color_handler ();
|
2011-08-03 07:15:01 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-26 13:18:22 -04:00
|
|
|
MidiRegionView::MidiRegionView (const MidiRegionView& other)
|
2009-02-25 19:58:35 -05:00
|
|
|
: sigc::trackable(other)
|
|
|
|
, RegionView (other)
|
2011-12-03 08:38:53 -05:00
|
|
|
, _current_range_min(0)
|
|
|
|
, _current_range_max(0)
|
2014-11-20 17:36:09 -05:00
|
|
|
, _region_relative_time_converter(other.region_relative_time_converter())
|
|
|
|
, _source_relative_time_converter(other.source_relative_time_converter())
|
2015-05-15 14:15:52 -04:00
|
|
|
, _region_relative_time_converter_double(other.region_relative_time_converter_double())
|
2008-09-26 13:18:22 -04:00
|
|
|
, _active_notes(0)
|
2014-06-22 11:41:05 -04:00
|
|
|
, _note_group (new ArdourCanvas::Container (get_canvas_group()))
|
2010-12-09 16:34:46 -05:00
|
|
|
, _note_diff_command (0)
|
2010-05-25 19:14:41 -04:00
|
|
|
, _ghost_note(0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _step_edit_cursor (0)
|
|
|
|
, _step_edit_cursor_width (1.0)
|
|
|
|
, _step_edit_cursor_position (0.0)
|
|
|
|
, _channel_selection_scoped_note (0)
|
2008-09-26 13:18:22 -04:00
|
|
|
, _mouse_state(None)
|
|
|
|
, _pressed_button(0)
|
2009-09-10 16:41:08 -04:00
|
|
|
, _optimization_iterator (_events.end())
|
2010-01-04 22:52:30 -05:00
|
|
|
, _list_editor (0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _no_sound_notes (false)
|
2014-12-30 19:14:29 -05:00
|
|
|
, _last_display_zoom (0)
|
2010-11-21 18:54:57 -05:00
|
|
|
, _last_event_x (0)
|
|
|
|
, _last_event_y (0)
|
2014-12-06 19:56:36 -05:00
|
|
|
, _grabbed_keyboard (false)
|
|
|
|
, _entered (false)
|
2016-11-19 05:40:41 -05:00
|
|
|
, _entered_note (0)
|
2015-01-10 12:07:31 -05:00
|
|
|
, _mouse_changed_selection (false)
|
2008-09-26 13:18:22 -04:00
|
|
|
{
|
2014-06-09 23:28:32 -04:00
|
|
|
init (false);
|
2008-09-26 13:18:22 -04:00
|
|
|
}
|
|
|
|
|
2009-02-15 12:30:42 -05:00
|
|
|
MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
|
|
|
|
: RegionView (other, boost::shared_ptr<Region> (region))
|
2011-12-03 08:38:53 -05:00
|
|
|
, _current_range_min(0)
|
|
|
|
, _current_range_max(0)
|
2014-11-20 17:36:09 -05:00
|
|
|
, _region_relative_time_converter(other.region_relative_time_converter())
|
|
|
|
, _source_relative_time_converter(other.source_relative_time_converter())
|
2015-05-15 14:15:52 -04:00
|
|
|
, _region_relative_time_converter_double(other.region_relative_time_converter_double())
|
2008-09-26 13:18:22 -04:00
|
|
|
, _active_notes(0)
|
2014-06-22 11:41:05 -04:00
|
|
|
, _note_group (new ArdourCanvas::Container (get_canvas_group()))
|
2010-12-09 16:34:46 -05:00
|
|
|
, _note_diff_command (0)
|
2010-05-25 19:14:41 -04:00
|
|
|
, _ghost_note(0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _step_edit_cursor (0)
|
|
|
|
, _step_edit_cursor_width (1.0)
|
|
|
|
, _step_edit_cursor_position (0.0)
|
|
|
|
, _channel_selection_scoped_note (0)
|
2008-09-26 13:18:22 -04:00
|
|
|
, _mouse_state(None)
|
|
|
|
, _pressed_button(0)
|
2009-09-10 16:41:08 -04:00
|
|
|
, _optimization_iterator (_events.end())
|
2010-01-04 22:52:30 -05:00
|
|
|
, _list_editor (0)
|
2011-05-19 17:11:21 -04:00
|
|
|
, _no_sound_notes (false)
|
2014-12-30 19:14:29 -05:00
|
|
|
, _last_display_zoom (0)
|
2010-11-21 18:54:57 -05:00
|
|
|
, _last_event_x (0)
|
|
|
|
, _last_event_y (0)
|
2014-12-06 19:56:36 -05:00
|
|
|
, _grabbed_keyboard (false)
|
|
|
|
, _entered (false)
|
2016-11-19 05:40:41 -05:00
|
|
|
, _entered_note (0)
|
2015-01-10 12:07:31 -05:00
|
|
|
, _mouse_changed_selection (false)
|
2008-09-26 13:18:22 -04:00
|
|
|
{
|
2014-06-09 23:28:32 -04:00
|
|
|
init (true);
|
2008-09-26 13:18:22 -04:00
|
|
|
}
|
|
|
|
|
2006-08-10 23:24:57 -04:00
|
|
|
void
|
2014-06-09 23:28:32 -04:00
|
|
|
MidiRegionView::init (bool wfd)
|
2006-08-10 23:24:57 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
|
2010-05-28 17:39:12 -04:00
|
|
|
|
2009-01-27 23:55:31 -05:00
|
|
|
if (wfd) {
|
2014-12-17 16:05:27 -05:00
|
|
|
Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
|
|
|
|
midi_region()->midi_source(0)->load_model(lm);
|
2009-01-27 23:55:31 -05:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2007-08-01 16:50:09 -04:00
|
|
|
_model = midi_region()->midi_source(0)->model();
|
|
|
|
_enable_display = false;
|
2017-09-19 10:03:40 -04:00
|
|
|
fill_color_name = "midi frame base";
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2014-06-09 23:28:32 -04:00
|
|
|
RegionView::init (false);
|
2006-08-10 23:24:57 -04:00
|
|
|
|
2016-04-12 15:38:31 -04:00
|
|
|
//set_height (trackview.current_height());
|
2006-08-10 23:24:57 -04:00
|
|
|
|
|
|
|
region_muted ();
|
2008-09-14 15:01:08 -04:00
|
|
|
region_sync_changed ();
|
2010-02-19 13:09:08 -05:00
|
|
|
region_resized (ARDOUR::bounds_change);
|
2016-04-12 15:38:31 -04:00
|
|
|
//region_locked ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2006-08-10 23:24:57 -04:00
|
|
|
set_colors ();
|
2007-06-03 16:06:01 -04:00
|
|
|
|
2007-08-01 16:50:09 -04:00
|
|
|
_enable_display = true;
|
|
|
|
if (_model) {
|
|
|
|
if (wfd) {
|
2009-08-13 23:00:41 -04:00
|
|
|
display_model (_model);
|
2007-08-01 16:50:09 -04:00
|
|
|
}
|
|
|
|
}
|
2007-08-11 02:17:42 -04:00
|
|
|
|
2012-06-12 14:02:03 -04:00
|
|
|
reset_width_dependent_items (_pixel_width);
|
|
|
|
|
2008-09-14 15:14:12 -04:00
|
|
|
group->raise_to_top();
|
2013-03-27 21:50:18 -04:00
|
|
|
|
2015-03-28 23:24:41 -04:00
|
|
|
midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
|
2013-03-27 21:50:18 -04:00
|
|
|
boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
|
|
|
|
gui_context ());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-06-11 18:59:35 -04:00
|
|
|
instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
|
|
|
|
boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
|
2010-05-25 19:14:41 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
|
2012-04-25 08:58:19 -04:00
|
|
|
boost::bind (&MidiRegionView::snap_changed, this),
|
2011-05-19 17:11:21 -04:00
|
|
|
gui_context());
|
2010-10-08 10:54:16 -04:00
|
|
|
|
2014-12-12 20:22:16 -05:00
|
|
|
trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
|
|
|
|
boost::bind (&MidiRegionView::mouse_mode_changed, this),
|
|
|
|
gui_context ());
|
|
|
|
|
2012-04-25 08:58:19 -04:00
|
|
|
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
|
2016-12-29 17:05:08 -05:00
|
|
|
UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
|
2010-10-08 10:54:16 -04:00
|
|
|
connect_to_diskstream ();
|
|
|
|
}
|
|
|
|
|
2012-06-11 18:59:35 -04:00
|
|
|
InstrumentInfo&
|
|
|
|
MidiRegionView::instrument_info () const
|
|
|
|
{
|
|
|
|
RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
|
|
|
|
return route_ui->route()->instrument_info();
|
|
|
|
}
|
|
|
|
|
2011-10-19 17:53:09 -04:00
|
|
|
const boost::shared_ptr<ARDOUR::MidiRegion>
|
|
|
|
MidiRegionView::midi_region() const
|
|
|
|
{
|
|
|
|
return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
|
|
|
|
}
|
|
|
|
|
2010-10-08 10:54:16 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::connect_to_diskstream ()
|
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
midi_view()->midi_track()->DataRecorded.connect(
|
|
|
|
*this, invalidator(*this),
|
2012-04-25 08:58:19 -04:00
|
|
|
boost::bind (&MidiRegionView::data_recorded, this, _1),
|
2011-05-19 17:11:21 -04:00
|
|
|
gui_context());
|
2007-07-14 21:56:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-04-25 16:06:12 -04:00
|
|
|
MidiRegionView::canvas_group_event(GdkEvent* ev)
|
2007-07-14 21:56:11 -04:00
|
|
|
{
|
2014-12-17 02:34:30 -05:00
|
|
|
if (in_destructor || _recregion) {
|
2014-11-15 15:56:56 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-05 16:54:29 -05:00
|
|
|
if (!trackview.editor().internal_editing()) {
|
|
|
|
// not in internal edit mode, so just act like a normal region
|
|
|
|
return RegionView::canvas_group_event (ev);
|
|
|
|
}
|
|
|
|
|
2018-02-09 09:21:45 -05:00
|
|
|
//For now, move the snapped cursor aside so it doesn't bother you during internal editing
|
|
|
|
//trackview.editor().set_snapped_cursor_position(_region->position());
|
|
|
|
|
2012-06-08 09:21:05 -04:00
|
|
|
bool r;
|
2012-06-17 09:37:52 -04:00
|
|
|
|
2010-11-21 18:54:57 -05:00
|
|
|
switch (ev->type) {
|
|
|
|
case GDK_ENTER_NOTIFY:
|
2014-12-05 16:54:29 -05:00
|
|
|
_last_event_x = ev->crossing.x;
|
|
|
|
_last_event_y = ev->crossing.y;
|
|
|
|
enter_notify(&ev->crossing);
|
|
|
|
// set entered_regionview (among other things)
|
|
|
|
return RegionView::canvas_group_event (ev);
|
|
|
|
|
2010-11-21 18:54:57 -05:00
|
|
|
case GDK_LEAVE_NOTIFY:
|
|
|
|
_last_event_x = ev->crossing.x;
|
|
|
|
_last_event_y = ev->crossing.y;
|
2014-12-05 16:54:29 -05:00
|
|
|
leave_notify(&ev->crossing);
|
|
|
|
// reset entered_regionview (among other things)
|
|
|
|
return RegionView::canvas_group_event (ev);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
case GDK_SCROLL:
|
2014-10-21 05:01:28 -04:00
|
|
|
if (scroll (&ev->scroll)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2007-08-01 17:59:07 -04:00
|
|
|
case GDK_KEY_PRESS:
|
2011-05-19 17:11:21 -04:00
|
|
|
return key_press (&ev->key);
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
case GDK_KEY_RELEASE:
|
2011-05-19 17:11:21 -04:00
|
|
|
return key_release (&ev->key);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
case GDK_BUTTON_PRESS:
|
2011-05-19 17:11:21 -04:00
|
|
|
return button_press (&ev->button);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
case GDK_BUTTON_RELEASE:
|
2012-06-08 09:21:05 -04:00
|
|
|
r = button_release (&ev->button);
|
|
|
|
return r;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
case GDK_MOTION_NOTIFY:
|
2014-12-05 16:54:29 -05:00
|
|
|
_last_event_x = ev->motion.x;
|
|
|
|
_last_event_y = ev->motion.y;
|
2011-05-19 17:11:21 -04:00
|
|
|
return motion (&ev->motion);
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
default:
|
2011-05-19 17:11:21 -04:00
|
|
|
break;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2014-12-05 16:54:29 -05:00
|
|
|
return RegionView::canvas_group_event (ev);
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
|
|
|
MidiRegionView::enter_notify (GdkEventCrossing* ev)
|
|
|
|
{
|
2016-08-30 13:27:35 -04:00
|
|
|
enter_internal (ev->state);
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2014-12-06 19:56:36 -05:00
|
|
|
_entered = true;
|
2011-05-19 17:11:21 -04:00
|
|
|
return false;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2009-09-10 18:20:37 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
2011-09-30 13:55:14 -04:00
|
|
|
MidiRegionView::leave_notify (GdkEventCrossing*)
|
2010-05-27 11:07:15 -04:00
|
|
|
{
|
2014-12-06 19:56:36 -05:00
|
|
|
leave_internal();
|
2011-08-17 08:46:42 -04:00
|
|
|
|
2014-12-06 19:56:36 -05:00
|
|
|
_entered = false;
|
2011-05-19 17:11:21 -04:00
|
|
|
return false;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2010-11-21 18:54:57 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::mouse_mode_changed ()
|
|
|
|
{
|
2019-04-08 15:40:33 -04:00
|
|
|
// Adjust frame colour (become more transparent for internal tools)
|
2019-04-08 14:00:02 -04:00
|
|
|
set_frame_color();
|
2014-12-12 20:22:16 -05:00
|
|
|
|
|
|
|
if (_entered) {
|
2015-11-30 10:49:09 -05:00
|
|
|
if (!trackview.editor().internal_editing()) {
|
2015-12-02 08:45:11 -05:00
|
|
|
/* 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. */
|
2014-12-12 20:22:16 -05:00
|
|
|
leave_internal();
|
|
|
|
}
|
2015-11-30 10:49:09 -05:00
|
|
|
else if (trackview.editor().current_mouse_mode() == MouseContent) {
|
|
|
|
// hide cursor and ghost note after changing to internal edit mode
|
|
|
|
remove_ghost_note ();
|
2015-12-02 08:45:11 -05:00
|
|
|
|
|
|
|
/* XXX This is problematic as the function is executed for every region
|
2016-11-19 05:40:41 -05:00
|
|
|
and only for one region _entered_note can be true. Still it's
|
2015-12-02 08:45:11 -05:00
|
|
|
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. */
|
2016-11-19 05:40:41 -05:00
|
|
|
if (!_entered_note) {
|
2015-12-02 08:45:11 -05:00
|
|
|
hide_verbose_cursor ();
|
|
|
|
}
|
2015-11-30 10:49:09 -05:00
|
|
|
}
|
2010-11-21 18:54:57 -05:00
|
|
|
}
|
2014-12-06 19:56:36 -05:00
|
|
|
}
|
2011-08-17 08:46:42 -04:00
|
|
|
|
2014-12-06 19:56:36 -05:00
|
|
|
void
|
2016-08-30 13:27:35 -04:00
|
|
|
MidiRegionView::enter_internal (uint32_t state)
|
2014-12-06 19:56:36 -05:00
|
|
|
{
|
|
|
|
if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
|
|
|
|
// Show ghost note under pencil
|
2016-08-30 13:27:35 -04:00
|
|
|
create_ghost_note(_last_event_x, _last_event_y, state);
|
2014-12-06 19:56:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!_selection.empty()) {
|
|
|
|
// Grab keyboard for moving selected notes with arrow keys
|
2011-08-17 08:46:42 -04:00
|
|
|
Keyboard::magic_widget_grab_focus();
|
2014-12-06 19:56:36 -05:00
|
|
|
_grabbed_keyboard = true;
|
|
|
|
}
|
2014-12-28 17:45:24 -05:00
|
|
|
|
2019-04-08 15:40:33 -04:00
|
|
|
// Lower frame handles below notes so they don't steal events
|
2019-04-08 14:25:05 -04:00
|
|
|
if (frame_handle_start) {
|
|
|
|
frame_handle_start->lower_to_bottom();
|
2014-12-28 17:45:24 -05:00
|
|
|
}
|
2019-04-08 14:25:05 -04:00
|
|
|
if (frame_handle_end) {
|
|
|
|
frame_handle_end->lower_to_bottom();
|
2014-12-28 17:45:24 -05:00
|
|
|
}
|
2014-12-06 19:56:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::leave_internal()
|
|
|
|
{
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2014-12-06 19:56:36 -05:00
|
|
|
remove_ghost_note ();
|
2016-11-19 05:40:41 -05:00
|
|
|
_entered_note = 0;
|
2014-12-06 19:56:36 -05:00
|
|
|
|
|
|
|
if (_grabbed_keyboard) {
|
|
|
|
Keyboard::magic_widget_drop_focus();
|
|
|
|
_grabbed_keyboard = false;
|
2011-08-17 08:46:42 -04:00
|
|
|
}
|
2014-12-28 17:45:24 -05:00
|
|
|
|
2019-04-08 18:31:29 -04:00
|
|
|
// Raise frame handles above notes so they catch events
|
2019-04-08 14:25:05 -04:00
|
|
|
if (frame_handle_start) {
|
|
|
|
frame_handle_start->raise_to_top();
|
2014-12-28 17:45:24 -05:00
|
|
|
}
|
2019-04-08 14:25:05 -04:00
|
|
|
if (frame_handle_end) {
|
|
|
|
frame_handle_end->raise_to_top();
|
2014-12-28 17:45:24 -05:00
|
|
|
}
|
2010-11-21 18:54:57 -05:00
|
|
|
}
|
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
|
|
|
MidiRegionView::button_press (GdkEventButton* ev)
|
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
if (ev->button != 1) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-03-04 10:16:21 -05:00
|
|
|
|
2012-01-19 17:23:28 -05:00
|
|
|
Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
|
|
|
|
MouseMode m = editor->current_mouse_mode();
|
2012-01-19 21:54:23 -05:00
|
|
|
|
2014-12-08 23:00:00 -05:00
|
|
|
if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
|
2014-12-20 01:11:28 -05:00
|
|
|
_press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
|
2014-11-14 02:28:15 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2012-01-19 17:23:28 -05:00
|
|
|
if (_mouse_state != SelectTouchDragging) {
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_pressed_button = ev->button;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2016-11-19 12:49:18 -05:00
|
|
|
if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
|
2016-11-20 12:02:21 -05:00
|
|
|
|
|
|
|
if (midi_view()->note_mode() == Percussive) {
|
2016-11-20 12:20:27 -05:00
|
|
|
editor->drags()->set (new HitCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
|
2016-11-20 12:02:21 -05:00
|
|
|
} else {
|
|
|
|
editor->drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
|
|
|
|
}
|
|
|
|
|
2016-11-19 12:49:18 -05:00
|
|
|
_mouse_state = AddDragging;
|
|
|
|
remove_ghost_note ();
|
|
|
|
hide_verbose_cursor ();
|
2016-11-20 12:02:21 -05:00
|
|
|
} else {
|
|
|
|
_mouse_state = Pressed;
|
2016-11-19 12:49:18 -05:00
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_pressed_button = ev->button;
|
2015-01-10 12:07:31 -05:00
|
|
|
_mouse_changed_selection = false;
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2007-08-01 17:59:07 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
|
|
|
MidiRegionView::button_release (GdkEventButton* ev)
|
|
|
|
{
|
|
|
|
double event_x, event_y;
|
2008-05-21 21:02:04 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (ev->button != 1) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-03-04 10:16:21 -05:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
event_x = ev->x;
|
|
|
|
event_y = ev->y;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
group->canvas_to_item (event_x, event_y);
|
|
|
|
group->ungrab ();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
PublicEditor& editor = trackview.editor ();
|
|
|
|
|
2014-12-20 01:11:28 -05:00
|
|
|
_press_cursor_ctx.reset();
|
2012-01-19 17:23:28 -05:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
switch (_mouse_state) {
|
2011-06-01 13:00:29 -04:00
|
|
|
case Pressed: // Clicked
|
2010-12-29 11:34:51 -05:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
switch (editor.current_mouse_mode()) {
|
2012-01-19 21:54:23 -05:00
|
|
|
case MouseRange:
|
2016-02-22 15:01:23 -05:00
|
|
|
/* no motion occurred - simple click */
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2015-01-10 12:07:31 -05:00
|
|
|
_mouse_changed_selection = true;
|
2012-01-19 21:54:23 -05:00
|
|
|
break;
|
|
|
|
|
2014-12-08 23:00:00 -05:00
|
|
|
case MouseContent:
|
2011-05-19 17:11:21 -04:00
|
|
|
case MouseTimeFX:
|
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
_mouse_changed_selection = true;
|
2016-11-19 12:49:18 -05:00
|
|
|
clear_editor_note_selection ();
|
2010-05-28 17:39:12 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
break;
|
|
|
|
}
|
2012-01-19 10:23:51 -05:00
|
|
|
case MouseDraw:
|
2016-11-19 12:49:18 -05:00
|
|
|
break;
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
default:
|
2010-12-29 11:34:51 -05:00
|
|
|
break;
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_mouse_state = None;
|
|
|
|
break;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-12-11 07:54:54 -05:00
|
|
|
case AddDragging:
|
2016-11-19 12:51:38 -05:00
|
|
|
/* 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. */
|
2015-11-30 04:46:26 -05:00
|
|
|
case SelectRectDragging:
|
2011-11-16 15:11:33 -05:00
|
|
|
editor.drags()->end_grab ((GdkEvent *) ev);
|
2011-05-19 17:11:21 -04:00
|
|
|
_mouse_state = None;
|
|
|
|
break;
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2015-01-12 02:05:19 -05:00
|
|
|
if (_mouse_changed_selection) {
|
2015-03-25 08:31:23 -04:00
|
|
|
trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().commit_reversible_selection_op ();
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return false;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
|
|
|
MidiRegionView::motion (GdkEventMotion* ev)
|
|
|
|
{
|
2011-08-20 16:02:04 -04:00
|
|
|
PublicEditor& editor = trackview.editor ();
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2016-11-19 05:40:41 -05:00
|
|
|
if (!_entered_note) {
|
2015-11-29 18:58:03 -05:00
|
|
|
|
2016-10-25 13:52:09 -04:00
|
|
|
if (_mouse_state == AddDragging) {
|
|
|
|
if (_ghost_note) {
|
|
|
|
remove_ghost_note ();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
|
2015-12-02 05:20:56 -05:00
|
|
|
Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
|
|
|
|
_mouse_state != AddDragging) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-08-30 13:27:35 -04:00
|
|
|
create_ghost_note (ev->x, ev->y, ev->state);
|
2012-01-19 17:23:28 -05:00
|
|
|
|
2015-12-02 05:20:56 -05:00
|
|
|
} else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
|
|
|
|
Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-08-30 13:27:35 -04:00
|
|
|
update_ghost_note (ev->x, ev->y, ev->state);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-12-02 05:20:56 -05:00
|
|
|
} else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-12-02 05:20:56 -05:00
|
|
|
remove_ghost_note ();
|
|
|
|
hide_verbose_cursor ();
|
2012-01-19 17:23:28 -05:00
|
|
|
|
2015-12-02 05:20:56 -05:00
|
|
|
} else if (editor.current_mouse_mode() == MouseDraw) {
|
2012-01-19 17:23:28 -05:00
|
|
|
|
2015-12-02 05:20:56 -05:00
|
|
|
if (_ghost_note) {
|
2016-08-30 13:27:35 -04:00
|
|
|
update_ghost_note (ev->x, ev->y, ev->state);
|
2015-12-02 05:20:56 -05:00
|
|
|
}
|
|
|
|
else {
|
2016-08-30 13:27:35 -04:00
|
|
|
create_ghost_note (ev->x, ev->y, ev->state);
|
2015-12-02 05:20:56 -05:00
|
|
|
}
|
2015-11-29 18:58:03 -05:00
|
|
|
}
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-05-28 13:39:28 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
/* any motion immediately hides velocity text that may have been visible */
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-05-28 13:39:28 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
(*i)->hide_velocity ();
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
switch (_mouse_state) {
|
2011-12-11 07:54:54 -05:00
|
|
|
case Pressed:
|
2011-05-19 17:11:21 -04:00
|
|
|
|
2012-01-19 17:23:28 -05:00
|
|
|
if (_pressed_button == 1) {
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2012-01-19 17:23:28 -05:00
|
|
|
MouseMode m = editor.current_mouse_mode();
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2016-11-19 12:49:18 -05:00
|
|
|
if (m == MouseContent && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
|
2012-01-19 17:23:28 -05:00
|
|
|
editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
|
2014-12-07 17:29:48 -05:00
|
|
|
if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2015-01-10 12:07:31 -05:00
|
|
|
_mouse_changed_selection = true;
|
2014-12-07 17:29:48 -05:00
|
|
|
}
|
2012-01-19 17:23:28 -05:00
|
|
|
_mouse_state = SelectRectDragging;
|
|
|
|
return true;
|
2012-01-19 21:54:23 -05:00
|
|
|
} else if (m == MouseRange) {
|
|
|
|
editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
|
|
|
|
_mouse_state = SelectVerticalDragging;
|
|
|
|
return true;
|
2012-01-19 17:23:28 -05:00
|
|
|
}
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return false;
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2011-11-16 15:11:33 -05:00
|
|
|
case SelectRectDragging:
|
2012-01-19 21:54:23 -05:00
|
|
|
case SelectVerticalDragging:
|
2011-12-11 07:54:54 -05:00
|
|
|
case AddDragging:
|
2011-11-16 15:11:33 -05:00
|
|
|
editor.drags()->motion_handler ((GdkEvent *) ev, false);
|
|
|
|
break;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
case SelectTouchDragging:
|
|
|
|
return false;
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
default:
|
|
|
|
break;
|
2014-02-11 22:46:21 -05:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-05-27 11:07:15 -04:00
|
|
|
|
2018-02-06 20:49:32 -05:00
|
|
|
//let RegionView do it's thing. drags are handled in here
|
|
|
|
return RegionView::canvas_group_event ((GdkEvent *) ev);
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
|
|
|
MidiRegionView::scroll (GdkEventScroll* ev)
|
|
|
|
{
|
2018-08-09 12:11:18 -04:00
|
|
|
if (trackview.editor().drags()->active()) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-05-28 12:37:04 -04:00
|
|
|
|
2015-10-09 10:14:15 -04:00
|
|
|
if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier) ||
|
|
|
|
Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
|
|
|
|
/* XXX: bit of a hack; allow PrimaryModifier and TertiaryModifier scroll
|
|
|
|
* through so that it still works for navigation.
|
2011-09-28 15:23:13 -04:00
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2010-05-28 12:37:04 -04:00
|
|
|
|
2012-06-06 08:16:15 -04:00
|
|
|
bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
|
2015-10-09 10:14:15 -04:00
|
|
|
Keyboard::ModifierMask mask_together(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier);
|
|
|
|
bool together = Keyboard::modifier_state_contains (ev->state, mask_together);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (ev->direction == GDK_SCROLL_UP) {
|
2012-06-06 08:16:15 -04:00
|
|
|
change_velocities (true, fine, false, together);
|
2011-05-19 17:11:21 -04:00
|
|
|
} else if (ev->direction == GDK_SCROLL_DOWN) {
|
2012-06-06 08:16:15 -04:00
|
|
|
change_velocities (false, fine, false, together);
|
2012-06-22 13:35:41 -04:00
|
|
|
} else {
|
|
|
|
/* left, right: we don't use them */
|
|
|
|
return false;
|
2011-06-01 13:00:29 -04:00
|
|
|
}
|
2012-06-22 13:35:41 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
2007-07-30 12:33:10 -04:00
|
|
|
|
2010-05-27 11:07:15 -04:00
|
|
|
bool
|
|
|
|
MidiRegionView::key_press (GdkEventKey* ev)
|
2011-06-01 13:00:29 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
/* since GTK bindings are generally activated on press, and since
|
|
|
|
detectable auto-repeat is the name of the game and only sends
|
|
|
|
repeated presses, carry out key actions at key press, not release.
|
|
|
|
*/
|
2011-08-17 08:46:42 -04:00
|
|
|
bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2016-11-20 09:17:24 -05:00
|
|
|
if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
|
|
|
|
|
|
|
|
if (_mouse_state != AddDragging) {
|
|
|
|
_mouse_state = SelectTouchDragging;
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-08-17 08:46:42 -04:00
|
|
|
} else if (ev->keyval == GDK_Escape && unmodified) {
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2011-05-19 17:11:21 -04:00
|
|
|
_mouse_state = None;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-29 20:50:39 -05:00
|
|
|
} else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
bool start = (ev->keyval == GDK_comma);
|
|
|
|
bool end = (ev->keyval == GDK_period);
|
|
|
|
bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
|
|
|
|
bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
change_note_lengths (fine, shorter, Temporal::Beats(), start, end);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2012-06-17 09:37:52 -04:00
|
|
|
} else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
|
|
|
|
|
|
|
|
if (_selection.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
delete_selection();
|
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-01-12 02:04:55 -05:00
|
|
|
} else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-03-25 08:31:23 -04:00
|
|
|
trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
|
2015-01-10 12:07:31 -05:00
|
|
|
|
2011-07-11 09:17:01 -04:00
|
|
|
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
|
|
|
|
goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
|
2011-05-19 17:11:21 -04:00
|
|
|
} else {
|
2011-07-11 09:17:01 -04:00
|
|
|
goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2015-01-10 12:07:31 -05:00
|
|
|
|
|
|
|
trackview.editor().commit_reversible_selection_op();
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
} else if (ev->keyval == GDK_Up) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
|
|
|
|
bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
|
2012-06-06 09:17:53 -04:00
|
|
|
bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
|
2012-06-06 08:16:15 -04:00
|
|
|
change_velocities (true, fine, allow_smush, together);
|
2011-05-19 17:11:21 -04:00
|
|
|
} else {
|
|
|
|
transpose (true, fine, allow_smush);
|
|
|
|
}
|
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
} else if (ev->keyval == GDK_Down) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
|
|
|
|
bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
|
2012-06-06 09:17:53 -04:00
|
|
|
bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2012-06-06 09:17:53 -04:00
|
|
|
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
|
2012-06-06 08:16:15 -04:00
|
|
|
change_velocities (false, fine, allow_smush, together);
|
2012-06-06 09:17:53 -04:00
|
|
|
} else {
|
|
|
|
transpose (false, fine, allow_smush);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-06 21:29:48 -05:00
|
|
|
} else if (ev->keyval == GDK_Left) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-06 21:29:48 -05:00
|
|
|
bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
|
|
|
|
nudge_notes (false, fine);
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-06 21:29:48 -05:00
|
|
|
} else if (ev->keyval == GDK_Right) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-06 21:29:48 -05:00
|
|
|
bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
|
|
|
|
nudge_notes (true, fine);
|
2011-05-19 17:11:21 -04:00
|
|
|
return true;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-08-17 08:46:42 -04:00
|
|
|
} else if (ev->keyval == GDK_c && unmodified) {
|
2011-06-13 10:48:48 -04:00
|
|
|
channel_edit ();
|
|
|
|
return true;
|
2012-06-06 09:17:53 -04:00
|
|
|
|
|
|
|
} else if (ev->keyval == GDK_v && unmodified) {
|
|
|
|
velocity_edit ();
|
|
|
|
return true;
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
return false;
|
2010-05-27 11:07:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiRegionView::key_release (GdkEventKey* ev)
|
|
|
|
{
|
2011-08-17 08:46:42 -04:00
|
|
|
if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
|
2011-05-19 17:11:21 -04:00
|
|
|
_mouse_state = None;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2006-08-10 23:24:57 -04:00
|
|
|
}
|
|
|
|
|
2011-06-13 10:48:48 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::channel_edit ()
|
|
|
|
{
|
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-06 09:17:53 -04:00
|
|
|
/* pick a note somewhat at random (since Selection is a set<>) to
|
|
|
|
* provide the "current" channel for the dialog.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint8_t current_channel = (*_selection.begin())->note()->channel ();
|
2011-06-13 10:48:48 -04:00
|
|
|
MidiChannelDialog channel_dialog (current_channel);
|
|
|
|
int ret = channel_dialog.run ();
|
|
|
|
|
|
|
|
switch (ret) {
|
|
|
|
case Gtk::RESPONSE_OK:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t new_channel = channel_dialog.active_channel ();
|
|
|
|
|
|
|
|
start_note_diff_command (_("channel edit"));
|
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
|
|
|
Selection::iterator next = i;
|
|
|
|
++next;
|
|
|
|
change_note_channel (*i, new_channel);
|
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
apply_diff ();
|
|
|
|
}
|
|
|
|
|
2012-06-06 09:17:53 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::velocity_edit ()
|
|
|
|
{
|
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2012-06-06 09:17:53 -04:00
|
|
|
/* pick a note somewhat at random (since Selection is a set<>) to
|
|
|
|
* provide the "current" velocity for the dialog.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
|
|
|
|
MidiVelocityDialog velocity_dialog (current_velocity);
|
|
|
|
int ret = velocity_dialog.run ();
|
|
|
|
|
|
|
|
switch (ret) {
|
|
|
|
case Gtk::RESPONSE_OK:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t new_velocity = velocity_dialog.velocity ();
|
|
|
|
|
|
|
|
start_note_diff_command (_("velocity edit"));
|
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
|
|
|
Selection::iterator next = i;
|
|
|
|
++next;
|
|
|
|
change_note_velocity (*i, new_velocity, false);
|
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
apply_diff ();
|
|
|
|
}
|
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::show_list_editor ()
|
|
|
|
{
|
2010-01-04 22:52:30 -05:00
|
|
|
if (!_list_editor) {
|
2012-01-31 22:33:42 -05:00
|
|
|
_list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
|
2010-01-04 22:52:30 -05:00
|
|
|
}
|
|
|
|
_list_editor->present ();
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
2007-06-09 02:10:30 -04:00
|
|
|
|
2009-02-15 12:30:42 -05:00
|
|
|
/** Add a note to the model, and the view, at a canvas (click) coordinate.
|
2017-09-18 12:39:17 -04:00
|
|
|
* \param t time in samples relative to the position of the region
|
2009-02-15 12:30:42 -05:00
|
|
|
* \param y vertical position in pixels
|
2011-12-30 08:41:16 -05:00
|
|
|
* \param length duration of the note in beats
|
|
|
|
* \param snap_t true to snap t to the grid, otherwise false.
|
2010-06-20 17:24:48 -04:00
|
|
|
*/
|
2007-07-30 12:33:10 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap)
|
2007-07-19 17:00:09 -04:00
|
|
|
{
|
2013-01-19 02:00:43 -05:00
|
|
|
if (length < 2 * DBL_EPSILON) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-28 19:21:07 -05:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
|
|
|
MidiStreamView* const view = mtv->midi_view();
|
2016-06-10 15:40:50 -04:00
|
|
|
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion> (_region);
|
|
|
|
|
|
|
|
if (!mr) {
|
|
|
|
return;
|
|
|
|
}
|
2007-07-19 17:00:09 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
// Start of note in samples relative to region start
|
2016-08-30 13:27:35 -04:00
|
|
|
const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats beat_time = snap_sample_to_grid_underneath (t, divisions, shift_snap);
|
2016-07-05 10:18:09 -04:00
|
|
|
|
2014-12-28 19:21:07 -05:00
|
|
|
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);
|
|
|
|
|
|
|
|
const boost::shared_ptr<NoteType> new_note(
|
|
|
|
new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_model->contains (new_note)) {
|
|
|
|
return;
|
|
|
|
}
|
2010-05-20 18:38:12 -04:00
|
|
|
|
2008-09-22 22:40:29 -04:00
|
|
|
view->update_note_range(new_note->note());
|
2007-07-30 12:33:10 -04:00
|
|
|
|
2015-01-16 12:55:05 -05:00
|
|
|
start_note_diff_command(_("add note"));
|
|
|
|
|
|
|
|
note_diff_add_note (new_note, true, false);
|
|
|
|
|
|
|
|
apply_diff();
|
2009-08-28 12:06:08 -04:00
|
|
|
|
|
|
|
play_midi_note (new_note);
|
2007-07-19 17:00:09 -04:00
|
|
|
}
|
|
|
|
|
2007-06-09 02:10:30 -04:00
|
|
|
void
|
2015-10-27 22:58:55 -04:00
|
|
|
MidiRegionView::clear_events ()
|
2007-06-09 02:10:30 -04:00
|
|
|
{
|
2015-10-27 22:58:55 -04:00
|
|
|
// clear selection without signaling
|
|
|
|
clear_selection_internal ();
|
2008-03-11 21:01:27 -04:00
|
|
|
|
2008-02-10 13:16:25 -05:00
|
|
|
MidiGhostRegion* gr;
|
2008-03-26 07:11:47 -04:00
|
|
|
for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
|
2008-02-21 20:45:29 -05:00
|
|
|
if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
|
2008-02-10 13:16:25 -05:00
|
|
|
gr->clear_events();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-21 20:45:29 -05:00
|
|
|
|
2016-12-23 10:01:37 -05:00
|
|
|
_note_group->clear (true);
|
2007-06-09 02:10:30 -04:00
|
|
|
_events.clear();
|
2010-12-28 13:19:40 -05:00
|
|
|
_patch_changes.clear();
|
2009-02-16 04:51:40 -05:00
|
|
|
_sys_exes.clear();
|
2009-09-10 16:41:08 -04:00
|
|
|
_optimization_iterator = _events.end();
|
2007-07-06 23:19:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-07-30 22:16:46 -04:00
|
|
|
MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
|
|
|
|
{
|
|
|
|
_model = model;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-08-13 23:00:41 -04:00
|
|
|
content_connection.disconnect ();
|
2010-03-30 11:18:43 -04:00
|
|
|
_model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
|
2015-01-12 02:05:19 -05:00
|
|
|
/* Don't signal as nobody else needs to know until selection has been altered. */
|
2016-12-29 07:15:35 -05:00
|
|
|
clear_events();
|
2009-08-28 12:06:08 -04:00
|
|
|
|
2009-02-15 12:30:42 -05:00
|
|
|
if (_enable_display) {
|
2007-08-01 16:50:09 -04:00
|
|
|
redisplay_model();
|
2009-02-15 12:30:42 -05:00
|
|
|
}
|
2007-07-30 22:16:46 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-05-21 21:02:04 -04:00
|
|
|
void
|
2010-12-09 16:34:46 -05:00
|
|
|
MidiRegionView::start_note_diff_command (string name)
|
2009-09-06 14:11:55 -04:00
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
if (!_note_diff_command) {
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().begin_reversible_command (name);
|
2010-12-09 16:34:46 -05:00
|
|
|
_note_diff_command = _model->new_note_diff_command (name);
|
2009-09-06 14:11:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-09 16:34:46 -05:00
|
|
|
MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
|
2008-05-21 21:02:04 -04:00
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
if (_note_diff_command) {
|
|
|
|
_note_diff_command->add (note);
|
2009-02-15 12:30:42 -05:00
|
|
|
}
|
|
|
|
if (selected) {
|
2008-05-21 21:02:04 -04:00
|
|
|
_marked_for_selection.insert(note);
|
2009-02-15 12:30:42 -05:00
|
|
|
}
|
2009-05-04 20:08:30 -04:00
|
|
|
if (show_velocity) {
|
|
|
|
_marked_for_velocity.insert(note);
|
|
|
|
}
|
2008-05-21 21:02:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::note_diff_remove_note (NoteBase* ev)
|
2008-05-21 21:02:04 -04:00
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
if (_note_diff_command && ev->note()) {
|
|
|
|
_note_diff_command->remove(ev->note());
|
2008-05-21 21:02:04 -04:00
|
|
|
}
|
|
|
|
}
|
2009-09-06 14:11:55 -04:00
|
|
|
|
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::note_diff_add_change (NoteBase* ev,
|
2011-05-19 17:11:21 -04:00
|
|
|
MidiModel::NoteDiffCommand::Property property,
|
|
|
|
uint8_t val)
|
2009-09-07 09:38:06 -04:00
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
if (_note_diff_command) {
|
|
|
|
_note_diff_command->change (ev->note(), property, val);
|
2009-09-07 09:38:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::note_diff_add_change (NoteBase* ev,
|
2011-05-19 17:11:21 -04:00
|
|
|
MidiModel::NoteDiffCommand::Property property,
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats val)
|
2009-09-06 14:11:55 -04:00
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
if (_note_diff_command) {
|
|
|
|
_note_diff_command->change (ev->note(), property, val);
|
2009-09-06 14:11:55 -04:00
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
void
|
2017-01-23 15:57:38 -05:00
|
|
|
MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
|
2009-09-06 14:11:55 -04:00
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
bool commit = false;
|
2010-06-12 09:55:22 -04:00
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
if (!_note_diff_command) {
|
2009-09-06 14:11:55 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-15 17:06:19 -05:00
|
|
|
bool add_or_remove = _note_diff_command->adds_or_removes();
|
|
|
|
|
|
|
|
if (!was_copy && add_or_remove) {
|
2011-05-19 17:11:21 -04:00
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
}
|
2010-06-12 09:55:22 -04:00
|
|
|
|
2011-03-02 11:18:59 -05:00
|
|
|
if (as_subcommand) {
|
|
|
|
_model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
|
|
|
|
} else {
|
|
|
|
_model->apply_command (*trackview.session(), _note_diff_command);
|
2015-01-10 12:07:31 -05:00
|
|
|
commit = true;
|
2011-03-02 11:18:59 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
_note_diff_command = 0;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (add_or_remove) {
|
|
|
|
_marked_for_selection.clear();
|
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
|
|
|
|
_marked_for_velocity.clear();
|
2019-10-17 19:32:56 -04:00
|
|
|
|
2015-01-10 12:07:31 -05:00
|
|
|
if (commit) {
|
|
|
|
trackview.editor().commit_reversible_command ();
|
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
2008-05-21 21:02:04 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::abort_command()
|
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
delete _note_diff_command;
|
|
|
|
_note_diff_command = 0;
|
2017-06-16 13:43:38 -04:00
|
|
|
trackview.editor().abort_reversible_command();
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection();
|
2008-05-21 21:02:04 -04:00
|
|
|
}
|
2007-07-30 22:16:46 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase*
|
2009-09-09 12:46:18 -04:00
|
|
|
MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
|
|
|
|
{
|
2017-01-03 08:19:31 -05:00
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
if (_optimization_iterator != _events.end()) {
|
|
|
|
++_optimization_iterator;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
if (_optimization_iterator != _events.end() && _optimization_iterator->first == note) {
|
|
|
|
return _optimization_iterator->second;
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-09-09 12:46:18 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
_optimization_iterator = _events.find (note);
|
|
|
|
if (_optimization_iterator != _events.end()) {
|
|
|
|
return _optimization_iterator->second;
|
2009-09-09 12:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-12 02:05:19 -05:00
|
|
|
/** This version finds any canvas note matching the supplied note. */
|
2015-01-10 12:07:31 -05:00
|
|
|
NoteBase*
|
2016-10-15 08:50:02 -04:00
|
|
|
MidiRegionView::find_canvas_note (Evoral::event_id_t id)
|
2015-01-10 12:07:31 -05:00
|
|
|
{
|
2015-01-13 06:52:02 -05:00
|
|
|
Events::iterator it;
|
2015-01-10 12:07:31 -05:00
|
|
|
|
2015-01-13 06:52:02 -05:00
|
|
|
for (it = _events.begin(); it != _events.end(); ++it) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if (it->first->id() == id) {
|
|
|
|
return it->second;
|
2015-01-10 12:07:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-15 06:35:23 -05:00
|
|
|
boost::shared_ptr<PatchChange>
|
|
|
|
MidiRegionView::find_canvas_patch_change (MidiModel::PatchChangePtr p)
|
|
|
|
{
|
2016-12-28 12:21:37 -05:00
|
|
|
PatchChanges::const_iterator f = _patch_changes.find (p);
|
|
|
|
|
|
|
|
if (f != _patch_changes.end()) {
|
2016-12-29 07:22:06 -05:00
|
|
|
return f->second;
|
2016-12-15 06:35:23 -05:00
|
|
|
}
|
|
|
|
|
2016-12-15 06:57:31 -05:00
|
|
|
return boost::shared_ptr<PatchChange>();
|
2016-12-15 06:35:23 -05:00
|
|
|
}
|
|
|
|
|
2017-02-01 11:34:21 -05:00
|
|
|
boost::shared_ptr<SysEx>
|
|
|
|
MidiRegionView::find_canvas_sys_ex (MidiModel::SysExPtr s)
|
|
|
|
{
|
|
|
|
SysExes::const_iterator f = _sys_exes.find (s);
|
|
|
|
|
|
|
|
if (f != _sys_exes.end()) {
|
|
|
|
return f->second;
|
|
|
|
}
|
|
|
|
|
2017-02-04 11:14:12 -05:00
|
|
|
return boost::shared_ptr<SysEx>();
|
2017-02-01 11:34:21 -05:00
|
|
|
}
|
|
|
|
|
2010-05-28 17:39:12 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::get_events (Events& e, Evoral::Sequence<Temporal::Beats>::NoteOperator op, uint8_t val, int chan_mask)
|
2010-05-28 17:39:12 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
MidiModel::Notes notes;
|
|
|
|
_model->get_notes (notes, op, val, chan_mask);
|
2010-05-28 17:39:12 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
|
2013-04-04 18:45:27 -04:00
|
|
|
NoteBase* cne = find_canvas_note (*n);
|
2011-05-19 17:11:21 -04:00
|
|
|
if (cne) {
|
2017-01-03 08:19:31 -05:00
|
|
|
e.insert (make_pair (*n, cne));
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
|
|
|
}
|
2010-05-28 17:39:12 -04:00
|
|
|
}
|
|
|
|
|
2007-07-30 22:16:46 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::redisplay_model()
|
2007-07-06 23:19:04 -04:00
|
|
|
{
|
2009-02-15 14:44:27 -05:00
|
|
|
if (_active_notes) {
|
2014-12-30 19:14:29 -05:00
|
|
|
// Currently recording
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplecnt_t zoom = trackview.editor().get_current_zoom();
|
2014-12-30 19:14:29 -05:00
|
|
|
if (zoom != _last_display_zoom) {
|
|
|
|
/* Update resolved canvas notes to reflect changes in zoom without
|
|
|
|
touching model. Leave active notes (with length 0) alone since
|
|
|
|
they are being extended. */
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if (i->second->note()->length() > 0) {
|
|
|
|
update_note(i->second);
|
2014-12-30 19:14:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_last_display_zoom = zoom;
|
2014-12-29 18:56:39 -05:00
|
|
|
}
|
2007-08-15 21:25:58 -04:00
|
|
|
return;
|
2009-02-15 14:44:27 -05:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
if (!_model) {
|
|
|
|
return;
|
|
|
|
}
|
2009-09-09 12:46:18 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
|
|
|
|
_optimization_iterator->second->invalidate();
|
|
|
|
}
|
|
|
|
|
2014-03-06 23:16:42 -05:00
|
|
|
bool empty_when_starting = _events.empty();
|
2017-01-03 08:19:31 -05:00
|
|
|
_optimization_iterator = _events.begin();
|
|
|
|
MidiModel::Notes missing_notes;
|
2016-12-22 08:51:34 -05:00
|
|
|
Note* sus = NULL;
|
|
|
|
Hit* hit = NULL;
|
2009-08-28 12:06:08 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
MidiModel::ReadLock lock(_model->read_lock());
|
|
|
|
MidiModel::Notes& notes (_model->notes());
|
2009-09-10 17:19:01 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
NoteBase* cne;
|
|
|
|
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
boost::shared_ptr<NoteType> note (*n);
|
|
|
|
bool visible;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
if (note_in_region_range (note, visible)) {
|
|
|
|
if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
|
|
|
|
cne->validate ();
|
2017-01-26 08:56:39 -05:00
|
|
|
if (visible) {
|
|
|
|
cne->show ();
|
2009-09-10 17:19:01 -04:00
|
|
|
} else {
|
|
|
|
cne->hide ();
|
|
|
|
}
|
2017-01-03 08:19:31 -05:00
|
|
|
} else {
|
|
|
|
missing_notes.insert (note);
|
2009-08-28 12:06:08 -04:00
|
|
|
}
|
2008-05-08 05:53:19 -04:00
|
|
|
}
|
2009-09-10 16:41:08 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
if (!empty_when_starting) {
|
|
|
|
MidiModel::Notes::iterator f;
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ) {
|
|
|
|
|
|
|
|
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 (cne);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete cne;
|
|
|
|
i = _events.erase (i);
|
|
|
|
|
|
|
|
} else {
|
2017-01-26 08:56:39 -05:00
|
|
|
bool visible = cne->item()->visible();
|
|
|
|
|
|
|
|
if ((sus = dynamic_cast<Note*>(cne))) {
|
|
|
|
|
|
|
|
if (visible) {
|
|
|
|
update_sustained (sus);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if ((hit = dynamic_cast<Hit*>(cne))) {
|
|
|
|
|
|
|
|
if (visible) {
|
|
|
|
update_hit (hit);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2017-01-03 08:19:31 -05:00
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2016-12-20 05:31:54 -05:00
|
|
|
for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
|
|
|
|
boost::shared_ptr<NoteType> note (*n);
|
2016-12-22 13:07:22 -05:00
|
|
|
NoteBase* cne;
|
2016-12-20 05:31:54 -05:00
|
|
|
bool visible;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2016-12-22 13:07:22 -05:00
|
|
|
if (note_in_region_range (note, visible)) {
|
|
|
|
if (visible) {
|
|
|
|
cne = add_note (note, true);
|
|
|
|
} else {
|
|
|
|
cne = add_note (note, false);
|
2016-12-22 08:51:34 -05:00
|
|
|
}
|
2016-12-22 13:07:22 -05:00
|
|
|
} else {
|
|
|
|
cne = add_note (note, false);
|
2016-12-22 08:51:34 -05:00
|
|
|
}
|
2016-12-20 11:18:18 -05:00
|
|
|
|
2016-12-22 13:07:22 -05:00
|
|
|
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);
|
2011-11-17 17:15:50 -05:00
|
|
|
}
|
2009-09-10 16:41:08 -04:00
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-29 09:46:38 -05:00
|
|
|
for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
|
|
|
|
MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
|
|
|
|
if (gr && !gr->trackview.hidden()) {
|
|
|
|
gr->redisplay_model ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
display_sysexes();
|
2010-12-28 13:19:40 -05:00
|
|
|
display_patch_changes ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
_marked_for_selection.clear ();
|
|
|
|
_marked_for_velocity.clear ();
|
2015-01-10 12:07:31 -05:00
|
|
|
_pending_note_selection.clear ();
|
2008-04-29 03:28:24 -04:00
|
|
|
|
2007-06-09 02:10:30 -04:00
|
|
|
}
|
|
|
|
|
2008-12-11 03:06:27 -05:00
|
|
|
void
|
2010-12-28 13:19:40 -05:00
|
|
|
MidiRegionView::display_patch_changes ()
|
2008-12-11 03:06:27 -05:00
|
|
|
{
|
2010-07-27 10:09:16 -04:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
2013-03-27 21:50:18 -04:00
|
|
|
uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
|
2010-07-27 10:09:16 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
for (uint8_t i = 0; i < 16; ++i) {
|
2012-06-08 08:17:33 -04:00
|
|
|
display_patch_changes_on_channel (i, chn_mask & (1 << i));
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-07-27 10:09:16 -04:00
|
|
|
}
|
|
|
|
|
2012-06-08 08:17:33 -04:00
|
|
|
/** @param active_channel true to display patch changes fully, false to display
|
|
|
|
* them `greyed-out' (as on an inactive channel)
|
|
|
|
*/
|
2010-07-27 10:09:16 -04:00
|
|
|
void
|
2012-06-08 08:17:33 -04:00
|
|
|
MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
|
2010-07-27 10:09:16 -04:00
|
|
|
{
|
2010-12-28 13:19:40 -05:00
|
|
|
for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
|
2016-12-15 06:35:23 -05:00
|
|
|
boost::shared_ptr<PatchChange> p;
|
2010-07-27 10:09:16 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
if ((*i)->channel() != channel) {
|
|
|
|
continue;
|
2008-12-11 03:06:27 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-12-15 06:35:23 -05:00
|
|
|
if ((p = find_canvas_patch_change (*i)) != 0) {
|
2016-12-28 12:21:37 -05:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplecnt_t region_samples = source_beats_to_region_samples ((*i)->time());
|
2016-12-28 12:21:37 -05:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
if (region_samples < 0 || region_samples >= _region->length()) {
|
2016-12-28 12:21:37 -05:00
|
|
|
p->hide();
|
|
|
|
} else {
|
2017-09-18 12:39:17 -04:00
|
|
|
const double x = trackview.editor().sample_to_pixel (region_samples);
|
2016-12-28 12:21:37 -05:00
|
|
|
const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
|
|
|
|
p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
|
2017-02-01 12:39:32 -05:00
|
|
|
p->set_text (patch_name);
|
2016-12-28 12:21:37 -05:00
|
|
|
|
|
|
|
p->show();
|
|
|
|
}
|
|
|
|
|
2016-12-15 06:35:23 -05:00
|
|
|
} else {
|
|
|
|
const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
|
|
|
|
add_canvas_patch_change (*i, patch_name, active_channel);
|
|
|
|
}
|
2009-02-16 00:33:23 -05:00
|
|
|
}
|
2008-12-11 03:06:27 -05:00
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2009-02-16 04:51:40 -05:00
|
|
|
MidiRegionView::display_sysexes()
|
|
|
|
{
|
2011-12-02 14:35:18 -05:00
|
|
|
bool have_periodic_system_messages = false;
|
|
|
|
bool display_periodic_messages = true;
|
|
|
|
|
2015-01-02 09:44:54 -05:00
|
|
|
if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
|
2011-12-02 14:35:18 -05:00
|
|
|
|
|
|
|
for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
|
2016-11-06 22:04:35 -05:00
|
|
|
if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
|
|
|
|
have_periodic_system_messages = true;
|
|
|
|
break;
|
2011-12-02 14:35:18 -05:00
|
|
|
}
|
|
|
|
}
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-12-02 14:35:18 -05:00
|
|
|
if (have_periodic_system_messages) {
|
2017-09-18 12:39:17 -04:00
|
|
|
double zoom = trackview.editor().get_current_zoom (); // samples per pixel
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-12-02 14:35:18 -05:00
|
|
|
/* get an approximate value for the number of samples per video frame */
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
double video_frame = trackview.session()->sample_rate() * (1.0/30);
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-12-02 14:35:18 -05:00
|
|
|
/* if we are zoomed out beyond than the cutoff (i.e. more
|
2017-09-18 12:39:17 -04:00
|
|
|
* samples per pixel than samples per 4 video frames), don't
|
2011-12-02 14:35:18 -05:00
|
|
|
* show periodic sysex messages.
|
|
|
|
*/
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-12-02 14:35:18 -05:00
|
|
|
if (zoom > (video_frame*4)) {
|
|
|
|
display_periodic_messages = false;
|
2015-10-04 14:51:05 -04:00
|
|
|
}
|
2011-12-02 14:35:18 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
display_periodic_messages = false;
|
|
|
|
}
|
|
|
|
|
2019-09-20 14:11:33 -04:00
|
|
|
const boost::shared_ptr<MidiRegion> mregion (midi_region());
|
|
|
|
|
2009-02-16 21:19:16 -05:00
|
|
|
for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
|
2017-02-01 11:34:21 -05:00
|
|
|
MidiModel::SysExPtr sysex_ptr = *i;
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats time = sysex_ptr->time();
|
2011-12-02 14:35:18 -05:00
|
|
|
|
2016-11-06 22:04:35 -05:00
|
|
|
if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
|
|
|
|
if (!display_periodic_messages) {
|
|
|
|
continue;
|
2011-12-02 14:35:18 -05:00
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-02-16 04:51:40 -05:00
|
|
|
ostringstream str;
|
|
|
|
str << hex;
|
|
|
|
for (uint32_t b = 0; b < (*i)->size(); ++b) {
|
|
|
|
str << int((*i)->buffer()[b]);
|
|
|
|
if (b != (*i)->size() -1) {
|
|
|
|
str << " ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
string text = str.str();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const double x = trackview.editor().sample_to_pixel(source_beats_to_region_samples(time));
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-02-16 04:51:40 -05:00
|
|
|
double height = midi_stream_view()->contents_height();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2013-04-04 18:45:27 -04:00
|
|
|
// CAIROCANVAS: no longer passing *i (the sysex event) to the
|
|
|
|
// SysEx canvas object!!!
|
2017-02-01 11:34:21 -05:00
|
|
|
boost::shared_ptr<SysEx> sysex = find_canvas_sys_ex (sysex_ptr);
|
2013-04-04 18:45:27 -04:00
|
|
|
|
2017-02-01 11:34:21 -05:00
|
|
|
if (!sysex) {
|
|
|
|
sysex = boost::shared_ptr<SysEx>(
|
|
|
|
new SysEx (*this, _note_group, text, height, x, 1.0, sysex_ptr));
|
|
|
|
_sys_exes.insert (make_pair (sysex_ptr, sysex));
|
2017-02-01 11:58:01 -05:00
|
|
|
} else {
|
2017-02-01 12:39:32 -05:00
|
|
|
sysex->set_height (height);
|
2017-02-01 11:58:01 -05:00
|
|
|
sysex->item().set_position (ArdourCanvas::Duple (x, 1.0));
|
2017-02-01 11:34:21 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-12-02 14:35:18 -05:00
|
|
|
// Show unless message is beyond the region bounds
|
2019-09-20 14:11:33 -04:00
|
|
|
if (time - mregion->start_beats() >= mregion->length_beats() || time < mregion->start_beats()) {
|
|
|
|
sysex->hide();
|
|
|
|
} else {
|
|
|
|
sysex->show();
|
|
|
|
}
|
2009-02-16 04:51:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-10 23:24:57 -04:00
|
|
|
MidiRegionView::~MidiRegionView ()
|
|
|
|
{
|
|
|
|
in_destructor = true;
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2010-06-28 17:03:12 -04:00
|
|
|
|
2010-01-04 22:52:30 -05:00
|
|
|
delete _list_editor;
|
|
|
|
|
2008-02-21 20:45:29 -05:00
|
|
|
RegionViewGoingAway (this); /* EMIT_SIGNAL */
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2008-05-12 06:03:53 -04:00
|
|
|
if (_active_notes) {
|
2007-08-15 21:25:58 -04:00
|
|
|
end_write();
|
2008-05-12 06:03:53 -04:00
|
|
|
}
|
2016-11-19 05:40:41 -05:00
|
|
|
_entered_note = 0;
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_events ();
|
2010-08-11 14:21:37 -04:00
|
|
|
|
2008-02-21 20:45:29 -05:00
|
|
|
delete _note_group;
|
2010-12-09 16:34:46 -05:00
|
|
|
delete _note_diff_command;
|
2011-05-19 17:11:21 -04:00
|
|
|
delete _step_edit_cursor;
|
2006-08-10 23:24:57 -04:00
|
|
|
}
|
|
|
|
|
2007-06-09 02:10:30 -04:00
|
|
|
void
|
2010-02-19 13:09:08 -05:00
|
|
|
MidiRegionView::region_resized (const PropertyChange& what_changed)
|
2007-06-09 02:10:30 -04:00
|
|
|
{
|
2016-03-20 06:57:44 -04:00
|
|
|
RegionView::region_resized(what_changed); // calls RegionView::set_duration()
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-02-19 13:09:08 -05:00
|
|
|
if (what_changed.contains (ARDOUR::Properties::position)) {
|
2014-11-20 17:36:09 -05:00
|
|
|
_region_relative_time_converter.set_origin_b(_region->position());
|
2015-05-15 14:15:52 -04:00
|
|
|
_region_relative_time_converter_double.set_origin_b(_region->position());
|
2016-03-20 06:57:44 -04:00
|
|
|
/* reset_width dependent_items() redisplays model */
|
2016-06-20 10:50:23 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2014-11-20 17:36:09 -05:00
|
|
|
|
|
|
|
if (what_changed.contains (ARDOUR::Properties::start) ||
|
|
|
|
what_changed.contains (ARDOUR::Properties::position)) {
|
|
|
|
_source_relative_time_converter.set_origin_b (_region->position() - _region->start());
|
|
|
|
}
|
2016-07-22 09:21:27 -04:00
|
|
|
/* catch end and start trim so we can update the view*/
|
2016-06-24 12:54:37 -04:00
|
|
|
if (!what_changed.contains (ARDOUR::Properties::start) &&
|
|
|
|
what_changed.contains (ARDOUR::Properties::length)) {
|
2016-06-20 10:50:23 -04:00
|
|
|
enable_display (true);
|
2016-07-22 09:21:27 -04:00
|
|
|
} else if (what_changed.contains (ARDOUR::Properties::start) &&
|
|
|
|
what_changed.contains (ARDOUR::Properties::length)) {
|
|
|
|
enable_display (true);
|
2016-06-20 10:50:23 -04:00
|
|
|
}
|
2007-06-09 02:10:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::reset_width_dependent_items (double pixel_width)
|
|
|
|
{
|
|
|
|
RegionView::reset_width_dependent_items(pixel_width);
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-02-15 12:30:42 -05:00
|
|
|
if (_enable_display) {
|
2007-08-01 16:50:09 -04:00
|
|
|
redisplay_model();
|
2016-07-22 09:21:27 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-12-28 12:21:37 -05:00
|
|
|
bool hide_all = false;
|
|
|
|
PatchChanges::iterator x = _patch_changes.begin();
|
|
|
|
if (x != _patch_changes.end()) {
|
2017-02-01 12:39:32 -05:00
|
|
|
hide_all = x->second->width() >= _pixel_width;
|
2016-12-28 12:21:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hide_all) {
|
|
|
|
for (; x != _patch_changes.end(); ++x) {
|
2017-01-03 08:19:31 -05:00
|
|
|
x->second->hide();
|
2012-06-12 13:55:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
move_step_edit_cursor (_step_edit_cursor_position);
|
|
|
|
set_step_edit_cursor_width (_step_edit_cursor_width);
|
2007-06-09 02:10:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-02-15 12:30:42 -05:00
|
|
|
MidiRegionView::set_height (double height)
|
2007-06-09 02:10:30 -04:00
|
|
|
{
|
2014-03-11 07:39:25 -04:00
|
|
|
double old_height = _height;
|
2008-09-19 15:32:10 -04:00
|
|
|
RegionView::set_height(height);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-03-11 07:39:25 -04:00
|
|
|
apply_note_range (midi_stream_view()->lowest_note(),
|
|
|
|
midi_stream_view()->highest_note(),
|
|
|
|
height != old_height);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2013-04-15 13:50:05 -04:00
|
|
|
if (name_text) {
|
|
|
|
name_text->raise_to_top();
|
2008-09-22 22:40:29 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
|
2016-12-28 12:21:37 -05:00
|
|
|
(*x).second->set_height (midi_stream_view()->contents_height());
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-08-11 15:11:14 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_step_edit_cursor) {
|
2013-04-04 00:32:52 -04:00
|
|
|
_step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2008-09-22 22:40:29 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2007-10-04 01:15:28 -04:00
|
|
|
|
2008-09-22 22:40:29 -04:00
|
|
|
/** Apply the current note range from the stream view
|
|
|
|
* by repositioning/hiding notes as necessary
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
|
|
|
|
{
|
2009-02-16 00:33:23 -05:00
|
|
|
if (!_enable_display) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!force && _current_range_min == min && _current_range_max == max) {
|
|
|
|
return;
|
|
|
|
}
|
2009-09-10 16:41:08 -04:00
|
|
|
|
2009-02-16 00:33:23 -05:00
|
|
|
_current_range_min = min;
|
|
|
|
_current_range_max = max;
|
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
redisplay_model ();
|
2007-06-09 02:10:30 -04:00
|
|
|
}
|
|
|
|
|
2006-08-10 23:24:57 -04:00
|
|
|
GhostRegion*
|
2009-07-09 13:58:13 -04:00
|
|
|
MidiRegionView::add_ghost (TimeAxisView& tv)
|
2006-08-10 23:24:57 -04:00
|
|
|
{
|
2013-04-12 11:31:50 -04:00
|
|
|
double unit_position = _region->position () / samples_per_pixel;
|
2009-07-09 13:58:13 -04:00
|
|
|
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
|
2008-02-10 13:16:25 -05:00
|
|
|
MidiGhostRegion* ghost;
|
|
|
|
|
2008-02-21 20:45:29 -05:00
|
|
|
if (mtv && mtv->midi_view()) {
|
2009-02-16 00:33:23 -05:00
|
|
|
/* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
|
2009-02-16 00:54:12 -05:00
|
|
|
to allow having midi notes on top of note lines and waveforms.
|
2011-05-19 17:11:21 -04:00
|
|
|
*/
|
2015-10-22 07:56:02 -04:00
|
|
|
ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
|
2009-02-15 12:30:42 -05:00
|
|
|
} else {
|
2015-10-22 07:56:02 -04:00
|
|
|
ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
|
2008-02-10 13:16:25 -05:00
|
|
|
}
|
2007-06-27 17:37:08 -04:00
|
|
|
|
2016-12-22 13:07:22 -05:00
|
|
|
ghost->set_colors ();
|
|
|
|
ghost->set_height ();
|
|
|
|
ghost->set_duration (_region->length() / samples_per_pixel);
|
|
|
|
|
2008-09-22 22:40:29 -04:00
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
ghost->add_note(i->second);
|
2008-02-10 13:16:25 -05:00
|
|
|
}
|
|
|
|
|
2011-02-27 22:59:41 -05:00
|
|
|
ghosts.push_back (ghost);
|
2017-01-03 08:19:31 -05:00
|
|
|
enable_display (true);
|
2007-06-27 17:37:08 -04:00
|
|
|
return ghost;
|
2006-08-10 23:24:57 -04:00
|
|
|
}
|
|
|
|
|
2007-06-01 19:27:29 -04:00
|
|
|
|
|
|
|
/** Begin tracking note state for successive calls to add_event
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
MidiRegionView::begin_write()
|
|
|
|
{
|
2013-01-19 02:00:43 -05:00
|
|
|
if (_active_notes) {
|
|
|
|
delete[] _active_notes;
|
|
|
|
}
|
2013-04-04 00:32:52 -04:00
|
|
|
_active_notes = new Note*[128];
|
2013-01-19 02:00:43 -05:00
|
|
|
for (unsigned i = 0; i < 128; ++i) {
|
2009-09-10 16:41:08 -04:00
|
|
|
_active_notes[i] = 0;
|
2008-05-09 12:10:36 -04:00
|
|
|
}
|
2007-06-01 19:27:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Destroy note state for add_event
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
MidiRegionView::end_write()
|
|
|
|
{
|
|
|
|
delete[] _active_notes;
|
2009-09-10 16:41:08 -04:00
|
|
|
_active_notes = 0;
|
2008-04-03 17:47:47 -04:00
|
|
|
_marked_for_selection.clear();
|
2009-05-04 20:08:30 -04:00
|
|
|
_marked_for_velocity.clear();
|
2007-06-01 19:27:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-04 01:15:28 -04:00
|
|
|
/** Resolve an active MIDI note (while recording).
|
2007-07-14 21:56:11 -04:00
|
|
|
*/
|
2007-06-01 19:27:29 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::resolve_note(uint8_t note, Temporal::Beats end_time)
|
2007-06-01 19:27:29 -04:00
|
|
|
{
|
2009-02-15 12:30:42 -05:00
|
|
|
if (midi_view()->note_mode() != Sustained) {
|
2007-10-04 01:15:28 -04:00
|
|
|
return;
|
2009-02-15 12:30:42 -05:00
|
|
|
}
|
2007-06-01 19:27:29 -04:00
|
|
|
|
2007-10-04 01:15:28 -04:00
|
|
|
if (_active_notes && _active_notes[note]) {
|
2014-12-30 19:14:29 -05:00
|
|
|
/* Set note length so update_note() works. Note this is a local note
|
|
|
|
for recording, not from a model, so we can safely mess with it. */
|
|
|
|
_active_notes[note]->note()->set_length(
|
|
|
|
end_time - _active_notes[note]->note()->time());
|
2010-12-29 11:34:51 -05:00
|
|
|
|
2014-12-30 19:14:29 -05:00
|
|
|
/* End time is relative to the region being recorded. */
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t end_time_samples = region_beats_to_region_samples(end_time);
|
2010-12-29 11:34:51 -05:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
_active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_samples));
|
2014-03-11 07:39:25 -04:00
|
|
|
_active_notes[note]->set_outline_all ();
|
2009-09-10 16:41:08 -04:00
|
|
|
_active_notes[note] = 0;
|
2007-06-01 19:27:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-01 20:55:32 -04:00
|
|
|
/** Extend active notes to rightmost edge of region (if length is changed)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
MidiRegionView::extend_active_notes()
|
|
|
|
{
|
2008-05-12 06:03:53 -04:00
|
|
|
if (!_active_notes) {
|
2007-06-01 20:55:32 -04:00
|
|
|
return;
|
2008-05-12 06:03:53 -04:00
|
|
|
}
|
2007-06-01 20:55:32 -04:00
|
|
|
|
2014-12-30 19:14:29 -05:00
|
|
|
for (unsigned i = 0; i < 128; ++i) {
|
2008-05-12 06:03:53 -04:00
|
|
|
if (_active_notes[i]) {
|
2014-12-17 18:40:38 -05:00
|
|
|
_active_notes[i]->set_x1(
|
2014-12-30 19:14:29 -05:00
|
|
|
trackview.editor().sample_to_pixel(_region->length()));
|
2008-05-12 06:03:53 -04:00
|
|
|
}
|
|
|
|
}
|
2007-06-01 20:55:32 -04:00
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2009-02-01 21:36:05 -05:00
|
|
|
MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
|
2008-12-25 18:08:57 -05:00
|
|
|
{
|
2015-01-02 09:44:54 -05:00
|
|
|
if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
|
2008-12-25 18:08:57 -05:00
|
|
|
return;
|
|
|
|
}
|
2009-01-06 21:40:13 -05:00
|
|
|
|
2009-07-09 13:58:13 -04:00
|
|
|
RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (!route_ui || !route_ui->midi_track()) {
|
|
|
|
return;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2012-06-08 09:21:05 -04:00
|
|
|
NotePlayer* np = new NotePlayer (route_ui->midi_track ());
|
2011-05-19 17:11:21 -04:00
|
|
|
np->add (note);
|
|
|
|
np->play ();
|
2012-06-08 09:21:05 -04:00
|
|
|
|
|
|
|
/* NotePlayer deletes itself */
|
2008-12-25 18:08:57 -05:00
|
|
|
}
|
|
|
|
|
2010-09-15 14:54:04 -04:00
|
|
|
void
|
2012-06-08 09:21:05 -04:00
|
|
|
MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
|
2008-12-25 18:08:57 -05:00
|
|
|
{
|
2014-12-24 16:54:38 -05:00
|
|
|
const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
|
|
|
|
start_playing_midi_chord(notes);
|
2012-06-08 09:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
|
|
|
|
{
|
2015-01-02 09:44:54 -05:00
|
|
|
if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
|
2012-06-08 09:21:05 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
|
|
|
|
|
|
|
|
if (!route_ui || !route_ui->midi_track()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-14 01:12:29 -04:00
|
|
|
NotePlayer* player = new NotePlayer (route_ui->midi_track());
|
2009-01-06 21:40:13 -05:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
|
2015-03-14 01:12:29 -04:00
|
|
|
player->add (*n);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2015-03-14 01:12:29 -04:00
|
|
|
player->play ();
|
2008-12-25 18:08:57 -05:00
|
|
|
}
|
|
|
|
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2009-02-15 14:44:27 -05:00
|
|
|
bool
|
2011-08-16 10:24:41 -04:00
|
|
|
MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
|
2009-02-15 14:44:27 -05:00
|
|
|
{
|
2016-06-18 09:24:05 -04:00
|
|
|
const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
|
2016-10-08 12:24:14 -04:00
|
|
|
|
|
|
|
/* must compare double explicitly as Beats::operator< rounds to ppqn */
|
2016-10-09 12:39:57 -04:00
|
|
|
const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
|
2016-11-21 10:59:57 -05:00
|
|
|
note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
|
2009-09-10 17:19:01 -04:00
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
visible = (note->note() >= _current_range_min) &&
|
|
|
|
(note->note() <= _current_range_max);
|
2009-09-10 17:19:01 -04:00
|
|
|
|
2009-02-15 14:44:27 -05:00
|
|
|
return !outside;
|
|
|
|
}
|
2007-06-01 20:55:32 -04:00
|
|
|
|
2014-12-28 20:23:52 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
|
|
|
|
{
|
|
|
|
Note* sus = NULL;
|
|
|
|
Hit* hit = NULL;
|
|
|
|
if ((sus = dynamic_cast<Note*>(note))) {
|
|
|
|
update_sustained(sus, update_ghost_regions);
|
|
|
|
} else if ((hit = dynamic_cast<Hit*>(note))) {
|
|
|
|
update_hit(hit, update_ghost_regions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-27 23:00:38 -05:00
|
|
|
/** Update a canvas note's size from its model note.
|
|
|
|
* @param ev Canvas note to update.
|
|
|
|
* @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
|
|
|
|
*/
|
2009-09-09 12:46:18 -04:00
|
|
|
void
|
2014-12-28 20:23:52 -05:00
|
|
|
MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
|
2009-09-09 12:46:18 -04:00
|
|
|
{
|
2016-06-18 12:48:48 -04:00
|
|
|
TempoMap& map (trackview.session()->tempo_map());
|
|
|
|
const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
|
2009-09-09 12:46:18 -04:00
|
|
|
boost::shared_ptr<NoteType> note = ev->note();
|
2016-10-15 12:11:05 -04:00
|
|
|
|
2016-11-08 12:51:19 -05:00
|
|
|
const double session_source_start = _region->quarter_note() - mr->start_beats();
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t note_start_samples = map.sample_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
|
2016-10-15 12:11:05 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const double x0 = trackview.editor().sample_to_pixel (note_start_samples);
|
2016-03-13 13:09:53 -04:00
|
|
|
double x1;
|
2016-11-22 16:04:14 -05:00
|
|
|
const double y0 = 1 + floor(note_to_y(note->note()));
|
2016-08-15 10:56:08 -04:00
|
|
|
double y1;
|
2011-08-16 10:24:41 -04:00
|
|
|
|
2016-08-15 10:56:08 -04:00
|
|
|
/* trim note display to not overlap the end of its region */
|
2016-11-20 08:17:31 -05:00
|
|
|
if (note->length().to_double() > 0.0) {
|
2016-10-09 12:39:57 -04:00
|
|
|
double note_end_time = note->end_time().to_double();
|
2016-06-18 12:48:48 -04:00
|
|
|
|
2016-10-15 12:11:05 -04:00
|
|
|
if (note_end_time > mr->start_beats() + mr->length_beats()) {
|
2016-09-22 13:39:05 -04:00
|
|
|
note_end_time = mr->start_beats() + mr->length_beats();
|
2016-06-18 12:48:48 -04:00
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t note_end_samples = map.sample_at_quarter_note (session_source_start + note_end_time) - _region->position();
|
2016-10-15 12:11:05 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_samples)) - 1;
|
2009-09-09 12:46:18 -04:00
|
|
|
} else {
|
2016-03-13 13:09:53 -04:00
|
|
|
x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
|
2009-09-09 12:46:18 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
y1 = y0 + std::max(1., floor(note_height()) - 1);
|
2016-03-13 13:09:53 -04:00
|
|
|
|
2016-10-15 12:11:05 -04:00
|
|
|
ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
|
2018-07-03 11:28:01 -04:00
|
|
|
ev->set_velocity (note->velocity()/127.0);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-11-22 04:05:42 -05:00
|
|
|
if (!note->length()) {
|
2013-01-19 02:00:43 -05:00
|
|
|
if (_active_notes && note->note() < 128) {
|
2014-12-30 19:14:29 -05:00
|
|
|
Note* const old_rect = _active_notes[note->note()];
|
2014-12-30 20:01:18 -05:00
|
|
|
if (old_rect) {
|
|
|
|
/* There is an active note on this key, so we have a stuck
|
|
|
|
note. Finish the old rectangle here. */
|
2016-03-13 13:09:53 -04:00
|
|
|
old_rect->set_x1 (x1);
|
2014-03-11 07:39:25 -04:00
|
|
|
old_rect->set_outline_all ();
|
2009-09-09 12:46:18 -04:00
|
|
|
}
|
|
|
|
_active_notes[note->note()] = ev;
|
|
|
|
}
|
|
|
|
/* outline all but right edge */
|
2014-03-11 07:39:25 -04:00
|
|
|
ev->set_outline_what (ArdourCanvas::Rectangle::What (
|
|
|
|
ArdourCanvas::Rectangle::TOP|
|
|
|
|
ArdourCanvas::Rectangle::LEFT|
|
|
|
|
ArdourCanvas::Rectangle::BOTTOM));
|
2009-09-09 12:46:18 -04:00
|
|
|
} else {
|
|
|
|
/* outline all edges */
|
2014-03-11 07:39:25 -04:00
|
|
|
ev->set_outline_all ();
|
2009-09-09 12:46:18 -04:00
|
|
|
}
|
2014-12-28 16:06:21 -05:00
|
|
|
|
|
|
|
// Update color in case velocity has changed
|
2016-09-22 13:39:05 -04:00
|
|
|
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()));
|
2014-12-28 16:06:21 -05:00
|
|
|
|
2009-09-09 12:46:18 -04:00
|
|
|
}
|
|
|
|
|
2014-03-06 13:27:51 -05:00
|
|
|
void
|
2014-12-28 20:23:52 -05:00
|
|
|
MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
|
2009-09-09 12:46:18 -04:00
|
|
|
{
|
|
|
|
boost::shared_ptr<NoteType> note = ev->note();
|
|
|
|
|
2016-11-08 12:51:19 -05:00
|
|
|
const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t note_start_samples = trackview.session()->tempo_map().sample_at_quarter_note (note_time_qn) - _region->position();
|
2016-08-30 13:27:35 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const double x = trackview.editor().sample_to_pixel(note_start_samples);
|
2016-11-22 16:04:14 -05:00
|
|
|
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;
|
2009-09-09 12:46:18 -04:00
|
|
|
|
2015-03-28 13:00:31 -04:00
|
|
|
// see DnD note in MidiRegionView::apply_note_range() above
|
|
|
|
if (y <= 0 || y >= _height) {
|
|
|
|
ev->hide();
|
|
|
|
} else {
|
|
|
|
ev->show();
|
|
|
|
}
|
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
ev->set_position (ArdourCanvas::Duple (x, y));
|
2014-03-06 13:27:51 -05:00
|
|
|
ev->set_height (diamond_size);
|
2014-12-28 20:23:52 -05:00
|
|
|
|
|
|
|
// Update color in case velocity has changed
|
2016-10-15 12:11:05 -04:00
|
|
|
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()));
|
2014-12-28 20:23:52 -05:00
|
|
|
|
2009-09-09 12:46:18 -04:00
|
|
|
}
|
|
|
|
|
2008-09-22 12:28:02 -04:00
|
|
|
/** Add a MIDI note to the view (with length).
|
2007-07-14 21:56:11 -04:00
|
|
|
*
|
2008-09-22 12:28:02 -04:00
|
|
|
* If in sustained mode, notes with length 0 will be considered active
|
2007-10-04 01:15:28 -04:00
|
|
|
* notes, and resolve_note should be called when the corresponding note off
|
|
|
|
* event arrives, to properly display the note.
|
2007-07-14 21:56:11 -04:00
|
|
|
*/
|
2015-01-12 02:03:49 -05:00
|
|
|
NoteBase*
|
2009-09-10 17:19:01 -04:00
|
|
|
MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
|
2007-07-14 21:56:11 -04:00
|
|
|
{
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase* event = 0;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2007-08-03 21:07:21 -04:00
|
|
|
if (midi_view()->note_mode() == Sustained) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-20 07:31:56 -05:00
|
|
|
Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
|
2009-09-09 12:46:18 -04:00
|
|
|
|
2014-12-30 19:14:29 -05:00
|
|
|
update_sustained (ev_rect);
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2008-04-03 17:47:47 -04:00
|
|
|
event = ev_rect;
|
2007-07-14 21:56:11 -04:00
|
|
|
|
2007-08-03 21:07:21 -04:00
|
|
|
} else if (midi_view()->note_mode() == Percussive) {
|
2009-09-09 12:46:18 -04:00
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
const double diamond_size = std::max(1., floor(note_height()) - 2.);
|
2007-07-14 21:56:11 -04:00
|
|
|
|
2017-01-20 07:31:56 -05:00
|
|
|
Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
|
2009-09-09 12:46:18 -04:00
|
|
|
|
|
|
|
update_hit (ev_diamond);
|
|
|
|
|
2008-04-03 17:47:47 -04:00
|
|
|
event = ev_diamond;
|
2009-09-09 12:46:18 -04:00
|
|
|
|
2008-04-03 17:47:47 -04:00
|
|
|
} else {
|
|
|
|
event = 0;
|
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2008-12-26 01:52:49 -05:00
|
|
|
if (event) {
|
2014-12-28 21:31:33 -05:00
|
|
|
MidiGhostRegion* gr;
|
|
|
|
|
|
|
|
for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
|
|
|
|
if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
|
|
|
|
gr->add_note(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-21 21:02:04 -04:00
|
|
|
if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
|
|
|
|
note_selected(event, true);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2009-05-04 20:08:30 -04:00
|
|
|
if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
|
|
|
|
event->show_velocity();
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2013-03-27 21:50:18 -04:00
|
|
|
event->on_channel_selection_change (get_selected_channels());
|
2017-01-03 08:19:31 -05:00
|
|
|
_events.insert (make_pair (event->note(), event));
|
2009-08-28 12:06:08 -04:00
|
|
|
|
2009-09-10 17:19:01 -04:00
|
|
|
if (visible) {
|
2009-02-15 14:44:27 -05:00
|
|
|
event->show();
|
|
|
|
} else {
|
2009-08-28 12:06:08 -04:00
|
|
|
event->hide ();
|
2009-02-15 14:44:27 -05:00
|
|
|
}
|
2007-07-14 21:56:11 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-29 11:34:51 -05:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
|
|
|
MidiStreamView* const view = mtv->midi_view();
|
|
|
|
|
2011-08-16 10:24:41 -04:00
|
|
|
view->update_note_range (note->note());
|
2015-01-12 02:03:49 -05:00
|
|
|
return event;
|
2007-07-14 21:56:11 -04:00
|
|
|
}
|
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
void
|
2010-07-25 17:19:55 -04:00
|
|
|
MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats pos, Temporal::Beats len)
|
2009-09-10 16:41:08 -04:00
|
|
|
{
|
|
|
|
boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
/* potentially extend region to hold new note */
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t end_sample = source_beats_to_absolute_samples (new_note->end_time());
|
|
|
|
samplepos_t region_end = _region->last_sample();
|
2009-09-10 16:41:08 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
if (end_sample > region_end) {
|
2016-06-15 10:18:27 -04:00
|
|
|
/* XX sets length in beats from audio space. make musical */
|
2017-09-18 12:39:17 -04:00
|
|
|
_region->set_length (end_sample - _region->position(), 0);
|
2009-09-10 16:41:08 -04:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-29 11:34:51 -05:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
|
|
|
MidiStreamView* const view = mtv->midi_view();
|
|
|
|
|
|
|
|
view->update_note_range(new_note->note());
|
2010-07-25 17:19:55 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_marked_for_selection.clear ();
|
2010-08-12 22:00:46 -04:00
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("step add"));
|
2015-01-16 12:55:05 -05:00
|
|
|
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_note (new_note, true, false);
|
2015-01-16 12:55:05 -05:00
|
|
|
|
2010-07-25 17:19:55 -04:00
|
|
|
apply_diff();
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
// last_step_edit_note = new_note;
|
2009-09-10 16:41:08 -04:00
|
|
|
}
|
|
|
|
|
2010-08-13 10:11:01 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::step_sustain (Temporal::Beats beats)
|
2010-08-13 10:11:01 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
change_note_lengths (false, false, beats, false, true);
|
2010-08-13 10:11:01 -04:00
|
|
|
}
|
|
|
|
|
2012-06-08 08:17:33 -04:00
|
|
|
/** Add a new patch change flag to the canvas.
|
|
|
|
* @param patch the patch change to add
|
|
|
|
* @param the text to display in the flag
|
|
|
|
* @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
|
|
|
|
*/
|
2008-04-29 03:28:24 -04:00
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
|
2008-04-29 03:28:24 -04:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
samplecnt_t region_samples = source_beats_to_region_samples (patch->time());
|
|
|
|
const double x = trackview.editor().sample_to_pixel (region_samples);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
double const height = midi_stream_view()->contents_height();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2013-04-04 18:45:27 -04:00
|
|
|
// CAIROCANVAS: active_channel info removed from PatcChange constructor
|
|
|
|
// 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(),
|
2016-12-13 12:05:08 -05:00
|
|
|
patch,
|
|
|
|
_patch_change_outline,
|
|
|
|
_patch_change_fill)
|
|
|
|
);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2013-04-04 18:45:27 -04:00
|
|
|
if (patch_change->item().width() < _pixel_width) {
|
2012-06-12 14:02:03 -04:00
|
|
|
// Show unless patch change is beyond the region bounds
|
2017-09-18 12:39:17 -04:00
|
|
|
if (region_samples < 0 || region_samples >= _region->length()) {
|
2012-06-12 14:02:03 -04:00
|
|
|
patch_change->hide();
|
|
|
|
} else {
|
|
|
|
patch_change->show();
|
|
|
|
}
|
2009-02-16 00:54:12 -05:00
|
|
|
} else {
|
2012-06-12 14:02:03 -04:00
|
|
|
patch_change->hide ();
|
2009-02-16 00:54:12 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2016-12-28 12:21:37 -05:00
|
|
|
_patch_changes.insert (make_pair (patch, patch_change));
|
2008-12-14 17:56:44 -05:00
|
|
|
}
|
|
|
|
|
2016-12-15 06:35:23 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
|
|
|
|
{
|
|
|
|
/* remove the canvas item */
|
|
|
|
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
|
2016-12-29 07:22:06 -05:00
|
|
|
if (x->second->patch() == pc->patch()) {
|
2016-12-15 06:35:23 -05:00
|
|
|
_patch_changes.erase (x);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-12 12:32:06 -04:00
|
|
|
MIDI::Name::PatchPrimaryKey
|
|
|
|
MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
|
|
|
|
{
|
|
|
|
return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
|
|
|
|
}
|
|
|
|
|
2013-01-19 21:31:41 -05:00
|
|
|
/// Return true iff @p pc applies to the given time on the given channel.
|
|
|
|
static bool
|
2017-09-24 12:03:54 -04:00
|
|
|
patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Temporal::Beats time, uint8_t channel)
|
2013-01-19 21:31:41 -05:00
|
|
|
{
|
|
|
|
return pc->time() <= time && pc->channel() == channel;
|
|
|
|
}
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2015-10-04 14:51:05 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::get_patch_key_at (Temporal::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
|
2008-12-15 06:05:41 -05:00
|
|
|
{
|
2013-01-19 21:31:41 -05:00
|
|
|
// The earliest event not before time
|
2010-12-28 13:19:40 -05:00
|
|
|
MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
|
2013-01-19 21:31:41 -05:00
|
|
|
|
|
|
|
// Go backwards until we find the latest PC for this channel, or the start
|
|
|
|
while (i != _model->patch_changes().begin() &&
|
|
|
|
(i == _model->patch_changes().end() ||
|
|
|
|
!patch_applies(*i, time, channel))) {
|
|
|
|
--i;
|
2008-12-15 06:05:41 -05:00
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2013-01-19 23:52:42 -05:00
|
|
|
if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
|
2014-12-17 19:43:09 -05:00
|
|
|
key.set_bank((*i)->bank());
|
|
|
|
key.set_program((*i)->program ());
|
2010-12-28 13:19:40 -05:00
|
|
|
} else {
|
2014-12-17 19:43:09 -05:00
|
|
|
key.set_bank(0);
|
|
|
|
key.set_program(0);
|
2013-01-19 02:00:43 -05:00
|
|
|
}
|
2008-12-15 06:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
|
2008-12-14 17:56:44 -05:00
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
string name = _("alter patch change");
|
|
|
|
trackview.editor().begin_reversible_command (name);
|
2016-12-15 06:35:23 -05:00
|
|
|
|
2015-01-10 12:07:31 -05:00
|
|
|
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-17 19:43:09 -05:00
|
|
|
if (pc.patch()->program() != new_patch.program()) {
|
|
|
|
c->change_program (pc.patch (), new_patch.program());
|
2008-12-15 06:05:41 -05:00
|
|
|
}
|
|
|
|
|
2014-12-17 19:43:09 -05:00
|
|
|
int const new_bank = new_patch.bank();
|
2010-12-28 13:19:40 -05:00
|
|
|
if (pc.patch()->bank() != new_bank) {
|
|
|
|
c->change_bank (pc.patch (), new_bank);
|
2008-12-15 06:05:41 -05:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
_model->apply_command (*trackview.session(), c);
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().commit_reversible_command ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2016-12-15 09:58:58 -05:00
|
|
|
remove_canvas_patch_change (&pc);
|
2011-05-19 17:11:21 -04:00
|
|
|
display_patch_changes ();
|
2008-12-14 17:56:44 -05:00
|
|
|
}
|
|
|
|
|
2010-12-19 22:42:59 -05:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Temporal::Beats> & new_change)
|
2010-12-22 18:45:04 -05:00
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
string name = _("alter patch change");
|
|
|
|
trackview.editor().begin_reversible_command (name);
|
|
|
|
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
if (old_change->time() != new_change.time()) {
|
|
|
|
c->change_time (old_change, new_change.time());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_change->channel() != new_change.channel()) {
|
|
|
|
c->change_channel (old_change, new_change.channel());
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
if (old_change->program() != new_change.program()) {
|
|
|
|
c->change_program (old_change, new_change.program());
|
|
|
|
}
|
2010-12-22 18:45:04 -05:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
if (old_change->bank() != new_change.bank()) {
|
|
|
|
c->change_bank (old_change, new_change.bank());
|
|
|
|
}
|
2010-12-22 18:45:04 -05:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
_model->apply_command (*trackview.session(), c);
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().commit_reversible_command ();
|
2010-12-22 18:45:04 -05:00
|
|
|
|
2016-12-15 09:58:58 -05:00
|
|
|
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
|
2016-12-29 07:22:06 -05:00
|
|
|
if (x->second->patch() == old_change) {
|
2016-12-15 09:58:58 -05:00
|
|
|
_patch_changes.erase (x);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
display_patch_changes ();
|
2010-12-22 18:45:04 -05:00
|
|
|
}
|
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
/** Add a patch change to the region.
|
2017-09-18 12:39:17 -04:00
|
|
|
* @param t Time in samples relative to region position
|
2010-12-28 13:19:40 -05:00
|
|
|
* @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
|
2011-05-25 11:19:47 -04:00
|
|
|
* MidiTimeAxisView::get_channel_for_add())
|
2010-12-28 13:19:40 -05:00
|
|
|
*/
|
2010-12-22 18:45:04 -05:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::add_patch_change (samplecnt_t t, Evoral::PatchChange<Temporal::Beats> const & patch)
|
2010-12-19 22:42:59 -05:00
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
string name = _("add patch change");
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().begin_reversible_command (name);
|
|
|
|
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
|
2010-12-28 13:19:40 -05:00
|
|
|
c->add (MidiModel::PatchChangePtr (
|
2017-09-24 12:03:54 -04:00
|
|
|
new Evoral::PatchChange<Temporal::Beats> (
|
2017-09-18 12:39:17 -04:00
|
|
|
absolute_samples_to_source_beats (_region->position() + t),
|
2017-03-29 17:52:00 -04:00
|
|
|
patch.channel(), patch.program(), patch.bank()
|
2011-08-16 10:24:41 -04:00
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
_model->apply_command (*trackview.session(), c);
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().commit_reversible_command ();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
display_patch_changes ();
|
2010-12-20 08:30:31 -05:00
|
|
|
}
|
2010-12-19 22:42:59 -05:00
|
|
|
|
2010-12-20 08:30:31 -05:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::move_patch_change (PatchChange& pc, Temporal::Beats t)
|
2010-12-20 08:30:31 -05:00
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().begin_reversible_command (_("move patch change"));
|
2010-12-28 13:19:40 -05:00
|
|
|
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
|
|
|
|
c->change_time (pc.patch (), t);
|
|
|
|
_model->apply_command (*trackview.session(), c);
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().commit_reversible_command ();
|
2010-12-19 22:42:59 -05:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
display_patch_changes ();
|
2010-12-19 22:42:59 -05:00
|
|
|
}
|
|
|
|
|
2008-12-23 01:03:45 -05:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::delete_patch_change (PatchChange* pc)
|
2008-12-23 01:03:45 -05:00
|
|
|
{
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().begin_reversible_command (_("delete patch change"));
|
2016-12-15 06:35:23 -05:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
|
|
|
|
c->remove (pc->patch ());
|
|
|
|
_model->apply_command (*trackview.session(), c);
|
2015-01-10 12:07:31 -05:00
|
|
|
trackview.editor().commit_reversible_command ();
|
2008-12-23 01:03:45 -05:00
|
|
|
|
2016-12-15 09:58:58 -05:00
|
|
|
remove_canvas_patch_change (pc);
|
2010-12-28 13:19:40 -05:00
|
|
|
display_patch_changes ();
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2014-12-17 20:18:11 -05:00
|
|
|
MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
|
2011-07-27 15:11:39 -04:00
|
|
|
{
|
2014-12-17 19:43:09 -05:00
|
|
|
MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
|
2014-12-17 20:18:11 -05:00
|
|
|
if (bank) {
|
|
|
|
key.set_bank(key.bank() + delta);
|
|
|
|
} else {
|
|
|
|
key.set_program(key.program() + delta);
|
|
|
|
}
|
2014-12-17 19:43:09 -05:00
|
|
|
change_patch_change(patch, key);
|
2011-07-27 15:11:39 -04:00
|
|
|
}
|
|
|
|
|
2010-05-10 12:05:24 -04:00
|
|
|
void
|
Replace static PBD::Signal in NoteBase with direct call to MidiRegionView
NoteBaseDeleted signal is static so each MidiRegionView(MRV) gets notified
about the deletion of each NodeBase instance even if it is contained in another
MRV
The NoteBase and MRV classes are currently coupled anyway, so this change uses
the reference to the MRV parent to directly call the parent when the NoteBase
is deleted. This is all in the GUI thread so I'm not sure why a PBD::Signal was
being used?
If the MRV class is the only reference holder to the NoteBase class
then I'm not sure if a callback is needed, perhaps the MRV should just remove
the note from the selection before deleting it but I'm not that familiar with
the code.
Signal emission/calls static NoteBaseDeleted signal vs direct with 10540
NoteBase instances.
static:
After Load Session: 6360638
After Unload Session: 12221026(5860388)
direct:
After load Session: 10540
After unload Session: 21080
Session Load/Unload time in master, debug/release with ~10000 Notes(seconds)
Load Debug: 32, 26
Unload Debug: 83
Load Release 32, 20, 42
Unload Release 26, 25
Session Load/Unload time with direct call debug/release(seconds)
Load Debug: 21.7, 18.1
Unload Debug: 69.4, 71
Load Release: 22.6, 13.4, 17.7
Unload Release: 24, 23.5
This is not a large Session, 1500 regions, 10000 notes so there is probably
some other funky stuff going on that needs fixing.
2015-10-14 06:37:07 -04:00
|
|
|
MidiRegionView::note_deleted (NoteBase* cne)
|
2010-05-10 12:05:24 -04:00
|
|
|
{
|
2016-11-19 05:40:41 -05:00
|
|
|
if (_entered_note && cne == _entered_note) {
|
|
|
|
_entered_note = 0;
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-06-21 17:29:22 -04:00
|
|
|
_selection.erase (cne);
|
2010-05-10 12:05:24 -04:00
|
|
|
}
|
|
|
|
|
2007-08-04 20:50:54 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::delete_selection()
|
|
|
|
{
|
2009-05-04 20:18:21 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-27 12:53:54 -04:00
|
|
|
if (trackview.editor().drags()->active()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("delete selection"));
|
2007-08-04 20:50:54 -04:00
|
|
|
|
2008-05-09 07:28:14 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
if ((*i)->selected()) {
|
2010-12-09 16:34:46 -05:00
|
|
|
_note_diff_command->remove((*i)->note());
|
2008-05-09 07:28:14 -04:00
|
|
|
}
|
|
|
|
}
|
2007-08-04 20:50:54 -04:00
|
|
|
|
|
|
|
_selection.clear();
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2010-06-12 09:55:22 -04:00
|
|
|
apply_diff ();
|
2016-11-18 11:03:20 -05:00
|
|
|
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2007-08-04 20:50:54 -04:00
|
|
|
}
|
|
|
|
|
2010-05-24 21:39:45 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
|
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("delete note"));
|
|
|
|
_note_diff_command->remove (n);
|
2010-06-12 09:55:22 -04:00
|
|
|
apply_diff ();
|
2010-05-24 21:39:45 -04:00
|
|
|
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2010-05-24 21:39:45 -04:00
|
|
|
}
|
|
|
|
|
2007-08-04 20:33:14 -04:00
|
|
|
void
|
2015-10-27 22:58:55 -04:00
|
|
|
MidiRegionView::clear_editor_note_selection ()
|
2007-08-04 20:33:14 -04:00
|
|
|
{
|
2015-10-27 22:58:55 -04:00
|
|
|
DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
|
|
|
|
PublicEditor& editor(trackview.editor());
|
|
|
|
editor.get_selection().clear_midi_notes();
|
|
|
|
}
|
2009-08-10 15:29:29 -04:00
|
|
|
|
2015-10-27 22:58:55 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::clear_selection ()
|
|
|
|
{
|
|
|
|
clear_selection_internal();
|
|
|
|
PublicEditor& editor(trackview.editor());
|
|
|
|
editor.get_selection().remove(this);
|
|
|
|
}
|
2011-12-19 13:32:57 -05:00
|
|
|
|
2015-10-27 22:58:55 -04:00
|
|
|
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();
|
2009-08-10 15:29:29 -04:00
|
|
|
}
|
2015-10-27 22:58:55 -04:00
|
|
|
_selection.clear();
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2015-10-27 22:58:55 -04:00
|
|
|
if (_entered) {
|
2014-12-06 19:56:36 -05:00
|
|
|
// Clearing selection entirely, ungrab keyboard
|
|
|
|
Keyboard::magic_widget_drop_focus();
|
|
|
|
_grabbed_keyboard = false;
|
|
|
|
}
|
2011-08-17 08:46:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::unique_select(NoteBase* ev)
|
2011-08-17 08:46:42 -04:00
|
|
|
{
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection();
|
|
|
|
add_to_selection(ev);
|
2007-08-04 20:33:14 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2011-01-26 20:31:03 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::select_all_notes ()
|
|
|
|
{
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2011-01-26 20:31:03 -05:00
|
|
|
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
add_to_selection (i->second);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2011-01-26 20:31:03 -05:00
|
|
|
}
|
|
|
|
|
2011-10-07 17:11:19 -04:00
|
|
|
void
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::select_range (samplepos_t start, samplepos_t end)
|
2011-10-07 17:11:19 -04:00
|
|
|
{
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2011-10-07 17:11:19 -04:00
|
|
|
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t t = source_beats_to_absolute_samples(i->first->time());
|
2011-10-07 17:11:19 -04:00
|
|
|
if (t >= start && t <= end) {
|
2017-01-03 08:19:31 -05:00
|
|
|
add_to_selection (i->second);
|
2011-10-07 17:11:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-07 16:27:12 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::invert_selection ()
|
|
|
|
{
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if (i->second->selected()) {
|
|
|
|
remove_from_selection(i->second);
|
2011-10-07 16:27:12 -04:00
|
|
|
} else {
|
2017-01-03 08:19:31 -05:00
|
|
|
add_to_selection (i->second);
|
2011-10-07 16:27:12 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-10 12:07:31 -05:00
|
|
|
/** Used for selection undo/redo.
|
|
|
|
The requested notes most likely won't exist in the view until the next model redisplay.
|
|
|
|
*/
|
|
|
|
void
|
2016-10-15 08:50:02 -04:00
|
|
|
MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
|
2015-01-10 12:07:31 -05:00
|
|
|
{
|
|
|
|
NoteBase* cne;
|
2016-10-15 08:50:02 -04:00
|
|
|
list<Evoral::event_id_t>::iterator n;
|
2015-01-10 12:07:31 -05:00
|
|
|
|
|
|
|
for (n = notes.begin(); n != notes.end(); ++n) {
|
2016-10-15 08:50:02 -04:00
|
|
|
if ((cne = find_canvas_note(*n)) != 0) {
|
2015-01-10 12:07:31 -05:00
|
|
|
add_to_selection (cne);
|
|
|
|
} else {
|
|
|
|
_pending_note_selection.insert(*n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-07 14:15:33 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
|
|
|
|
{
|
2013-10-23 10:38:50 -04:00
|
|
|
bool have_selection = !_selection.empty();
|
2010-01-07 14:15:33 -05:00
|
|
|
uint8_t low_note = 127;
|
|
|
|
uint8_t high_note = 0;
|
|
|
|
MidiModel::Notes& notes (_model->notes());
|
|
|
|
_optimization_iterator = _events.begin();
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2013-10-23 10:38:50 -04:00
|
|
|
if (extend && !have_selection) {
|
|
|
|
extend = false;
|
|
|
|
}
|
2010-01-07 14:15:33 -05:00
|
|
|
|
2013-10-23 10:38:50 -04:00
|
|
|
/* scan existing selection to get note range */
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2013-10-23 10:38:50 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
if ((*i)->note()->note() < low_note) {
|
|
|
|
low_note = (*i)->note()->note();
|
|
|
|
}
|
|
|
|
if ((*i)->note()->note() > high_note) {
|
|
|
|
high_note = (*i)->note()->note();
|
|
|
|
}
|
|
|
|
}
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (!add) {
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2010-05-28 13:39:28 -04:00
|
|
|
|
2013-10-23 10:38:50 -04:00
|
|
|
if (!extend && (low_note == high_note) && (high_note == notenum)) {
|
|
|
|
/* only note previously selected is the one we are
|
|
|
|
* reselecting. treat this as cancelling the selection.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
2010-01-07 14:15:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (extend) {
|
|
|
|
low_note = min (low_note, notenum);
|
|
|
|
high_note = max (high_note, notenum);
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_no_sound_notes = true;
|
2010-01-07 14:15:33 -05:00
|
|
|
|
|
|
|
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
|
|
|
|
|
|
|
|
boost::shared_ptr<NoteType> note (*n);
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase* cne;
|
2010-01-07 14:15:33 -05:00
|
|
|
bool select = false;
|
|
|
|
|
2010-05-28 17:39:12 -04:00
|
|
|
if (((1 << note->channel()) & channel_mask) != 0) {
|
2010-01-07 14:15:33 -05:00
|
|
|
if (extend) {
|
|
|
|
if ((note->note() >= low_note && note->note() <= high_note)) {
|
|
|
|
select = true;
|
|
|
|
}
|
|
|
|
} else if (note->note() == notenum) {
|
|
|
|
select = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (select) {
|
|
|
|
if ((cne = find_canvas_note (note)) != 0) {
|
2011-06-01 13:00:29 -04:00
|
|
|
// extend is false because we've taken care of it,
|
2010-01-07 14:15:33 -05:00
|
|
|
// since it extends by time range, not pitch.
|
|
|
|
note_selected (cne, add, false);
|
|
|
|
}
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-01-07 14:15:33 -05:00
|
|
|
add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_no_sound_notes = false;
|
2010-01-07 14:15:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
|
|
|
|
{
|
|
|
|
MidiModel::Notes& notes (_model->notes());
|
|
|
|
_optimization_iterator = _events.begin();
|
|
|
|
|
|
|
|
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
|
|
|
|
|
|
|
|
boost::shared_ptr<NoteType> note (*n);
|
2013-04-04 00:32:52 -04:00
|
|
|
NoteBase* cne;
|
2010-01-07 14:15:33 -05:00
|
|
|
|
|
|
|
if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
|
|
|
|
if ((cne = find_canvas_note (note)) != 0) {
|
|
|
|
if (cne->selected()) {
|
|
|
|
note_deselected (cne);
|
|
|
|
} else {
|
|
|
|
note_selected (cne, true, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-04 20:33:14 -04:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
|
2007-08-04 20:33:14 -04:00
|
|
|
{
|
2009-08-10 15:29:29 -04:00
|
|
|
if (!add) {
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection();
|
|
|
|
add_to_selection (ev);
|
2008-05-09 07:28:14 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-08-17 22:37:57 -04:00
|
|
|
if (!extend) {
|
|
|
|
|
|
|
|
if (!ev->selected()) {
|
|
|
|
add_to_selection (ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* find end of latest note selected, select all between that and the start of "ev" */
|
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
|
|
|
|
Temporal::Beats latest = Temporal::Beats();
|
2009-08-17 22:37:57 -04:00
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
if ((*i)->note()->end_time() > latest) {
|
|
|
|
latest = (*i)->note()->end_time();
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-08-17 22:37:57 -04:00
|
|
|
if ((*i)->note()->time() < earliest) {
|
|
|
|
earliest = (*i)->note()->time();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev->note()->end_time() > latest) {
|
|
|
|
latest = ev->note()->end_time();
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ev->note()->time() < earliest) {
|
2009-08-17 22:37:57 -04:00
|
|
|
earliest = ev->note()->time();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
/* find notes entirely within OR spanning the earliest..latest range */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
|
|
|
|
(i->first->time() <= earliest && i->first->end_time() >= latest)) {
|
|
|
|
add_to_selection (i->second);
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-08-17 22:37:57 -04:00
|
|
|
}
|
2008-05-09 07:28:14 -04:00
|
|
|
}
|
2007-08-04 20:33:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::note_deselected(NoteBase* ev)
|
2007-08-04 20:33:14 -04:00
|
|
|
{
|
2009-08-10 15:29:29 -04:00
|
|
|
remove_from_selection (ev);
|
2007-08-04 20:33:14 -04:00
|
|
|
}
|
|
|
|
|
2007-08-06 20:09:22 -04:00
|
|
|
void
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::update_drag_selection(samplepos_t start, samplepos_t end, double gy0, double gy1, bool extend)
|
2007-08-06 20:09:22 -04:00
|
|
|
{
|
2014-11-16 22:35:37 -05:00
|
|
|
PublicEditor& editor = trackview.editor();
|
|
|
|
|
|
|
|
// Convert to local coordinates
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t p = _region->position();
|
2014-11-16 22:35:37 -05:00
|
|
|
const double y = midi_view()->y_position();
|
2017-09-18 12:39:17 -04:00
|
|
|
const double x0 = editor.sample_to_pixel(max((samplepos_t)0, start - p));
|
|
|
|
const double x1 = editor.sample_to_pixel(max((samplepos_t)0, end - p));
|
2014-11-16 22:35:37 -05:00
|
|
|
const double y0 = max(0.0, gy0 - y);
|
|
|
|
const double y1 = max(0.0, gy1 - y);
|
|
|
|
|
2008-05-22 19:14:19 -04:00
|
|
|
// TODO: Make this faster by storing the last updated selection rect, and only
|
|
|
|
// adjusting things that are in the area that appears/disappeared.
|
|
|
|
// We probably need a tree to be able to find events in O(log(n)) time.
|
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
|
2013-01-20 14:39:51 -05:00
|
|
|
// Rectangles intersect
|
2017-01-03 08:19:31 -05:00
|
|
|
if (!i->second->selected()) {
|
|
|
|
add_to_selection (i->second);
|
2007-08-09 18:34:56 -04:00
|
|
|
}
|
2017-01-03 08:19:31 -05:00
|
|
|
} else if (i->second->selected() && !extend) {
|
2013-01-20 14:39:51 -05:00
|
|
|
// Rectangles do not intersect
|
2017-01-03 08:19:31 -05:00
|
|
|
remove_from_selection (i->second);
|
2012-01-19 21:54:23 -05:00
|
|
|
}
|
|
|
|
}
|
2014-11-16 22:35:37 -05:00
|
|
|
|
|
|
|
typedef RouteTimeAxisView::AutomationTracks ATracks;
|
|
|
|
typedef std::list<Selectable*> Selectables;
|
|
|
|
|
|
|
|
/* Add control points to selection. */
|
|
|
|
const ATracks& atracks = midi_view()->automation_tracks();
|
|
|
|
Selectables selectables;
|
|
|
|
editor.get_selection().clear_points();
|
|
|
|
for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
|
|
|
|
a->second->get_selectables(start, end, gy0, gy1, selectables);
|
|
|
|
for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
|
|
|
|
ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
|
|
|
|
if (cp) {
|
|
|
|
editor.get_selection().add(cp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a->second->set_selected_points(editor.get_selection().points);
|
|
|
|
}
|
2012-01-19 21:54:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
|
|
|
|
{
|
|
|
|
if (y1 > y2) {
|
|
|
|
swap (y1, y2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Make this faster by storing the last updated selection rect, and only
|
|
|
|
// adjusting things that are in the area that appears/disappeared.
|
|
|
|
// 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) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
|
2012-01-19 21:54:23 -05:00
|
|
|
// within y- (note-) range
|
2017-01-03 08:19:31 -05:00
|
|
|
if (!i->second->selected()) {
|
|
|
|
add_to_selection (i->second);
|
2012-01-19 21:54:23 -05:00
|
|
|
}
|
2017-01-03 08:19:31 -05:00
|
|
|
} else if (i->second->selected() && !extend) {
|
|
|
|
remove_from_selection (i->second);
|
2007-08-06 20:09:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-10 15:29:29 -04:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::remove_from_selection (NoteBase* ev)
|
2009-08-10 15:29:29 -04:00
|
|
|
{
|
|
|
|
Selection::iterator i = _selection.find (ev);
|
|
|
|
|
|
|
|
if (i != _selection.end()) {
|
|
|
|
_selection.erase (i);
|
2014-12-06 19:56:36 -05:00
|
|
|
if (_selection.empty() && _grabbed_keyboard) {
|
|
|
|
// Ungrab keyboard
|
|
|
|
Keyboard::magic_widget_drop_focus();
|
|
|
|
_grabbed_keyboard = false;
|
|
|
|
}
|
2009-08-10 15:29:29 -04:00
|
|
|
}
|
|
|
|
|
2010-06-24 15:46:28 -04:00
|
|
|
ev->set_selected (false);
|
2009-08-10 15:29:29 -04:00
|
|
|
ev->hide_velocity ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-10 15:29:29 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
PublicEditor& editor (trackview.editor());
|
|
|
|
editor.get_selection().remove (this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::add_to_selection (NoteBase* ev)
|
2009-08-10 15:29:29 -04:00
|
|
|
{
|
2014-12-06 19:56:36 -05:00
|
|
|
const bool selection_was_empty = _selection.empty();
|
2009-08-10 15:29:29 -04:00
|
|
|
|
|
|
|
if (_selection.insert (ev).second) {
|
2010-06-24 15:46:28 -04:00
|
|
|
ev->set_selected (true);
|
2012-06-08 09:21:05 -04:00
|
|
|
start_playing_midi_note ((ev)->note());
|
2014-12-06 19:56:36 -05:00
|
|
|
if (selection_was_empty && _entered) {
|
|
|
|
// Grab keyboard for moving notes with arrow keys
|
|
|
|
Keyboard::magic_widget_grab_focus();
|
|
|
|
_grabbed_keyboard = true;
|
|
|
|
}
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2009-08-10 15:29:29 -04:00
|
|
|
|
2014-12-06 19:56:36 -05:00
|
|
|
if (selection_was_empty) {
|
2009-08-10 15:29:29 -04:00
|
|
|
PublicEditor& editor (trackview.editor());
|
|
|
|
editor.get_selection().add (this);
|
|
|
|
}
|
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats
|
2017-02-04 13:02:01 -05:00
|
|
|
MidiRegionView::earliest_in_selection ()
|
2007-08-07 22:01:14 -04:00
|
|
|
{
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
|
2010-09-17 16:32:18 -04:00
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
2011-05-19 17:11:21 -04:00
|
|
|
if ((*i)->note()->time() < earliest) {
|
|
|
|
earliest = (*i)->note()->time();
|
|
|
|
}
|
|
|
|
}
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
return earliest;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
|
|
|
|
{
|
|
|
|
typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
|
|
|
|
Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
|
|
|
|
TempoMap& tmap (editor->session()->tempo_map());
|
|
|
|
PossibleChord to_play;
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats earliest = earliest_in_selection();
|
2017-02-04 13:02:01 -05:00
|
|
|
|
2009-05-04 20:08:30 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
2017-02-04 13:02:01 -05:00
|
|
|
NoteBase* n = *i;
|
|
|
|
if (n->note()->time() == earliest) {
|
|
|
|
to_play.push_back (n->note());
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2017-02-04 13:02:01 -05:00
|
|
|
double const note_time_qn = session_relative_qn (n->note()->time().to_double());
|
2017-02-23 08:36:58 -05:00
|
|
|
double dx = 0.0;
|
|
|
|
if (midi_view()->note_mode() == Sustained) {
|
2017-09-18 12:39:17 -04:00
|
|
|
dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
|
2017-02-23 08:36:58 -05:00
|
|
|
- n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
|
|
|
|
} else {
|
|
|
|
/* Hit::x0() is offset by _position.x, unlike Note::x0() */
|
|
|
|
Hit* hit = dynamic_cast<Hit*>(n);
|
|
|
|
if (hit) {
|
2017-09-18 12:39:17 -04:00
|
|
|
dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
|
2017-02-23 08:36:58 -05:00
|
|
|
- n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
|
|
|
|
}
|
|
|
|
}
|
2017-02-04 13:02:01 -05:00
|
|
|
|
2008-04-03 17:47:47 -04:00
|
|
|
(*i)->move_event(dx, dy);
|
2017-02-06 11:10:02 -05:00
|
|
|
|
|
|
|
/* update length */
|
|
|
|
if (midi_view()->note_mode() == Sustained) {
|
|
|
|
Note* sus = dynamic_cast<Note*> (*i);
|
|
|
|
double const len_dx = editor->sample_to_pixel_unrounded (
|
2017-09-18 12:39:17 -04:00
|
|
|
tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
|
2017-02-06 11:10:02 -05:00
|
|
|
|
|
|
|
sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
|
|
|
|
}
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2015-01-02 09:44:54 -05:00
|
|
|
if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
|
2010-09-16 12:11:26 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (to_play.size() > 1) {
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
PossibleChord shifted;
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
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);
|
|
|
|
}
|
2010-09-15 12:54:17 -04:00
|
|
|
|
2012-06-08 09:21:05 -04:00
|
|
|
start_playing_midi_chord (shifted);
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
} else if (!to_play.empty()) {
|
2010-09-15 14:54:04 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
|
|
|
|
moved_note->set_note (moved_note->note() + cumulative_dy);
|
2012-06-08 09:21:05 -04:00
|
|
|
start_playing_midi_note (moved_note);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
|
|
|
}
|
2007-08-07 22:01:14 -04:00
|
|
|
}
|
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
NoteBase*
|
2017-02-04 13:02:01 -05:00
|
|
|
MidiRegionView::copy_selection (NoteBase* primary)
|
2017-01-23 15:57:38 -05:00
|
|
|
{
|
|
|
|
_copy_drag_events.clear ();
|
|
|
|
|
|
|
|
if (_selection.empty()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
NoteBase* note;
|
|
|
|
NoteBase* ret = 0;
|
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
if ((*i) == primary) {
|
|
|
|
ret = note;
|
|
|
|
}
|
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
_copy_drag_events.push_back (note);
|
|
|
|
}
|
|
|
|
|
2017-02-04 13:02:01 -05:00
|
|
|
return ret;
|
2017-01-23 15:57:38 -05:00
|
|
|
}
|
|
|
|
|
2007-08-07 22:01:14 -04:00
|
|
|
void
|
2017-02-04 13:02:01 -05:00
|
|
|
MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
|
2017-01-23 15:57:38 -05:00
|
|
|
{
|
|
|
|
typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
|
2017-02-04 13:02:01 -05:00
|
|
|
Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
|
|
|
|
TempoMap& tmap (editor->session()->tempo_map());
|
2017-01-23 15:57:38 -05:00
|
|
|
PossibleChord to_play;
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats earliest = earliest_in_selection();
|
2017-01-23 15:57:38 -05:00
|
|
|
|
|
|
|
for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
|
2017-02-04 13:02:01 -05:00
|
|
|
NoteBase* n = *i;
|
|
|
|
if (n->note()->time() == earliest) {
|
|
|
|
to_play.push_back (n->note());
|
2017-01-23 15:57:38 -05:00
|
|
|
}
|
2017-02-04 13:02:01 -05:00
|
|
|
double const note_time_qn = session_relative_qn (n->note()->time().to_double());
|
2017-02-23 09:11:56 -05:00
|
|
|
double dx = 0.0;
|
|
|
|
if (midi_view()->note_mode() == Sustained) {
|
2017-09-18 12:39:17 -04:00
|
|
|
dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
|
2017-02-23 09:11:56 -05:00
|
|
|
- n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
|
|
|
|
} else {
|
|
|
|
Hit* hit = dynamic_cast<Hit*>(n);
|
|
|
|
if (hit) {
|
2017-09-18 12:39:17 -04:00
|
|
|
dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
|
2017-02-23 09:11:56 -05:00
|
|
|
- n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
|
|
|
|
}
|
|
|
|
}
|
2017-01-23 15:57:38 -05:00
|
|
|
|
|
|
|
(*i)->move_event(dx, dy);
|
2017-02-06 11:10:02 -05:00
|
|
|
|
|
|
|
if (midi_view()->note_mode() == Sustained) {
|
|
|
|
Note* sus = dynamic_cast<Note*> (*i);
|
|
|
|
double const len_dx = editor->sample_to_pixel_unrounded (
|
2017-09-18 12:39:17 -04:00
|
|
|
tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
|
2017-02-06 11:10:02 -05:00
|
|
|
|
|
|
|
sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
|
|
|
|
}
|
2017-01-23 15:57:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2017-02-04 13:02:01 -05:00
|
|
|
MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
|
2007-08-07 22:01:14 -04:00
|
|
|
{
|
2009-10-06 18:07:10 -04:00
|
|
|
uint8_t lowest_note_in_selection = 127;
|
|
|
|
uint8_t highest_note_in_selection = 0;
|
2013-01-19 02:00:43 -05:00
|
|
|
uint8_t highest_note_difference = 0;
|
2008-04-11 11:49:52 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
if (!copy) {
|
|
|
|
// find highest and lowest notes first
|
2009-10-06 18:07:10 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
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;
|
|
|
|
*/
|
2009-10-06 18:07:10 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
// 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;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
start_note_diff_command (_("move notes"));
|
2008-03-26 07:11:47 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
|
2017-02-04 13:02:01 -05:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
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);
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
2017-01-23 15:57:38 -05:00
|
|
|
} else {
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
clear_editor_note_selection ();
|
2008-05-21 21:02:04 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
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);
|
|
|
|
}
|
2009-10-06 18:07:10 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
// 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;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
start_note_diff_command (_("copy notes"));
|
2009-02-16 00:54:12 -05:00
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
|
|
|
|
|
|
|
|
/* update time */
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
|
2017-01-23 15:57:38 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2017-01-24 17:07:27 -05:00
|
|
|
(*i)->note()->set_note (new_pitch);
|
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
// 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 ();
|
2009-02-16 00:54:12 -05:00
|
|
|
}
|
|
|
|
|
2017-01-23 15:57:38 -05:00
|
|
|
apply_diff (false, copy);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-02-16 00:54:12 -05:00
|
|
|
// care about notes being moved beyond the upper/lower bounds on the canvas
|
|
|
|
if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
|
2009-10-06 18:07:10 -04:00
|
|
|
highest_note_in_selection > midi_stream_view()->highest_note()) {
|
2017-01-23 15:57:38 -05:00
|
|
|
midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
|
2007-08-07 22:01:14 -04:00
|
|
|
}
|
|
|
|
}
|
2008-03-26 07:11:47 -04:00
|
|
|
|
2011-10-22 19:18:59 -04:00
|
|
|
/** @param x Pixel relative to the region position.
|
2015-05-22 13:09:48 -04:00
|
|
|
* @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
|
|
|
|
* Used for inverting the snap logic with key modifiers and snap delta calculation.
|
2017-09-18 12:39:17 -04:00
|
|
|
* @return Snapped sample relative to the region position.
|
2011-10-22 19:18:59 -04:00
|
|
|
*/
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t
|
2015-05-22 13:09:48 -04:00
|
|
|
MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
|
2008-03-26 07:11:47 -04:00
|
|
|
{
|
2011-04-20 19:48:49 -04:00
|
|
|
PublicEditor& editor (trackview.editor());
|
2017-09-18 12:39:17 -04:00
|
|
|
return snap_sample_to_sample (editor.pixel_to_sample (x), ensure_snap).sample;
|
2015-05-16 14:26:05 -04:00
|
|
|
}
|
|
|
|
|
2011-10-22 19:18:59 -04:00
|
|
|
/** @param x Pixel relative to the region position.
|
2015-05-22 13:09:48 -04:00
|
|
|
* @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
|
2011-10-22 19:18:59 -04:00
|
|
|
* @return Snapped pixel relative to the region position.
|
|
|
|
*/
|
2008-04-05 09:56:20 -04:00
|
|
|
double
|
2015-05-22 13:09:48 -04:00
|
|
|
MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
|
2015-05-16 14:26:05 -04:00
|
|
|
{
|
2015-05-22 13:09:48 -04:00
|
|
|
return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
|
2015-05-16 14:26:05 -04:00
|
|
|
}
|
|
|
|
|
2008-03-26 07:11:47 -04:00
|
|
|
double
|
2008-12-23 16:05:50 -05:00
|
|
|
MidiRegionView::get_position_pixels()
|
2008-03-26 07:11:47 -04:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t region_sample = get_position();
|
|
|
|
return trackview.editor().sample_to_pixel(region_sample);
|
2008-03-26 07:11:47 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-09-08 22:09:04 -04:00
|
|
|
double
|
|
|
|
MidiRegionView::get_end_position_pixels()
|
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t sample = get_position() + get_duration ();
|
|
|
|
return trackview.editor().sample_to_pixel(sample);
|
2009-09-08 22:09:04 -04:00
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::source_beats_to_absolute_samples(Temporal::Beats beats) const
|
2009-02-15 12:30:42 -05:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
/* the time converter will return the sample corresponding to `beats'
|
2011-08-16 10:24:41 -04:00
|
|
|
relative to the start of the source. The start of the source
|
|
|
|
is an implied position given by region->position - region->start
|
|
|
|
*/
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t source_start = _region->position() - _region->start();
|
2011-08-16 10:24:41 -04:00
|
|
|
return source_start + _source_relative_time_converter.to (beats);
|
2009-02-15 12:30:42 -05:00
|
|
|
}
|
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::absolute_samples_to_source_beats(samplepos_t samples) const
|
2009-02-15 12:30:42 -05:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
/* the `samples' argument needs to be converted into a sample count
|
2011-08-16 10:24:41 -04:00
|
|
|
relative to the start of the source before being passed in to the
|
|
|
|
converter.
|
|
|
|
*/
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t source_start = _region->position() - _region->start();
|
|
|
|
return _source_relative_time_converter.from (samples - source_start);
|
2011-08-16 10:24:41 -04:00
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::region_beats_to_region_samples(Temporal::Beats beats) const
|
2011-08-16 10:24:41 -04:00
|
|
|
{
|
|
|
|
return _region_relative_time_converter.to(beats);
|
|
|
|
}
|
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::region_samples_to_region_beats(samplepos_t samples) const
|
2011-08-16 10:24:41 -04:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
return _region_relative_time_converter.from(samples);
|
2009-02-15 12:30:42 -05:00
|
|
|
}
|
|
|
|
|
2015-05-15 14:15:52 -04:00
|
|
|
double
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::region_samples_to_region_beats_double (samplepos_t samples) const
|
2015-05-15 14:15:52 -04:00
|
|
|
{
|
2017-09-18 12:39:17 -04:00
|
|
|
return _region_relative_time_converter_double.from(samples);
|
2015-05-15 14:15:52 -04:00
|
|
|
}
|
|
|
|
|
2008-03-14 20:37:17 -04:00
|
|
|
void
|
2009-09-15 11:23:59 -04:00
|
|
|
MidiRegionView::begin_resizing (bool /*at_front*/)
|
2008-03-14 20:37:17 -04:00
|
|
|
{
|
|
|
|
_resize_data.clear();
|
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
2013-04-04 18:45:27 -04:00
|
|
|
Note *note = dynamic_cast<Note*> (*i);
|
2008-03-14 20:37:17 -04:00
|
|
|
|
|
|
|
// only insert CanvasNotes into the map
|
2008-05-11 22:40:48 -04:00
|
|
|
if (note) {
|
2008-03-14 20:37:17 -04:00
|
|
|
NoteResizeData *resize_data = new NoteResizeData();
|
2013-04-04 00:32:52 -04:00
|
|
|
resize_data->note = note;
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2008-03-26 07:11:47 -04:00
|
|
|
// create a new SimpleRect from the note which will be the resize preview
|
2015-10-04 14:51:05 -04:00
|
|
|
ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
|
2013-04-04 18:45:27 -04:00
|
|
|
ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
|
2008-03-26 07:11:47 -04:00
|
|
|
|
|
|
|
// calculate the colors: get the color settings
|
2018-07-05 16:52:41 -04:00
|
|
|
uint32_t fill_color = NoteBase::meter_style_fill_color (note->note()->velocity(), true);
|
2008-03-26 07:11:47 -04:00
|
|
|
|
|
|
|
// make the resize preview notes more transparent and bright
|
2008-03-14 20:37:17 -04:00
|
|
|
fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
|
|
|
|
|
2008-03-26 07:11:47 -04:00
|
|
|
// calculate color based on note velocity
|
2013-04-04 00:32:52 -04:00
|
|
|
resize_rect->set_fill_color (UINT_INTERPOLATE(
|
2013-04-04 18:45:27 -04:00
|
|
|
NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
|
2011-05-19 17:11:21 -04:00
|
|
|
fill_color,
|
2013-04-04 00:32:52 -04:00
|
|
|
0.85));
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
resize_rect->set_outline_color (NoteBase::calculate_outline (
|
2018-07-05 16:52:41 -04:00
|
|
|
UIConfiguration::instance().color ("midi note selected outline")));
|
2008-03-26 07:11:47 -04:00
|
|
|
|
2008-03-14 20:37:17 -04:00
|
|
|
resize_data->resize_rect = resize_rect;
|
|
|
|
_resize_data.push_back(resize_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-08 19:45:58 -04:00
|
|
|
/** Update resizing notes while user drags.
|
|
|
|
* @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
|
|
|
|
* @param at_front which end of the note (true == note on, false == note off)
|
2011-06-01 13:00:29 -04:00
|
|
|
* @param delta_x change in mouse position since the start of the drag
|
2010-06-08 19:45:58 -04:00
|
|
|
* @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
|
|
|
|
* a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
|
|
|
|
* amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
|
|
|
|
* as the \a primary note.
|
2015-05-15 14:15:52 -04:00
|
|
|
* @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
|
2015-05-22 13:44:42 -04:00
|
|
|
* @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
|
2010-06-08 19:45:58 -04:00
|
|
|
*/
|
2008-03-14 20:37:17 -04:00
|
|
|
void
|
2015-05-22 13:09:48 -04:00
|
|
|
MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
|
2008-03-14 20:37:17 -04:00
|
|
|
{
|
2016-07-05 12:03:06 -04:00
|
|
|
TempoMap& tmap (trackview.session()->tempo_map());
|
2011-05-19 17:11:21 -04:00
|
|
|
bool cursor_set = false;
|
2015-10-30 15:02:54 -04:00
|
|
|
bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2008-03-14 20:37:17 -04:00
|
|
|
for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
|
2013-04-04 18:45:27 -04:00
|
|
|
ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
|
2013-04-04 00:32:52 -04:00
|
|
|
Note* canvas_note = (*i)->note;
|
2009-09-08 17:45:44 -04:00
|
|
|
double current_x;
|
2008-03-26 07:11:47 -04:00
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
if (at_front) {
|
|
|
|
if (relative) {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = canvas_note->x0() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
} else {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = primary->x0() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
} else {
|
2009-09-08 17:45:44 -04:00
|
|
|
if (relative) {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = canvas_note->x1() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
} else {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = primary->x1() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-10-21 09:12:13 -04:00
|
|
|
if (current_x < 0) {
|
2019-04-08 18:27:05 -04:00
|
|
|
/* This works even with snapping because RegionView::snap_sample_to_sample()
|
|
|
|
* snaps forward if the snapped sample is before the beginning of the region
|
|
|
|
*/
|
2014-10-21 09:12:13 -04:00
|
|
|
current_x = 0;
|
|
|
|
}
|
|
|
|
if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
|
|
|
|
current_x = trackview.editor().sample_to_pixel(_region->length());
|
|
|
|
}
|
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
if (at_front) {
|
2015-05-22 13:09:48 -04:00
|
|
|
if (with_snap) {
|
2015-10-30 15:02:54 -04:00
|
|
|
resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
|
2015-05-21 16:54:16 -04:00
|
|
|
} else {
|
2015-05-22 13:09:48 -04:00
|
|
|
resize_rect->set_x0 (current_x - snap_delta);
|
2015-05-21 16:54:16 -04:00
|
|
|
}
|
2013-04-04 00:32:52 -04:00
|
|
|
resize_rect->set_x1 (canvas_note->x1());
|
2008-03-14 20:37:17 -04:00
|
|
|
} else {
|
2015-05-22 13:09:48 -04:00
|
|
|
if (with_snap) {
|
2015-10-30 15:02:54 -04:00
|
|
|
resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
|
2015-05-21 16:54:16 -04:00
|
|
|
} else {
|
2015-05-22 13:09:48 -04:00
|
|
|
resize_rect->set_x1 (current_x - snap_delta);
|
2015-05-21 16:54:16 -04:00
|
|
|
}
|
2013-04-04 00:32:52 -04:00
|
|
|
resize_rect->set_x0 (canvas_note->x0());
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (!cursor_set) {
|
2015-05-22 15:36:03 -04:00
|
|
|
/* Convert snap delta from pixels to beats. */
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
|
2015-05-28 09:37:46 -04:00
|
|
|
double snap_delta_beats = 0.0;
|
2015-05-15 14:15:52 -04:00
|
|
|
int sign = 1;
|
2015-05-22 15:36:03 -04:00
|
|
|
|
2015-05-15 14:15:52 -04:00
|
|
|
/* negative beat offsets aren't allowed */
|
2015-05-22 15:36:03 -04:00
|
|
|
if (snap_delta_samps > 0) {
|
2017-09-18 12:39:17 -04:00
|
|
|
snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
|
2015-05-22 15:36:03 -04:00
|
|
|
} else if (snap_delta_samps < 0) {
|
2019-04-08 18:27:05 -04:00
|
|
|
snap_delta_beats = region_samples_to_region_beats_double (- snap_delta_samps);
|
2015-05-15 14:15:52 -04:00
|
|
|
sign = -1;
|
|
|
|
}
|
|
|
|
|
2016-07-05 12:03:06 -04:00
|
|
|
double snapped_x;
|
2016-08-30 13:27:35 -04:00
|
|
|
int32_t divisions = 0;
|
2016-07-05 12:03:06 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2018-02-09 09:21:45 -05:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
const Temporal::Beats beats = Temporal::Beats (tmap.exact_beat_at_sample (snapped_x + midi_region()->position(), divisions)
|
2019-04-08 18:27:05 -04:00
|
|
|
- midi_region()->beat())
|
|
|
|
+ midi_region()->start_beats();
|
2016-07-05 12:03:06 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats len = Temporal::Beats();
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (at_front) {
|
|
|
|
if (beats < canvas_note->note()->end_time()) {
|
2015-05-22 15:36:03 -04:00
|
|
|
len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
|
2011-05-19 17:11:21 -04:00
|
|
|
len += canvas_note->note()->length();
|
|
|
|
}
|
|
|
|
} else {
|
2011-06-01 13:00:29 -04:00
|
|
|
if (beats >= canvas_note->note()->time()) {
|
2015-05-22 15:36:03 -04:00
|
|
|
len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
|
|
|
}
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
len = std::max(Temporal::Beats(1 / 512.0), len);
|
2015-01-08 19:13:00 -05:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
char buf[16];
|
2014-11-22 21:49:42 -05:00
|
|
|
snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
|
2011-05-19 17:11:21 -04:00
|
|
|
show_verbose_cursor (buf, 0, 0);
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
cursor_set = true;
|
2018-02-09 09:21:45 -05:00
|
|
|
|
|
|
|
trackview.editor().set_snapped_cursor_position ( snapped_x + midi_region()->position() );
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-08 19:45:58 -04:00
|
|
|
|
|
|
|
/** Finish resizing notes when the user releases the mouse button.
|
|
|
|
* Parameters the same as for \a update_resizing().
|
|
|
|
*/
|
2008-03-14 20:37:17 -04:00
|
|
|
void
|
2015-05-22 13:09:48 -04:00
|
|
|
MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
|
2008-03-14 20:37:17 -04:00
|
|
|
{
|
2015-01-16 12:55:05 -05:00
|
|
|
_note_diff_command = _model->new_note_diff_command (_("resize notes"));
|
2016-07-05 12:03:06 -04:00
|
|
|
TempoMap& tmap (trackview.session()->tempo_map());
|
2015-05-21 16:54:16 -04:00
|
|
|
|
2015-10-31 10:43:13 -04:00
|
|
|
/* XX why doesn't snap_pixel_to_sample() handle this properly? */
|
2015-10-30 15:02:54 -04:00
|
|
|
bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
|
|
|
|
|
2008-03-14 20:37:17 -04:00
|
|
|
for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
|
2013-04-04 00:32:52 -04:00
|
|
|
Note* canvas_note = (*i)->note;
|
2013-04-04 18:45:27 -04:00
|
|
|
ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
|
2011-07-02 18:13:19 -04:00
|
|
|
|
|
|
|
/* Get the new x position for this resize, which is in pixels relative
|
|
|
|
* to the region position.
|
|
|
|
*/
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
double current_x;
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-09-08 17:45:44 -04:00
|
|
|
if (at_front) {
|
|
|
|
if (relative) {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = canvas_note->x0() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
} else {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = primary->x0() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (relative) {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = canvas_note->x1() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
} else {
|
2015-05-15 14:15:52 -04:00
|
|
|
current_x = primary->x1() + delta_x + snap_delta;
|
2009-09-08 17:45:44 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2014-10-21 09:12:13 -04:00
|
|
|
if (current_x < 0) {
|
|
|
|
current_x = 0;
|
|
|
|
}
|
|
|
|
if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
|
|
|
|
current_x = trackview.editor().sample_to_pixel(_region->length());
|
|
|
|
}
|
2015-05-22 15:36:03 -04:00
|
|
|
|
|
|
|
/* Convert snap delta from pixels to beats with sign. */
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
|
2015-05-28 09:37:46 -04:00
|
|
|
double snap_delta_beats = 0.0;
|
2015-05-15 14:15:52 -04:00
|
|
|
int sign = 1;
|
2015-05-22 15:36:03 -04:00
|
|
|
|
2015-05-21 16:54:16 -04:00
|
|
|
if (snap_delta_samps > 0) {
|
2017-09-18 12:39:17 -04:00
|
|
|
snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
|
2015-05-21 16:54:16 -04:00
|
|
|
} else if (snap_delta_samps < 0) {
|
2017-09-18 12:39:17 -04:00
|
|
|
snap_delta_beats = region_samples_to_region_beats_double ( - snap_delta_samps);
|
2015-05-15 14:15:52 -04:00
|
|
|
sign = -1;
|
|
|
|
}
|
2014-10-21 09:12:13 -04:00
|
|
|
|
2016-07-05 12:03:06 -04:00
|
|
|
uint32_t divisions = 0;
|
2017-09-18 12:39:17 -04:00
|
|
|
/* Convert the new x position to a sample within the source */
|
|
|
|
samplepos_t current_fr;
|
2015-06-10 11:36:34 -04:00
|
|
|
if (with_snap) {
|
2016-06-15 10:18:27 -04:00
|
|
|
current_fr = snap_pixel_to_sample (current_x, ensure_snap);
|
2016-07-05 12:03:06 -04:00
|
|
|
divisions = trackview.editor().get_grid_music_divisions (0);
|
2015-06-10 11:36:34 -04:00
|
|
|
} else {
|
2016-06-15 10:18:27 -04:00
|
|
|
current_fr = trackview.editor().pixel_to_sample (current_x);
|
2015-06-10 11:36:34 -04:00
|
|
|
}
|
2015-05-17 09:47:01 -04:00
|
|
|
|
2011-07-02 18:13:19 -04:00
|
|
|
/* and then to beats */
|
2017-09-18 12:39:17 -04:00
|
|
|
const double e_qaf = tmap.exact_qn_at_sample (current_fr + midi_region()->position(), divisions);
|
2016-11-08 12:51:19 -05:00
|
|
|
const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
|
2017-09-24 12:03:54 -04:00
|
|
|
const Temporal::Beats x_beats = Temporal::Beats (e_qaf - quarter_note_start);
|
2009-10-06 18:07:10 -04:00
|
|
|
|
2015-05-21 16:54:16 -04:00
|
|
|
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));
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
|
2009-10-06 18:07:10 -04:00
|
|
|
len += canvas_note->note()->length();
|
|
|
|
|
2015-05-20 10:29:20 -04:00
|
|
|
if (!!len) {
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
|
2009-10-06 18:07:10 -04:00
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
2009-09-08 17:45:44 -04:00
|
|
|
|
|
|
|
if (!at_front) {
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats len = std::max(Temporal::Beats(1 / 512.0),
|
2015-05-21 16:54:16 -04:00
|
|
|
x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
|
2015-01-08 19:13:00 -05:00
|
|
|
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
delete resize_rect;
|
|
|
|
delete (*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
_resize_data.clear();
|
2015-01-16 12:55:05 -05:00
|
|
|
apply_diff(true);
|
2008-03-14 20:37:17 -04:00
|
|
|
}
|
2007-08-10 16:55:27 -04:00
|
|
|
|
2011-12-02 22:05:59 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::abort_resizing ()
|
|
|
|
{
|
|
|
|
for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
|
|
|
|
delete (*i)->resize_rect;
|
|
|
|
delete *i;
|
|
|
|
}
|
|
|
|
|
|
|
|
_resize_data.clear ();
|
|
|
|
}
|
|
|
|
|
2008-12-23 16:05:50 -05:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
|
2008-12-23 16:05:50 -05:00
|
|
|
{
|
2009-09-07 12:53:53 -04:00
|
|
|
uint8_t new_velocity;
|
2008-12-23 16:05:50 -05:00
|
|
|
|
|
|
|
if (relative) {
|
2009-09-07 12:53:53 -04:00
|
|
|
new_velocity = event->note()->velocity() + velocity;
|
2009-02-15 14:44:27 -05:00
|
|
|
clamp_to_0_127(new_velocity);
|
2008-12-23 16:05:50 -05:00
|
|
|
} else {
|
2009-09-07 12:53:53 -04:00
|
|
|
new_velocity = velocity;
|
2008-12-23 16:05:50 -05:00
|
|
|
}
|
|
|
|
|
2011-06-01 13:00:29 -04:00
|
|
|
event->set_selected (event->selected()); // change color
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
|
2008-12-23 16:05:50 -05:00
|
|
|
}
|
2008-04-03 17:47:47 -04:00
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
|
2009-08-17 11:58:47 -04:00
|
|
|
{
|
2009-09-06 14:11:55 -04:00
|
|
|
uint8_t new_note;
|
2009-08-17 11:58:47 -04:00
|
|
|
|
|
|
|
if (relative) {
|
2009-09-06 14:11:55 -04:00
|
|
|
new_note = event->note()->note() + note;
|
2009-08-17 11:58:47 -04:00
|
|
|
} else {
|
2009-09-06 14:11:55 -04:00
|
|
|
new_note = note;
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
2009-09-06 14:11:55 -04:00
|
|
|
clamp_to_0_127 (new_note);
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::trim_note (NoteBase* event, Temporal::Beats front_delta, Temporal::Beats end_delta)
|
2009-08-26 23:09:30 -04:00
|
|
|
{
|
2009-09-07 09:38:06 -04:00
|
|
|
bool change_start = false;
|
|
|
|
bool change_length = false;
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats new_start;
|
|
|
|
Temporal::Beats new_length;
|
2009-09-07 09:38:06 -04:00
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
/* NOTE: the semantics of the two delta arguments are slightly subtle:
|
|
|
|
|
|
|
|
front_delta: if positive - move the start of the note later in time (shortening it)
|
2011-05-19 17:11:21 -04:00
|
|
|
if negative - move the start of the note earlier in time (lengthening it)
|
2009-08-26 23:09:30 -04:00
|
|
|
|
|
|
|
end_delta: if positive - move the end of the note later in time (lengthening it)
|
2011-05-19 17:11:21 -04:00
|
|
|
if negative - move the end of the note earlier in time (shortening it)
|
|
|
|
*/
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2015-05-20 10:29:42 -04:00
|
|
|
if (!!front_delta) {
|
2009-08-26 23:09:30 -04:00
|
|
|
if (front_delta < 0) {
|
2009-09-07 09:38:06 -04:00
|
|
|
|
|
|
|
if (event->note()->time() < -front_delta) {
|
2017-09-24 12:03:54 -04:00
|
|
|
new_start = Temporal::Beats();
|
2009-08-26 23:09:30 -04:00
|
|
|
} else {
|
2009-09-07 09:38:06 -04:00
|
|
|
new_start = event->note()->time() + front_delta; // moves earlier
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
2009-09-07 09:38:06 -04:00
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
/* start moved toward zero, so move the end point out to where it used to be.
|
|
|
|
Note that front_delta is negative, so this increases the length.
|
2009-09-07 09:38:06 -04:00
|
|
|
*/
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2009-09-07 09:38:06 -04:00
|
|
|
new_length = event->note()->length() - front_delta;
|
|
|
|
change_start = true;
|
|
|
|
change_length = true;
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2009-09-07 09:38:06 -04:00
|
|
|
} else {
|
2009-08-26 23:09:30 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats new_pos = event->note()->time() + front_delta;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-09-07 09:38:06 -04:00
|
|
|
if (new_pos < event->note()->end_time()) {
|
|
|
|
new_start = event->note()->time() + front_delta;
|
|
|
|
/* start moved toward the end, so move the end point back to where it used to be */
|
2009-10-14 12:10:01 -04:00
|
|
|
new_length = event->note()->length() - front_delta;
|
2009-09-07 09:38:06 -04:00
|
|
|
change_start = true;
|
|
|
|
change_length = true;
|
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-05-20 10:29:20 -04:00
|
|
|
if (!!end_delta) {
|
2009-09-07 09:38:06 -04:00
|
|
|
bool can_change = true;
|
2009-08-26 23:09:30 -04:00
|
|
|
if (end_delta < 0) {
|
2009-09-07 09:38:06 -04:00
|
|
|
if (event->note()->length() < -end_delta) {
|
|
|
|
can_change = false;
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-09-07 09:38:06 -04:00
|
|
|
|
|
|
|
if (can_change) {
|
|
|
|
new_length = event->note()->length() + end_delta;
|
|
|
|
change_length = true;
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 09:38:06 -04:00
|
|
|
if (change_start) {
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
|
2009-09-07 09:38:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (change_length) {
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
|
2009-09-07 09:38:06 -04:00
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
|
|
|
|
2011-06-13 10:48:48 -04:00
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
|
2011-06-13 10:48:48 -04:00
|
|
|
{
|
|
|
|
uint8_t new_channel;
|
|
|
|
|
|
|
|
if (relative) {
|
|
|
|
if (chn < 0.0) {
|
|
|
|
if (event->note()->channel() < -chn) {
|
|
|
|
new_channel = 0;
|
|
|
|
} else {
|
|
|
|
new_channel = event->note()->channel() + chn;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
new_channel = event->note()->channel() + chn;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
new_channel = (uint8_t) chn;
|
|
|
|
}
|
|
|
|
|
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
|
|
|
|
}
|
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::change_note_time (NoteBase* event, Temporal::Beats delta, bool relative)
|
2009-08-17 11:58:47 -04:00
|
|
|
{
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats new_time;
|
2009-08-17 11:58:47 -04:00
|
|
|
|
|
|
|
if (relative) {
|
|
|
|
if (delta < 0.0) {
|
2009-09-07 12:53:53 -04:00
|
|
|
if (event->note()->time() < -delta) {
|
2017-09-24 12:03:54 -04:00
|
|
|
new_time = Temporal::Beats();
|
2009-08-17 11:58:47 -04:00
|
|
|
} else {
|
2009-09-07 12:53:53 -04:00
|
|
|
new_time = event->note()->time() + delta;
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
} else {
|
2009-09-07 12:53:53 -04:00
|
|
|
new_time = event->note()->time() + delta;
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
} else {
|
2009-09-07 12:53:53 -04:00
|
|
|
new_time = delta;
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
2010-08-13 22:00:50 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::change_note_length (NoteBase* event, Temporal::Beats t)
|
2010-08-13 22:00:50 -04:00
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
|
2010-08-13 22:00:50 -04:00
|
|
|
}
|
|
|
|
|
2008-04-03 17:47:47 -04:00
|
|
|
void
|
2012-06-06 08:16:15 -04:00
|
|
|
MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
|
2008-04-03 17:47:47 -04:00
|
|
|
{
|
2009-08-28 12:06:08 -04:00
|
|
|
int8_t delta;
|
2012-06-13 11:38:15 -04:00
|
|
|
int8_t value = 0;
|
2009-08-28 12:06:08 -04:00
|
|
|
|
2009-08-17 22:37:57 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
2008-04-14 02:23:11 -04:00
|
|
|
}
|
|
|
|
|
2009-08-28 12:06:08 -04:00
|
|
|
if (fine) {
|
|
|
|
delta = 1;
|
|
|
|
} else {
|
|
|
|
delta = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!up) {
|
|
|
|
delta = -delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!allow_smush) {
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
2012-12-19 15:31:30 -05:00
|
|
|
if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
|
|
|
|
goto cursor_label;
|
2009-08-28 12:06:08 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("change velocities"));
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
|
|
|
|
Selection::iterator next = i;
|
|
|
|
++next;
|
2012-06-06 08:16:15 -04:00
|
|
|
|
|
|
|
if (all_together) {
|
|
|
|
if (i == _selection.begin()) {
|
|
|
|
change_note_velocity (*i, delta, true);
|
|
|
|
value = (*i)->note()->velocity() + delta;
|
|
|
|
} else {
|
|
|
|
change_note_velocity (*i, value, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
change_note_velocity (*i, delta, true);
|
|
|
|
}
|
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
i = next;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-07-11 21:09:33 -04:00
|
|
|
apply_diff();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2012-12-19 15:31:30 -05:00
|
|
|
cursor_label:
|
2011-05-19 17:11:21 -04:00
|
|
|
if (!_selection.empty()) {
|
|
|
|
char buf[24];
|
2011-06-01 13:00:29 -04:00
|
|
|
snprintf (buf, sizeof (buf), "Vel %d",
|
2011-05-19 17:11:21 -04:00
|
|
|
(int) (*_selection.begin())->note()->velocity());
|
|
|
|
show_verbose_cursor (buf, 10, 10);
|
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2009-08-28 12:06:08 -04:00
|
|
|
MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
|
2009-08-17 11:58:47 -04:00
|
|
|
{
|
2009-08-17 22:37:57 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
int8_t delta;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
if (fine) {
|
|
|
|
delta = 1;
|
|
|
|
} else {
|
|
|
|
delta = 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!up) {
|
|
|
|
delta = -delta;
|
|
|
|
}
|
|
|
|
|
2009-08-28 12:06:08 -04:00
|
|
|
if (!allow_smush) {
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
if (!up) {
|
|
|
|
if ((int8_t) (*i)->note()->note() + delta <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((int8_t) (*i)->note()->note() + delta > 127) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("transpose"));
|
2009-08-17 11:58:47 -04:00
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
|
|
|
Selection::iterator next = i;
|
|
|
|
++next;
|
|
|
|
change_note_note (*i, delta, true);
|
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
|
2009-09-06 14:11:55 -04:00
|
|
|
apply_diff ();
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::change_note_lengths (bool fine, bool shorter, Temporal::Beats delta, bool start, bool end)
|
2009-08-26 23:09:30 -04:00
|
|
|
{
|
2014-11-22 04:05:42 -05:00
|
|
|
if (!delta) {
|
2011-05-19 17:11:21 -04:00
|
|
|
if (fine) {
|
2017-09-24 12:03:54 -04:00
|
|
|
delta = Temporal::Beats(1.0/128.0);
|
2011-05-19 17:11:21 -04:00
|
|
|
} else {
|
|
|
|
/* grab the current grid distance */
|
2014-11-21 02:37:42 -05:00
|
|
|
delta = get_grid_beats(_region->position());
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
|
|
|
|
if (shorter) {
|
|
|
|
delta = -delta;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("change note lengths"));
|
2009-08-26 23:09:30 -04:00
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
|
|
|
Selection::iterator next = i;
|
|
|
|
++next;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
/* note the negation of the delta for start */
|
|
|
|
|
2014-11-22 04:05:42 -05:00
|
|
|
trim_note (*i,
|
2017-09-24 12:03:54 -04:00
|
|
|
(start ? -delta : Temporal::Beats()),
|
|
|
|
(end ? delta : Temporal::Beats()));
|
2009-08-26 23:09:30 -04:00
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
|
2009-09-07 09:38:06 -04:00
|
|
|
apply_diff ();
|
2009-08-26 23:09:30 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
void
|
2014-12-06 21:29:48 -05:00
|
|
|
MidiRegionView::nudge_notes (bool forward, bool fine)
|
2009-08-17 11:58:47 -04:00
|
|
|
{
|
2009-08-17 22:37:57 -04:00
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
/* pick a note as the point along the timeline to get the nudge distance.
|
|
|
|
its not necessarily the earliest note, so we may want to pull the notes out
|
2009-08-17 22:37:57 -04:00
|
|
|
into a vector and sort before using the first one.
|
|
|
|
*/
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplepos_t ref_point = source_beats_to_absolute_samples ((*(_selection.begin()))->note()->time());
|
2017-09-24 12:57:27 -04:00
|
|
|
Temporal::Beats delta;
|
2014-12-06 21:29:48 -05:00
|
|
|
|
|
|
|
if (!fine) {
|
2009-08-17 22:37:57 -04:00
|
|
|
|
2014-12-06 21:29:48 -05:00
|
|
|
/* non-fine, move by 1 bar regardless of snap */
|
2017-09-24 12:03:54 -04:00
|
|
|
delta = Temporal::Beats(trackview.session()->tempo_map().meter_at_sample (ref_point).divisions_per_bar());
|
2014-12-06 21:29:48 -05:00
|
|
|
|
|
|
|
} else if (trackview.editor().snap_mode() == Editing::SnapOff) {
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-10-26 19:43:04 -04:00
|
|
|
/* grid is off - use nudge distance */
|
2009-08-17 22:37:57 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t unused;
|
|
|
|
const samplecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
|
|
|
|
delta = region_samples_to_region_beats (fabs ((double)distance));
|
2009-10-26 19:43:04 -04:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* use grid */
|
2009-08-17 22:37:57 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
MusicSample next_pos (ref_point, 0);
|
2009-08-26 23:09:30 -04:00
|
|
|
if (forward) {
|
2017-09-18 12:39:17 -04:00
|
|
|
if (max_samplepos - 1 < next_pos.sample) {
|
|
|
|
next_pos.sample += 1;
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
} else {
|
2017-09-18 12:39:17 -04:00
|
|
|
if (next_pos.sample == 0) {
|
2009-08-26 23:09:30 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-09-18 12:39:17 -04:00
|
|
|
next_pos.sample -= 1;
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2018-07-26 15:23:59 -04:00
|
|
|
trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), SnapToGrid_Unscaled, false);
|
2017-09-18 12:39:17 -04:00
|
|
|
const samplecnt_t distance = ref_point - next_pos.sample;
|
|
|
|
delta = region_samples_to_region_beats (fabs ((double)distance));
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
|
|
|
|
2014-12-06 21:29:48 -05:00
|
|
|
if (!delta) {
|
2009-08-26 23:09:30 -04:00
|
|
|
return;
|
|
|
|
}
|
2009-08-17 22:37:57 -04:00
|
|
|
|
|
|
|
if (!forward) {
|
|
|
|
delta = -delta;
|
|
|
|
}
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("nudge"));
|
2009-08-17 11:58:47 -04:00
|
|
|
|
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
|
|
|
Selection::iterator next = i;
|
|
|
|
++next;
|
|
|
|
change_note_time (*i, delta, true);
|
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
|
2009-09-07 12:53:53 -04:00
|
|
|
apply_diff ();
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
2008-04-14 02:23:11 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::change_channel(uint8_t channel)
|
|
|
|
{
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command(_("change channel"));
|
2009-09-07 12:53:53 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
|
2008-04-03 17:47:47 -04:00
|
|
|
}
|
2010-05-28 12:37:04 -04:00
|
|
|
|
2009-09-07 12:53:53 -04:00
|
|
|
apply_diff();
|
2008-04-03 17:47:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-10 16:55:27 -04:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::note_entered(NoteBase* ev)
|
2007-08-10 16:55:27 -04:00
|
|
|
{
|
2016-11-19 05:40:41 -05:00
|
|
|
_entered_note = ev;
|
2015-11-29 18:58:03 -05:00
|
|
|
|
2010-09-21 11:15:06 -04:00
|
|
|
Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2009-05-04 20:18:21 -04:00
|
|
|
if (_mouse_state == SelectTouchDragging) {
|
2015-12-02 05:20:56 -05:00
|
|
|
|
2010-05-28 12:37:04 -04:00
|
|
|
note_selected (ev, true);
|
2015-12-02 05:20:56 -05:00
|
|
|
|
2014-12-20 01:11:28 -05:00
|
|
|
} else if (editor->current_mouse_mode() == MouseContent) {
|
2015-12-02 05:20:56 -05:00
|
|
|
|
|
|
|
remove_ghost_note ();
|
2014-12-20 01:11:28 -05:00
|
|
|
show_verbose_cursor (ev->note ());
|
2015-12-02 05:20:56 -05:00
|
|
|
|
2014-12-20 01:11:28 -05:00
|
|
|
} else if (editor->current_mouse_mode() == MouseDraw) {
|
2015-12-02 05:20:56 -05:00
|
|
|
|
|
|
|
remove_ghost_note ();
|
2014-12-20 01:11:28 -05:00
|
|
|
show_verbose_cursor (ev->note ());
|
2007-08-10 16:55:27 -04:00
|
|
|
}
|
|
|
|
}
|
2008-03-14 20:37:17 -04:00
|
|
|
|
2009-08-28 12:06:08 -04:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::note_left (NoteBase*)
|
2009-08-28 12:06:08 -04:00
|
|
|
{
|
2016-11-19 05:40:41 -05:00
|
|
|
_entered_note = 0;
|
2015-11-29 18:58:03 -05:00
|
|
|
|
2010-05-28 13:39:28 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
(*i)->hide_velocity ();
|
|
|
|
}
|
|
|
|
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2009-08-28 12:06:08 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::patch_entered (PatchChange* p)
|
2010-12-28 13:19:40 -05:00
|
|
|
{
|
|
|
|
ostringstream s;
|
2015-10-04 14:51:05 -04:00
|
|
|
s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
|
2016-05-22 08:23:39 -04:00
|
|
|
<< instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
|
2013-01-20 04:00:53 -05:00
|
|
|
<< _("Channel ") << ((int) p->patch()->channel() + 1);
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor (s.str(), 10, 20);
|
2013-04-04 18:45:27 -04:00
|
|
|
p->item().grab_focus();
|
2010-12-28 13:19:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::patch_left (PatchChange *)
|
2010-12-28 13:19:40 -05:00
|
|
|
{
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2012-06-17 09:37:52 -04:00
|
|
|
/* focus will transfer back via the enter-notify event sent to this
|
|
|
|
* midi region view.
|
|
|
|
*/
|
2010-12-28 13:19:40 -05:00
|
|
|
}
|
|
|
|
|
2012-10-11 00:05:22 -04:00
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::sysex_entered (SysEx* p)
|
2012-10-11 00:05:22 -04:00
|
|
|
{
|
2016-11-19 05:42:50 -05:00
|
|
|
// ostringstream s;
|
2013-04-04 18:45:27 -04:00
|
|
|
// CAIROCANVAS
|
|
|
|
// need a way to extract text from p->_flag->_text
|
|
|
|
// s << p->text();
|
|
|
|
// show_verbose_cursor (s.str(), 10, 20);
|
|
|
|
p->item().grab_focus();
|
2012-10-11 00:05:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::sysex_left (SysEx *)
|
2012-10-11 00:05:22 -04:00
|
|
|
{
|
2015-08-14 23:13:14 -04:00
|
|
|
hide_verbose_cursor ();
|
2012-10-11 00:05:22 -04:00
|
|
|
/* focus will transfer back via the enter-notify event sent to this
|
|
|
|
* midi region view.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2010-09-20 23:02:30 -04:00
|
|
|
void
|
2010-11-13 00:14:48 -05:00
|
|
|
MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
|
2010-09-20 23:02:30 -04:00
|
|
|
{
|
2010-09-21 11:15:06 -04:00
|
|
|
Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
|
2012-02-02 14:31:47 -05:00
|
|
|
Editing::MouseMode mm = editor->current_mouse_mode();
|
2014-12-08 23:00:00 -05:00
|
|
|
bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2014-12-23 13:46:53 -05:00
|
|
|
Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
|
|
|
|
if (can_set_cursor && ctx) {
|
2014-11-14 02:28:15 -05:00
|
|
|
if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
|
2014-12-23 13:46:53 -05:00
|
|
|
ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
|
2014-11-14 02:28:15 -05:00
|
|
|
} else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
|
2014-12-23 13:46:53 -05:00
|
|
|
ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
|
2014-12-20 01:11:28 -05:00
|
|
|
} else {
|
2014-12-23 13:46:53 -05:00
|
|
|
ctx->cursor_ctx->change(editor->cursors()->grabber_note);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
|
|
|
}
|
2010-09-20 23:02:30 -04:00
|
|
|
}
|
|
|
|
|
2014-12-12 20:22:16 -05:00
|
|
|
uint32_t
|
2014-12-16 20:37:16 -05:00
|
|
|
MidiRegionView::get_fill_color() const
|
2014-12-12 20:22:16 -05:00
|
|
|
{
|
2014-12-16 20:37:16 -05:00
|
|
|
const std::string mod_name = (_dragging ? "dragging region" :
|
|
|
|
trackview.editor().internal_editing() ? "editable region" :
|
2017-09-19 10:03:40 -04:00
|
|
|
"midi frame base");
|
2011-02-08 09:09:42 -05:00
|
|
|
if (_selected) {
|
2015-01-02 09:44:54 -05:00
|
|
|
return UIConfiguration::instance().color_mod ("selected region base", mod_name);
|
|
|
|
} else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
|
|
|
|
!UIConfiguration::instance().get_color_regions_using_track_color()) {
|
2017-09-19 10:03:40 -04:00
|
|
|
return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
|
2008-01-13 12:45:17 -05:00
|
|
|
}
|
2015-01-02 09:44:54 -05:00
|
|
|
return UIConfiguration::instance().color_mod (fill_color, mod_name);
|
2008-01-13 12:45:17 -05:00
|
|
|
}
|
2008-04-20 18:33:13 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2013-03-27 21:50:18 -04:00
|
|
|
MidiRegionView::midi_channel_mode_changed ()
|
2008-04-20 18:33:13 -04:00
|
|
|
{
|
2013-03-27 21:50:18 -04:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
|
|
|
uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
|
|
|
|
ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
|
|
|
|
|
2012-04-29 21:13:04 -04:00
|
|
|
if (mode == ForceChannel) {
|
2008-05-11 22:40:48 -04:00
|
|
|
mask = 0xFFFF; // Show all notes as active (below)
|
2012-04-29 21:13:04 -04:00
|
|
|
}
|
2008-05-11 22:40:48 -04:00
|
|
|
|
|
|
|
// Update notes for selection
|
2008-09-22 22:40:29 -04:00
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
i->second->on_channel_selection_change (mask);
|
2008-04-20 18:33:13 -04:00
|
|
|
}
|
2008-05-11 22:40:48 -04:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
_patch_changes.clear ();
|
|
|
|
display_patch_changes ();
|
2008-04-20 18:33:13 -04:00
|
|
|
}
|
2008-05-11 22:40:48 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2012-06-11 08:07:17 -04:00
|
|
|
MidiRegionView::instrument_settings_changed ()
|
2008-12-11 03:06:27 -05:00
|
|
|
{
|
|
|
|
redisplay_model();
|
|
|
|
}
|
|
|
|
|
2009-08-10 15:29:29 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
|
|
|
|
{
|
|
|
|
if (_selection.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-08-12 21:57:03 -04:00
|
|
|
PublicEditor& editor (trackview.editor());
|
2009-08-10 15:29:29 -04:00
|
|
|
|
2009-08-12 21:57:03 -04:00
|
|
|
switch (op) {
|
2011-06-12 11:50:47 -04:00
|
|
|
case Delete:
|
|
|
|
/* XXX what to do ? */
|
|
|
|
break;
|
2009-08-12 21:57:03 -04:00
|
|
|
case Cut:
|
|
|
|
case Copy:
|
|
|
|
editor.get_cut_buffer().add (selection_as_cut_buffer());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (op != Copy) {
|
2009-08-10 15:29:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
start_note_diff_command();
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
|
|
|
switch (op) {
|
|
|
|
case Copy:
|
|
|
|
break;
|
2011-06-12 11:50:47 -04:00
|
|
|
case Delete:
|
2011-05-19 17:11:21 -04:00
|
|
|
case Cut:
|
|
|
|
case Clear:
|
|
|
|
note_diff_remove_note (*i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
apply_diff();
|
|
|
|
}
|
2009-08-10 15:29:29 -04:00
|
|
|
}
|
|
|
|
|
2009-08-12 21:57:03 -04:00
|
|
|
MidiCutBuffer*
|
|
|
|
MidiRegionView::selection_as_cut_buffer () const
|
|
|
|
{
|
2009-10-19 11:23:42 -04:00
|
|
|
Notes notes;
|
2009-08-12 21:57:03 -04:00
|
|
|
|
2013-10-03 04:52:39 -04:00
|
|
|
for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
2011-05-19 17:11:21 -04:00
|
|
|
NoteType* n = (*i)->note().get();
|
2010-03-26 17:24:17 -04:00
|
|
|
notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
|
2009-08-12 21:57:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
|
|
|
|
cb->set (notes);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-12 21:57:03 -04:00
|
|
|
return cb;
|
|
|
|
}
|
|
|
|
|
2014-11-16 22:35:37 -05:00
|
|
|
/** This method handles undo */
|
|
|
|
bool
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::paste (samplepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
|
2014-11-16 22:35:37 -05:00
|
|
|
{
|
2015-01-24 19:55:36 -05:00
|
|
|
bool commit = false;
|
2014-12-06 10:26:25 -05:00
|
|
|
// Paste notes, if available
|
2014-12-06 12:20:52 -05:00
|
|
|
MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
|
2014-12-06 10:26:25 -05:00
|
|
|
if (m != selection.midi_notes.end()) {
|
2014-12-06 12:20:52 -05:00
|
|
|
ctx.counts.increase_n_notes();
|
2016-08-08 17:20:59 -04:00
|
|
|
if (!(*m)->empty()) {
|
|
|
|
commit = true;
|
|
|
|
}
|
2014-12-06 12:20:52 -05:00
|
|
|
paste_internal(pos, ctx.count, ctx.times, **m);
|
2014-12-06 10:26:25 -05:00
|
|
|
}
|
2014-11-16 22:35:37 -05:00
|
|
|
|
2014-12-06 10:26:25 -05:00
|
|
|
// Paste control points to automation children, if available
|
2014-11-16 22:35:37 -05:00
|
|
|
typedef RouteTimeAxisView::AutomationTracks ATracks;
|
|
|
|
const ATracks& atracks = midi_view()->automation_tracks();
|
|
|
|
for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
|
2016-06-16 13:20:37 -04:00
|
|
|
if (a->second->paste(pos, selection, ctx, sub_num)) {
|
2016-08-08 17:20:59 -04:00
|
|
|
if(!commit) {
|
|
|
|
trackview.editor().begin_reversible_command (Operations::paste);
|
|
|
|
}
|
2015-01-24 19:55:36 -05:00
|
|
|
commit = true;
|
|
|
|
}
|
2014-11-16 22:35:37 -05:00
|
|
|
}
|
|
|
|
|
2015-01-24 19:55:36 -05:00
|
|
|
if (commit) {
|
|
|
|
trackview.editor().commit_reversible_command ();
|
|
|
|
}
|
2014-11-16 22:35:37 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-03-02 11:18:59 -05:00
|
|
|
/** This method handles undo */
|
2009-08-12 21:57:03 -04:00
|
|
|
void
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::paste_internal (samplepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
|
2009-08-12 21:57:03 -04:00
|
|
|
{
|
2009-08-13 15:48:10 -04:00
|
|
|
if (mcb.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
start_note_diff_command (_("paste"));
|
2009-08-13 15:48:10 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
const Temporal::Beats snap_beats = get_grid_beats(pos);
|
|
|
|
const Temporal::Beats first_time = (*mcb.notes().begin())->time();
|
|
|
|
const Temporal::Beats last_time = (*mcb.notes().rbegin())->end_time();
|
|
|
|
const Temporal::Beats duration = last_time - first_time;
|
|
|
|
const Temporal::Beats snap_duration = duration.snap_to(snap_beats);
|
|
|
|
const Temporal::Beats paste_offset = snap_duration * paste_count;
|
2019-04-08 18:27:05 -04:00
|
|
|
const Temporal::Beats quarter_note = absolute_samples_to_source_beats(pos) + paste_offset;
|
|
|
|
Temporal::Beats end_point = Temporal::Beats();
|
2009-08-13 08:10:34 -04:00
|
|
|
|
2014-11-14 17:33:02 -05:00
|
|
|
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,
|
2014-11-14 20:04:09 -05:00
|
|
|
duration, pos, _region->position(),
|
2016-11-08 12:51:19 -05:00
|
|
|
quarter_note));
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2015-10-27 22:58:55 -04:00
|
|
|
clear_editor_note_selection ();
|
2009-08-13 08:10:34 -04:00
|
|
|
|
2009-08-13 15:48:10 -04:00
|
|
|
for (int n = 0; n < (int) times; ++n) {
|
2009-08-12 21:57:03 -04:00
|
|
|
|
2009-10-19 11:23:42 -04:00
|
|
|
for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-08-13 15:48:10 -04:00
|
|
|
boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
|
2016-11-08 12:51:19 -05:00
|
|
|
copied_note->set_time (quarter_note + copied_note->time() - first_time);
|
2015-09-18 14:37:00 -04:00
|
|
|
copied_note->set_id (Evoral::next_event_id());
|
2009-08-13 15:48:10 -04:00
|
|
|
|
|
|
|
/* make all newly added notes selected */
|
|
|
|
|
2010-12-09 16:34:46 -05:00
|
|
|
note_diff_add_note (copied_note, true);
|
2009-08-13 23:00:41 -04:00
|
|
|
end_point = copied_note->end_time();
|
2009-08-13 15:48:10 -04:00
|
|
|
}
|
|
|
|
}
|
2009-08-13 23:00:41 -04:00
|
|
|
|
|
|
|
/* if we pasted past the current end of the region, extend the region */
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t end_sample = source_beats_to_absolute_samples (end_point);
|
|
|
|
samplepos_t region_end = _region->position() + _region->length() - 1;
|
2009-08-13 23:00:41 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
if (end_sample > region_end) {
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_sample));
|
2011-02-20 20:52:15 -05:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
_region->clear_changes ();
|
2016-06-15 10:18:27 -04:00
|
|
|
/* we probably need to get the snap modifier somehow to make this correct for non-musical use */
|
2017-09-18 12:39:17 -04:00
|
|
|
_region->set_length (end_sample - _region->position(), trackview.editor().get_grid_music_divisions (0));
|
2010-03-02 13:05:26 -05:00
|
|
|
trackview.session()->add_command (new StatefulDiffCommand (_region));
|
2009-08-13 23:00:41 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2011-03-02 11:18:59 -05:00
|
|
|
apply_diff (true);
|
2009-08-12 21:57:03 -04:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2009-09-10 16:41:08 -04:00
|
|
|
struct EventNoteTimeEarlyFirstComparator {
|
2013-04-04 18:45:27 -04:00
|
|
|
bool operator() (NoteBase* a, NoteBase* b) {
|
2011-05-19 17:11:21 -04:00
|
|
|
return a->note()->time() < b->note()->time();
|
|
|
|
}
|
2009-09-10 16:41:08 -04:00
|
|
|
};
|
|
|
|
|
2009-08-17 11:58:47 -04:00
|
|
|
void
|
2011-07-11 09:17:01 -04:00
|
|
|
MidiRegionView::goto_next_note (bool add_to_selection)
|
2009-08-17 11:58:47 -04:00
|
|
|
{
|
2009-08-17 22:37:57 -04:00
|
|
|
bool use_next = false;
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2011-11-15 09:24:14 -05:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
2013-03-27 21:50:18 -04:00
|
|
|
uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
|
2017-01-03 08:19:31 -05:00
|
|
|
NoteBase* first_note = 0;
|
2011-11-15 09:24:14 -05:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
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;
|
2011-11-15 09:24:14 -05:00
|
|
|
}
|
2011-07-11 09:17:01 -04:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
}
|
2009-08-17 22:37:57 -04:00
|
|
|
|
|
|
|
/* use the first one */
|
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
if (!_events.empty() && first_note) {
|
|
|
|
unique_select (first_note);
|
2011-11-15 09:24:14 -05:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-07-11 09:17:01 -04:00
|
|
|
MidiRegionView::goto_previous_note (bool add_to_selection)
|
2009-08-17 11:58:47 -04:00
|
|
|
{
|
2009-08-17 22:37:57 -04:00
|
|
|
bool use_next = false;
|
|
|
|
|
2011-11-15 09:24:14 -05:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
2013-03-27 21:50:18 -04:00
|
|
|
uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
|
2017-01-03 08:19:31 -05:00
|
|
|
NoteBase* last_note = 0;
|
2011-11-15 09:24:14 -05:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
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;
|
2011-11-15 09:24:14 -05:00
|
|
|
}
|
2011-07-11 09:17:01 -04:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
|
|
|
}
|
2009-08-17 22:37:57 -04:00
|
|
|
|
|
|
|
/* use the last one */
|
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
if (!_events.empty() && last_note) {
|
|
|
|
unique_select (last_note);
|
2011-11-15 09:24:14 -05:00
|
|
|
}
|
2009-08-17 11:58:47 -04:00
|
|
|
}
|
2009-08-26 23:09:30 -04:00
|
|
|
|
|
|
|
void
|
2010-05-20 11:06:30 -04:00
|
|
|
MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
|
2009-08-26 23:09:30 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
bool had_selected = false;
|
2010-05-20 11:06:30 -04:00
|
|
|
|
2017-01-03 08:19:31 -05:00
|
|
|
/* we previously time sorted events here, but Notes is a multiset sorted by time */
|
2009-09-10 16:41:08 -04:00
|
|
|
|
2009-08-26 23:09:30 -04:00
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if (i->second->selected()) {
|
|
|
|
selected.insert (i->first);
|
2011-05-19 17:11:21 -04:00
|
|
|
had_selected = true;
|
2010-05-20 11:06:30 -04:00
|
|
|
}
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (allow_all_if_none_selected && !had_selected) {
|
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
selected.insert (i->first);
|
2009-08-26 23:09:30 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-05-25 19:14:41 -04:00
|
|
|
|
|
|
|
void
|
2016-08-30 13:27:35 -04:00
|
|
|
MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
|
2010-05-25 19:14:41 -04:00
|
|
|
{
|
2014-12-17 03:17:07 -05:00
|
|
|
x = std::max(0.0, x);
|
|
|
|
|
2011-05-25 11:19:47 -04:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-05-25 19:14:41 -04:00
|
|
|
_last_ghost_x = x;
|
|
|
|
_last_ghost_y = y;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2013-04-04 00:32:52 -04:00
|
|
|
_note_group->canvas_to_item (x, y);
|
2011-07-16 17:53:30 -04:00
|
|
|
|
|
|
|
PublicEditor& editor = trackview.editor ();
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t const unsnapped_sample = editor.pixel_to_sample (x);
|
2011-12-19 13:32:57 -05:00
|
|
|
|
2016-08-30 13:27:35 -04:00
|
|
|
const int32_t divisions = editor.get_grid_music_divisions (state);
|
2016-11-20 12:02:21 -05:00
|
|
|
const bool shift_snap = midi_view()->note_mode() != Percussive;
|
2017-09-24 12:03:54 -04:00
|
|
|
const Temporal::Beats snapped_beats = snap_sample_to_grid_underneath (unsnapped_sample, divisions, shift_snap);
|
2016-10-25 13:52:09 -04:00
|
|
|
|
2016-11-21 10:01:01 -05:00
|
|
|
/* prevent Percussive mode from displaying a ghost hit at region end */
|
2016-11-21 10:21:05 -05:00
|
|
|
if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
|
2016-11-21 10:01:01 -05:00
|
|
|
_ghost_note->hide();
|
|
|
|
hide_verbose_cursor ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-25 13:52:09 -04:00
|
|
|
/* 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();
|
|
|
|
}
|
2016-08-30 13:27:35 -04:00
|
|
|
|
2014-12-28 19:21:07 -05:00
|
|
|
/* calculate time in beats relative to start of source */
|
2017-09-24 12:03:54 -04:00
|
|
|
const Temporal::Beats length = get_grid_beats(unsnapped_sample + _region->position());
|
2016-08-15 10:52:18 -04:00
|
|
|
|
2016-08-30 13:27:35 -04:00
|
|
|
_ghost_note->note()->set_time (snapped_beats);
|
2010-05-25 19:14:41 -04:00
|
|
|
_ghost_note->note()->set_length (length);
|
2016-11-22 16:04:14 -05:00
|
|
|
_ghost_note->note()->set_note (y_to_note (y));
|
2011-05-25 11:19:47 -04:00
|
|
|
_ghost_note->note()->set_channel (mtv->get_channel_for_add ());
|
2016-08-30 13:27:35 -04:00
|
|
|
_ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
|
2011-02-27 23:00:38 -05:00
|
|
|
/* the ghost note does not appear in ghost regions, so pass false in here */
|
|
|
|
update_note (_ghost_note, false);
|
2010-05-26 07:54:49 -04:00
|
|
|
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor (_ghost_note->note ());
|
2010-05-25 19:14:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-08-30 13:27:35 -04:00
|
|
|
MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
|
2010-05-25 19:14:41 -04:00
|
|
|
{
|
2011-12-11 07:54:54 -05:00
|
|
|
remove_ghost_note ();
|
2010-05-25 19:14:41 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<NoteType> g (new NoteType);
|
2014-12-28 20:23:52 -05:00
|
|
|
if (midi_view()->note_mode() == Sustained) {
|
|
|
|
_ghost_note = new Note (*this, _note_group, g);
|
|
|
|
} else {
|
|
|
|
_ghost_note = new Hit (*this, _note_group, 10, g);
|
|
|
|
}
|
2013-04-04 00:32:52 -04:00
|
|
|
_ghost_note->set_ignore_events (true);
|
|
|
|
_ghost_note->set_outline_color (0x000000aa);
|
2016-08-30 13:27:35 -04:00
|
|
|
update_ghost_note (x, y, state);
|
2010-05-25 19:14:41 -04:00
|
|
|
_ghost_note->show ();
|
|
|
|
|
2011-05-02 09:38:16 -04:00
|
|
|
show_verbose_cursor (_ghost_note->note ());
|
2010-05-25 19:14:41 -04:00
|
|
|
}
|
|
|
|
|
2014-01-20 10:52:27 -05:00
|
|
|
void
|
|
|
|
MidiRegionView::remove_ghost_note ()
|
|
|
|
{
|
|
|
|
delete _ghost_note;
|
|
|
|
_ghost_note = 0;
|
|
|
|
}
|
|
|
|
|
2015-08-14 23:13:14 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::hide_verbose_cursor ()
|
|
|
|
{
|
|
|
|
trackview.editor().verbose_cursor()->hide ();
|
|
|
|
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
|
|
|
if (mtv) {
|
|
|
|
mtv->set_note_highlight (NO_MIDI_NOTE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-25 19:14:41 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::snap_changed ()
|
|
|
|
{
|
|
|
|
if (!_ghost_note) {
|
|
|
|
return;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-08-30 13:27:35 -04:00
|
|
|
create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
|
2010-05-25 19:14:41 -04:00
|
|
|
}
|
2010-05-26 07:54:49 -04:00
|
|
|
|
2010-05-28 17:39:12 -04:00
|
|
|
void
|
|
|
|
MidiRegionView::drop_down_keys ()
|
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
_mouse_state = None;
|
2010-05-28 17:39:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-13 00:14:48 -05:00
|
|
|
MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
|
2010-05-28 17:39:12 -04:00
|
|
|
{
|
2014-12-06 19:56:36 -05:00
|
|
|
/* XXX: This is dead code. What was it for? */
|
|
|
|
|
2016-11-22 16:04:14 -05:00
|
|
|
double note = y_to_note(y);
|
2011-05-19 17:11:21 -04:00
|
|
|
Events e;
|
2010-05-28 17:39:12 -04:00
|
|
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
2010-06-17 18:09:07 -04:00
|
|
|
|
2013-03-27 21:50:18 -04:00
|
|
|
uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
|
2010-05-28 17:39:12 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
|
2017-09-24 12:03:54 -04:00
|
|
|
get_events (e, Evoral::Sequence<Temporal::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
|
2011-05-19 17:11:21 -04:00
|
|
|
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
|
2017-09-24 12:03:54 -04:00
|
|
|
get_events (e, Evoral::Sequence<Temporal::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
|
2011-05-19 17:11:21 -04:00
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2010-05-28 17:39:12 -04:00
|
|
|
|
|
|
|
bool add_mrv_selection = false;
|
|
|
|
|
|
|
|
if (_selection.empty()) {
|
|
|
|
add_mrv_selection = true;
|
|
|
|
}
|
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
for (Events::iterator i = e.begin(); i != e.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
if (_selection.insert (i->second).second) {
|
|
|
|
i->second->set_selected (true);
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-05-28 17:39:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (add_mrv_selection) {
|
|
|
|
PublicEditor& editor (trackview.editor());
|
|
|
|
editor.get_selection().add (this);
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
}
|
2010-06-24 15:46:28 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::color_handler ()
|
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
RegionView::color_handler ();
|
2010-06-24 15:46:28 -04:00
|
|
|
|
2016-12-13 12:05:08 -05:00
|
|
|
_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");
|
|
|
|
|
2010-06-24 15:46:28 -04:00
|
|
|
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
2017-01-03 08:19:31 -05:00
|
|
|
i->second->set_selected (i->second->selected()); // will change color
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-06-24 15:46:28 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
/* XXX probably more to do here */
|
2010-06-24 15:46:28 -04:00
|
|
|
}
|
2010-06-28 14:43:40 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::enable_display (bool yn)
|
|
|
|
{
|
|
|
|
RegionView::enable_display (yn);
|
|
|
|
}
|
2010-08-11 14:21:37 -04:00
|
|
|
|
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::show_step_edit_cursor (Temporal::Beats pos)
|
2010-08-11 14:21:37 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_step_edit_cursor == 0) {
|
2014-06-22 11:41:05 -04:00
|
|
|
ArdourCanvas::Item* const group = get_canvas_group();
|
2010-08-11 14:21:37 -04:00
|
|
|
|
2013-04-04 18:45:27 -04:00
|
|
|
_step_edit_cursor = new ArdourCanvas::Rectangle (group);
|
2013-04-04 00:32:52 -04:00
|
|
|
_step_edit_cursor->set_y0 (0);
|
|
|
|
_step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
|
|
|
|
_step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
|
|
|
|
_step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-08-11 14:21:37 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
move_step_edit_cursor (pos);
|
|
|
|
_step_edit_cursor->show ();
|
2010-08-11 14:21:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::move_step_edit_cursor (Temporal::Beats pos)
|
2010-08-11 14:21:37 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
_step_edit_cursor_position = pos;
|
2010-08-11 16:16:39 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_step_edit_cursor) {
|
2017-09-18 12:39:17 -04:00
|
|
|
double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_samples (pos));
|
2013-04-04 00:32:52 -04:00
|
|
|
_step_edit_cursor->set_x0 (pixel);
|
2011-05-19 17:11:21 -04:00
|
|
|
set_step_edit_cursor_width (_step_edit_cursor_width);
|
|
|
|
}
|
2010-08-11 14:21:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::hide_step_edit_cursor ()
|
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_step_edit_cursor) {
|
|
|
|
_step_edit_cursor->hide ();
|
|
|
|
}
|
2010-08-11 14:21:37 -04:00
|
|
|
}
|
2010-08-11 15:11:14 -04:00
|
|
|
|
|
|
|
void
|
2017-09-24 12:03:54 -04:00
|
|
|
MidiRegionView::set_step_edit_cursor_width (Temporal::Beats beats)
|
2010-08-11 15:11:14 -04:00
|
|
|
{
|
2011-05-19 17:11:21 -04:00
|
|
|
_step_edit_cursor_width = beats;
|
2010-08-11 16:16:39 -04:00
|
|
|
|
2011-05-19 17:11:21 -04:00
|
|
|
if (_step_edit_cursor) {
|
2019-04-08 18:27:05 -04:00
|
|
|
_step_edit_cursor->set_x1 (_step_edit_cursor->x0()
|
|
|
|
+ trackview.editor().sample_to_pixel (
|
|
|
|
region_beats_to_region_samples (_step_edit_cursor_position + beats)
|
|
|
|
- region_beats_to_region_samples (_step_edit_cursor_position)));
|
2011-05-19 17:11:21 -04:00
|
|
|
}
|
2010-08-11 15:11:14 -04:00
|
|
|
}
|
|
|
|
|
2010-10-08 10:54:16 -04:00
|
|
|
/** Called when a diskstream on our track has received some data. Update the view, if applicable.
|
2011-11-04 13:52:12 -04:00
|
|
|
* @param w Source that the data will end up in.
|
2010-10-08 10:54:16 -04:00
|
|
|
*/
|
|
|
|
void
|
2011-11-04 13:52:12 -04:00
|
|
|
MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
|
2010-10-08 10:54:16 -04:00
|
|
|
{
|
|
|
|
if (!_active_notes) {
|
|
|
|
/* we aren't actively being recorded to */
|
|
|
|
return;
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-10-08 10:54:16 -04:00
|
|
|
boost::shared_ptr<MidiSource> src = w.lock ();
|
|
|
|
if (!src || src != midi_region()->midi_source()) {
|
|
|
|
/* recorded data was not destined for our source */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
|
2011-11-04 13:52:12 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
samplepos_t back = max_samplepos;
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-10-08 10:54:16 -04:00
|
|
|
for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
|
2016-11-06 22:04:35 -05:00
|
|
|
const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
|
2010-10-08 10:54:16 -04:00
|
|
|
|
2013-01-19 02:00:43 -05:00
|
|
|
if (ev.is_channel_event()) {
|
2013-03-27 21:50:18 -04:00
|
|
|
if (get_channel_mode() == FilterChannels) {
|
|
|
|
if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
|
2012-10-11 02:32:09 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
/* convert from session samples to source beats */
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats const time_beats = _source_relative_time_converter.from(
|
2018-11-15 10:33:44 -05:00
|
|
|
ev.time() - src->natural_position() + _region->start());
|
2010-10-08 10:54:16 -04:00
|
|
|
|
|
|
|
if (ev.type() == MIDI_CMD_NOTE_ON) {
|
2011-03-03 19:35:47 -05:00
|
|
|
boost::shared_ptr<NoteType> note (
|
2017-09-24 12:03:54 -04:00
|
|
|
new NoteType (ev.channel(), time_beats, Temporal::Beats(), ev.note(), ev.velocity()));
|
2010-10-08 10:54:16 -04:00
|
|
|
|
|
|
|
add_note (note, true);
|
|
|
|
|
|
|
|
/* fix up our note range */
|
|
|
|
if (ev.note() < _current_range_min) {
|
|
|
|
midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
|
|
|
|
} else if (ev.note() > _current_range_max) {
|
|
|
|
midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
|
|
|
|
}
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2010-10-08 10:54:16 -04:00
|
|
|
} else if (ev.type() == MIDI_CMD_NOTE_OFF) {
|
|
|
|
resolve_note (ev.note (), time_beats);
|
|
|
|
}
|
2010-11-14 10:01:53 -05:00
|
|
|
|
|
|
|
back = ev.time ();
|
2010-10-08 10:54:16 -04:00
|
|
|
}
|
2010-11-14 10:01:53 -05:00
|
|
|
|
|
|
|
midi_stream_view()->check_record_layers (region(), back);
|
2010-10-08 10:54:16 -04:00
|
|
|
}
|
2010-11-23 20:04:53 -05:00
|
|
|
|
|
|
|
void
|
2010-12-07 12:16:59 -05:00
|
|
|
MidiRegionView::trim_front_starting ()
|
2010-11-23 20:04:53 -05:00
|
|
|
{
|
2016-07-22 09:21:27 -04:00
|
|
|
/* We used to eparent the note group to the region view's parent, so that it didn't change.
|
|
|
|
now we update it.
|
2010-11-23 20:04:53 -05:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-12-07 12:16:59 -05:00
|
|
|
MidiRegionView::trim_front_ending ()
|
2010-11-23 20:04:53 -05:00
|
|
|
{
|
2017-03-05 23:51:53 -05:00
|
|
|
if (_region->start() < 0) {
|
|
|
|
/* Trim drag made start time -ve; fix this */
|
|
|
|
midi_region()->fix_negative_start ();
|
|
|
|
}
|
2010-11-23 20:04:53 -05:00
|
|
|
}
|
2010-12-26 11:19:32 -05:00
|
|
|
|
2010-12-28 13:19:40 -05:00
|
|
|
void
|
2013-04-04 00:32:52 -04:00
|
|
|
MidiRegionView::edit_patch_change (PatchChange* pc)
|
2010-12-28 13:19:40 -05:00
|
|
|
{
|
2012-12-22 13:27:33 -05:00
|
|
|
PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
|
2012-06-24 08:56:56 -04:00
|
|
|
|
2012-12-22 13:27:33 -05:00
|
|
|
int response = d.run();
|
2012-06-24 08:56:56 -04:00
|
|
|
|
2012-12-22 13:27:33 -05:00
|
|
|
switch (response) {
|
|
|
|
case Gtk::RESPONSE_ACCEPT:
|
|
|
|
break;
|
|
|
|
case Gtk::RESPONSE_REJECT:
|
|
|
|
delete_patch_change (pc);
|
|
|
|
return;
|
|
|
|
default:
|
2010-12-28 13:19:40 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
change_patch_change (pc->patch(), d.patch ());
|
|
|
|
}
|
2011-05-02 09:38:16 -04:00
|
|
|
|
2012-10-10 23:22:17 -04:00
|
|
|
void
|
2013-04-04 18:45:27 -04:00
|
|
|
MidiRegionView::delete_sysex (SysEx* /*sysex*/)
|
2012-10-10 23:22:17 -04:00
|
|
|
{
|
2013-04-04 18:45:27 -04:00
|
|
|
// CAIROCANVAS
|
|
|
|
// sysyex object doesn't have a pointer to a sysex event
|
|
|
|
// MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
|
|
|
|
// c->remove (sysex->sysex());
|
|
|
|
// _model->apply_command (*trackview.session(), c);
|
2012-10-10 23:22:17 -04:00
|
|
|
|
2013-04-04 18:45:27 -04:00
|
|
|
//_sys_exes.clear ();
|
|
|
|
// display_sysexes();
|
2012-10-10 23:22:17 -04:00
|
|
|
}
|
2011-05-02 09:38:16 -04:00
|
|
|
|
2016-02-10 07:55:37 -05:00
|
|
|
std::string
|
|
|
|
MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
|
2011-05-02 09:38:16 -04:00
|
|
|
{
|
2013-01-19 19:46:55 -05:00
|
|
|
using namespace MIDI::Name;
|
|
|
|
std::string name;
|
|
|
|
|
2015-08-14 21:08:55 -04:00
|
|
|
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
2013-01-19 19:46:55 -05:00
|
|
|
if (mtv) {
|
|
|
|
boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
|
|
|
|
if (device_names) {
|
|
|
|
MIDI::Name::PatchPrimaryKey patch_key;
|
|
|
|
get_patch_key_at(n->time(), n->channel(), patch_key);
|
|
|
|
name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
|
|
|
|
n->channel(),
|
2014-12-17 19:43:09 -05:00
|
|
|
patch_key.bank(),
|
|
|
|
patch_key.program(),
|
2016-02-10 07:55:37 -05:00
|
|
|
note_value);
|
2013-01-19 19:46:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[128];
|
|
|
|
snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
|
2016-02-10 07:55:37 -05:00
|
|
|
(int) note_value,
|
2016-07-12 17:00:31 -04:00
|
|
|
name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
|
2011-05-19 17:11:21 -04:00
|
|
|
(int) n->channel() + 1,
|
|
|
|
(int) n->velocity());
|
2011-06-01 13:00:29 -04:00
|
|
|
|
2016-02-10 07:55:37 -05:00
|
|
|
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());
|
2011-05-02 09:38:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
|
|
|
|
{
|
2014-06-26 15:07:29 -04:00
|
|
|
trackview.editor().verbose_cursor()->set (text);
|
2011-05-02 09:38:16 -04:00
|
|
|
trackview.editor().verbose_cursor()->show ();
|
2014-06-26 15:07:29 -04:00
|
|
|
trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
|
2011-05-02 09:38:16 -04:00
|
|
|
}
|
2011-08-20 16:02:04 -04:00
|
|
|
|
2014-12-28 19:21:07 -05:00
|
|
|
uint8_t
|
|
|
|
MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
|
|
|
|
{
|
|
|
|
if (_model->notes().empty()) {
|
|
|
|
return 0x40; // No notes, use default
|
|
|
|
}
|
|
|
|
|
|
|
|
MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
|
|
|
|
if (m == _model->notes().begin()) {
|
|
|
|
// Before the start, use the velocity of the first note
|
|
|
|
return (*m)->velocity();
|
|
|
|
} else if (m == _model->notes().end()) {
|
|
|
|
// Past the end, use the velocity of the last note
|
|
|
|
--m;
|
|
|
|
return (*m)->velocity();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolate velocity of surrounding notes
|
|
|
|
MidiModel::Notes::const_iterator n = m;
|
|
|
|
--n;
|
|
|
|
|
|
|
|
const double frac = ((time - (*n)->time()).to_double() /
|
|
|
|
((*m)->time() - (*n)->time()).to_double());
|
|
|
|
|
|
|
|
return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
|
|
|
|
}
|
|
|
|
|
2017-09-18 12:39:17 -04:00
|
|
|
/** @param p A session samplepos.
|
2016-08-30 13:27:35 -04:00
|
|
|
* @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.
|
2011-08-20 16:02:04 -04:00
|
|
|
*/
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions, bool shift_snap) const
|
2011-08-20 16:02:04 -04:00
|
|
|
{
|
2016-08-30 13:27:35 -04:00
|
|
|
TempoMap& map (trackview.session()->tempo_map());
|
2017-09-18 12:39:17 -04:00
|
|
|
double eqaf = map.exact_qn_at_sample (p + _region->position(), divisions);
|
2011-08-20 16:02:04 -04:00
|
|
|
|
2016-09-22 15:06:03 -04:00
|
|
|
if (divisions != 0 && shift_snap) {
|
2017-09-18 12:39:17 -04:00
|
|
|
const double qaf = map.quarter_note_at_sample (p + _region->position());
|
2016-08-30 13:27:35 -04:00
|
|
|
/* 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.
|
|
|
|
*/
|
2017-09-24 12:03:54 -04:00
|
|
|
const Temporal::Beats grid_beats = get_grid_beats (p + _region->position());
|
2016-09-22 13:39:05 -04:00
|
|
|
const double rem = eqaf - qaf;
|
2016-10-25 13:52:09 -04:00
|
|
|
if (rem >= 0.0) {
|
2016-08-30 13:27:35 -04:00
|
|
|
eqaf -= grid_beats.to_double();
|
|
|
|
}
|
|
|
|
}
|
2016-11-08 12:51:19 -05:00
|
|
|
const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
|
2016-08-15 10:48:54 -04:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
return Temporal::Beats (eqaf - session_start_off);
|
2011-08-20 16:02:04 -04:00
|
|
|
}
|
2011-09-28 10:07:42 -04:00
|
|
|
|
2013-03-27 21:50:18 -04:00
|
|
|
ChannelMode
|
|
|
|
MidiRegionView::get_channel_mode () const
|
|
|
|
{
|
|
|
|
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
|
|
|
|
return rtav->midi_track()->get_playback_channel_mode();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t
|
|
|
|
MidiRegionView::get_selected_channels () const
|
|
|
|
{
|
|
|
|
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
|
|
|
|
return rtav->midi_track()->get_playback_channel_mask();
|
|
|
|
}
|
|
|
|
|
2014-11-21 02:37:42 -05:00
|
|
|
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats
|
2017-09-18 12:39:17 -04:00
|
|
|
MidiRegionView::get_grid_beats(samplepos_t pos) const
|
2014-11-21 02:37:42 -05:00
|
|
|
{
|
2015-01-07 00:12:07 -05:00
|
|
|
PublicEditor& editor = trackview.editor();
|
|
|
|
bool success = false;
|
2017-09-24 12:03:54 -04:00
|
|
|
Temporal::Beats beats = editor.get_grid_type_as_beats (success, pos);
|
2014-11-21 02:37:42 -05:00
|
|
|
if (!success) {
|
2017-09-24 12:03:54 -04:00
|
|
|
beats = Temporal::Beats(1);
|
2014-11-21 02:37:42 -05:00
|
|
|
}
|
|
|
|
return beats;
|
|
|
|
}
|
2016-11-22 16:04:14 -05:00
|
|
|
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;
|
|
|
|
}
|
2017-02-04 13:02:01 -05:00
|
|
|
|
|
|
|
double
|
|
|
|
MidiRegionView::session_relative_qn (double qn) const
|
|
|
|
{
|
|
|
|
return qn + (region()->quarter_note() - midi_region()->start_beats());
|
|
|
|
}
|