summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2009-09-03 12:39:50 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2009-09-03 12:39:50 +0000
commitb0b584c2a595bfdf6bb4b980bd8d8fc7f3546fc5 (patch)
tree0bbe7220f24b7e7cefadc546d19c409ea357820f
parentc8932292e19f31cab856096c94788bd3f6c5b5fc (diff)
the basics of step editing, more details to follow
git-svn-id: svn://localhost/ardour2/branches/3.0@5629 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/editor_drag.cc31
-rw-r--r--gtk2_ardour/export_file_notebook.cc3
-rw-r--r--gtk2_ardour/midi_list_editor.cc57
-rw-r--r--gtk2_ardour/midi_list_editor.h14
-rw-r--r--gtk2_ardour/midi_region_view.cc27
-rw-r--r--gtk2_ardour/midi_region_view.h3
-rw-r--r--gtk2_ardour/midi_time_axis.cc175
-rw-r--r--gtk2_ardour/midi_time_axis.h16
-rw-r--r--gtk2_ardour/route_time_axis.cc2
-rw-r--r--gtk2_ardour/route_ui.cc29
-rw-r--r--gtk2_ardour/route_ui.h4
-rw-r--r--libs/ardour/ardour/midi_track.h10
-rw-r--r--libs/ardour/ardour/tempo.h2
-rw-r--r--libs/ardour/ardour/track.h4
-rw-r--r--libs/ardour/midi_track.cc48
15 files changed, 367 insertions, 58 deletions
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index a8557dee4e..be261a5de0 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -1366,46 +1366,19 @@ void
RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
{
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
- if (!mtv) {
- return;
- }
- const boost::shared_ptr<MidiDiskstream> diskstream =
- boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
-
- if (!diskstream) {
- warning << "Cannot create non-MIDI region" << endl;
+ if (!mtv) {
return;
}
if (!movement_occurred) {
- _editor->begin_reversible_command (_("create region"));
- XMLNode &before = mtv->playlist()->get_state();
-
- nframes64_t start = _grab_frame;
- _editor->snap_to (start, -1);
- const Meter& m = _editor->session->tempo_map().meter_at(start);
- const Tempo& t = _editor->session->tempo_map().tempo_at(start);
- double length = floor (m.frames_per_bar(t, _editor->session->frame_rate()));
-
- boost::shared_ptr<Source> src = _editor->session->create_midi_source_for_session(*diskstream.get());
-
- mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
- (RegionFactory::create(src, 0, (nframes_t) length,
- PBD::basename_nosuffix(src->name()))), start);
- XMLNode &after = mtv->playlist()->get_state();
- _editor->session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
- _editor->commit_reversible_command();
-
+ mtv->add_region (_grab_frame);
} else {
motion (event, false);
// TODO: create region-create-drag region here
}
}
-
-
-
void
RegionGainDrag::motion (GdkEvent* /*event*/, bool)
{
diff --git a/gtk2_ardour/export_file_notebook.cc b/gtk2_ardour/export_file_notebook.cc
index b10c0e58c1..9b5136c9a4 100644
--- a/gtk2_ardour/export_file_notebook.cc
+++ b/gtk2_ardour/export_file_notebook.cc
@@ -32,7 +32,8 @@ ExportFileNotebook::ExportFileNotebook () :
{
/* Last page */
- new_file_button.add (*Gtk::manage (new Gtk::Image (::get_icon("add"))));
+ new_file_button.set_image (*Gtk::manage (new Gtk::Image (::get_icon("add"))));
+ new_file_button.set_label (_(" Click here to add another format"));
new_file_button.set_alignment (0, 0.5);
new_file_button.set_relief (Gtk::RELIEF_NONE);
diff --git a/gtk2_ardour/midi_list_editor.cc b/gtk2_ardour/midi_list_editor.cc
index 2db6cbd25e..d4a751192a 100644
--- a/gtk2_ardour/midi_list_editor.cc
+++ b/gtk2_ardour/midi_list_editor.cc
@@ -19,6 +19,8 @@
#include "evoral/midi_util.h"
#include "ardour/midi_region.h"
+#include "ardour/session.h"
+#include "ardour/tempo.h"
#include "midi_list_editor.h"
@@ -29,20 +31,21 @@ using namespace Gtk;
using namespace Glib;
using namespace ARDOUR;
-MidiListEditor::MidiListEditor (boost::shared_ptr<MidiRegion> r)
+MidiListEditor::MidiListEditor (Session& s, boost::shared_ptr<MidiRegion> r)
: ArdourDialog (r->name(), false, false)
+ , session (s)
, region (r)
{
model = ListStore::create (columns);
view.set_model (model);
+ view.append_column (_("Start"), columns.start);
view.append_column (_("Channel"), columns.channel);
- view.append_column (_("Note"), columns.note);
+ view.append_column (_("Num"), columns.note);
view.append_column (_("Name"), columns.note_name);
- view.append_column (_("Velocity"), columns.velocity);
- view.append_column (_("Start"), columns.start);
- view.append_column (_("End"), columns.end);
+ view.append_column (_("Vel"), columns.velocity);
view.append_column (_("Length"), columns.length);
+ view.append_column (_("End"), columns.end);
view.set_headers_visible (true);
view.set_name (X_("MidiListView"));
view.set_rules_hint (true);
@@ -70,8 +73,20 @@ MidiListEditor::~MidiListEditor ()
}
void
-MidiListEditor::edited (const Glib::ustring& /* path */, const Glib::ustring& /* text */)
+MidiListEditor::edited (const Glib::ustring& path, const Glib::ustring& /* text */)
{
+ TreeModel::iterator iter = model->get_iter (path);
+
+ cerr << "Edit at " << path << endl;
+
+ if (!iter) {
+ return;
+ }
+
+ boost::shared_ptr<NoteType> note = (*iter)[columns._note];
+
+ cerr << "Edited " << *note << endl;
+
redisplay_model ();
}
@@ -87,12 +102,34 @@ MidiListEditor::redisplay_model ()
for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
row = *(model->append());
row[columns.channel] = (*i)->channel();
- row[columns.note_name] = Evoral::midi_note_name ((*i)->note());
+ row[columns.note_name] = _("Note");
row[columns.note] = (*i)->note();
row[columns.velocity] = (*i)->velocity();
- row[columns.start] = (*i)->time();
- row[columns.length] = (*i)->length();
- row[columns.end] = (*i)->end_time();
+
+ BBT_Time bbt;
+ BBT_Time dur;
+ stringstream ss;
+
+ session.tempo_map().bbt_time (region->position(), bbt);
+
+ dur.bars = 0;
+ dur.beats = floor ((*i)->time());
+ dur.ticks = 0;
+
+ session.tempo_map().bbt_duration_at (region->position(), dur, 0);
+
+ ss << bbt;
+ row[columns.start] = ss.str();
+ ss << dur;
+ row[columns.length] = ss.str();
+
+ session.tempo_map().bbt_time (region->position(), bbt);
+ /* XXX get end point */
+
+ ss << bbt;
+ row[columns.end] = ss.str();
+
+ row[columns._note] = (*i);
}
view.set_model (model);
diff --git a/gtk2_ardour/midi_list_editor.h b/gtk2_ardour/midi_list_editor.h
index ac931dcca6..d52c1d83ac 100644
--- a/gtk2_ardour/midi_list_editor.h
+++ b/gtk2_ardour/midi_list_editor.h
@@ -31,12 +31,15 @@
namespace ARDOUR {
class MidiRegion;
class MidiModel;
+ class Session;
};
class MidiListEditor : public ArdourDialog
{
public:
- MidiListEditor(boost::shared_ptr<ARDOUR::MidiRegion>);
+ typedef Evoral::Note<Evoral::MusicalTime> NoteType;
+
+ MidiListEditor(ARDOUR::Session&, boost::shared_ptr<ARDOUR::MidiRegion>);
~MidiListEditor();
private:
@@ -49,16 +52,19 @@ class MidiListEditor : public ArdourDialog
add (start);
add (length);
add (end);
+ add (note);
};
Gtk::TreeModelColumn<uint8_t> channel;
Gtk::TreeModelColumn<uint8_t> note;
Gtk::TreeModelColumn<std::string> note_name;
Gtk::TreeModelColumn<uint8_t> velocity;
- Gtk::TreeModelColumn<Evoral::MusicalTime> start;
- Gtk::TreeModelColumn<Evoral::MusicalTime> length;
- Gtk::TreeModelColumn<Evoral::MusicalTime> end;
+ Gtk::TreeModelColumn<std::string> start;
+ Gtk::TreeModelColumn<std::string> length;
+ Gtk::TreeModelColumn<std::string> end;
+ Gtk::TreeModelColumn<boost::shared_ptr<NoteType> > _note;
};
+ ARDOUR::Session& session;
MidiListModelColumns columns;
Glib::RefPtr<Gtk::ListStore> model;
Gtk::TreeView view;
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index 317e1f33fe..d3af36ab92 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -500,7 +500,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
void
MidiRegionView::show_list_editor ()
{
- MidiListEditor* mle = new MidiListEditor (midi_region());
+ MidiListEditor* mle = new MidiListEditor (trackview.session(), midi_region());
mle->show ();
}
@@ -761,6 +761,7 @@ MidiRegionView::display_sysexes()
string text = str.str();
ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
+
const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
double height = midi_stream_view()->contents_height();
@@ -2236,6 +2237,30 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
}
void
+MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
+ Evoral::MusicalTime pos, Evoral::MusicalTime len)
+{
+ boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
+
+ start_delta_command (_("step add"));
+ command_add_note (new_note, true, false);
+ apply_command ();
+
+ /* potentially extend region to hold new note */
+
+
+ nframes64_t end_frame = _region->position() + beats_to_frames (new_note->length());
+ nframes64_t region_end = _region->position() + _region->length() - 1;
+
+ if (end_frame > region_end) {
+ cerr << "Resize region!\n";
+ _region->set_length (end_frame, this);
+ } else {
+ redisplay_model ();
+ }
+}
+
+void
MidiRegionView::goto_next_note ()
{
// nframes64_t pos = -1;
diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h
index b88583e64e..c32fd2cb69 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -88,6 +88,9 @@ class MidiRegionView : public RegionView
inline MidiStreamView* midi_stream_view() const
{ return midi_view()->midi_view(); }
+ void add_note (uint8_t channel, uint8_t number, uint8_t velocity,
+ Evoral::MusicalTime pos, Evoral::MusicalTime len);
+
void set_height (double);
void apply_note_range(uint8_t lowest, uint8_t highest, bool force=false);
diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc
index c8cd5fe464..33315cfaa6 100644
--- a/gtk2_ardour/midi_time_axis.cc
+++ b/gtk2_ardour/midi_time_axis.cc
@@ -28,7 +28,9 @@
#include "pbd/error.h"
#include "pbd/stl_delete.h"
#include "pbd/whitespace.h"
+#include "pbd/basename.h"
#include "pbd/enumwriter.h"
+#include "pbd/memento_command.h"
#include <gtkmm2ext/gtk_ui.h>
#include <gtkmm2ext/selector.h>
@@ -39,40 +41,44 @@
#include "ardour/midi_playlist.h"
#include "ardour/midi_diskstream.h"
#include "ardour/midi_patch_manager.h"
+#include "ardour/midi_source.h"
#include "ardour/processor.h"
#include "ardour/ladspa_plugin.h"
#include "ardour/location.h"
#include "ardour/playlist.h"
+#include "ardour/region_factory.h"
#include "ardour/session.h"
#include "ardour/session_playlist.h"
+#include "ardour/tempo.h"
#include "ardour/utils.h"
+#include "add_midi_cc_track_dialog.h"
#include "ardour_ui.h"
-#include "midi_time_axis.h"
-#include "automation_time_axis.h"
#include "automation_line.h"
-#include "add_midi_cc_track_dialog.h"
+#include "automation_time_axis.h"
+#include "canvas-note-event.h"
#include "canvas_impl.h"
#include "crossfade_view.h"
+#include "editor.h"
#include "enums.h"
+#include "ghostregion.h"
#include "gui_thread.h"
#include "keyboard.h"
+#include "midi_scroomer.h"
+#include "midi_streamview.h"
+#include "midi_region_view.h"
+#include "midi_time_axis.h"
+#include "piano_roll_header.h"
#include "playlist_selector.h"
#include "plugin_selector.h"
#include "plugin_ui.h"
#include "point_selection.h"
#include "prompter.h"
-#include "public_editor.h"
#include "region_view.h"
#include "rgb_macros.h"
#include "selection.h"
#include "simplerect.h"
-#include "midi_streamview.h"
#include "utils.h"
-#include "midi_scroomer.h"
-#include "piano_roll_header.h"
-#include "ghostregion.h"
-#include "canvas-note-event.h"
#include "ardour/midi_track.h"
@@ -553,3 +559,154 @@ MidiTimeAxisView::route_active_changed ()
}
}
+void
+MidiTimeAxisView::build_rec_context_menu ()
+{
+ using namespace Menu_Helpers;
+
+ if (!is_track()) {
+ return;
+ }
+
+ rec_context_menu = manage (new Menu);
+ rec_context_menu->set_name ("ArdourContextMenu");
+
+ MenuList& items = rec_context_menu->items();
+
+ items.push_back (CheckMenuElem (_("Step Edit"),
+ (mem_fun (*this, &MidiTimeAxisView::toggle_step_editing))));
+ _step_edit_item = dynamic_cast<CheckMenuItem*>(&items.back());
+ _step_edit_item->set_active (midi_track()->step_editing());
+}
+
+void
+MidiTimeAxisView::toggle_step_editing ()
+{
+ if (!is_track()) {
+ return;
+ }
+
+ bool yn = _step_edit_item->get_active();
+
+ if (yn) {
+ start_step_editing ();
+ } else {
+ stop_step_editing ();
+ }
+
+ midi_track()->set_step_editing (yn);
+}
+
+void
+MidiTimeAxisView::start_step_editing ()
+{
+ step_edit_connection = Glib::signal_timeout().connect (mem_fun (*this, &MidiTimeAxisView::check_step_edit), 20);
+ step_edit_insert_position = _editor.get_preferred_edit_position ();
+ step_edit_beat_pos = 0;
+ step_edit_region = playlist()->top_region_at (step_edit_insert_position);
+
+ if (step_edit_region) {
+ RegionView* rv = view()->find_view (step_edit_region);
+ step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
+ } else {
+ step_edit_region_view = 0;
+ }
+}
+
+void
+MidiTimeAxisView::stop_step_editing ()
+{
+ step_edit_connection.disconnect ();
+}
+
+bool
+MidiTimeAxisView::check_step_edit ()
+{
+ MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
+ Evoral::Note<Evoral::MusicalTime> note;
+ 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) {
+
+ if (step_edit_region == 0) {
+ cerr << "Add new region first ..\n";
+
+ step_edit_region = add_region (step_edit_insert_position);
+ RegionView* rv = view()->find_view (step_edit_region);
+
+ if (rv) {
+ step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
+ } else {
+ fatal << X_("programming error: no view found for new MIDI region") << endmsg;
+ /*NOTREACHED*/
+ }
+ }
+
+ if (step_edit_region_view) {
+
+ bool success;
+ Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
+
+ if (!success) {
+ continue;
+ }
+
+ cerr << "will add note at " << step_edit_beat_pos << endl;
+ step_edit_region_view->add_note (buf[0] & 0xf, buf[1], buf[2], step_edit_beat_pos, beats);
+ step_edit_beat_pos += beats;
+ }
+ }
+
+ }
+
+ return true; /* keep checking */
+}
+
+boost::shared_ptr<Region>
+MidiTimeAxisView::add_region (nframes64_t pos)
+{
+ Editor* real_editor = dynamic_cast<Editor*> (&_editor);
+
+ real_editor->begin_reversible_command (_("create region"));
+ XMLNode &before = playlist()->get_state();
+
+ nframes64_t start = pos;
+ real_editor->snap_to (start, -1);
+ const Meter& m = _session.tempo_map().meter_at(start);
+ const Tempo& t = _session.tempo_map().tempo_at(start);
+ double length = floor (m.frames_per_bar(t, _session.frame_rate()));
+
+ const boost::shared_ptr<MidiDiskstream> diskstream =
+ boost::dynamic_pointer_cast<MidiDiskstream>(view()->trackview().track()->diskstream());
+
+ boost::shared_ptr<Source> src = _session.create_midi_source_for_session (*diskstream.get());
+
+ boost::shared_ptr<Region> region = (RegionFactory::create (src, 0, (nframes_t) length,
+ PBD::basename_nosuffix(src->name())));
+
+ playlist()->add_region (region, start);
+ XMLNode &after = playlist()->get_state();
+ _session.add_command (new MementoCommand<Playlist> (*playlist().get(), &before, &after));
+
+ real_editor->commit_reversible_command();
+
+ return region;
+}
diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h
index acf9c32ea5..850fb90e34 100644
--- a/gtk2_ardour/midi_time_axis.h
+++ b/gtk2_ardour/midi_time_axis.h
@@ -67,6 +67,8 @@ class MidiTimeAxisView : public RouteTimeAxisView
void set_height (uint32_t);
void hide ();
+ boost::shared_ptr<ARDOUR::Region> add_region (nframes64_t pos);
+
void show_all_automation ();
void show_existing_automation ();
void add_cc_track ();
@@ -102,6 +104,7 @@ class MidiTimeAxisView : public RouteTimeAxisView
void set_note_range(MidiStreamView::VisibleNoteRange range);
void route_active_changed ();
+ void build_rec_context_menu ();
void add_insert_to_subplugin_menu (ARDOUR::Processor *);
@@ -120,6 +123,19 @@ class MidiTimeAxisView : public RouteTimeAxisView
MidiMultipleChannelSelector _channel_selector;
Gtk::ComboBoxText _model_selector;
Gtk::ComboBoxText _custom_device_mode_selector;
+
+ Gtk::CheckMenuItem* _step_edit_item;
+ sigc::connection step_edit_connection;
+
+ nframes64_t step_edit_insert_position;
+ Evoral::MusicalTime step_edit_beat_pos;
+ boost::shared_ptr<ARDOUR::Region> step_edit_region;
+ MidiRegionView* step_edit_region_view;
+
+ void toggle_step_editing ();
+ void start_step_editing ();
+ void stop_step_editing ();
+ bool check_step_edit ();
};
#endif /* __ardour_midi_time_axis_h__ */
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc
index ab7fe6292a..cbd2bc5492 100644
--- a/gtk2_ardour/route_time_axis.cc
+++ b/gtk2_ardour/route_time_axis.cc
@@ -184,7 +184,7 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh
rec_enable_button->show_all ();
rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
- rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
+ rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release), false);
controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
ARDOUR_UI::instance()->tooltips().set_tip(*rec_enable_button, _("Record"));
diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc
index 6d21de844e..6c42de5848 100644
--- a/gtk2_ardour/route_ui.cc
+++ b/gtk2_ardour/route_ui.cc
@@ -95,6 +95,7 @@ RouteUI::init ()
mute_menu = 0;
solo_menu = 0;
sends_menu = 0;
+ rec_context_menu = 0;
ignore_toggle = false;
wait_for_release = false;
route_active_menu_item = 0;
@@ -498,6 +499,10 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
set_route_group_rec_enable (_route, !_route->record_enabled());
+ } else if (Keyboard::is_context_menu_event (ev)) {
+
+ /* do this on release */
+
} else {
reversibly_apply_track_boolean ("rec-enable change", &Track::set_record_enable, !track()->record_enabled(), this);
check_rec_enable_sensitivity ();
@@ -507,9 +512,31 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
return true;
}
+
+void
+RouteUI::show_rec_context_menu ()
+{
+ if (!rec_context_menu) {
+ cerr << "build menu\n";
+ build_rec_context_menu ();
+ }
+
+ if (rec_context_menu) {
+ /* only do this if build_rec_context_menu() actually did something */
+ cerr << "show menu\n";
+ rec_context_menu->popup (1, gtk_get_current_event_time());
+ }
+}
+
bool
-RouteUI::rec_enable_release (GdkEventButton*)
+RouteUI::rec_enable_release (GdkEventButton* ev)
{
+ cerr << "release\n";
+ if (Keyboard::is_context_menu_event(ev)) {
+ cerr << "context\n";
+ show_rec_context_menu ();
+ }
+
return true;
}
diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h
index 1ac87a0404..17e5f77cf6 100644
--- a/gtk2_ardour/route_ui.h
+++ b/gtk2_ardour/route_ui.h
@@ -98,6 +98,10 @@ class RouteUI : public virtual AxisView
Gtk::Menu* mute_menu;
Gtk::Menu* solo_menu;
Gtk::Menu* sends_menu;
+ Gtk::Menu* rec_context_menu;
+
+ virtual void build_rec_context_menu () { }
+ void show_rec_context_menu ();
XMLNode *xml_node;
void ensure_xml_node ();
diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h
index 5c5ef5c26c..0030fdc520 100644
--- a/libs/ardour/ardour/midi_track.h
+++ b/libs/ardour/ardour/midi_track.h
@@ -77,6 +77,10 @@ public:
NoteMode note_mode() const { return _note_mode; }
void set_note_mode (NoteMode m);
+
+ bool step_editing() const { return _step_editing; }
+ void set_step_editing (bool yn);
+ MidiRingBuffer<nframes_t>& step_edit_ring_buffer() { return _step_edit_ring_buffer; }
protected:
XMLNode& state (bool full);
@@ -93,7 +97,13 @@ private:
void set_state_part_three ();
MidiRingBuffer<nframes_t> _immediate_events;
+ MidiRingBuffer<nframes_t> _step_edit_ring_buffer;
NoteMode _note_mode;
+ bool _step_editing;
+
+ int no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+ bool state_changing, bool can_record, bool rec_monitors_input);
+ void push_midi_input_to_step_edit_ringbuffer (nframes_t nframes);
};
} /* namespace ARDOUR*/
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index 843437dab7..49e370c7b4 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -179,6 +179,8 @@ class TempoMap : public PBD::StatefulDestructible
nframes_t frame_time (const BBT_Time&) const;
nframes_t bbt_duration_at (nframes_t, const BBT_Time&, int dir) const;
+ void bbt_time_add (nframes64_t origin, BBT_Time& start, const BBT_Time& shift);
+
static const Tempo& default_tempo() { return _default_tempo; }
static const Meter& default_meter() { return _default_meter; }
diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h
index c2f69f0b9b..cb2d05caa6 100644
--- a/libs/ardour/ardour/track.h
+++ b/libs/ardour/ardour/track.h
@@ -45,8 +45,8 @@ class Track : public Route
virtual bool can_use_mode (TrackMode /*m*/, bool& /*bounce_required*/) { return false; }
sigc::signal<void> TrackModeChanged;
- int no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
- bool state_changing, bool can_record, bool rec_monitors_input);
+ virtual int no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+ bool state_changing, bool can_record, bool rec_monitors_input);
int silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
bool can_record, bool rec_monitors_input);
diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc
index 4c575e8cc4..267cacf787 100644
--- a/libs/ardour/midi_track.cc
+++ b/libs/ardour/midi_track.cc
@@ -36,6 +36,7 @@
#include "ardour/midi_source.h"
#include "ardour/midi_track.h"
#include "ardour/panner.h"
+#include "ardour/port.h"
#include "ardour/processor.h"
#include "ardour/route_group_specialized.h"
#include "ardour/session.h"
@@ -50,7 +51,9 @@ using namespace PBD;
MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mode)
: Track (sess, name, flag, mode, DataType::MIDI)
, _immediate_events(1024) // FIXME: size?
+ , _step_edit_ring_buffer(64) // FIXME: size?
, _note_mode(Sustained)
+ , _step_editing (false)
{
use_new_diskstream ();
@@ -63,7 +66,9 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo
MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
: Track (sess, node, DataType::MIDI )
, _immediate_events(1024) // FIXME: size?
+ , _step_edit_ring_buffer(64) // FIXME: size?
, _note_mode(Sustained)
+ , _step_editing (false)
{
_set_state(node, false);
}
@@ -276,6 +281,9 @@ MidiTrack::state(bool full_state)
root.add_child_nocopy (_rec_enable_control->get_state());
+ root.add_property ("step-editing", (_step_editing ? "yes" : "no"));
+ root.add_property ("note-mode", enum_2_string (_note_mode));
+
return root;
}
@@ -449,6 +457,41 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
return 0;
}
+int
+MidiTrack::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+ bool state_changing, bool can_record, bool rec_monitors_input)
+{
+ int ret = Track::no_roll (nframes, start_frame, end_frame, state_changing, can_record, rec_monitors_input);
+
+ if (ret == 0 && _step_editing) {
+ push_midi_input_to_step_edit_ringbuffer (nframes);
+ }
+
+ return ret;
+}
+
+void
+MidiTrack::push_midi_input_to_step_edit_ringbuffer (nframes_t nframes)
+{
+ PortSet& ports (_input->ports());
+
+ for (PortSet::iterator p = ports.begin(DataType::MIDI); p != ports.end(DataType::MIDI); ++p) {
+
+ Buffer& b (p->get_buffer (nframes));
+ const MidiBuffer* const mb = dynamic_cast<MidiBuffer*>(&b);
+ assert (mb);
+
+ for (MidiBuffer::const_iterator e = mb->begin(); e != mb->end(); ++e) {
+
+ const Evoral::MIDIEvent<nframes_t> ev(*e, false);
+
+ /* we don't care about the time for this purpose */
+
+ _step_edit_ring_buffer.write (0, ev.type(), ev.size(), ev.buffer());
+ }
+ }
+}
+
void
MidiTrack::write_out_of_band_data (BufferSet& bufs, sframes_t /*start*/, sframes_t /*end*/, nframes_t nframes)
{
@@ -594,3 +637,8 @@ MidiTrack::MidiControl::set_value(float val)
AutomationControl::set_value(val);
}
+void
+MidiTrack::set_step_editing (bool yn)
+{
+ _step_editing = yn;
+}