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
This commit is contained in:
Paul Davis 2010-08-15 16:39:51 +00:00
parent 66ea8edc6e
commit faca3e5f5d
7 changed files with 555 additions and 439 deletions

View file

@ -80,8 +80,8 @@
#include "region_view.h" #include "region_view.h"
#include "rgb_macros.h" #include "rgb_macros.h"
#include "selection.h" #include "selection.h"
#include "step_editor.h"
#include "simplerect.h" #include "simplerect.h"
#include "step_entry.h"
#include "utils.h" #include "utils.h"
#include "ardour/midi_track.h" #include "ardour/midi_track.h"
@ -116,7 +116,6 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
, _midi_thru_item (0) , _midi_thru_item (0)
, default_channel_menu (0) , default_channel_menu (0)
, controller_menu (0) , controller_menu (0)
, step_editor (0)
{ {
subplugin_menu.set_name ("ArdourContextMenu"); subplugin_menu.set_name ("ArdourContextMenu");
@ -127,8 +126,6 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
mute_button->set_active (false); mute_button->set_active (false);
solo_button->set_active (false); solo_button->set_active (false);
step_edit_insert_position = 0;
if (is_midi_track()) { if (is_midi_track()) {
controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected"); controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
_note_mode = midi_track()->note_mode(); _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->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
_view->attach (); _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()); HBox* midi_controls_hbox = manage(new HBox());
@ -235,35 +227,17 @@ MidiTimeAxisView::~MidiTimeAxisView ()
_range_scroomer = 0; _range_scroomer = 0;
delete controller_menu; delete controller_menu;
delete _step_editor;
} }
void void
MidiTimeAxisView::playlist_changed () MidiTimeAxisView::check_step_edit ()
{ {
step_edit_region_connection.disconnect (); _step_editor->check_step_edit ();
midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
ui_bind (&MidiTimeAxisView::region_removed, this, _1),
gui_context());
} }
void void
MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr) MidiTimeAxisView::model_changed()
{
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()
{ {
std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance() std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
.custom_device_mode_names_by_model(_model_selector.get_active_text()); .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 void
MidiTimeAxisView::add_note_selection (uint8_t note) MidiTimeAxisView::add_note_selection (uint8_t note)
@ -1380,3 +1001,52 @@ MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
return 0; 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 ();
}
}

View file

@ -54,6 +54,7 @@ class MidiStreamView;
class MidiScroomer; class MidiScroomer;
class PianoRollHeader; class PianoRollHeader;
class StepEntry; class StepEntry;
class StepEditor;
class MidiTimeAxisView : public RouteTimeAxisView class MidiTimeAxisView : public RouteTimeAxisView
{ {
@ -87,28 +88,14 @@ class MidiTimeAxisView : public RouteTimeAxisView
return _midi_patch_settings_changed; 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; } const MidiMultipleChannelSelector& channel_selector() { return _channel_selector; }
Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter); Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter);
StepEditor* step_editor() { return _step_editor; }
void check_step_edit ();
protected: protected:
void start_step_editing (); void start_step_editing ();
void stop_step_editing (); void stop_step_editing ();
@ -149,16 +136,6 @@ class MidiTimeAxisView : public RouteTimeAxisView
Gtk::CheckMenuItem* _midi_thru_item; Gtk::CheckMenuItem* _midi_thru_item;
Gtk::Menu* default_channel_menu; 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(); Gtk::Menu* build_def_channel_menu();
void set_default_channel (int); void set_default_channel (int);
@ -184,11 +161,7 @@ class MidiTimeAxisView : public RouteTimeAxisView
/** parameter -> menu item map for the controller menu */ /** parameter -> menu item map for the controller menu */
ParameterMenuMap _controller_menu_map; ParameterMenuMap _controller_menu_map;
StepEntry* step_editor; StepEditor* _step_editor;
bool step_editor_hidden (GdkEventAny*);
void step_editor_hide ();
void resync_step_edit_position ();
void prepare_step_edit_region ();
}; };
#endif /* __ardour_midi_time_axis_h__ */ #endif /* __ardour_midi_time_axis_h__ */

395
gtk2_ardour/step_editor.cc Normal file
View file

@ -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();
}

73
gtk2_ardour/step_editor.h Normal file
View file

@ -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__ */

View file

@ -31,6 +31,7 @@
#include "ardour_ui.h" #include "ardour_ui.h"
#include "midi_channel_selector.h" #include "midi_channel_selector.h"
#include "midi_time_axis.h" #include "midi_time_axis.h"
#include "step_editor.h"
#include "step_entry.h" #include "step_entry.h"
#include "utils.h" #include "utils.h"
@ -55,8 +56,8 @@ _rest_event_handler (GtkWidget* widget, gpointer arg)
((StepEntry*)arg)->rest_event_handler (); ((StepEntry*)arg)->rest_event_handler ();
} }
StepEntry::StepEntry (MidiTimeAxisView& mtv) StepEntry::StepEntry (StepEditor& seditor)
: ArdourDialog (string_compose (_("Step Entry: %1"), mtv.name())) : ArdourDialog (string_compose (_("Step Entry: %1"), seditor.name()))
, _current_note_length (1.0) , _current_note_length (1.0)
, _current_note_velocity (64) , _current_note_velocity (64)
, triplet_button ("3") , triplet_button ("3")
@ -84,17 +85,18 @@ StepEntry::StepEntry (MidiTimeAxisView& mtv)
, program_button (_("+")) , program_button (_("+"))
, _piano (0) , _piano (0)
, piano (0) , piano (0)
, _mtv (&mtv) , se (&seditor)
{ {
register_actions (); register_actions ();
load_bindings (); load_bindings ();
#if 0
/* set channel selector to first selected channel. if none /* set channel selector to first selected channel. if none
are selected, it will remain at the value set in its are selected, it will remain at the value set in its
constructor, above (1) 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) { for (uint32_t i = 0; i < 16; ++i) {
if (chn_mask & (1<<i)) { if (chn_mask & (1<<i)) {
@ -103,6 +105,8 @@ StepEntry::StepEntry (MidiTimeAxisView& mtv)
} }
} }
#endif
RadioButtonGroup length_group = length_1_button.get_group(); RadioButtonGroup length_group = length_1_button.get_group();
length_2_button.set_group (length_group); length_2_button.set_group (length_group);
length_4_button.set_group (length_group); length_4_button.set_group (length_group);
@ -510,7 +514,7 @@ StepEntry::on_key_release_event (GdkEventKey* ev)
void void
StepEntry::rest_event_handler () StepEntry::rest_event_handler ()
{ {
_mtv->step_edit_rest (0.0); se->step_edit_rest (0.0);
} }
Evoral::MusicalTime Evoral::MusicalTime
@ -565,13 +569,13 @@ StepEntry::on_show ()
void void
StepEntry::beat_resync_click () StepEntry::beat_resync_click ()
{ {
_mtv->step_edit_beat_sync (); se->step_edit_beat_sync ();
} }
void void
StepEntry::bar_resync_click () StepEntry::bar_resync_click ()
{ {
_mtv->step_edit_bar_sync (); se->step_edit_bar_sync ();
} }
void void
@ -703,13 +707,13 @@ StepEntry::load_bindings ()
void void
StepEntry::toggle_triplet () StepEntry::toggle_triplet ()
{ {
_mtv->set_step_edit_cursor_width (note_length()); se->set_step_edit_cursor_width (note_length());
} }
void void
StepEntry::toggle_chord () StepEntry::toggle_chord ()
{ {
_mtv->step_edit_toggle_chord (); se->step_edit_toggle_chord ();
} }
void void
@ -756,37 +760,37 @@ StepEntry::dot_value_change ()
dot2_button.set_inconsistent (inconsistent); dot2_button.set_inconsistent (inconsistent);
dot3_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 void
StepEntry::program_click () 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 void
StepEntry::bank_click () 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 void
StepEntry::insert_rest () StepEntry::insert_rest ()
{ {
_mtv->step_edit_rest (note_length()); se->step_edit_rest (note_length());
} }
void void
StepEntry::insert_grid_rest () StepEntry::insert_grid_rest ()
{ {
_mtv->step_edit_rest (0.0); se->step_edit_rest (0.0);
} }
void void
StepEntry::insert_note (uint8_t note) 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 void
StepEntry::insert_c () StepEntry::insert_c ()
@ -972,7 +976,7 @@ StepEntry::length_value_change ()
length_32_button.set_inconsistent (inconsistent); length_32_button.set_inconsistent (inconsistent);
length_64_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 bool
@ -1132,17 +1136,17 @@ StepEntry::octave_n (int n)
void void
StepEntry::do_sustain () StepEntry::do_sustain ()
{ {
_mtv->step_edit_sustain (note_length()); se->step_edit_sustain (note_length());
} }
void void
StepEntry::back () StepEntry::back ()
{ {
_mtv->move_step_edit_beat_pos (-note_length()); se->move_step_edit_beat_pos (-note_length());
} }
void void
StepEntry::sync_to_edit_point () StepEntry::sync_to_edit_point ()
{ {
_mtv->resync_step_edit_to_edit_point (); se->resync_step_edit_to_edit_point ();
} }

View file

@ -30,12 +30,12 @@
#include "ardour_dialog.h" #include "ardour_dialog.h"
#include "gtk_pianokeyboard.h" #include "gtk_pianokeyboard.h"
class MidiTimeAxisView; class StepEditor;
class StepEntry : public ArdourDialog class StepEntry : public ArdourDialog
{ {
public: public:
StepEntry (MidiTimeAxisView&); StepEntry (StepEditor&);
~StepEntry (); ~StepEntry ();
void note_off_event_handler (int note); void note_off_event_handler (int note);
@ -124,7 +124,7 @@ class StepEntry : public ArdourDialog
PianoKeyboard* _piano; PianoKeyboard* _piano;
Gtk::Widget* piano; Gtk::Widget* piano;
MidiTimeAxisView* _mtv; StepEditor* se;
void bank_click (); void bank_click ();
void program_click (); void program_click ();

View file

@ -190,6 +190,7 @@ gtk2_ardour_sources = [
'simplerect.cc', 'simplerect.cc',
'splash.cc', 'splash.cc',
'startup.cc', 'startup.cc',
'step_editor.cc',
'step_entry.cc', 'step_entry.cc',
'streamview.cc', 'streamview.cc',
'strip_silence_dialog.cc', 'strip_silence_dialog.cc',