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.
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/file_source.h"
42 #include "ardour/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
56 #include "midi++/names.h"
58 #include "add_midi_cc_track_dialog.h"
59 #include "ardour_ui.h"
60 #include "automation_line.h"
61 #include "automation_time_axis.h"
62 #include "canvas-note-event.h"
63 #include "canvas_impl.h"
64 #include "crossfade_view.h"
67 #include "ghostregion.h"
68 #include "gui_thread.h"
70 #include "midi_scroomer.h"
71 #include "midi_streamview.h"
72 #include "midi_region_view.h"
73 #include "midi_time_axis.h"
74 #include "piano_roll_header.h"
75 #include "playlist_selector.h"
76 #include "plugin_selector.h"
77 #include "plugin_ui.h"
78 #include "point_selection.h"
80 #include "region_view.h"
81 #include "rgb_macros.h"
82 #include "selection.h"
83 #include "simplerect.h"
84 #include "step_entry.h"
87 #include "ardour/midi_track.h"
91 using namespace ARDOUR
;
94 using namespace Gtkmm2ext
;
95 using namespace Editing
;
97 // Minimum height at which a control is displayed
98 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT
= 162;
99 static const uint32_t KEYBOARD_MIN_HEIGHT
= 140;
101 MidiTimeAxisView::MidiTimeAxisView (PublicEditor
& ed
, Session
* sess
,
102 boost::shared_ptr
<Route
> rt
, Canvas
& canvas
)
103 : AxisView(sess
) // virtually inherited
104 , RouteTimeAxisView(ed
, sess
, rt
, canvas
)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained
)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors
)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , default_channel_menu (0)
118 , controller_menu (0)
121 subplugin_menu
.set_name ("ArdourContextMenu");
123 _view
= new MidiStreamView (*this);
125 ignore_toggle
= false;
127 mute_button
->set_active (false);
128 solo_button
->set_active (false);
130 step_edit_insert_position
= 0;
132 if (is_midi_track()) {
133 controls_ebox
.set_name ("MidiTimeAxisViewControlsBaseUnselected");
134 _note_mode
= midi_track()->note_mode();
135 } else { // MIDI bus (which doesn't exist yet..)
136 controls_ebox
.set_name ("MidiBusControlsBaseUnselected");
139 /* map current state of the route */
141 processors_changed (RouteProcessorChange ());
145 set_state (*xml_node
, Stateful::loading_state_version
);
147 _route
->processors_changed
.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed
, this, _1
), gui_context());
150 _piano_roll_header
= new PianoRollHeader(*midi_view());
152 _piano_roll_header
->AddNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection
));
153 _piano_roll_header
->ExtendNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection
));
154 _piano_roll_header
->ToggleNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection
));
156 _range_scroomer
= new MidiScroomer(midi_view()->note_range_adjustment
);
158 controls_hbox
.pack_start(*_range_scroomer
);
159 controls_hbox
.pack_start(*_piano_roll_header
);
161 controls_ebox
.set_name ("MidiTrackControlsBaseUnselected");
162 controls_base_selected_name
= "MidiTrackControlsBaseSelected";
163 controls_base_unselected_name
= "MidiTrackControlsBaseUnselected";
165 midi_view()->NoteRangeChanged
.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range
));
167 /* ask for notifications of any new RegionViews */
168 _view
->RegionViewAdded
.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added
));
171 midi_track()->PlaylistChanged
.connect (*this, invalidator (*this),
172 boost::bind (&MidiTimeAxisView::playlist_changed
, this),
178 HBox
* midi_controls_hbox
= manage(new HBox());
180 MIDI::Name::MidiPatchManager
& patch_manager
= MIDI::Name::MidiPatchManager::instance();
182 MIDI::Name::MasterDeviceNames::Models::const_iterator m
= patch_manager
.all_models().begin();
183 for (; m
!= patch_manager
.all_models().end(); ++m
) {
184 _model_selector
.append_text(m
->c_str());
187 _model_selector
.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed
));
189 _custom_device_mode_selector
.signal_changed().connect(
190 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed
));
192 // TODO: persist the choice
193 // this initializes the comboboxes and sends out the signal
194 _model_selector
.set_active(0);
196 midi_controls_hbox
->pack_start(_channel_selector
, true, false);
197 if (!patch_manager
.all_models().empty()) {
198 _midi_controls_box
.pack_start(_model_selector
, true, false);
199 _midi_controls_box
.pack_start(_custom_device_mode_selector
, true, false);
202 _midi_controls_box
.pack_start(*midi_controls_hbox
, true, true);
204 controls_vbox
.pack_start(_midi_controls_box
, false, false);
206 // restore channel selector settings
207 _channel_selector
.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
208 _channel_selector
.mode_changed
.connect(
209 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode
));
210 _channel_selector
.mode_changed
.connect(
211 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode
));
214 if ((prop
= xml_node
->property ("color-mode")) != 0) {
215 _color_mode
= ColorMode (string_2_enum(prop
->value(), _color_mode
));
216 if (_color_mode
== ChannelColors
) {
217 _channel_selector
.set_channel_colors(CanvasNoteEvent::midi_channel_colors
);
221 if ((prop
= xml_node
->property ("note-mode")) != 0) {
222 _note_mode
= NoteMode (string_2_enum(prop
->value(), _note_mode
));
223 if (_percussion_mode_item
) {
224 _percussion_mode_item
->set_active (_note_mode
== Percussive
);
229 MidiTimeAxisView::~MidiTimeAxisView ()
231 delete _piano_roll_header
;
232 _piano_roll_header
= 0;
234 delete _range_scroomer
;
237 delete controller_menu
;
241 MidiTimeAxisView::playlist_changed ()
243 step_edit_region_connection
.disconnect ();
244 midi_track()->playlist()->RegionRemoved
.connect (step_edit_region_connection
, invalidator (*this),
245 ui_bind (&MidiTimeAxisView::region_removed
, this, _1
),
250 MidiTimeAxisView::region_removed (boost::weak_ptr
<Region
> wr
)
252 boost::shared_ptr
<Region
> r (wr
.lock());
258 if (step_edit_region
== r
) {
259 step_edit_region
.reset();
260 // force a recompute of the insert position
261 step_edit_beat_pos
= -1.0;
265 void MidiTimeAxisView::model_changed()
267 std::list
<std::string
> device_modes
= MIDI::Name::MidiPatchManager::instance()
268 .custom_device_mode_names_by_model(_model_selector
.get_active_text());
270 _custom_device_mode_selector
.clear_items();
272 for (std::list
<std::string
>::const_iterator i
= device_modes
.begin();
273 i
!= device_modes
.end(); ++i
) {
274 cerr
<< "found custom device mode " << *i
<< " thread_id: " << pthread_self() << endl
;
275 _custom_device_mode_selector
.append_text(*i
);
278 _custom_device_mode_selector
.set_active(0);
281 void MidiTimeAxisView::custom_device_mode_changed()
283 _midi_patch_settings_changed
.emit(_model_selector
.get_active_text(),
284 _custom_device_mode_selector
.get_active_text());
288 MidiTimeAxisView::midi_view()
290 return dynamic_cast<MidiStreamView
*>(_view
);
294 MidiTimeAxisView::show_at (double y
, int& nth
, Gtk::VBox
*parent
)
297 xml_node
->add_property ("shown-editor", "yes");
299 guint32 ret
= TimeAxisView::show_at (y
, nth
, parent
);
304 MidiTimeAxisView::hide ()
307 xml_node
->add_property ("shown-editor", "no");
309 TimeAxisView::hide ();
313 MidiTimeAxisView::set_height (uint32_t h
)
315 RouteTimeAxisView::set_height (h
);
317 if (height
>= MIDI_CONTROLS_BOX_MIN_HEIGHT
) {
318 _midi_controls_box
.show_all ();
320 _midi_controls_box
.hide();
323 if (height
>= KEYBOARD_MIN_HEIGHT
) {
324 if (is_track() && _range_scroomer
)
325 _range_scroomer
->show();
326 if (is_track() && _piano_roll_header
)
327 _piano_roll_header
->show();
329 if (is_track() && _range_scroomer
)
330 _range_scroomer
->hide();
331 if (is_track() && _piano_roll_header
)
332 _piano_roll_header
->hide();
337 MidiTimeAxisView::append_extra_display_menu_items ()
339 using namespace Menu_Helpers
;
341 MenuList
& items
= display_menu
->items();
344 Menu
*range_menu
= manage(new Menu
);
345 MenuList
& range_items
= range_menu
->items();
346 range_menu
->set_name ("ArdourContextMenu");
348 range_items
.push_back (MenuElem (_("Show Full Range"), sigc::bind (
349 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range
),
350 MidiStreamView::FullRange
)));
352 range_items
.push_back (MenuElem (_("Fit Contents"), sigc::bind (
353 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range
),
354 MidiStreamView::ContentsRange
)));
356 items
.push_back (MenuElem (_("Note range"), *range_menu
));
357 items
.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
358 items
.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
360 items
.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru
)));
361 _midi_thru_item
= dynamic_cast<CheckMenuItem
*>(&items
.back());
365 MidiTimeAxisView::build_def_channel_menu ()
367 using namespace Menu_Helpers
;
369 default_channel_menu
= manage (new Menu ());
371 uint8_t defchn
= midi_track()->default_channel();
372 MenuList
& def_channel_items
= default_channel_menu
->items();
374 RadioMenuItem::Group dc_group
;
376 for (int i
= 0; i
< 16; ++i
) {
378 snprintf (buf
, sizeof (buf
), "%d", i
+1);
380 def_channel_items
.push_back (RadioMenuElem (dc_group
, buf
,
381 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel
), i
)));
382 item
= dynamic_cast<RadioMenuItem
*>(&def_channel_items
.back());
383 item
->set_active ((i
== defchn
));
386 return default_channel_menu
;
390 MidiTimeAxisView::set_default_channel (int chn
)
392 midi_track()->set_default_channel (chn
);
396 MidiTimeAxisView::toggle_midi_thru ()
398 if (!_midi_thru_item
) {
402 bool view_yn
= _midi_thru_item
->get_active();
403 if (view_yn
!= midi_track()->midi_thru()) {
404 midi_track()->set_midi_thru (view_yn
);
409 MidiTimeAxisView::build_automation_action_menu ()
411 using namespace Menu_Helpers
;
413 /* If we have a controller menu, we need to detach it before
414 RouteTimeAxis::build_automation_action_menu destroys the
415 menu it is attached to. Otherwise GTK destroys
416 controller_menu's gobj, meaning that it can't be reattached
417 below. See bug #3134.
420 if (controller_menu
) {
421 detach_menu (*controller_menu
);
424 _channel_command_menu_map
.clear ();
425 RouteTimeAxisView::build_automation_action_menu ();
427 MenuList
& automation_items
= automation_action_menu
->items();
429 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
431 if (selected_channels
!= 0) {
433 automation_items
.push_back (SeparatorElem());
435 /* these 3 MIDI "command" types are semantically more like automation than note data,
436 but they are not MIDI controllers. We give them special status in this menu, since
437 they will not show up in the controller list and anyone who actually knows
438 something about MIDI (!) would not expect to find them there.
441 add_channel_command_menu_item (automation_items
, _("Program Change"), MidiPgmChangeAutomation
, 0);
442 add_channel_command_menu_item (automation_items
, _("Bender"), MidiPitchBenderAutomation
, 0);
443 add_channel_command_menu_item (automation_items
, _("Pressure"), MidiChannelPressureAutomation
, 0);
445 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
446 since it might need to be updated after a channel mode change or other change. Also detach it
447 first in case it has been used anywhere else.
450 build_controller_menu ();
452 automation_items
.push_back (SeparatorElem());
453 automation_items
.push_back (MenuElem (_("Controllers"), *controller_menu
));
455 automation_items
.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
461 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn
, Evoral::Parameter param
)
463 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
465 for (uint8_t chn
= 0; chn
< 16; chn
++) {
466 if (selected_channels
& (0x0001 << chn
)) {
468 Evoral::Parameter
fully_qualified_param (param
.type(), chn
, param
.id());
469 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (fully_qualified_param
);
472 menu
->set_active (yn
);
479 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList
& items
, const string
& label
, AutomationType auto_type
, uint8_t cmd
)
481 using namespace Menu_Helpers
;
483 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
486 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
489 for (uint8_t chn
= 0; chn
< 16; chn
++) {
490 if (selected_channels
& (0x0001 << chn
)) {
499 /* multiple channels - create a submenu, with 1 item per channel */
501 Menu
* chn_menu
= manage (new Menu
);
502 MenuList
& chn_items (chn_menu
->items());
503 Evoral::Parameter
param_without_channel (auto_type
, 0, cmd
);
505 /* add a couple of items to hide/show all of them */
507 chn_items
.push_back (MenuElem (_("Hide all channels"),
508 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
509 false, param_without_channel
)));
510 chn_items
.push_back (MenuElem (_("Show all channels"),
511 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
512 true, param_without_channel
)));
514 for (uint8_t chn
= 0; chn
< 16; chn
++) {
515 if (selected_channels
& (0x0001 << chn
)) {
517 /* for each selected channel, add a menu item for this controller */
519 Evoral::Parameter
fully_qualified_param (auto_type
, chn
, cmd
);
520 chn_items
.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn
+1),
521 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
522 fully_qualified_param
)));
524 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
525 bool visible
= false;
528 if (track
->marked_for_display()) {
533 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&chn_items
.back());
534 _channel_command_menu_map
[fully_qualified_param
] = cmi
;
535 cmi
->set_active (visible
);
539 /* now create an item in the parent menu that has the per-channel list as a submenu */
541 items
.push_back (MenuElem (label
, *chn_menu
));
545 /* just one channel - create a single menu item for this command+channel combination*/
547 for (uint8_t chn
= 0; chn
< 16; chn
++) {
548 if (selected_channels
& (0x0001 << chn
)) {
550 Evoral::Parameter
fully_qualified_param (auto_type
, chn
, cmd
);
551 items
.push_back (CheckMenuElem (label
,
552 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
553 fully_qualified_param
)));
555 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
556 bool visible
= false;
559 if (track
->marked_for_display()) {
564 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&items
.back());
565 _channel_command_menu_map
[fully_qualified_param
] = cmi
;
566 cmi
->set_active (visible
);
568 /* one channel only */
576 MidiTimeAxisView::build_controller_menu ()
578 using namespace Menu_Helpers
;
580 if (controller_menu
) {
581 /* it exists and has not been invalidated by a channel mode change, so just return it */
585 controller_menu
= new Menu
; // explicitly managed by us
586 MenuList
& items (controller_menu
->items());
588 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
589 for each controller+channel combination covering the currently selected channels for this track
592 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
594 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
599 for (uint8_t chn
= 0; chn
< 16; chn
++) {
600 if (selected_channels
& (0x0001 << chn
)) {
607 /* loop over all 127 MIDI controllers, in groups of 16 */
609 for (int i
= 0; i
< 127; i
+= 16) {
611 Menu
* ctl_menu
= manage (new Menu
);
612 MenuList
& ctl_items (ctl_menu
->items());
615 /* for each controller, consider whether to create a submenu or a single item */
617 for (int ctl
= i
; ctl
< i
+16; ++ctl
) {
621 /* multiple channels - create a submenu, with 1 item per channel */
623 Menu
* chn_menu
= manage (new Menu
);
624 MenuList
& chn_items (chn_menu
->items());
626 /* add a couple of items to hide/show this controller on all channels */
628 Evoral::Parameter
param_without_channel (MidiCCAutomation
, 0, ctl
);
629 chn_items
.push_back (MenuElem (_("Hide all channels"),
630 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
631 false, param_without_channel
)));
632 chn_items
.push_back (MenuElem (_("Show all channels"),
633 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
634 true, param_without_channel
)));
636 for (uint8_t chn
= 0; chn
< 16; chn
++) {
637 if (selected_channels
& (0x0001 << chn
)) {
639 /* for each selected channel, add a menu item for this controller */
641 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
642 chn_items
.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn
+1),
643 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
644 fully_qualified_param
)));
646 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
647 bool visible
= false;
650 if (track
->marked_for_display()) {
655 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&chn_items
.back());
656 _controller_menu_map
[fully_qualified_param
] = cmi
;
657 cmi
->set_active (visible
);
661 /* add the per-channel menu to the list of controllers, with the name of the controller */
662 ctl_items
.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl
, midi_name (ctl
)), *chn_menu
));
663 dynamic_cast<Label
*> (ctl_items
.back().get_child())->set_use_markup (true);
667 /* just one channel - create a single menu item for this ctl+channel combination*/
669 for (uint8_t chn
= 0; chn
< 16; chn
++) {
670 if (selected_channels
& (0x0001 << chn
)) {
672 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
673 ctl_items
.push_back (CheckMenuElem (_route
->describe_parameter (fully_qualified_param
),
674 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
675 fully_qualified_param
)));
677 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
678 bool visible
= false;
681 if (track
->marked_for_display()) {
686 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&ctl_items
.back());
687 _controller_menu_map
[fully_qualified_param
] = cmi
;
688 cmi
->set_active (visible
);
690 /* one channel only */
697 /* add the menu for this block of controllers to the overall controller menu */
699 items
.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i
, i
+15), *ctl_menu
));
704 MidiTimeAxisView::build_note_mode_menu()
706 using namespace Menu_Helpers
;
708 Menu
* mode_menu
= manage (new Menu
);
709 MenuList
& items
= mode_menu
->items();
710 mode_menu
->set_name ("ArdourContextMenu");
712 RadioMenuItem::Group mode_group
;
713 items
.push_back (RadioMenuElem (mode_group
, _("Sustained"),
714 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode
), Sustained
)));
715 _note_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
716 _note_mode_item
->set_active(_note_mode
== Sustained
);
718 items
.push_back (RadioMenuElem (mode_group
, _("Percussive"),
719 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode
), Percussive
)));
720 _percussion_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
721 _percussion_mode_item
->set_active(_note_mode
== Percussive
);
727 MidiTimeAxisView::build_color_mode_menu()
729 using namespace Menu_Helpers
;
731 Menu
* mode_menu
= manage (new Menu
);
732 MenuList
& items
= mode_menu
->items();
733 mode_menu
->set_name ("ArdourContextMenu");
735 RadioMenuItem::Group mode_group
;
736 items
.push_back (RadioMenuElem (mode_group
, _("Meter Colors"),
737 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
), MeterColors
)));
738 _meter_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
739 _meter_color_mode_item
->set_active(_color_mode
== MeterColors
);
741 items
.push_back (RadioMenuElem (mode_group
, _("Channel Colors"),
742 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
), ChannelColors
)));
743 _channel_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
744 _channel_color_mode_item
->set_active(_color_mode
== ChannelColors
);
746 items
.push_back (RadioMenuElem (mode_group
, _("Track Color"),
747 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
), TrackColor
)));
748 _channel_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
749 _channel_color_mode_item
->set_active(_color_mode
== TrackColor
);
755 MidiTimeAxisView::set_note_mode(NoteMode mode
)
757 if (_note_mode
!= mode
|| midi_track()->note_mode() != mode
) {
759 midi_track()->set_note_mode(mode
);
760 xml_node
->add_property ("note-mode", enum_2_string(_note_mode
));
761 _view
->redisplay_track();
766 MidiTimeAxisView::set_color_mode(ColorMode mode
)
768 if (_color_mode
!= mode
) {
769 if (mode
== ChannelColors
) {
770 _channel_selector
.set_channel_colors(CanvasNoteEvent::midi_channel_colors
);
772 _channel_selector
.set_default_channel_color();
776 xml_node
->add_property ("color-mode", enum_2_string(_color_mode
));
777 _view
->redisplay_track();
782 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range
)
784 if (!_ignore_signals
)
785 midi_view()->set_note_range(range
);
790 MidiTimeAxisView::update_range()
792 MidiGhostRegion
* mgr
;
794 for(list
<GhostRegion
*>::iterator i
= ghosts
.begin(); i
!= ghosts
.end(); ++i
) {
795 if ((mgr
= dynamic_cast<MidiGhostRegion
*>(*i
)) != 0) {
802 MidiTimeAxisView::show_all_automation ()
805 const set
<Evoral::Parameter
> params
= midi_track()->midi_playlist()->contained_automation();
807 for (set
<Evoral::Parameter
>::const_iterator i
= params
.begin(); i
!= params
.end(); ++i
) {
808 create_automation_child(*i
, true);
812 RouteTimeAxisView::show_all_automation ();
816 MidiTimeAxisView::show_existing_automation ()
819 const set
<Evoral::Parameter
> params
= midi_track()->midi_playlist()->contained_automation();
821 for (set
<Evoral::Parameter
>::const_iterator i
= params
.begin(); i
!= params
.end(); ++i
) {
822 create_automation_child(*i
, true);
826 RouteTimeAxisView::show_existing_automation ();
829 /** Create an automation track for the given parameter (pitch bend, channel pressure).
832 MidiTimeAxisView::create_automation_child (const Evoral::Parameter
& param
, bool show
)
834 /* These controllers are region "automation", so we do not create
835 * an AutomationList/Line for the track */
837 if (param
.type() == NullAutomation
) {
838 cerr
<< "WARNING: Attempt to create NullAutomation child, ignoring" << endl
;
842 AutomationTracks::iterator existing
= _automation_tracks
.find (param
);
843 if (existing
!= _automation_tracks
.end()) {
847 boost::shared_ptr
<AutomationControl
> c
= _route
->get_control (param
);
851 boost::shared_ptr
<AutomationTimeAxisView
> track(new AutomationTimeAxisView (_session
,
852 _route
, boost::shared_ptr
<ARDOUR::Automatable
>(), c
,
857 _route
->describe_parameter(param
)));
859 add_automation_child (param
, track
, show
);
864 MidiTimeAxisView::route_active_changed ()
866 RouteUI::route_active_changed ();
869 if (_route
->active()) {
870 controls_ebox
.set_name ("MidiTrackControlsBaseUnselected");
871 controls_base_selected_name
= "MidiTrackControlsBaseSelected";
872 controls_base_unselected_name
= "MidiTrackControlsBaseUnselected";
874 controls_ebox
.set_name ("MidiTrackControlsBaseInactiveUnselected");
875 controls_base_selected_name
= "MidiTrackControlsBaseInactiveSelected";
876 controls_base_unselected_name
= "MidiTrackControlsBaseInactiveUnselected";
882 if (_route
->active()) {
883 controls_ebox
.set_name ("BusControlsBaseUnselected");
884 controls_base_selected_name
= "BusControlsBaseSelected";
885 controls_base_unselected_name
= "BusControlsBaseUnselected";
887 controls_ebox
.set_name ("BusControlsBaseInactiveUnselected");
888 controls_base_selected_name
= "BusControlsBaseInactiveSelected";
889 controls_base_unselected_name
= "BusControlsBaseInactiveUnselected";
895 MidiTimeAxisView::start_step_editing ()
897 step_edit_insert_position
= _editor
.get_preferred_edit_position ();
898 _step_edit_triplet_countdown
= 0;
899 _step_edit_within_chord
= 0;
900 _step_edit_chord_duration
= 0.0;
902 step_edit_region
= playlist()->top_region_at (step_edit_insert_position
);
904 if (step_edit_region
) {
905 RegionView
* rv
= view()->find_view (step_edit_region
);
906 step_edit_region_view
= dynamic_cast<MidiRegionView
*> (rv
);
909 step_edit_region
= add_region (step_edit_insert_position
);
910 RegionView
* rv
= view()->find_view (step_edit_region
);
911 step_edit_region_view
= dynamic_cast<MidiRegionView
*>(rv
);
914 assert (step_edit_region
);
915 assert (step_edit_region_view
);
917 if (step_editor
== 0) {
918 step_editor
= new StepEntry (*this);
919 step_editor
->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden
));
920 step_editor
->signal_hide().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hide
));
923 framecnt_t frames_from_start
= _editor
.get_preferred_edit_position() - step_edit_region
->position();
925 assert (frames_from_start
>= 0);
927 step_edit_beat_pos
= step_edit_region_view
->frames_to_beats (frames_from_start
);
929 step_edit_region_view
->show_step_edit_cursor (step_edit_beat_pos
);
930 step_edit_region_view
->set_step_edit_cursor_width (step_editor
->note_length());
932 step_editor
->set_position (WIN_POS_MOUSE
);
933 step_editor
->present ();
937 MidiTimeAxisView::step_editor_hidden (GdkEventAny
*)
944 MidiTimeAxisView::step_editor_hide ()
946 /* everything else will follow the change in the model */
947 midi_track()->set_step_editing (false);
951 MidiTimeAxisView::stop_step_editing ()
954 step_editor
->hide ();
957 if (step_edit_region_view
) {
958 step_edit_region_view
->hide_step_edit_cursor();
963 MidiTimeAxisView::check_step_edit ()
965 MidiRingBuffer
<nframes_t
>& incoming (midi_track()->step_edit_ring_buffer());
967 uint32_t bufsize
= 32;
969 buf
= new uint8_t[bufsize
];
971 while (incoming
.read_space()) {
973 Evoral::EventType type
;
976 incoming
.read_prefix (&time
, &type
, &size
);
978 if (size
> bufsize
) {
981 buf
= new uint8_t[bufsize
];
984 incoming
.read_contents (size
, buf
);
986 if ((buf
[0] & 0xf0) == MIDI_CMD_NOTE_ON
) {
987 step_add_note (buf
[0] & 0xf, buf
[1], buf
[2], 0.0);
993 MidiTimeAxisView::step_add_bank_change (uint8_t channel
, uint8_t bank
)
999 MidiTimeAxisView::step_add_program_change (uint8_t channel
, uint8_t program
)
1005 MidiTimeAxisView::step_add_note (uint8_t channel
, uint8_t pitch
, uint8_t velocity
, Evoral::MusicalTime beat_duration
)
1007 if (step_edit_region
&& step_edit_region_view
) {
1009 if (beat_duration
== 0.0) {
1011 beat_duration
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
1018 MidiStreamView
* msv
= midi_view();
1020 /* make sure its visible on the vertical axis */
1022 if (pitch
< msv
->lowest_note() || pitch
> msv
->highest_note()) {
1023 msv
->update_note_range (pitch
);
1024 msv
->set_note_range (MidiStreamView::ContentsRange
);
1027 /* make sure its visible on the horizontal axis */
1029 nframes64_t fpos
= step_edit_region
->position() +
1030 step_edit_region_view
->beats_to_frames (step_edit_beat_pos
+ beat_duration
);
1032 if (fpos
>= (_editor
.leftmost_position() + _editor
.current_page_frames())) {
1033 _editor
.reset_x_origin (fpos
- (_editor
.current_page_frames()/4));
1036 step_edit_region_view
->step_add_note (channel
, pitch
, velocity
, step_edit_beat_pos
, beat_duration
);
1038 if (_step_edit_triplet_countdown
> 0) {
1039 _step_edit_triplet_countdown
--;
1041 if (_step_edit_triplet_countdown
== 0) {
1042 _step_edit_triplet_countdown
= 3;
1046 if (!_step_edit_within_chord
) {
1047 step_edit_beat_pos
+= beat_duration
;
1048 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
1050 step_edit_beat_pos
+= 1.0/Meter::ticks_per_beat
; // tiny, but no longer overlapping
1051 _step_edit_chord_duration
= max (_step_edit_chord_duration
, beat_duration
);
1059 MidiTimeAxisView::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
1061 if (step_edit_region_view
) {
1062 step_edit_region_view
->set_step_edit_cursor_width (beats
);
1067 MidiTimeAxisView::step_edit_within_triplet() const
1069 return _step_edit_triplet_countdown
> 0;
1073 MidiTimeAxisView::step_edit_within_chord() const
1075 return _step_edit_within_chord
;
1079 MidiTimeAxisView::step_edit_toggle_triplet ()
1081 if (_step_edit_triplet_countdown
== 0) {
1082 _step_edit_within_chord
= false;
1083 _step_edit_triplet_countdown
= 3;
1085 _step_edit_triplet_countdown
= 0;
1090 MidiTimeAxisView::step_edit_toggle_chord ()
1092 if (_step_edit_within_chord
) {
1093 _step_edit_within_chord
= false;
1094 step_edit_beat_pos
+= _step_edit_chord_duration
;
1095 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
1097 _step_edit_triplet_countdown
= 0;
1098 _step_edit_within_chord
= true;
1103 MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats
)
1108 beats
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
1114 step_edit_beat_pos
+= beats
;
1115 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
1120 MidiTimeAxisView::step_edit_beat_sync ()
1122 step_edit_beat_pos
= ceil (step_edit_beat_pos
);
1123 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
1127 MidiTimeAxisView::step_edit_bar_sync ()
1129 if (!_session
|| !step_edit_region_view
|| !step_edit_region
) {
1133 nframes64_t fpos
= step_edit_region
->position() +
1134 step_edit_region_view
->beats_to_frames (step_edit_beat_pos
);
1135 fpos
= _session
->tempo_map().round_to_bar (fpos
, 1);
1136 step_edit_beat_pos
= ceil (step_edit_region_view
->frames_to_beats (fpos
- step_edit_region
->position()));
1137 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
1140 boost::shared_ptr
<Region
>
1141 MidiTimeAxisView::add_region (framepos_t pos
)
1143 Editor
* real_editor
= dynamic_cast<Editor
*> (&_editor
);
1145 real_editor
->begin_reversible_command (_("create region"));
1146 playlist()->clear_history ();
1148 framepos_t start
= pos
;
1149 real_editor
->snap_to (start
, -1);
1150 const Meter
& m
= _session
->tempo_map().meter_at(start
);
1151 const Tempo
& t
= _session
->tempo_map().tempo_at(start
);
1152 double length
= floor (m
.frames_per_bar(t
, _session
->frame_rate()));
1154 boost::shared_ptr
<Source
> src
= _session
->create_midi_source_for_session (view()->trackview().track().get(),
1155 view()->trackview().track()->name());
1158 plist
.add (ARDOUR::Properties::start
, 0);
1159 plist
.add (ARDOUR::Properties::length
, length
);
1160 plist
.add (ARDOUR::Properties::name
, PBD::basename_nosuffix(src
->name()));
1162 boost::shared_ptr
<Region
> region
= (RegionFactory::create (src
, plist
));
1164 playlist()->add_region (region
, start
);
1165 _session
->add_command (new StatefulDiffCommand (playlist()));
1167 real_editor
->commit_reversible_command();
1173 MidiTimeAxisView::add_note_selection (uint8_t note
)
1175 if (!_editor
.internal_editing()) {
1179 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
1181 if (_view
->num_selected_regionviews() == 0) {
1182 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
1184 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
1189 MidiTimeAxisView::extend_note_selection (uint8_t note
)
1191 if (!_editor
.internal_editing()) {
1195 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
1197 if (_view
->num_selected_regionviews() == 0) {
1198 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
1200 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
1205 MidiTimeAxisView::toggle_note_selection (uint8_t note
)
1207 if (!_editor
.internal_editing()) {
1211 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
1213 if (_view
->num_selected_regionviews() == 0) {
1214 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
1216 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
1221 MidiTimeAxisView::add_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1223 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, false, false);
1227 MidiTimeAxisView::extend_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1229 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, true, true);
1233 MidiTimeAxisView::toggle_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1235 dynamic_cast<MidiRegionView
*>(rv
)->toggle_matching_notes (note
, chn_mask
);
1239 MidiTimeAxisView::set_channel_mode (ChannelMode
, uint16_t)
1241 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1245 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
1246 bool changed
= false;
1250 for (uint32_t ctl
= 0; ctl
< 127; ++ctl
) {
1252 for (uint32_t chn
= 0; chn
< 16; ++chn
) {
1253 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
1254 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
1260 if ((selected_channels
& (0x0001 << chn
)) == 0) {
1261 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1262 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1264 changed
= track
->set_visibility (false) || changed
;
1266 changed
= track
->set_visibility (true) || changed
;
1273 /* TODO: Bender, PgmChange, Pressure */
1275 /* invalidate the controller menu, so that we rebuilt it next time */
1276 _controller_menu_map
.clear ();
1277 delete controller_menu
;
1278 controller_menu
= 0;
1281 _route
->gui_changed ("track_height", this);
1286 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
1288 Gtk::CheckMenuItem
* m
= RouteTimeAxisView::automation_child_menu_item (param
);
1293 ParameterMenuMap::iterator i
= _controller_menu_map
.find (param
);
1294 if (i
!= _controller_menu_map
.end()) {
1298 i
= _channel_command_menu_map
.find (param
);
1299 if (i
!= _channel_command_menu_map
.end()) {