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_beat_pos
= -1.0;
899 _step_edit_triplet_countdown
= 0;
900 _step_edit_within_chord
= 0;
901 _step_edit_chord_duration
= 0.0;
903 step_edit_region
= playlist()->top_region_at (step_edit_insert_position
);
905 if (step_edit_region
) {
906 RegionView
* rv
= view()->find_view (step_edit_region
);
907 step_edit_region_view
= dynamic_cast<MidiRegionView
*> (rv
);
909 step_edit_region_view
= 0;
913 if (step_editor
== 0) {
914 step_editor
= new StepEntry (*this);
915 step_editor
->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden
));
918 step_editor
->set_position (WIN_POS_MOUSE
);
919 step_editor
->present ();
923 MidiTimeAxisView::step_editor_hidden (GdkEventAny
*)
925 /* everything else will follow the change in the model */
926 midi_track()->set_step_editing (false);
931 MidiTimeAxisView::stop_step_editing ()
934 step_editor
->hide ();
939 MidiTimeAxisView::check_step_edit ()
941 MidiRingBuffer
<nframes_t
>& incoming (midi_track()->step_edit_ring_buffer());
943 uint32_t bufsize
= 32;
945 buf
= new uint8_t[bufsize
];
947 while (incoming
.read_space()) {
949 Evoral::EventType type
;
952 incoming
.read_prefix (&time
, &type
, &size
);
954 if (size
> bufsize
) {
957 buf
= new uint8_t[bufsize
];
960 incoming
.read_contents (size
, buf
);
962 if ((buf
[0] & 0xf0) == MIDI_CMD_NOTE_ON
) {
963 step_add_note (buf
[0] & 0xf, buf
[1], buf
[2], 0.0);
969 MidiTimeAxisView::step_add_note (uint8_t channel
, uint8_t pitch
, uint8_t velocity
, Evoral::MusicalTime beat_duration
)
972 if (step_edit_region
== 0) {
974 step_edit_region
= add_region (step_edit_insert_position
);
975 RegionView
* rv
= view()->find_view (step_edit_region
);
976 step_edit_region_view
= dynamic_cast<MidiRegionView
*>(rv
);
979 if (step_edit_region
&& step_edit_region_view
) {
980 if (step_edit_beat_pos
< 0.0) {
981 framecnt_t frames_from_start
= _editor
.get_preferred_edit_position() - step_edit_region
->position();
982 if (frames_from_start
< 0) {
985 step_edit_beat_pos
= step_edit_region_view
->frames_to_beats (frames_from_start
);
988 if (beat_duration
== 0.0) {
990 beat_duration
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
997 MidiStreamView
* msv
= midi_view();
999 /* make sure its visible on the vertical axis */
1001 if (pitch
< msv
->lowest_note() || pitch
> msv
->highest_note()) {
1002 msv
->update_note_range (pitch
);
1003 msv
->set_note_range (MidiStreamView::ContentsRange
);
1006 /* make sure its visible on the horizontal axis */
1008 nframes64_t fpos
= step_edit_region
->position() +
1009 step_edit_region_view
->beats_to_frames (step_edit_beat_pos
+ beat_duration
);
1011 if (fpos
>= (_editor
.leftmost_position() + _editor
.current_page_frames())) {
1012 _editor
.reset_x_origin (fpos
- (_editor
.current_page_frames()/4));
1015 step_edit_region_view
->step_add_note (channel
, pitch
, velocity
, step_edit_beat_pos
, beat_duration
);
1017 if (_step_edit_triplet_countdown
> 0) {
1018 _step_edit_triplet_countdown
--;
1020 if (_step_edit_triplet_countdown
== 0) {
1021 _step_edit_triplet_countdown
= 3;
1025 if (!_step_edit_within_chord
) {
1026 step_edit_beat_pos
+= beat_duration
;
1028 step_edit_beat_pos
+= 1.0/Meter::ticks_per_beat
; // tiny, but no longer overlapping
1029 _step_edit_chord_duration
= beat_duration
;
1037 MidiTimeAxisView::step_edit_within_triplet() const
1039 return _step_edit_triplet_countdown
> 0;
1043 MidiTimeAxisView::step_edit_within_chord() const
1045 return _step_edit_within_chord
;
1049 MidiTimeAxisView::step_edit_toggle_triplet ()
1051 if (_step_edit_triplet_countdown
== 0) {
1052 _step_edit_within_chord
= false;
1053 _step_edit_triplet_countdown
= 3;
1055 _step_edit_triplet_countdown
= 0;
1060 MidiTimeAxisView::step_edit_toggle_chord ()
1062 if (_step_edit_within_chord
) {
1063 _step_edit_within_chord
= false;
1064 step_edit_beat_pos
+= _step_edit_chord_duration
;
1066 _step_edit_triplet_countdown
= 0;
1067 _step_edit_within_chord
= true;
1072 MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats
)
1077 beats
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
1083 step_edit_beat_pos
+= beats
;
1087 boost::shared_ptr
<Region
>
1088 MidiTimeAxisView::add_region (framepos_t pos
)
1090 Editor
* real_editor
= dynamic_cast<Editor
*> (&_editor
);
1092 real_editor
->begin_reversible_command (_("create region"));
1093 playlist()->clear_history ();
1095 framepos_t start
= pos
;
1096 real_editor
->snap_to (start
, -1);
1097 const Meter
& m
= _session
->tempo_map().meter_at(start
);
1098 const Tempo
& t
= _session
->tempo_map().tempo_at(start
);
1099 double length
= floor (m
.frames_per_bar(t
, _session
->frame_rate()));
1101 boost::shared_ptr
<Source
> src
= _session
->create_midi_source_for_session (view()->trackview().track().get(),
1102 view()->trackview().track()->name());
1105 plist
.add (ARDOUR::Properties::start
, 0);
1106 plist
.add (ARDOUR::Properties::length
, length
);
1107 plist
.add (ARDOUR::Properties::name
, PBD::basename_nosuffix(src
->name()));
1109 boost::shared_ptr
<Region
> region
= (RegionFactory::create (src
, plist
));
1111 playlist()->add_region (region
, start
);
1112 _session
->add_command (new StatefulDiffCommand (playlist()));
1114 real_editor
->commit_reversible_command();
1120 MidiTimeAxisView::add_note_selection (uint8_t note
)
1122 if (!_editor
.internal_editing()) {
1126 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
1128 if (_view
->num_selected_regionviews() == 0) {
1129 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
1131 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
1136 MidiTimeAxisView::extend_note_selection (uint8_t note
)
1138 if (!_editor
.internal_editing()) {
1142 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
1144 if (_view
->num_selected_regionviews() == 0) {
1145 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
1147 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
1152 MidiTimeAxisView::toggle_note_selection (uint8_t note
)
1154 if (!_editor
.internal_editing()) {
1158 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
1160 if (_view
->num_selected_regionviews() == 0) {
1161 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
1163 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
1168 MidiTimeAxisView::add_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1170 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, false, false);
1174 MidiTimeAxisView::extend_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1176 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, true, true);
1180 MidiTimeAxisView::toggle_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1182 dynamic_cast<MidiRegionView
*>(rv
)->toggle_matching_notes (note
, chn_mask
);
1186 MidiTimeAxisView::set_channel_mode (ChannelMode
, uint16_t)
1188 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1192 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
1193 bool changed
= false;
1197 for (uint32_t ctl
= 0; ctl
< 127; ++ctl
) {
1199 for (uint32_t chn
= 0; chn
< 16; ++chn
) {
1200 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
1201 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
1207 if ((selected_channels
& (0x0001 << chn
)) == 0) {
1208 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1209 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1211 changed
= track
->set_visibility (false) || changed
;
1213 changed
= track
->set_visibility (true) || changed
;
1220 /* TODO: Bender, PgmChange, Pressure */
1222 /* invalidate the controller menu, so that we rebuilt it next time */
1223 _controller_menu_map
.clear ();
1224 delete controller_menu
;
1225 controller_menu
= 0;
1228 _route
->gui_changed ("track_height", this);
1233 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
1235 Gtk::CheckMenuItem
* m
= RouteTimeAxisView::automation_child_menu_item (param
);
1240 ParameterMenuMap::iterator i
= _controller_menu_map
.find (param
);
1241 if (i
!= _controller_menu_map
.end()) {
1245 i
= _channel_command_menu_map
.find (param
);
1246 if (i
!= _channel_command_menu_map
.end()) {