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

View File

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

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

View File

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

View File

@ -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',