first somewhat version of show-clip-capture MIDI notes as they arrive

This commit is contained in:
Paul Davis 2024-10-13 09:03:44 -06:00
parent bd0b5495c6
commit d7e85de7c2
7 changed files with 176 additions and 80 deletions

View file

@ -78,6 +78,8 @@ MidiCueEditor::MidiCueEditor()
_toolbox.pack_start (viewport(), true, true); _toolbox.pack_start (viewport(), true, true);
view = new MidiCueView (nullptr, 0, *data_group, *this, *bg, 0xff0000ff);
_verbose_cursor = new VerboseCursor (*this); _verbose_cursor = new VerboseCursor (*this);
// _playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event, X_("playhead")); // _playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event, X_("playhead"));
@ -329,12 +331,9 @@ MidiCueEditor::canvas_allocate (Gtk::Allocation alloc)
_visible_canvas_width = alloc.get_width(); _visible_canvas_width = alloc.get_width();
_visible_canvas_height = alloc.get_height(); _visible_canvas_height = alloc.get_height();
if (view) { double timebars = n_timebars * timebar_height;
double timebars = n_timebars * timebar_height; view->set_height (alloc.get_height() - timebars);
view->set_height (alloc.get_height() - timebars); bg->set_size (alloc.get_width(), alloc.get_height());
} else {
bg->set_size (alloc.get_width(), alloc.get_height());
}
} }
timepos_t timepos_t
@ -444,26 +443,42 @@ MidiCueEditor::data_captured ()
void void
MidiCueEditor::set_box (std::shared_ptr<ARDOUR::TriggerBox> b) MidiCueEditor::set_box (std::shared_ptr<ARDOUR::TriggerBox> b)
{ {
capture_connection.disconnect (); capture_connections.drop_connections ();
if (b) { if (b) {
std::cerr << "Bix set to " << b->order() << std::endl; b->Captured.connect (capture_connections, invalidator (*this), boost::bind (&MidiCueEditor::data_captured, this), gui_context());
b->Captured.connect (capture_connection, invalidator (*this), boost::bind (&MidiCueEditor::data_captured, this), gui_context()); /* Don't bind a shared_ptr<TriggerBox> within the lambda */
TriggerBox* tb (b.get());
b->RecEnableChanged.connect (capture_connections, invalidator (*this), [&, tb]() { rec_enable_change (tb); }, gui_context());
} }
} }
void void
MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, uint32_t slot_index, std::shared_ptr<ARDOUR::MidiRegion> r) MidiCueEditor::rec_enable_change (ARDOUR::TriggerBox* b)
{ {
delete view; if (b->record_enabled()) {
view = nullptr; view->begin_write();
} else {
view->end_write ();
}
}
if (!t || !r) { void
MidiCueEditor::set_track (std::shared_ptr<ARDOUR::MidiTrack> t)
{
view->set_track (t);
}
void
MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiRegion> r)
{
if (!r) {
bg->set_view (nullptr); bg->set_view (nullptr);
prh->set_view (nullptr); prh->set_view (nullptr);
#warning paul unset view model
return; return;
} }
view = new MidiCueView (t, r, slot_index, *data_group, *this, *bg, 0xff0000ff); view->set_region (r);
bg->set_view (view); bg->set_view (view);
prh->set_view (view); prh->set_view (view);

View file

@ -72,8 +72,9 @@ class MidiCueEditor : public CueEditor
int32_t get_grid_beat_divisions (Editing::GridType gt) const { return 1; } int32_t get_grid_beat_divisions (Editing::GridType gt) const { return 1; }
int32_t get_grid_music_divisions (Editing::GridType gt, uint32_t event_state) const { return 1; } int32_t get_grid_music_divisions (Editing::GridType gt, uint32_t event_state) const { return 1; }
void set_region (std::shared_ptr<ARDOUR::MidiTrack>, uint32_t slot_index, std::shared_ptr<ARDOUR::MidiRegion>); void set_region (std::shared_ptr<ARDOUR::MidiRegion>);
void set_box (std::shared_ptr<ARDOUR::TriggerBox>); void set_box (std::shared_ptr<ARDOUR::TriggerBox>);
void set_track (std::shared_ptr<ARDOUR::MidiTrack>);
ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; } ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; }
ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const { return cursor_scroll_group; } ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const { return cursor_scroll_group; }
@ -205,8 +206,9 @@ class MidiCueEditor : public CueEditor
void visual_changer (const VisualChange&); void visual_changer (const VisualChange&);
void bindings_changed (); void bindings_changed ();
void rec_enable_change (ARDOUR::TriggerBox*);
void data_captured (); void data_captured ();
PBD::ScopedConnection capture_connection; PBD::ScopedConnectionList capture_connections;
}; };

View file

@ -42,7 +42,6 @@
using namespace Gtkmm2ext; using namespace Gtkmm2ext;
MidiCueView::MidiCueView (std::shared_ptr<ARDOUR::MidiTrack> mt, MidiCueView::MidiCueView (std::shared_ptr<ARDOUR::MidiTrack> mt,
std::shared_ptr<ARDOUR::MidiRegion> region,
uint32_t slot_index, uint32_t slot_index,
ArdourCanvas::Item& parent, ArdourCanvas::Item& parent,
EditingContext& ec, EditingContext& ec,
@ -83,7 +82,6 @@ MidiCueView::MidiCueView (std::shared_ptr<ARDOUR::MidiTrack> mt,
} }
set_extensible (true); set_extensible (true);
set_region (region);
Evoral::Parameter fully_qualified_param (ARDOUR::MidiCCAutomation, 0, MIDI_CTL_MSB_MODWHEEL); Evoral::Parameter fully_qualified_param (ARDOUR::MidiCCAutomation, 0, MIDI_CTL_MSB_MODWHEEL);
show_automation (fully_qualified_param); show_automation (fully_qualified_param);
@ -242,6 +240,10 @@ MidiCueView::show_automation (Evoral::Parameter const & param)
{ {
using namespace ARDOUR; using namespace ARDOUR;
if (!_midi_region) {
return;
}
if (param.type() == NullAutomation) { if (param.type() == NullAutomation) {
return; return;
} }

View file

@ -33,7 +33,6 @@ class MidiCueView : public MidiView
{ {
public: public:
MidiCueView (std::shared_ptr<ARDOUR::MidiTrack> mt, MidiCueView (std::shared_ptr<ARDOUR::MidiTrack> mt,
std::shared_ptr<ARDOUR::MidiRegion> region,
uint32_t slot_index, uint32_t slot_index,
ArdourCanvas::Item& parent, ArdourCanvas::Item& parent,
EditingContext& ec, EditingContext& ec,

View file

@ -141,7 +141,8 @@ MidiView::MidiView (std::shared_ptr<MidiTrack> mt,
MidiView::MidiView (MidiView const & other) MidiView::MidiView (MidiView const & other)
: _editing_context (other.editing_context()) : sigc::trackable (other)
, _editing_context (other.editing_context())
, _midi_context (other.midi_context()) , _midi_context (other.midi_context())
, _midi_region (other.midi_region()) , _midi_region (other.midi_region())
, _active_notes(0) , _active_notes(0)
@ -190,6 +191,12 @@ MidiView::set_track (std::shared_ptr<MidiTrack> mt)
if (_midi_track) { if (_midi_track) {
_midi_track->DropReferences.connect (track_going_away_connection, invalidator (*this), boost::bind (&MidiView::track_going_away, this), gui_context()); _midi_track->DropReferences.connect (track_going_away_connection, invalidator (*this), boost::bind (&MidiView::track_going_away, this), gui_context());
if (_midi_track->triggerbox()->record_enabled()) {
begin_write ();
} else {
end_write ();
}
} }
} }
@ -1516,6 +1523,7 @@ MidiView::apply_note_range (uint8_t min, uint8_t max, bool force)
void void
MidiView::begin_write() MidiView::begin_write()
{ {
std::cerr << "MV::begin write\n";
if (_active_notes) { if (_active_notes) {
delete[] _active_notes; delete[] _active_notes;
} }
@ -1531,8 +1539,9 @@ MidiView::begin_write()
void void
MidiView::end_write() MidiView::end_write()
{ {
std::cerr << "MV::end write\n";
delete[] _active_notes; delete[] _active_notes;
_active_notes = 0; _active_notes = nullptr;
_marked_for_selection.clear(); _marked_for_selection.clear();
_marked_for_velocity.clear(); _marked_for_velocity.clear();
} }
@ -1593,6 +1602,9 @@ MidiView::start_playing_midi_chord (vector<std::shared_ptr<NoteType> > notes)
bool bool
MidiView::note_in_region_time_range (const std::shared_ptr<NoteType> note) const MidiView::note_in_region_time_range (const std::shared_ptr<NoteType> note) const
{ {
if (!_midi_region) {
return true;
}
const std::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region(); const std::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
return (timepos_t (note->time()) >= _midi_region->start()) && (timepos_t (note->time()) < _midi_region->start() + _midi_region->length()); return (timepos_t (note->time()) >= _midi_region->start()) && (timepos_t (note->time()) < _midi_region->start() + _midi_region->length());
} }
@ -1600,6 +1612,10 @@ MidiView::note_in_region_time_range (const std::shared_ptr<NoteType> note) const
bool bool
MidiView::note_in_region_range (const std::shared_ptr<NoteType> note, bool& visible) const MidiView::note_in_region_range (const std::shared_ptr<NoteType> note, bool& visible) const
{ {
if (!_midi_region) {
return true;
}
const std::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region(); const std::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
const bool outside = !note_in_region_time_range (note); const bool outside = !note_in_region_time_range (note);
@ -1628,63 +1644,15 @@ MidiView::update_note (NoteBase* note)
void void
MidiView::update_sustained (Note* ev) MidiView::update_sustained (Note* ev)
{ {
const std::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
std::shared_ptr<NoteType> note = ev->note(); std::shared_ptr<NoteType> note = ev->note();
const timepos_t note_start (note->time()); double x0, x1, y0, y1;
timepos_t note_end (note->end_time());
/* The note is drawn as a child item of this region view, so its
* coordinate system is relative to the region view. This means that x0
* and x1 are pixel offsets relative to beginning of the region (view)
*/
/* compute absolute time where the start of the source is
*/
const timepos_t session_source_start = _midi_region->source_position();
/* this computes the number of samples from the start of the region of the start of the
* note. We add the source start to get to the absolute time of the
* note, then subtract the start of the region
*/
const samplepos_t note_start_samples = _midi_region->position().distance ((note_start + session_source_start)).samples();
const double x0 = _editing_context.sample_to_pixel (note_start_samples);
double x1;
const double y0 = 1 + floor(note_to_y(note->note()));
double y1;
if (note->length() == Temporal::Beats()) {
/* special case actual zero-length notes */
x1 = x0 + 1.;
} else if (note->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
/* normal note */
const Temporal::Beats source_end ((_midi_region->start() + _midi_region->length()).beats());
if (!_extensible && note->end_time() > source_end) {
note_end = timepos_t (source_end);
}
const samplepos_t note_end_samples = _midi_region->position().distance ((session_source_start + note_end)).samples();
x1 = std::max(1., _editing_context.sample_to_pixel (note_end_samples));
if (_midi_region) {
region_update_sustained (ev, x0, x1, y0, y1);
} else { } else {
clip_capture_update_sustained (ev, x0, x1, y0, y1);
/* nascent note currently being recorded, noteOff has not yet arrived */
x1 = std::max(1., _editing_context.duration_to_pixels (_midi_region->length()));
} }
y1 = y0 + std::max(1., floor(note_height()) - 1);
ev->set (ArdourCanvas::Rect (x0, y0, x1, y1)); ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
ev->set_velocity (note->velocity()/127.0); ev->set_velocity (note->velocity()/127.0);
@ -1713,8 +1681,111 @@ MidiView::update_sustained (Note* ev)
const uint32_t base_col = ev->base_color(); const uint32_t base_col = ev->base_color();
ev->set_fill_color (base_col); ev->set_fill_color (base_col);
ev->set_outline_color (ev->calculate_outline(base_col, ev->selected())); ev->set_outline_color (ev->calculate_outline(base_col, ev->selected()));
} }
void
MidiView::clip_capture_update_sustained (Note *ev, double& x0, double& x1, double& y0, double& y1)
{
std::shared_ptr<NoteType> note = ev->note();
const timepos_t note_start (note->time());
timepos_t note_end (note->end_time());
x0 = _editing_context.sample_to_pixel (note_start.samples());
y0 = 1 + floor(note_to_y(note->note()));
if (note->length() == Temporal::Beats()) {
/* special case actual zero-length notes */
x1 = x0 + 1.;
} else if (note->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
/* normal note */
#warning paul make this use the distance captured so far
const Temporal::Beats source_end (4,0);
if (!_extensible && note->end_time() > source_end) {
note_end = timepos_t (source_end);
}
#warning paul this needs to use the correct part of the tempo map, which will start at SlotArmInfo::start_samples
const samplepos_t note_end_samples = note_end.samples();
x1 = std::max(1., _editing_context.sample_to_pixel (note_end_samples));
} else {
/* nascent note currently being recorded, noteOff has not yet arrived */
#warning paul make this use the distance captured so far
x1 = std::max(1., _editing_context.duration_to_pixels (timecnt_t (Temporal::Beats (1, 0))));
}
y1 = y0 + std::max(1., floor(note_height()) - 1);
}
void
MidiView::region_update_sustained (Note *ev, double& x0, double& x1, double& y0, double& y1)
{
const std::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
std::shared_ptr<NoteType> note = ev->note();
const timepos_t note_start (note->time());
timepos_t note_end (note->end_time());
/* The note is drawn as a child item of this region view, so its
* coordinate system is relative to the region view. This means that x0
* and x1 are pixel offsets relative to beginning of the region (view)
*/
/* compute absolute time where the start of the source is
*/
const timepos_t session_source_start = _midi_region->source_position();
/* this computes the number of samples from the start of the region of the start of the
* note. We add the source start to get to the absolute time of the
* note, then subtract the start of the region
*/
const samplepos_t note_start_samples = _midi_region->position().distance ((note_start + session_source_start)).samples();
x0 = _editing_context.sample_to_pixel (note_start_samples);
y0 = 1 + floor(note_to_y(note->note()));
if (note->length() == Temporal::Beats()) {
/* special case actual zero-length notes */
x1 = x0 + 1.;
} else if (note->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
/* normal note */
const Temporal::Beats source_end ((_midi_region->start() + _midi_region->length()).beats());
if (!_extensible && note->end_time() > source_end) {
note_end = timepos_t (source_end);
}
const samplepos_t note_end_samples = _midi_region->position().distance ((session_source_start + note_end)).samples();
x1 = std::max(1., _editing_context.sample_to_pixel (note_end_samples));
} else {
/* nascent note currently being recorded, noteOff has not yet arrived */
x1 = std::max(1., _editing_context.duration_to_pixels (_midi_region->length()));
}
y1 = y0 + std::max(1., floor(note_height()) - 1);
}
void void
MidiView::update_hit (Hit* ev) MidiView::update_hit (Hit* ev)
{ {
@ -4220,6 +4291,8 @@ MidiView::set_step_edit_cursor_width (Temporal::Beats beats)
void void
MidiView::clip_data_recorded () MidiView::clip_data_recorded ()
{ {
std::cerr << "cd recorded, mt " << _midi_track << std::endl;
if (!_midi_track) { if (!_midi_track) {
return; return;
} }

View file

@ -643,6 +643,8 @@ class MidiView : public virtual sigc::trackable, public LineMerger
void join_notes_on_channel (int channel); void join_notes_on_channel (int channel);
void add_split_notes (); void add_split_notes ();
void region_update_sustained (Note *, double&, double&, double&, double&);
void clip_capture_update_sustained (Note *, double&, double&, double&, double&);
}; };

View file

@ -387,17 +387,15 @@ TriggerPage::selection_changed ()
{ {
Selection& selection (Editor::instance ().get_selection ()); Selection& selection (Editor::instance ().get_selection ());
/* hide everything */
_slot_prop_box.hide (); _slot_prop_box.hide ();
_audio_trig_box.hide (); _audio_trig_box.hide ();
_midi_trig_box.hide (); _midi_trig_box.hide ();
_midi_editor->viewport().hide (); _midi_editor->viewport().hide ();
_parameter_box.hide (); _parameter_box.hide ();
std::cerr << "here, st = " << selection.triggers.size() << std::endl;
if (!selection.triggers.empty ()) { if (!selection.triggers.empty ()) {
TriggerSelection ts = selection.triggers; TriggerSelection ts = selection.triggers;
TriggerEntry* entry = *ts.begin (); TriggerEntry* entry = *ts.begin ();
@ -419,17 +417,22 @@ TriggerPage::selection_changed ()
_midi_editor->set_box (ref.box()); _midi_editor->set_box (ref.box());
std::shared_ptr<MidiTrack> mt = std::dynamic_pointer_cast<MidiTrack> (entry->strip().stripable());
assert (mt);
_midi_editor->set_track (mt);
if (trigger->the_region()) { if (trigger->the_region()) {
std::shared_ptr<MidiRegion> mr = std::dynamic_pointer_cast<MidiRegion> (trigger->the_region()); std::shared_ptr<MidiRegion> mr = std::dynamic_pointer_cast<MidiRegion> (trigger->the_region());
if (mr) { if (mr) {
std::shared_ptr<MidiTrack> mt = std::dynamic_pointer_cast<MidiTrack> (entry->strip().stripable()); _midi_editor->set_region (mr);
_midi_editor->set_region (mt, ref.slot(), mr);
_midi_editor->viewport().show ();
} }
} }
_midi_editor->viewport().show ();
} }
_parameter_box.show (); _parameter_box.show ();
} }
} }