2
Copyright (C) 2000 Paul Davis
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
#include <strings.h> // for ffs(3)
28
#include <sigc++/bind.h>
30
#include "pbd/error.h"
31
#include "pbd/stl_delete.h"
32
#include "pbd/whitespace.h"
33
#include "pbd/basename.h"
34
#include "pbd/enumwriter.h"
35
#include "pbd/memento_command.h"
36
#include "pbd/stateful_diff_command.h"
38
#include "gtkmm2ext/gtk_ui.h"
39
#include "gtkmm2ext/selector.h"
40
#include "gtkmm2ext/bindable_button.h"
41
#include "gtkmm2ext/utils.h"
43
#include "ardour/event_type_map.h"
44
#include "ardour/midi_patch_manager.h"
45
#include "ardour/midi_playlist.h"
46
#include "ardour/midi_region.h"
47
#include "ardour/midi_source.h"
48
#include "ardour/midi_track.h"
49
#include "ardour/operations.h"
50
#include "ardour/playlist.h"
51
#include "ardour/region.h"
52
#include "ardour/region_factory.h"
53
#include "ardour/route.h"
54
#include "ardour/session.h"
55
#include "ardour/session_object.h"
56
#include "ardour/source.h"
57
#include "ardour/track.h"
58
#include "ardour/types.h"
60
#include "ardour_ui.h"
61
#include "ardour_button.h"
62
#include "automation_line.h"
63
#include "automation_time_axis.h"
64
#include "canvas-note-event.h"
65
#include "canvas_impl.h"
68
#include "ghostregion.h"
69
#include "gui_thread.h"
71
#include "midi_channel_selector.h"
72
#include "midi_scroomer.h"
73
#include "midi_streamview.h"
74
#include "midi_region_view.h"
75
#include "midi_time_axis.h"
76
#include "piano_roll_header.h"
77
#include "playlist_selector.h"
78
#include "plugin_selector.h"
79
#include "plugin_ui.h"
80
#include "point_selection.h"
82
#include "region_view.h"
83
#include "rgb_macros.h"
84
#include "selection.h"
85
#include "step_editor.h"
86
#include "simplerect.h"
89
#include "ardour/midi_track.h"
93
using namespace ARDOUR;
96
using namespace Gtkmm2ext;
97
using namespace Editing;
99
// Minimum height at which a control is displayed
100
static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
101
static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
103
MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
104
: AxisView(sess) // virtually inherited
105
, RouteTimeAxisView(ed, sess, canvas)
106
, _ignore_signals(false)
108
, _piano_roll_header(0)
109
, _note_mode(Sustained)
111
, _percussion_mode_item(0)
112
, _color_mode(MeterColors)
113
, _meter_color_mode_item(0)
114
, _channel_color_mode_item(0)
115
, _track_color_mode_item(0)
116
, _channel_selector (0)
117
, _step_edit_item (0)
118
, controller_menu (0)
124
MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
128
_view = new MidiStreamView (*this);
131
_piano_roll_header = new PianoRollHeader(*midi_view());
132
_range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
133
_range_scroomer->DoubleClicked.connect (
134
sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
135
MidiStreamView::ContentsRange, false));
138
/* This next call will result in our height being set up, so it must come after
139
the creation of the piano roll / range scroomer as their visibility is set up
142
RouteTimeAxisView::set_route (rt);
144
_view->apply_color (_color, StreamView::RegionColor);
146
subplugin_menu.set_name ("ArdourContextMenu");
148
if (!gui_property ("note-range-min").empty ()) {
149
midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
150
atoi (gui_property ("note-range-max").c_str()),
154
midi_view()->NoteRangeChanged.connect (
155
sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
156
_view->ContentsHeightChanged.connect (
157
sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
159
ignore_toggle = false;
161
if (is_midi_track()) {
162
controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
163
_note_mode = midi_track()->note_mode();
164
} else { // MIDI bus (which doesn't exist yet..)
165
controls_ebox.set_name ("MidiBusControlsBaseUnselected");
168
/* map current state of the route */
170
processors_changed (RouteProcessorChange ());
172
_route->processors_changed.connect (*this, invalidator (*this),
173
boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
177
_piano_roll_header->SetNoteSelection.connect (
178
sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
179
_piano_roll_header->AddNoteSelection.connect (
180
sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
181
_piano_roll_header->ExtendNoteSelection.connect (
182
sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
183
_piano_roll_header->ToggleNoteSelection.connect (
184
sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
186
/* Suspend updates of the StreamView during scroomer drags to speed things up */
187
_range_scroomer->DragStarting.connect (
188
sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
189
_range_scroomer->DragFinishing.connect (
190
sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
192
/* Put the scroomer and the keyboard in a VBox with a padding
193
label so that they can be reduced in height for stacked-view
196
VBox* v = manage (new VBox);
197
HBox* h = manage (new HBox);
198
h->pack_start (*_range_scroomer);
199
h->pack_start (*_piano_roll_header);
200
v->pack_start (*h, false, false);
201
v->pack_start (*manage (new Label ("")), true, true);
204
controls_hbox.pack_start(*v, false, false);
206
controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
207
controls_base_selected_name = "MidiTrackControlsBaseSelected";
208
controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
210
midi_view()->NoteRangeChanged.connect (
211
sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
213
/* ask for notifications of any new RegionViews */
214
_view->RegionViewAdded.connect (
215
sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
217
midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
218
boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
220
midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
221
boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
223
midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
224
boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
226
midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
227
boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
230
playback_channel_mode_changed ();
231
capture_channel_mode_changed ();
233
if (!_editor.have_idled()) {
234
/* first idle will do what we need */
240
MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
242
MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
243
for (; m != patch_manager.all_models().end(); ++m) {
244
_midnam_model_selector.append_text(m->c_str());
247
if (gui_property (X_("midnam-model-name")).empty()) {
248
set_gui_property (X_("midnam-model-name"), "Generic");
251
if (gui_property (X_("midnam-custom-device-mode")).empty()) {
252
boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
254
set_gui_property (X_("midnam-custom-device-mode"),
255
*device_names->custom_device_mode_names().begin());
259
_midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
260
_midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
262
ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
263
ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
265
_midi_controls_box.set_homogeneous(false);
266
_midi_controls_box.set_border_width (10);
268
_channel_status_box.set_homogeneous (false);
269
_channel_status_box.set_spacing (6);
271
_channel_selector_button.set_label (_("Chns"));
272
ARDOUR_UI::instance()->set_tip (_channel_selector_button, _("Click to edit channel settings"));
274
/* fixed sized labels to prevent silly nonsense (though obviously,
275
* they cause their own too)
278
_playback_channel_status.set_size_request (65, -1);
279
_capture_channel_status.set_size_request (60, -1);
281
_channel_status_box.pack_start (_playback_channel_status, false, false);
282
_channel_status_box.pack_start (_capture_channel_status, false, false);
283
_channel_status_box.pack_start (_channel_selector_button, false, false);
284
_channel_status_box.show_all ();
286
_channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
288
_midi_controls_box.pack_start (_channel_status_box, false, false, 10);
290
if (!patch_manager.all_models().empty()) {
292
_midnam_model_selector.set_size_request(22, 30);
293
_midnam_model_selector.set_border_width(2);
294
_midnam_model_selector.show ();
295
_midi_controls_box.pack_start (_midnam_model_selector);
297
_midnam_custom_device_mode_selector.set_size_request(10, 30);
298
_midnam_custom_device_mode_selector.set_border_width(2);
299
_midnam_custom_device_mode_selector.show ();
301
_midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
305
custom_device_mode_changed();
307
_midnam_model_selector.signal_changed().connect(
308
sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
309
_midnam_custom_device_mode_selector.signal_changed().connect(
310
sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
312
controls_vbox.pack_start(_midi_controls_box, false, false);
314
const string color_mode = gui_property ("color-mode");
315
if (!color_mode.empty()) {
316
_color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
317
if (_channel_selector && _color_mode == ChannelColors) {
318
_channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
322
set_color_mode (_color_mode, true, false);
324
const string note_mode = gui_property ("note-mode");
325
if (!note_mode.empty()) {
326
_note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
327
if (_percussion_mode_item) {
328
_percussion_mode_item->set_active (_note_mode == Percussive);
332
/* Look for any GUI object state nodes that represent automation children
333
* that should exist, and create the children.
336
const list<string> gui_ids = gui_object_state().all_ids ();
337
for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
340
Evoral::Parameter parameter (0, 0, 0);
342
bool const p = AutomationTimeAxisView::parse_state_id (
343
*i, route_id, has_parameter, parameter);
344
if (p && route_id == _route->id () && has_parameter) {
345
const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
346
create_automation_child (parameter, string_is_affirmative (visible));
352
MidiTimeAxisView::first_idle ()
359
MidiTimeAxisView::~MidiTimeAxisView ()
361
delete _channel_selector;
363
delete _piano_roll_header;
364
_piano_roll_header = 0;
366
delete _range_scroomer;
369
delete controller_menu;
374
MidiTimeAxisView::enter_internal_edit_mode ()
377
midi_view()->enter_internal_edit_mode ();
382
MidiTimeAxisView::leave_internal_edit_mode ()
385
midi_view()->leave_internal_edit_mode ();
390
MidiTimeAxisView::check_step_edit ()
392
ensure_step_editor ();
393
_step_editor->check_step_edit ();
397
MidiTimeAxisView::model_changed()
399
const Glib::ustring model = _midnam_model_selector.get_active_text();
400
set_gui_property (X_("midnam-model-name"), model);
402
const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
403
.custom_device_mode_names_by_model(model);
405
_midnam_custom_device_mode_selector.clear_items();
407
for (std::list<std::string>::const_iterator i = device_modes.begin();
408
i != device_modes.end(); ++i) {
409
_midnam_custom_device_mode_selector.append_text(*i);
412
_midnam_custom_device_mode_selector.set_active(0);
414
_route->instrument_info().set_external_instrument (
415
_midnam_model_selector.get_active_text(),
416
_midnam_custom_device_mode_selector.get_active_text());
418
// Rebuild controller menu
419
_controller_menu_map.clear ();
420
delete controller_menu;
422
build_automation_action_menu(false);
426
MidiTimeAxisView::custom_device_mode_changed()
428
const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
429
set_gui_property (X_("midnam-custom-device-mode"), mode);
430
_route->instrument_info().set_external_instrument (
431
_midnam_model_selector.get_active_text(), mode);
435
MidiTimeAxisView::midi_view()
437
return dynamic_cast<MidiStreamView*>(_view);
441
MidiTimeAxisView::set_height (uint32_t h)
443
if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
444
_midi_controls_box.show ();
446
_midi_controls_box.hide();
449
if (h >= KEYBOARD_MIN_HEIGHT) {
450
if (is_track() && _range_scroomer) {
451
_range_scroomer->show();
453
if (is_track() && _piano_roll_header) {
454
_piano_roll_header->show();
457
if (is_track() && _range_scroomer) {
458
_range_scroomer->hide();
460
if (is_track() && _piano_roll_header) {
461
_piano_roll_header->hide();
465
/* We need to do this after changing visibility of our stuff, as it will
466
eventually trigger a call to Editor::reset_controls_layout_width(),
467
which needs to know if we have just shown or hidden a scroomer /
470
RouteTimeAxisView::set_height (h);
474
MidiTimeAxisView::append_extra_display_menu_items ()
476
using namespace Menu_Helpers;
478
MenuList& items = display_menu->items();
481
Menu *range_menu = manage(new Menu);
482
MenuList& range_items = range_menu->items();
483
range_menu->set_name ("ArdourContextMenu");
485
range_items.push_back (
486
MenuElem (_("Show Full Range"),
487
sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
488
MidiStreamView::FullRange, true)));
490
range_items.push_back (
491
MenuElem (_("Fit Contents"),
492
sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
493
MidiStreamView::ContentsRange, true)));
495
items.push_back (MenuElem (_("Note Range"), *range_menu));
496
items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
497
items.push_back (MenuElem (_("Channel Selector"),
498
sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
500
color_mode_menu = build_color_mode_menu();
501
if (color_mode_menu) {
502
items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
505
items.push_back (SeparatorElem ());
509
MidiTimeAxisView::toggle_channel_selector ()
511
if (!_channel_selector) {
512
_channel_selector = new MidiChannelSelectorWindow (midi_track());
514
if (_color_mode == ChannelColors) {
515
_channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
517
_channel_selector->set_default_channel_color ();
520
_channel_selector->show_all ();
522
_channel_selector->cycle_visibility ();
527
MidiTimeAxisView::build_automation_action_menu (bool for_selection)
529
using namespace Menu_Helpers;
531
/* If we have a controller menu, we need to detach it before
532
RouteTimeAxis::build_automation_action_menu destroys the
533
menu it is attached to. Otherwise GTK destroys
534
controller_menu's gobj, meaning that it can't be reattached
535
below. See bug #3134.
538
if (controller_menu) {
539
detach_menu (*controller_menu);
542
_channel_command_menu_map.clear ();
543
RouteTimeAxisView::build_automation_action_menu (for_selection);
545
MenuList& automation_items = automation_action_menu->items();
547
uint16_t selected_channels = midi_track()->get_playback_channel_mask();
549
if (selected_channels != 0) {
551
automation_items.push_back (SeparatorElem());
553
/* these 2 MIDI "command" types are semantically more like automation
554
than note data, but they are not MIDI controllers. We give them
555
special status in this menu, since they will not show up in the
556
controller list and anyone who actually knows something about MIDI
557
(!) would not expect to find them there.
560
add_channel_command_menu_item (
561
automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
562
automation_items.back().set_sensitive (
563
!for_selection || _editor.get_selection().tracks.size() == 1);
564
add_channel_command_menu_item (
565
automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
566
automation_items.back().set_sensitive (
567
!for_selection || _editor.get_selection().tracks.size() == 1);
569
/* now all MIDI controllers. Always offer the possibility that we will
570
rebuild the controllers menu since it might need to be updated after
571
a channel mode change or other change. Also detach it first in case
572
it has been used anywhere else.
575
build_controller_menu ();
577
automation_items.push_back (SeparatorElem());
578
automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
579
automation_items.back().set_sensitive (
580
!for_selection || _editor.get_selection().tracks.size() == 1);
582
automation_items.push_back (
583
MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
584
dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
589
MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
591
const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
593
for (uint8_t chn = 0; chn < 16; chn++) {
594
if (selected_channels & (0x0001 << chn)) {
596
Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
597
Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
600
menu->set_active (yn);
607
MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
609
AutomationType auto_type,
612
using namespace Menu_Helpers;
614
/* count the number of selected channels because we will build a different menu
615
structure if there is more than 1 selected.
618
const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
621
for (uint8_t chn = 0; chn < 16; chn++) {
622
if (selected_channels & (0x0001 << chn)) {
631
/* multiple channels - create a submenu, with 1 item per channel */
633
Menu* chn_menu = manage (new Menu);
634
MenuList& chn_items (chn_menu->items());
635
Evoral::Parameter param_without_channel (auto_type, 0, cmd);
637
/* add a couple of items to hide/show all of them */
639
chn_items.push_back (
640
MenuElem (_("Hide all channels"),
641
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
642
false, param_without_channel)));
643
chn_items.push_back (
644
MenuElem (_("Show all channels"),
645
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
646
true, param_without_channel)));
648
for (uint8_t chn = 0; chn < 16; chn++) {
649
if (selected_channels & (0x0001 << chn)) {
651
/* for each selected channel, add a menu item for this controller */
653
Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
654
chn_items.push_back (
655
CheckMenuElem (string_compose (_("Channel %1"), chn+1),
656
sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
657
fully_qualified_param)));
659
boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
660
bool visible = false;
663
if (track->marked_for_display()) {
668
CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
669
_channel_command_menu_map[fully_qualified_param] = cmi;
670
cmi->set_active (visible);
674
/* now create an item in the parent menu that has the per-channel list as a submenu */
676
items.push_back (MenuElem (label, *chn_menu));
680
/* just one channel - create a single menu item for this command+channel combination*/
682
for (uint8_t chn = 0; chn < 16; chn++) {
683
if (selected_channels & (0x0001 << chn)) {
685
Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
687
CheckMenuElem (label,
688
sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
689
fully_qualified_param)));
691
boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
692
bool visible = false;
695
if (track->marked_for_display()) {
700
CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
701
_channel_command_menu_map[fully_qualified_param] = cmi;
702
cmi->set_active (visible);
704
/* one channel only */
711
/** Add a single menu item for a controller on one channel. */
713
MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
715
const std::string& name)
717
using namespace Menu_Helpers;
719
const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
720
for (uint8_t chn = 0; chn < 16; chn++) {
721
if (selected_channels & (0x0001 << chn)) {
723
Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
724
ctl_items.push_back (
726
string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
728
sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
729
fully_qualified_param)));
730
dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
732
boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
733
fully_qualified_param);
735
bool visible = false;
737
if (track->marked_for_display()) {
742
CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
743
_controller_menu_map[fully_qualified_param] = cmi;
744
cmi->set_active (visible);
746
/* one channel only */
752
/** Add a submenu with 1 item per channel for a controller on many channels. */
754
MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
756
const std::string& name)
758
using namespace Menu_Helpers;
760
const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
762
Menu* chn_menu = manage (new Menu);
763
MenuList& chn_items (chn_menu->items());
765
/* add a couple of items to hide/show this controller on all channels */
767
Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
768
chn_items.push_back (
769
MenuElem (_("Hide all channels"),
770
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
771
false, param_without_channel)));
772
chn_items.push_back (
773
MenuElem (_("Show all channels"),
774
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
775
true, param_without_channel)));
777
for (uint8_t chn = 0; chn < 16; chn++) {
778
if (selected_channels & (0x0001 << chn)) {
780
/* for each selected channel, add a menu item for this controller */
782
Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
783
chn_items.push_back (
784
CheckMenuElem (string_compose (_("Channel %1"), chn+1),
785
sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
786
fully_qualified_param)));
788
boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
789
fully_qualified_param);
790
bool visible = false;
793
if (track->marked_for_display()) {
798
CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
799
_controller_menu_map[fully_qualified_param] = cmi;
800
cmi->set_active (visible);
804
/* add the per-channel menu to the list of controllers, with the name of the controller */
805
ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
807
dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
810
boost::shared_ptr<MIDI::Name::CustomDeviceMode>
811
MidiTimeAxisView::get_device_mode()
813
using namespace MIDI::Name;
815
boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
817
return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
820
return device_names->custom_device_mode_by_name(
821
gui_property (X_("midnam-custom-device-mode")));
824
boost::shared_ptr<MIDI::Name::MasterDeviceNames>
825
MidiTimeAxisView::get_device_names()
827
using namespace MIDI::Name;
829
const std::string model = gui_property (X_("midnam-model-name"));
831
boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
832
.document_by_model(model);
834
return midnam->master_device_names(model);
836
return boost::shared_ptr<MasterDeviceNames>();
841
MidiTimeAxisView::build_controller_menu ()
843
using namespace Menu_Helpers;
845
if (controller_menu) {
846
/* it exists and has not been invalidated by a channel mode change */
850
controller_menu = new Menu; // explicitly managed by us
851
MenuList& items (controller_menu->items());
853
/* create several "top level" menu items for sets of controllers (16 at a
854
time), and populate each one with a submenu for each controller+channel
855
combination covering the currently selected channels for this track
858
const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
860
/* count the number of selected channels because we will build a different menu
861
structure if there is more than 1 selected.
865
for (uint8_t chn = 0; chn < 16; chn++) {
866
if (selected_channels & (0x0001 << chn)) {
873
using namespace MIDI::Name;
874
boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
876
if (device_names && !device_names->controls().empty()) {
877
/* Controllers names available in midnam file, generate fancy menu */
878
unsigned n_items = 0;
879
unsigned n_groups = 0;
881
/* TODO: This is not correct, should look up the currently applicable ControlNameList
882
and only build a menu for that one. */
883
for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
884
l != device_names->controls().end(); ++l) {
885
boost::shared_ptr<ControlNameList> name_list = l->second;
886
Menu* ctl_menu = NULL;
888
for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
889
c != name_list->controls().end();) {
890
const uint16_t ctl = c->second->number();
891
if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
892
/* Skip bank select controllers since they're handled specially */
894
/* Create a new submenu */
895
ctl_menu = manage (new Menu);
898
MenuList& ctl_items (ctl_menu->items());
900
add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
902
add_single_channel_controller_item(ctl_items, ctl, c->second->name());
907
if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
908
/* Submenu has 16 items or we're done, add it to controller menu and reset */
910
MenuElem(string_compose(_("Controllers %1-%2"),
911
(16 * n_groups), (16 * n_groups) + n_items - 1),
920
/* No controllers names, generate generic numeric menu */
921
for (int i = 0; i < 127; i += 16) {
922
Menu* ctl_menu = manage (new Menu);
923
MenuList& ctl_items (ctl_menu->items());
925
for (int ctl = i; ctl < i+16; ++ctl) {
926
if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
927
/* Skip bank select controllers since they're handled specially */
932
add_multi_channel_controller_item(
933
ctl_items, ctl, string_compose(_("Controller %1"), ctl));
935
add_single_channel_controller_item(
936
ctl_items, ctl, string_compose(_("Controller %1"), ctl));
940
/* Add submenu for this block of controllers to controller menu */
942
MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
949
MidiTimeAxisView::build_note_mode_menu()
951
using namespace Menu_Helpers;
953
Menu* mode_menu = manage (new Menu);
954
MenuList& items = mode_menu->items();
955
mode_menu->set_name ("ArdourContextMenu");
957
RadioMenuItem::Group mode_group;
959
RadioMenuElem (mode_group,_("Sustained"),
960
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
962
_note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
963
_note_mode_item->set_active(_note_mode == Sustained);
966
RadioMenuElem (mode_group, _("Percussive"),
967
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
969
_percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
970
_percussion_mode_item->set_active(_note_mode == Percussive);
976
MidiTimeAxisView::build_color_mode_menu()
978
using namespace Menu_Helpers;
980
Menu* mode_menu = manage (new Menu);
981
MenuList& items = mode_menu->items();
982
mode_menu->set_name ("ArdourContextMenu");
984
RadioMenuItem::Group mode_group;
986
RadioMenuElem (mode_group, _("Meter Colors"),
987
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
988
MeterColors, false, true, true)));
989
_meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
990
_meter_color_mode_item->set_active(_color_mode == MeterColors);
993
RadioMenuElem (mode_group, _("Channel Colors"),
994
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
995
ChannelColors, false, true, true)));
996
_channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
997
_channel_color_mode_item->set_active(_color_mode == ChannelColors);
1000
RadioMenuElem (mode_group, _("Track Color"),
1001
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1002
TrackColor, false, true, true)));
1003
_channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1004
_channel_color_mode_item->set_active(_color_mode == TrackColor);
1010
MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1012
if (apply_to_selection) {
1013
_editor.get_selection().tracks.foreach_midi_time_axis (
1014
boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1016
if (_note_mode != mode || midi_track()->note_mode() != mode) {
1018
midi_track()->set_note_mode(mode);
1019
set_gui_property ("note-mode", enum_2_string(_note_mode));
1020
_view->redisplay_track();
1026
MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1028
if (apply_to_selection) {
1029
_editor.get_selection().tracks.foreach_midi_time_axis (
1030
boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1032
if (_color_mode == mode && !force) {
1036
if (_channel_selector) {
1037
if (mode == ChannelColors) {
1038
_channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
1040
_channel_selector->set_default_channel_color();
1045
set_gui_property ("color-mode", enum_2_string(_color_mode));
1047
_view->redisplay_track();
1053
MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1055
if (apply_to_selection) {
1056
_editor.get_selection().tracks.foreach_midi_time_axis (
1057
boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1059
if (!_ignore_signals) {
1060
midi_view()->set_note_range(range);
1066
MidiTimeAxisView::update_range()
1068
MidiGhostRegion* mgr;
1070
for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1071
if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1072
mgr->update_range();
1078
MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1080
if (apply_to_selection) {
1081
_editor.get_selection().tracks.foreach_midi_time_axis (
1082
boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1085
const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1087
for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1088
create_automation_child(*i, true);
1092
RouteTimeAxisView::show_all_automation ();
1097
MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1099
if (apply_to_selection) {
1100
_editor.get_selection().tracks.foreach_midi_time_axis (
1101
boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1104
const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1106
for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1107
create_automation_child (*i, true);
1111
RouteTimeAxisView::show_existing_automation ();
1115
/** Create an automation track for the given parameter (pitch bend, channel pressure).
1118
MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1120
if (param.type() == NullAutomation) {
1124
AutomationTracks::iterator existing = _automation_tracks.find (param);
1126
if (existing != _automation_tracks.end()) {
1128
/* automation track created because we had existing data for
1129
* the processor, but visibility may need to be controlled
1130
* since it will have been set visible by default.
1133
if (existing->second->set_marked_for_display (show) && !no_redraw) {
1140
boost::shared_ptr<AutomationTimeAxisView> track;
1142
switch (param.type()) {
1144
case GainAutomation:
1145
create_gain_automation_child (param, show);
1148
case PluginAutomation:
1149
/* handled elsewhere */
1152
case MidiCCAutomation:
1153
case MidiPgmChangeAutomation:
1154
case MidiPitchBenderAutomation:
1155
case MidiChannelPressureAutomation:
1156
case MidiSystemExclusiveAutomation:
1157
/* These controllers are region "automation" - they are owned
1158
* by regions (and their MidiModels), not by the track. As a
1159
* result we do not create an AutomationList/Line for the track
1160
* ... except here we are doing something!! XXX
1163
track.reset (new AutomationTimeAxisView (
1166
boost::shared_ptr<Automatable> (),
1167
boost::shared_ptr<AutomationControl> (),
1173
_route->describe_parameter(param)));
1176
_view->foreach_regionview (
1177
sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1180
add_automation_child (param, track, show);
1184
error << "MidiTimeAxisView: unknown automation child "
1185
<< EventTypeMap::instance().to_symbol(param) << endmsg;
1190
MidiTimeAxisView::route_active_changed ()
1192
RouteUI::route_active_changed ();
1195
if (_route->active()) {
1196
controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1197
controls_base_selected_name = "MidiTrackControlsBaseSelected";
1198
controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1200
controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1201
controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1202
controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1205
if (_route->active()) {
1206
controls_ebox.set_name ("BusControlsBaseUnselected");
1207
controls_base_selected_name = "BusControlsBaseSelected";
1208
controls_base_unselected_name = "BusControlsBaseUnselected";
1210
controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1211
controls_base_selected_name = "BusControlsBaseInactiveSelected";
1212
controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1218
MidiTimeAxisView::set_note_selection (uint8_t note)
1220
if (!_editor.internal_editing()) {
1224
uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1226
if (_view->num_selected_regionviews() == 0) {
1227
_view->foreach_regionview (
1228
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1231
_view->foreach_selected_regionview (
1232
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1238
MidiTimeAxisView::add_note_selection (uint8_t note)
1240
if (!_editor.internal_editing()) {
1244
const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1246
if (_view->num_selected_regionviews() == 0) {
1247
_view->foreach_regionview (
1248
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1251
_view->foreach_selected_regionview (
1252
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1258
MidiTimeAxisView::extend_note_selection (uint8_t note)
1260
if (!_editor.internal_editing()) {
1264
const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1266
if (_view->num_selected_regionviews() == 0) {
1267
_view->foreach_regionview (
1268
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1271
_view->foreach_selected_regionview (
1272
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1278
MidiTimeAxisView::toggle_note_selection (uint8_t note)
1280
if (!_editor.internal_editing()) {
1284
const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1286
if (_view->num_selected_regionviews() == 0) {
1287
_view->foreach_regionview (
1288
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1291
_view->foreach_selected_regionview (
1292
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1298
MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1300
dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1304
MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1306
dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1310
MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1312
dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1316
MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1318
dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1322
MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1324
/* hide all automation tracks that use the wrong channel(s) and show all those that use
1328
const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1329
bool changed = false;
1333
for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1335
for (uint32_t chn = 0; chn < 16; ++chn) {
1336
Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1337
boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1343
if ((selected_channels & (0x0001 << chn)) == 0) {
1344
/* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1345
which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1347
changed = track->set_marked_for_display (false) || changed;
1349
changed = track->set_marked_for_display (true) || changed;
1356
/* TODO: Bender, Pressure */
1358
/* invalidate the controller menu, so that we rebuild it next time */
1359
_controller_menu_map.clear ();
1360
delete controller_menu;
1361
controller_menu = 0;
1369
MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1371
Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1376
ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1377
if (i != _controller_menu_map.end()) {
1381
i = _channel_command_menu_map.find (param);
1382
if (i != _channel_command_menu_map.end()) {
1389
boost::shared_ptr<MidiRegion>
1390
MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1392
Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1394
real_editor->begin_reversible_command (Operations::create_region);
1395
playlist()->clear_changes ();
1397
real_editor->snap_to (pos, 0);
1399
boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1400
view()->trackview().track().get(), view()->trackview().track()->name());
1403
plist.add (ARDOUR::Properties::start, 0);
1404
plist.add (ARDOUR::Properties::length, length);
1405
plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1407
boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1409
playlist()->add_region (region, pos);
1410
_session->add_command (new StatefulDiffCommand (playlist()));
1413
real_editor->commit_reversible_command ();
1416
return boost::dynamic_pointer_cast<MidiRegion>(region);
1420
MidiTimeAxisView::ensure_step_editor ()
1422
if (!_step_editor) {
1423
_step_editor = new StepEditor (_editor, midi_track(), *this);
1428
MidiTimeAxisView::start_step_editing ()
1430
ensure_step_editor ();
1431
_step_editor->start_step_editing ();
1435
MidiTimeAxisView::stop_step_editing ()
1438
_step_editor->stop_step_editing ();
1442
/** @return channel (counted from 0) to add an event to, based on the current setting
1443
* of the channel selector.
1446
MidiTimeAxisView::get_channel_for_add () const
1448
uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1450
uint8_t channel = 0;
1452
/* pick the highest selected channel, unless all channels are selected,
1453
which is interpreted to mean channel 1 (zero)
1456
for (uint16_t i = 0; i < 16; ++i) {
1457
if (chn_mask & (1<<i)) {
1463
if (chn_cnt == 16) {
1471
MidiTimeAxisView::note_range_changed ()
1473
set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1474
set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1478
MidiTimeAxisView::contents_height_changed ()
1480
_range_scroomer->set_size_request (-1, _view->child_height ());
1484
MidiTimeAxisView::playback_channel_mode_changed ()
1486
switch (midi_track()->get_playback_channel_mode()) {
1488
_playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1490
case FilterChannels:
1491
_playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1494
_playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), ffs (midi_track()->get_playback_channel_mask())));
1500
MidiTimeAxisView::capture_channel_mode_changed ()
1502
switch (midi_track()->get_capture_channel_mode()) {
1504
_capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1506
case FilterChannels:
1507
_capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1510
_capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), ffs (midi_track()->get_capture_channel_mask())));