diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2010-08-15 16:39:51 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2010-08-15 16:39:51 +0000 |
commit | faca3e5f5d666fc543b23f6ab7b93a14f6c8ff7f (patch) | |
tree | 35d65ba93abd081308f659e590603d0a7d5645ab | |
parent | 66ea8edc6e317eba925558df7d8369ce5d260752 (diff) |
split out the logic behind step editing from MidiTimeAxisView as much as possible
git-svn-id: svn://localhost/ardour2/branches/3.0@7633 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | gtk2_ardour/midi_time_axis.cc | 440 | ||||
-rw-r--r-- | gtk2_ardour/midi_time_axis.h | 37 | ||||
-rw-r--r-- | gtk2_ardour/step_editor.cc | 395 | ||||
-rw-r--r-- | gtk2_ardour/step_editor.h | 73 | ||||
-rw-r--r-- | gtk2_ardour/step_entry.cc | 42 | ||||
-rw-r--r-- | gtk2_ardour/step_entry.h | 6 | ||||
-rw-r--r-- | gtk2_ardour/wscript | 1 |
7 files changed, 555 insertions, 439 deletions
diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 3eb7fff692..62beda8774 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -80,8 +80,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 +116,6 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, , _midi_thru_item (0) , default_channel_menu (0) , controller_menu (0) - , step_editor (0) { subplugin_menu.set_name ("ArdourContextMenu"); @@ -127,8 +126,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(); @@ -168,11 +165,6 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, _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 (); - } HBox* midi_controls_hbox = manage(new HBox()); @@ -235,35 +227,17 @@ MidiTimeAxisView::~MidiTimeAxisView () _range_scroomer = 0; delete controller_menu; + delete _step_editor; } void -MidiTimeAxisView::playlist_changed () +MidiTimeAxisView::check_step_edit () { - 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()); + _step_editor->check_step_edit (); } -void -MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr) -{ - boost::shared_ptr<Region> r (wr.lock()); - - if (!r) { - return; - } - - if (step_edit_region == r) { - step_edit_region.reset(); - step_edit_region_view = 0; - // force a recompute of the insert position - step_edit_beat_pos = -1.0; - } -} - -void MidiTimeAxisView::model_changed() +void +MidiTimeAxisView::model_changed() { std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance() .custom_device_mode_names_by_model(_model_selector.get_active_text()); @@ -892,360 +866,7 @@ MidiTimeAxisView::route_active_changed () } } -void -MidiTimeAxisView::start_step_editing () -{ - _step_edit_triplet_countdown = 0; - _step_edit_within_chord = 0; - _step_edit_chord_duration = 0.0; - step_edit_region.reset (); - step_edit_region_view = 0; - - resync_step_edit_position (); - prepare_step_edit_region (); - reset_step_edit_beat_pos (); - - 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)); - } - - 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 (); -} - -void -MidiTimeAxisView::resync_step_edit_position () -{ - step_edit_insert_position = _editor.get_preferred_edit_position (); -} - -void -MidiTimeAxisView::resync_step_edit_to_edit_point () -{ - resync_step_edit_position (); - if (step_edit_region) { - reset_step_edit_beat_pos (); - } -} - -void -MidiTimeAxisView::prepare_step_edit_region () -{ - boost::shared_ptr<Region> r = playlist()->top_region_at (step_edit_insert_position); - - if (r) { - step_edit_region = boost::dynamic_pointer_cast<MidiRegion>(r); - } - - if (step_edit_region) { - RegionView* rv = view()->find_view (step_edit_region); - step_edit_region_view = dynamic_cast<MidiRegionView*> (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<MidiRegionView*>(rv); - } -} - - -void -MidiTimeAxisView::reset_step_edit_beat_pos () -{ - assert (step_edit_region); - assert (step_edit_region_view); - - 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->move_step_edit_cursor (step_edit_beat_pos); -} - -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<nframes_t>& 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); - } -} - -void -MidiTimeAxisView::move_step_edit_beat_pos (Evoral::MusicalTime beats) -{ - if (beats > 0.0) { - step_edit_beat_pos = min (step_edit_beat_pos + beats, - step_edit_region_view->frames_to_beats (step_edit_region->length())); - } else if (beats < 0.0) { - if (beats < step_edit_beat_pos) { - step_edit_beat_pos += beats; // its negative, remember - } else { - step_edit_beat_pos = 0; - } - } - step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); -} - -int -MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration) -{ - /* do these things in case undo removed the step edit region - */ - if (!step_edit_region) { - resync_step_edit_position (); - prepare_step_edit_region (); - reset_step_edit_beat_pos (); - 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()); - } - - assert (step_edit_region); - assert (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; - } -} - -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<MidiRegion> -MidiTimeAxisView::add_region (framepos_t pos) -{ - Editor* real_editor = dynamic_cast<Editor*> (&_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<Source> 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> 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<MidiRegion>(region); -} void MidiTimeAxisView::add_note_selection (uint8_t note) @@ -1380,3 +1001,52 @@ MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param) return 0; } + +boost::shared_ptr<MidiRegion> +MidiTimeAxisView::add_region (framepos_t pos) +{ + Editor* real_editor = dynamic_cast<Editor*> (&_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<Source> 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> 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<MidiRegion>(region); +} + +void +MidiTimeAxisView::start_step_editing () +{ + if (!_step_editor) { + _step_editor = new StepEditor (_editor, midi_track(), *this); + } + + _step_editor->start_step_editing (); + +} +void +MidiTimeAxisView::stop_step_editing () +{ + if (_step_editor) { + _step_editor->stop_step_editing (); + } +} diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 63372b9218..73c48f2613 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -54,6 +54,7 @@ class MidiStreamView; class MidiScroomer; class PianoRollHeader; class StepEntry; +class StepEditor; class MidiTimeAxisView : public RouteTimeAxisView { @@ -87,28 +88,14 @@ class MidiTimeAxisView : public RouteTimeAxisView return _midi_patch_settings_changed; } - void check_step_edit (); - void step_edit_rest (Evoral::MusicalTime beats); - void step_edit_beat_sync (); - void step_edit_bar_sync (); - int step_add_bank_change (uint8_t channel, uint8_t bank); - int step_add_program_change (uint8_t channel, uint8_t program); - int step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, - Evoral::MusicalTime beat_duration); - void step_edit_sustain (Evoral::MusicalTime beats); - bool step_edit_within_triplet () const; - void step_edit_toggle_triplet (); - bool step_edit_within_chord () const; - void step_edit_toggle_chord (); - void reset_step_edit_beat_pos (); - void resync_step_edit_to_edit_point (); - void move_step_edit_beat_pos (Evoral::MusicalTime beats); - void set_step_edit_cursor_width (Evoral::MusicalTime beats); const MidiMultipleChannelSelector& channel_selector() { return _channel_selector; } Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter); + StepEditor* step_editor() { return _step_editor; } + void check_step_edit (); + protected: void start_step_editing (); void stop_step_editing (); @@ -149,16 +136,6 @@ class MidiTimeAxisView : public RouteTimeAxisView Gtk::CheckMenuItem* _midi_thru_item; Gtk::Menu* default_channel_menu; - nframes64_t step_edit_insert_position; - Evoral::MusicalTime step_edit_beat_pos; - boost::shared_ptr<ARDOUR::MidiRegion> step_edit_region; - MidiRegionView* step_edit_region_view; - uint8_t _step_edit_triplet_countdown; - bool _step_edit_within_chord; - Evoral::MusicalTime _step_edit_chord_duration; - void region_removed (boost::weak_ptr<ARDOUR::Region>); - void playlist_changed (); - PBD::ScopedConnection step_edit_region_connection; Gtk::Menu* build_def_channel_menu(); void set_default_channel (int); @@ -184,11 +161,7 @@ class MidiTimeAxisView : public RouteTimeAxisView /** parameter -> menu item map for the controller menu */ ParameterMenuMap _controller_menu_map; - StepEntry* step_editor; - bool step_editor_hidden (GdkEventAny*); - void step_editor_hide (); - void resync_step_edit_position (); - void prepare_step_edit_region (); + StepEditor* _step_editor; }; #endif /* __ardour_midi_time_axis_h__ */ diff --git a/gtk2_ardour/step_editor.cc b/gtk2_ardour/step_editor.cc new file mode 100644 index 0000000000..822af4c559 --- /dev/null +++ b/gtk2_ardour/step_editor.cc @@ -0,0 +1,395 @@ +#include "ardour/midi_track.h" +#include "ardour/midi_region.h" +#include "ardour/tempo.h" +#include "ardour/types.h" + +#include "gui_thread.h" +#include "midi_region_view.h" +#include "public_editor.h" +#include "step_editor.h" +#include "step_entry.h" + +using namespace ARDOUR; +using namespace Gtk; +using namespace std; + +StepEditor::StepEditor (PublicEditor& e, boost::shared_ptr<MidiTrack> t, MidiTimeAxisView& mtv) + : _editor (e) + , _track (t) + , step_editor (0) + , _mtv (mtv) +{ + step_edit_insert_position = 0; + _step_edit_triplet_countdown = 0; + _step_edit_within_chord = 0; + _step_edit_chord_duration = 0.0; + step_edit_region_view = 0; + + _track->PlaylistChanged.connect (*this, invalidator (*this), + boost::bind (&StepEditor::playlist_changed, this), + gui_context()); + playlist_changed (); +} + +StepEditor::~StepEditor() +{ + delete step_editor; +} + +void +StepEditor::start_step_editing () +{ + _step_edit_triplet_countdown = 0; + _step_edit_within_chord = 0; + _step_edit_chord_duration = 0.0; + step_edit_region.reset (); + step_edit_region_view = 0; + + resync_step_edit_position (); + prepare_step_edit_region (); + reset_step_edit_beat_pos (); + + 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, &StepEditor::step_editor_hidden)); + step_editor->signal_hide().connect (sigc::mem_fun (*this, &StepEditor::step_editor_hide)); + } + + 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 (); +} + +void +StepEditor::resync_step_edit_position () +{ + step_edit_insert_position = _editor.get_preferred_edit_position (); +} + +void +StepEditor::resync_step_edit_to_edit_point () +{ + resync_step_edit_position (); + if (step_edit_region) { + reset_step_edit_beat_pos (); + } +} + +void +StepEditor::prepare_step_edit_region () +{ + boost::shared_ptr<Region> r = _track->playlist()->top_region_at (step_edit_insert_position); + + if (r) { + step_edit_region = boost::dynamic_pointer_cast<MidiRegion>(r); + } + + if (step_edit_region) { + RegionView* rv = _mtv.midi_view()->find_view (step_edit_region); + step_edit_region_view = dynamic_cast<MidiRegionView*> (rv); + + } else { + step_edit_region = _mtv.add_region (step_edit_insert_position); + RegionView* rv = _mtv.midi_view()->find_view (step_edit_region); + step_edit_region_view = dynamic_cast<MidiRegionView*>(rv); + } +} + + +void +StepEditor::reset_step_edit_beat_pos () +{ + assert (step_edit_region); + assert (step_edit_region_view); + + 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->move_step_edit_cursor (step_edit_beat_pos); +} + +bool +StepEditor::step_editor_hidden (GdkEventAny*) +{ + step_editor_hide (); + return true; +} + +void +StepEditor::step_editor_hide () +{ + /* everything else will follow the change in the model */ + _track->set_step_editing (false); +} + +void +StepEditor::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 +StepEditor::check_step_edit () +{ + MidiRingBuffer<nframes_t>& incoming (_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 +StepEditor::step_add_bank_change (uint8_t channel, uint8_t bank) +{ + return 0; +} + +int +StepEditor::step_add_program_change (uint8_t channel, uint8_t program) +{ + return 0; +} + +void +StepEditor::step_edit_sustain (Evoral::MusicalTime beats) +{ + if (step_edit_region_view) { + step_edit_region_view->step_sustain (beats); + } +} + +void +StepEditor::move_step_edit_beat_pos (Evoral::MusicalTime beats) +{ + if (beats > 0.0) { + step_edit_beat_pos = min (step_edit_beat_pos + beats, + step_edit_region_view->frames_to_beats (step_edit_region->length())); + } else if (beats < 0.0) { + if (beats < step_edit_beat_pos) { + step_edit_beat_pos += beats; // its negative, remember + } else { + step_edit_beat_pos = 0; + } + } + step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos); +} + +int +StepEditor::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration) +{ + /* do these things in case undo removed the step edit region + */ + if (!step_edit_region) { + resync_step_edit_position (); + prepare_step_edit_region (); + reset_step_edit_beat_pos (); + 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()); + } + + assert (step_edit_region); + assert (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 = _mtv.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 +StepEditor::set_step_edit_cursor_width (Evoral::MusicalTime beats) +{ + if (step_edit_region_view) { + step_edit_region_view->set_step_edit_cursor_width (beats); + } +} + +bool +StepEditor::step_edit_within_triplet() const +{ + return _step_edit_triplet_countdown > 0; +} + +bool +StepEditor::step_edit_within_chord() const +{ + return _step_edit_within_chord; +} + +void +StepEditor::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 +StepEditor::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; + } +} + +void +StepEditor::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 +StepEditor::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 +StepEditor::step_edit_bar_sync () +{ + Session* _session = _mtv.session (); + + 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); +} + +void +StepEditor::playlist_changed () +{ + step_edit_region_connection.disconnect (); + _track->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this), + ui_bind (&StepEditor::region_removed, this, _1), + gui_context()); +} + +void +StepEditor::region_removed (boost::weak_ptr<Region> wr) +{ + boost::shared_ptr<Region> r (wr.lock()); + + if (!r) { + return; + } + + if (step_edit_region == r) { + step_edit_region.reset(); + step_edit_region_view = 0; + // force a recompute of the insert position + step_edit_beat_pos = -1.0; + } +} + +string +StepEditor::name() const +{ + return _track->name(); +} diff --git a/gtk2_ardour/step_editor.h b/gtk2_ardour/step_editor.h new file mode 100644 index 0000000000..00be7890a3 --- /dev/null +++ b/gtk2_ardour/step_editor.h @@ -0,0 +1,73 @@ +#ifndef __pbd__step_editor_h__ +#define __pbd__step_editor_h__ + +#include <string> + +#include <gdk/gdk.h> +#include <sigc++/trackable.h> + +#include "pbd/signals.h" +#include "evoral/types.hpp" + +namespace ARDOUR { + class MidiTrack; + class MidiRegion; +} + +class MidiRegionView; +class MidiTimeAxisView; +class PublicEditor; +class StepEntry; + +class StepEditor : public PBD::ScopedConnectionList, public sigc::trackable +{ + public: + StepEditor (PublicEditor&, boost::shared_ptr<ARDOUR::MidiTrack>, MidiTimeAxisView&); + virtual ~StepEditor (); + + void check_step_edit (); + void step_edit_rest (Evoral::MusicalTime beats); + void step_edit_beat_sync (); + void step_edit_bar_sync (); + int step_add_bank_change (uint8_t channel, uint8_t bank); + int step_add_program_change (uint8_t channel, uint8_t program); + int step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, + Evoral::MusicalTime beat_duration); + void step_edit_sustain (Evoral::MusicalTime beats); + bool step_edit_within_triplet () const; + void step_edit_toggle_triplet (); + bool step_edit_within_chord () const; + void step_edit_toggle_chord (); + void reset_step_edit_beat_pos (); + void resync_step_edit_to_edit_point (); + void move_step_edit_beat_pos (Evoral::MusicalTime beats); + void set_step_edit_cursor_width (Evoral::MusicalTime beats); + + std::string name() const; + + void start_step_editing (); + void stop_step_editing (); + + private: + ARDOUR::framepos_t step_edit_insert_position; + Evoral::MusicalTime step_edit_beat_pos; + boost::shared_ptr<ARDOUR::MidiRegion> step_edit_region; + MidiRegionView* step_edit_region_view; + uint8_t _step_edit_triplet_countdown; + bool _step_edit_within_chord; + Evoral::MusicalTime _step_edit_chord_duration; + PBD::ScopedConnection step_edit_region_connection; + PublicEditor& _editor; + boost::shared_ptr<ARDOUR::MidiTrack> _track; + StepEntry* step_editor; + MidiTimeAxisView& _mtv; + + void region_removed (boost::weak_ptr<ARDOUR::Region>); + void playlist_changed (); + bool step_editor_hidden (GdkEventAny*); + void step_editor_hide (); + void resync_step_edit_position (); + void prepare_step_edit_region (); +}; + +#endif /* __pbd__step_editor_h__ */ diff --git a/gtk2_ardour/step_entry.cc b/gtk2_ardour/step_entry.cc index d3ecca434c..1fd6b37cfb 100644 --- a/gtk2_ardour/step_entry.cc +++ b/gtk2_ardour/step_entry.cc @@ -31,6 +31,7 @@ #include "ardour_ui.h" #include "midi_channel_selector.h" #include "midi_time_axis.h" +#include "step_editor.h" #include "step_entry.h" #include "utils.h" @@ -55,8 +56,8 @@ _rest_event_handler (GtkWidget* widget, gpointer arg) ((StepEntry*)arg)->rest_event_handler (); } -StepEntry::StepEntry (MidiTimeAxisView& mtv) - : ArdourDialog (string_compose (_("Step Entry: %1"), mtv.name())) +StepEntry::StepEntry (StepEditor& seditor) + : ArdourDialog (string_compose (_("Step Entry: %1"), seditor.name())) , _current_note_length (1.0) , _current_note_velocity (64) , triplet_button ("3") @@ -84,17 +85,18 @@ StepEntry::StepEntry (MidiTimeAxisView& mtv) , program_button (_("+")) , _piano (0) , piano (0) - , _mtv (&mtv) + , se (&seditor) { register_actions (); load_bindings (); +#if 0 /* set channel selector to first selected channel. if none are selected, it will remain at the value set in its constructor, above (1) */ - uint16_t chn_mask = _mtv->channel_selector().get_selected_channels(); + uint16_t chn_mask = se->channel_selector().get_selected_channels(); for (uint32_t i = 0; i < 16; ++i) { if (chn_mask & (1<<i)) { @@ -103,6 +105,8 @@ StepEntry::StepEntry (MidiTimeAxisView& mtv) } } +#endif + RadioButtonGroup length_group = length_1_button.get_group(); length_2_button.set_group (length_group); length_4_button.set_group (length_group); @@ -510,7 +514,7 @@ StepEntry::on_key_release_event (GdkEventKey* ev) void StepEntry::rest_event_handler () { - _mtv->step_edit_rest (0.0); + se->step_edit_rest (0.0); } Evoral::MusicalTime @@ -565,13 +569,13 @@ StepEntry::on_show () void StepEntry::beat_resync_click () { - _mtv->step_edit_beat_sync (); + se->step_edit_beat_sync (); } void StepEntry::bar_resync_click () { - _mtv->step_edit_bar_sync (); + se->step_edit_bar_sync (); } void @@ -703,13 +707,13 @@ StepEntry::load_bindings () void StepEntry::toggle_triplet () { - _mtv->set_step_edit_cursor_width (note_length()); + se->set_step_edit_cursor_width (note_length()); } void StepEntry::toggle_chord () { - _mtv->step_edit_toggle_chord (); + se->step_edit_toggle_chord (); } void @@ -756,37 +760,37 @@ StepEntry::dot_value_change () dot2_button.set_inconsistent (inconsistent); dot3_button.set_inconsistent (inconsistent); - _mtv->set_step_edit_cursor_width (note_length()); + se->set_step_edit_cursor_width (note_length()); } void StepEntry::program_click () { - _mtv->step_add_program_change (note_channel(), (int8_t) floor (program_adjustment.get_value())); + se->step_add_program_change (note_channel(), (int8_t) floor (program_adjustment.get_value())); } void StepEntry::bank_click () { - _mtv->step_add_bank_change (note_channel(), (int8_t) floor (bank_adjustment.get_value())); + se->step_add_bank_change (note_channel(), (int8_t) floor (bank_adjustment.get_value())); } void StepEntry::insert_rest () { - _mtv->step_edit_rest (note_length()); + se->step_edit_rest (note_length()); } void StepEntry::insert_grid_rest () { - _mtv->step_edit_rest (0.0); + se->step_edit_rest (0.0); } void StepEntry::insert_note (uint8_t note) { - _mtv->step_add_note (note_channel(), note, note_velocity(), note_length()); + se->step_add_note (note_channel(), note, note_velocity(), note_length()); } void StepEntry::insert_c () @@ -972,7 +976,7 @@ StepEntry::length_value_change () length_32_button.set_inconsistent (inconsistent); length_64_button.set_inconsistent (inconsistent); - _mtv->set_step_edit_cursor_width (note_length()); + se->set_step_edit_cursor_width (note_length()); } bool @@ -1132,17 +1136,17 @@ StepEntry::octave_n (int n) void StepEntry::do_sustain () { - _mtv->step_edit_sustain (note_length()); + se->step_edit_sustain (note_length()); } void StepEntry::back () { - _mtv->move_step_edit_beat_pos (-note_length()); + se->move_step_edit_beat_pos (-note_length()); } void StepEntry::sync_to_edit_point () { - _mtv->resync_step_edit_to_edit_point (); + se->resync_step_edit_to_edit_point (); } diff --git a/gtk2_ardour/step_entry.h b/gtk2_ardour/step_entry.h index 934e5e5c18..e534c83e01 100644 --- a/gtk2_ardour/step_entry.h +++ b/gtk2_ardour/step_entry.h @@ -30,12 +30,12 @@ #include "ardour_dialog.h" #include "gtk_pianokeyboard.h" -class MidiTimeAxisView; +class StepEditor; class StepEntry : public ArdourDialog { public: - StepEntry (MidiTimeAxisView&); + StepEntry (StepEditor&); ~StepEntry (); void note_off_event_handler (int note); @@ -124,7 +124,7 @@ class StepEntry : public ArdourDialog PianoKeyboard* _piano; Gtk::Widget* piano; - MidiTimeAxisView* _mtv; + StepEditor* se; void bank_click (); void program_click (); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 5ec8ac5726..d87e31a604 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -190,6 +190,7 @@ gtk2_ardour_sources = [ 'simplerect.cc', 'splash.cc', 'startup.cc', + 'step_editor.cc', 'step_entry.cc', 'streamview.cc', 'strip_silence_dialog.cc', |