X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_time_axis.cc;h=b1068e7e3f5ea2353c40b1c27d462c9907da0502;hb=965a9740835a77cc2bf9e6cea237e56ae41150d8;hp=464ed630f5453741153e19ceaed47fbe799b1b8c;hpb=aac46a38fbd3b979dd1d352a4d1454ac8a70793f;p=ardour.git diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 464ed630f5..b1068e7e3f 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -52,6 +52,7 @@ #include "ardour/session_playlist.h" #include "ardour/tempo.h" #include "ardour/utils.h" +#include "ardour/operations.h" #include "midi++/names.h" @@ -80,8 +81,8 @@ #include "region_view.h" #include "rgb_macros.h" #include "selection.h" +#include "step_editor.h" #include "simplerect.h" -#include "step_entry.h" #include "utils.h" #include "ardour/midi_track.h" @@ -116,7 +117,7 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, , _midi_thru_item (0) , default_channel_menu (0) , controller_menu (0) - , step_editor (0) + , _step_editor (0) { subplugin_menu.set_name ("ArdourContextMenu"); @@ -127,8 +128,6 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, mute_button->set_active (false); solo_button->set_active (false); - step_edit_insert_position = 0; - if (is_midi_track()) { controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected"); _note_mode = midi_track()->note_mode(); @@ -166,13 +165,12 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, /* ask for notifications of any new RegionViews */ _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added)); - _view->attach (); - - midi_track()->PlaylistChanged.connect (*this, invalidator (*this), - boost::bind (&MidiTimeAxisView::playlist_changed, this), - gui_context()); - playlist_changed (); - + + if (!_editor.have_idled()) { + /* first idle will do what we need */ + } else { + first_idle (); + } } HBox* midi_controls_hbox = manage(new HBox()); @@ -226,6 +224,14 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, } } +void +MidiTimeAxisView::first_idle () +{ + if (is_track ()) { + _view->attach (); + } +} + MidiTimeAxisView::~MidiTimeAxisView () { delete _piano_roll_header; @@ -235,34 +241,34 @@ MidiTimeAxisView::~MidiTimeAxisView () _range_scroomer = 0; delete controller_menu; + delete _step_editor; } void -MidiTimeAxisView::playlist_changed () +MidiTimeAxisView::enter_internal_edit_mode () { - step_edit_region_connection.disconnect (); - midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this), - ui_bind (&MidiTimeAxisView::region_removed, this, _1), - gui_context()); + if (midi_view()) { + midi_view()->enter_internal_edit_mode (); + } } void -MidiTimeAxisView::region_removed (boost::weak_ptr wr) +MidiTimeAxisView::leave_internal_edit_mode () { - boost::shared_ptr r (wr.lock()); - - if (!r) { - return; + if (midi_view()) { + midi_view()->leave_internal_edit_mode (); } +} - if (step_edit_region == r) { - step_edit_region.reset(); - // force a recompute of the insert position - step_edit_beat_pos = -1.0; - } +void +MidiTimeAxisView::check_step_edit () +{ + ensure_step_editor (); + _step_editor->check_step_edit (); } -void MidiTimeAxisView::model_changed() +void +MidiTimeAxisView::model_changed() { std::list device_modes = MIDI::Name::MidiPatchManager::instance() .custom_device_mode_names_by_model(_model_selector.get_active_text()); @@ -359,6 +365,8 @@ MidiTimeAxisView::append_extra_display_menu_items () items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru))); _midi_thru_item = dynamic_cast(&items.back()); + + items.push_back (SeparatorElem ()); } Gtk::Menu* @@ -432,13 +440,12 @@ MidiTimeAxisView::build_automation_action_menu () automation_items.push_back (SeparatorElem()); - /* these 3 MIDI "command" types are semantically more like automation than note data, + /* these 2 MIDI "command" types are semantically more like automation than note data, but they are not MIDI controllers. We give them special status in this menu, since they will not show up in the controller list and anyone who actually knows something about MIDI (!) would not expect to find them there. */ - add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0); add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0); add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0); @@ -453,6 +460,7 @@ MidiTimeAxisView::build_automation_action_menu () automation_items.push_back (MenuElem (_("Controllers"), *controller_menu)); } else { automation_items.push_back (MenuElem (string_compose ("%1", _("No MIDI Channels selected")))); + dynamic_cast (automation_items.back().get_child())->set_use_markup (true); } } @@ -604,7 +612,8 @@ MidiTimeAxisView::build_controller_menu () } } - /* loop over all 127 MIDI controllers, in groups of 16 */ + /* loop over all 127 MIDI controllers, in groups of 16; except don't offer + bank select controllers, as they are handled by the `patch' code */ for (int i = 0; i < 127; i += 16) { @@ -616,6 +625,10 @@ MidiTimeAxisView::build_controller_menu () for (int ctl = i; ctl < i+16; ++ctl) { + if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) { + continue; + } + if (chn_cnt > 1) { /* multiple channels - create a submenu, with 1 item per channel */ @@ -670,9 +683,14 @@ MidiTimeAxisView::build_controller_menu () if (selected_channels & (0x0001 << chn)) { Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl); - ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), - fully_qualified_param))); + ctl_items.push_back ( + CheckMenuElem ( + string_compose ("%1: %2 [%3]", ctl, midi_name (ctl), int (chn)), + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), + fully_qualified_param) + ) + ); + dynamic_cast (ctl_items.back().get_child())->set_use_markup (true); boost::shared_ptr track = automation_child (fully_qualified_param); bool visible = false; @@ -831,32 +849,44 @@ MidiTimeAxisView::show_existing_automation () void MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show) { - /* These controllers are region "automation", so we do not create - * an AutomationList/Line for the track */ - if (param.type() == NullAutomation) { cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl; return; } - + AutomationTracks::iterator existing = _automation_tracks.find (param); if (existing != _automation_tracks.end()) { return; } - - boost::shared_ptr c = _route->get_control (param); - - assert(c); - - boost::shared_ptr track(new AutomationTimeAxisView (_session, - _route, boost::shared_ptr(), c, - _editor, - *this, - true, - parent_canvas, - _route->describe_parameter(param))); - - add_automation_child (param, track, show); + + if (param.type() == GainAutomation) { + create_gain_automation_child (param, show); + } else { + + /* These controllers are region "automation", so we do not create + * an AutomationList/Line for the track */ + + boost::shared_ptr track ( + new AutomationTimeAxisView ( + _session, + _route, + boost::shared_ptr (), + boost::shared_ptr (), + param, + _editor, + *this, + true, + parent_canvas, + _route->describe_parameter(param) + ) + ); + + if (_view) { + _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost)); + } + + add_automation_child (param, track, show); + } } @@ -891,303 +921,7 @@ MidiTimeAxisView::route_active_changed () } } -void -MidiTimeAxisView::start_step_editing () -{ - step_edit_insert_position = _editor.get_preferred_edit_position (); - _step_edit_triplet_countdown = 0; - _step_edit_within_chord = 0; - _step_edit_chord_duration = 0.0; - - boost::shared_ptr r = playlist()->top_region_at (step_edit_insert_position); - - if (r) { - step_edit_region = boost::dynamic_pointer_cast(r); - } - - if (step_edit_region) { - RegionView* rv = view()->find_view (step_edit_region); - step_edit_region_view = dynamic_cast (rv); - - } else { - step_edit_region = add_region (step_edit_insert_position); - RegionView* rv = view()->find_view (step_edit_region); - step_edit_region_view = dynamic_cast(rv); - } - - assert (step_edit_region); - assert (step_edit_region_view); - - if (step_editor == 0) { - step_editor = new StepEntry (*this); - step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden)); - step_editor->signal_hide().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hide)); - } - - framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position(); - - if (frames_from_start < 0) { - /* this can happen with snap enabled, and the edit point == Playhead. we snap the - position of the new region, and it can end up after the edit point. - */ - frames_from_start = 0; - } - - step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start); - - step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos); - step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length()); - - step_editor->set_position (WIN_POS_MOUSE); - step_editor->present (); -} - -bool -MidiTimeAxisView::step_editor_hidden (GdkEventAny*) -{ - step_editor_hide (); - return true; -} - -void -MidiTimeAxisView::step_editor_hide () -{ - /* everything else will follow the change in the model */ - midi_track()->set_step_editing (false); -} - -void -MidiTimeAxisView::stop_step_editing () -{ - if (step_editor) { - step_editor->hide (); - } - - if (step_edit_region_view) { - step_edit_region_view->hide_step_edit_cursor(); - } - - step_edit_region.reset (); -} - -void -MidiTimeAxisView::check_step_edit () -{ - MidiRingBuffer& incoming (midi_track()->step_edit_ring_buffer()); - uint8_t* buf; - uint32_t bufsize = 32; - - buf = new uint8_t[bufsize]; - - while (incoming.read_space()) { - nframes_t time; - Evoral::EventType type; - uint32_t size; - - incoming.read_prefix (&time, &type, &size); - - if (size > bufsize) { - delete [] buf; - bufsize = size; - buf = new uint8_t[bufsize]; - } - - incoming.read_contents (size, buf); - - if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) { - step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0); - } - } -} - -int -MidiTimeAxisView::step_add_bank_change (uint8_t channel, uint8_t bank) -{ - return 0; -} - -int -MidiTimeAxisView::step_add_program_change (uint8_t channel, uint8_t program) -{ - return 0; -} - -void -MidiTimeAxisView::step_edit_sustain (Evoral::MusicalTime beats) -{ - if (step_edit_region_view) { - step_edit_region_view->step_sustain (beats); - } -} - -int -MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration) -{ - if (step_edit_region && step_edit_region_view) { - - if (beat_duration == 0.0) { - bool success; - beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position); - - if (!success) { - return -1; - } - } - - MidiStreamView* msv = midi_view(); - - /* make sure its visible on the vertical axis */ - - if (pitch < msv->lowest_note() || pitch > msv->highest_note()) { - msv->update_note_range (pitch); - msv->set_note_range (MidiStreamView::ContentsRange); - } - - /* make sure its visible on the horizontal axis */ - - nframes64_t fpos = step_edit_region->position() + - step_edit_region_view->beats_to_frames (step_edit_beat_pos + beat_duration); - - if (fpos >= (_editor.leftmost_position() + _editor.current_page_frames())) { - _editor.reset_x_origin (fpos - (_editor.current_page_frames()/4)); - } - - step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration); - - if (_step_edit_triplet_countdown > 0) { - _step_edit_triplet_countdown--; - - if (_step_edit_triplet_countdown == 0) { - _step_edit_triplet_countdown = 3; - } - } - - if (!_step_edit_within_chord) { - step_edit_beat_pos += beat_duration; - step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); - } else { - step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping - _step_edit_chord_duration = max (_step_edit_chord_duration, beat_duration); - } - } - - return 0; -} - -void -MidiTimeAxisView::set_step_edit_cursor_width (Evoral::MusicalTime beats) -{ - if (step_edit_region_view) { - step_edit_region_view->set_step_edit_cursor_width (beats); - } -} - -bool -MidiTimeAxisView::step_edit_within_triplet() const -{ - return _step_edit_triplet_countdown > 0; -} -bool -MidiTimeAxisView::step_edit_within_chord() const -{ - return _step_edit_within_chord; -} - -void -MidiTimeAxisView::step_edit_toggle_triplet () -{ - if (_step_edit_triplet_countdown == 0) { - _step_edit_within_chord = false; - _step_edit_triplet_countdown = 3; - } else { - _step_edit_triplet_countdown = 0; - } -} - -void -MidiTimeAxisView::step_edit_toggle_chord () -{ - if (_step_edit_within_chord) { - _step_edit_within_chord = false; - step_edit_beat_pos += _step_edit_chord_duration; - step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); - } else { - _step_edit_triplet_countdown = 0; - _step_edit_within_chord = true; - } - - cerr << "Within chord now: " << _step_edit_within_chord << endl; -} - -void -MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats) -{ - bool success; - - if (beats == 0.0) { - beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position); - } else { - success = true; - } - - if (success) { - step_edit_beat_pos += beats; - step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); - } -} - -void -MidiTimeAxisView::step_edit_beat_sync () -{ - step_edit_beat_pos = ceil (step_edit_beat_pos); - step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); -} - -void -MidiTimeAxisView::step_edit_bar_sync () -{ - if (!_session || !step_edit_region_view || !step_edit_region) { - return; - } - - framepos_t fpos = step_edit_region->position() + - step_edit_region_view->beats_to_frames (step_edit_beat_pos); - fpos = _session->tempo_map().round_to_bar (fpos, 1); - step_edit_beat_pos = ceil (step_edit_region_view->frames_to_beats (fpos - step_edit_region->position())); - step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); -} - -boost::shared_ptr -MidiTimeAxisView::add_region (framepos_t pos) -{ - Editor* real_editor = dynamic_cast (&_editor); - - real_editor->begin_reversible_command (_("create region")); - playlist()->clear_history (); - - real_editor->snap_to (pos, 0); - const Meter& m = _session->tempo_map().meter_at(pos); - const Tempo& t = _session->tempo_map().tempo_at(pos); - double length = floor (m.frames_per_bar(t, _session->frame_rate())); - - boost::shared_ptr src = _session->create_midi_source_for_session (view()->trackview().track().get(), - view()->trackview().track()->name()); - PropertyList plist; - - plist.add (ARDOUR::Properties::start, 0); - plist.add (ARDOUR::Properties::length, length); - plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name())); - - boost::shared_ptr region = (RegionFactory::create (src, plist)); - - playlist()->add_region (region, pos); - _session->add_command (new StatefulDiffCommand (playlist())); - - real_editor->commit_reversible_command(); - - return boost::dynamic_pointer_cast(region); -} void MidiTimeAxisView::add_note_selection (uint8_t note) @@ -1290,9 +1024,9 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t) no_redraw = false; - /* TODO: Bender, PgmChange, Pressure */ + /* TODO: Bender, Pressure */ - /* invalidate the controller menu, so that we rebuilt it next time */ + /* invalidate the controller menu, so that we rebuild it next time */ _controller_menu_map.clear (); delete controller_menu; controller_menu = 0; @@ -1322,3 +1056,56 @@ MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param) return 0; } + +boost::shared_ptr +MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit) +{ + Editor* real_editor = dynamic_cast (&_editor); + + real_editor->begin_reversible_command (Operations::create_region); + playlist()->clear_changes (); + + real_editor->snap_to (pos, 0); + + boost::shared_ptr src = _session->create_midi_source_for_session (view()->trackview().track().get(), + view()->trackview().track()->name()); + PropertyList plist; + + plist.add (ARDOUR::Properties::start, 0); + plist.add (ARDOUR::Properties::length, length); + plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name())); + + boost::shared_ptr region = (RegionFactory::create (src, plist)); + + playlist()->add_region (region, pos); + _session->add_command (new StatefulDiffCommand (playlist())); + + if (commit) { + real_editor->commit_reversible_command (); + } + + return boost::dynamic_pointer_cast(region); +} + +void +MidiTimeAxisView::ensure_step_editor () +{ + if (!_step_editor) { + _step_editor = new StepEditor (_editor, midi_track(), *this); + } +} + +void +MidiTimeAxisView::start_step_editing () +{ + ensure_step_editor (); + _step_editor->start_step_editing (); + +} +void +MidiTimeAxisView::stop_step_editing () +{ + if (_step_editor) { + _step_editor->stop_step_editing (); + } +}