basically functioning piano roll for midi cue editor

This commit is contained in:
Paul Davis 2024-02-09 11:29:23 -07:00
parent 7329508fd1
commit 9f76533f76
4 changed files with 140 additions and 70 deletions

View File

@ -50,8 +50,9 @@ using namespace Temporal;
MidiCueEditor::MidiCueEditor()
: timebar_height (15.)
, n_timebars (3)
, view (nullptr)
, prh (nullptr)
, bg (nullptr)
, view (nullptr)
, mouse_mode (Editing::MouseContent)
, bbt_metric (*this)
{
@ -118,6 +119,11 @@ MidiCueEditor::build_canvas ()
CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
_canvas->add_scroller (*h_scroll_group);
v_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
_canvas->add_scroller (*v_scroll_group);
hv_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(),
ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
ArdourCanvas::ScrollGroup::ScrollsHorizontally));
@ -177,7 +183,6 @@ MidiCueEditor::build_canvas ()
CANVAS_DEBUG_NAME (bbt_ruler, "cue bbt ruler");
data_group = new ArdourCanvas::Container (hv_scroll_group);
data_group->move (ArdourCanvas::Duple (30, timebar_height * n_timebars));
CANVAS_DEBUG_NAME (data_group, "cue data group");
bg = new CueMidiBackground (data_group);
@ -314,7 +319,16 @@ MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, std::shared_ptr
bg->set_view (view);
delete prh;
prh = new ArdourCanvas::PianoRollHeader (data_group, *view);
prh = new ArdourCanvas::PianoRollHeader (v_scroll_group, *view);
double w, h;
prh->size_request (w, h);
/* Move stuff around */
prh->move (Duple (0., n_timebars * timebar_height));
data_group->move (ArdourCanvas::Duple (w, timebar_height * n_timebars));
h_scroll_group->move (Duple (w, 0.));
/* Compute zoom level to show entire source plus some margin if possible */
@ -337,8 +351,6 @@ MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, std::shared_ptr
double width = bg->width();
samplecnt_t samples = duration.samples();
std::cerr << "region is " << samples << " long\n";
samplecnt_t spp = floor (samples / width);
reset_zoom (spp);
}

View File

@ -114,6 +114,7 @@ class MidiCueEditor : public CueEditor
/* The group containing all other groups that are scrolled horizontally ONLY
*/
ArdourCanvas::ScrollGroup* h_scroll_group;
ArdourCanvas::ScrollGroup* v_scroll_group;
/* Scroll group for cursors, scrolled horizontally, above everything else
*/

View File

@ -90,13 +90,69 @@ PianoRollHeader::PianoRollHeader (Item* parent, MidiView& v)
_view.midi_context().NoteRangeChanged.connect (sigc::mem_fun (*this, &PianoRollHeader::note_range_changed));
double w, h;
size_request (w, h);
set (Rect (0., 0., w, h));
/* draw vertical lines on both sides of the rectangle */
set_outline_color (0x000000ff); /* XXX theme me */
set_outline_what (Rectangle::What (Rectangle::LEFT|Rectangle::RIGHT));
Event.connect (sigc::mem_fun (*this, &PianoRollHeader::event_handler));
}
void
PianoRollHeader::size_request (double& w, double& h) const
{
h = _view.midi_context().contents_height();
if (show_scroomer()) {
_scroomer_size = 60.f * UIConfiguration::instance().get_ui_scale();
w = 60.f * UIConfiguration::instance().get_ui_scale();
} else {
_scroomer_size = 20.f * UIConfiguration::instance().get_ui_scale();
w = 20.f * UIConfiguration::instance().get_ui_scale();
}
set (Rect (0., 0., _scroomer_size + 20., 120.));
w += 20.;
}
void
PianoRollHeader::size_allocate (ArdourCanvas::Rect const &r)
{
_alloc = r;
}
bool
PianoRollHeader::event_handler (GdkEvent* ev)
{
/* Remember that ev uses canvas coordinates, not item */
switch (ev->type) {
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
return button_press_handler (&ev->button);
case GDK_BUTTON_RELEASE:
return button_release_handler (&ev->button);
case GDK_ENTER_NOTIFY:
return enter_handler (&ev->crossing);
case GDK_LEAVE_NOTIFY:
return leave_handler (&ev->crossing);
case GDK_SCROLL:
return scroll_handler (&ev->scroll);
case GDK_MOTION_NOTIFY:
return motion_handler (&ev->motion);
default:
break;
}
return false;
}
inline void
@ -110,26 +166,18 @@ create_path (Cairo::RefPtr<Cairo::Context> cr, double x[], double y[], int start
}
inline void
render_rect(Cairo::RefPtr<Cairo::Context> cr, int note, double x[], double y[],
Gtkmm2ext::Color& bg)
render_rect(Cairo::RefPtr<Cairo::Context> cr, int note, double x[], double y[], Gtkmm2ext::Color& bg)
{
set_source_rgba(cr, bg);
create_path(cr, x, y, 0, 4);
cr->fill();
}
void
PianoRollHeader::size_allocate (ArdourCanvas::Rect const &r)
{
_alloc = r;
set_source_rgba (cr, bg);
create_path (cr, x, y, 0, 4);
cr->fill ();
}
void
PianoRollHeader::render_scroomer(Cairo::RefPtr<Cairo::Context> cr) const
{
double scroomer_top = max (1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * _alloc.height () );
double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * _alloc.height ();
double scroomer_width = _scroomer_size;
double scroomer_top = max (1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * get().height () );
double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * get().height ();
Gtkmm2ext::Color c = UIConfiguration::instance().color_mod (X_("scroomer"), X_("scroomer alpha"));
Gtkmm2ext::Color save_color (c);
@ -140,8 +188,8 @@ PianoRollHeader::render_scroomer(Cairo::RefPtr<Cairo::Context> cr) const
set_source_rgba (cr, c);
cr->move_to (1.f, scroomer_top);
cr->line_to (scroomer_width - 1.f, scroomer_top);
cr->line_to (scroomer_width - 1.f, scroomer_bottom);
cr->line_to (_scroomer_size - 1.f, scroomer_top);
cr->line_to (_scroomer_size - 1.f, scroomer_bottom);
cr->line_to (1.f, scroomer_bottom);
cr->line_to (1.f, scroomer_top);
cr->fill();
@ -152,9 +200,9 @@ PianoRollHeader::render_scroomer(Cairo::RefPtr<Cairo::Context> cr) const
set_source_rgba (cr, c);
cr->set_line_width (4.);
cr->move_to (1.f, scroomer_top + 2.);
cr->line_to (scroomer_width - 1.f, scroomer_top + 2.);
cr->line_to (_scroomer_size - 1.f, scroomer_top + 2.);
cr->stroke ();
cr->line_to (scroomer_width - 1.f, scroomer_bottom - 2.);
cr->line_to (_scroomer_size - 1.f, scroomer_bottom - 2.);
cr->line_to (2.f, scroomer_bottom - 2.);
cr->stroke ();
cr->restore ();
@ -191,7 +239,8 @@ PianoRollHeader::scroll_handler (GdkEventScroll* ev)
}
}
set_note_highlight (_view.midi_context().y_to_note (ev->y));
Duple evd (canvas_to_item (Duple (ev->x, ev->y)));
set_note_highlight (_view.midi_context().y_to_note (evd.y));
_adj.value_changed ();
redraw ();
@ -202,11 +251,10 @@ PianoRollHeader::scroll_handler (GdkEventScroll* ev)
void
PianoRollHeader::get_path (int note, double x[], double y[]) const
{
double scroomer_size = _scroomer_size;
double y_pos = floor(_view.midi_context().note_to_y(note));
double note_height;
_raw_note_height = floor(_view.midi_context().note_to_y(note - 1)) - y_pos;
double width = _alloc.width() - 1.0f;
double width = get().width() - 1.0f;
if (note == 0) {
note_height = floor(_view.midi_context().contents_height()) - y_pos;
@ -214,10 +262,10 @@ PianoRollHeader::get_path (int note, double x[], double y[]) const
note_height = _raw_note_height <= 3 ? _raw_note_height : _raw_note_height - 1.f;
}
x[0] = scroomer_size;
x[0] = _scroomer_size;
y[0] = y_pos + note_height;
x[1] = scroomer_size;
x[1] = _scroomer_size;
y[1] = y_pos;
x[2] = width;
@ -226,9 +274,8 @@ PianoRollHeader::get_path (int note, double x[], double y[]) const
x[3] = width;
y[3] = y_pos + note_height;
x[4] = scroomer_size;
y[4] = y_pos + note_height;
return;
x[4] = x[0];
y[4] = y[0];
}
void
@ -239,9 +286,19 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::C
double x[9];
double y[9];
int oct_rel;
double y1 = max (area.y0, 0.);
double y2 = min (area.y1, (ArdourCanvas::Coord) floor(_view.midi_context().contents_height()));
double av_note_height = _alloc.height () / _adj.get_page_size ();
Rectangle::render (area, cr);
Duple origin (item_to_window (Duple (0., 0.)));
cr->save ();
// cr->translate (origin.x, origin.y);
Rect self (item_to_window (get()));
double y1 = max (self.y0, 0.);
double y2 = min (self.y1, (ArdourCanvas::Coord) floor(_view.midi_context().contents_height()));
double av_note_height = _view.midi_context().note_height();
int bc_height, bc_width;
//Reduce the frequency of Pango layout resizing
@ -284,21 +341,13 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::C
Gtkmm2ext::Color black_highlight = UIConfiguration::instance().color (X_("piano key highlight"));
Gtkmm2ext::Color textc = UIConfiguration::instance().color (X_("gtk_foreground"));
/* draw vertical lines on both sides of the widget */
cr->set_source_rgb(0.0f, 0.0f, 0.0f);
cr->move_to(0.f, area.y0);
cr->line_to(0.f, area.y1);
cr->stroke();
cr->move_to(_alloc.width(),area.y0);
cr->line_to(_alloc.width(), area.y1);
cr->stroke();
// Render the MIDNAM text or its equivalent. First, set up a clip
// region so that the text doesn't spill, regardless of its length.
cr->save();
cr->rectangle (0,0,_scroomer_size, _alloc.height () );
cr->rectangle (0,0,_scroomer_size, get().height () );
cr->clip();
if (show_scroomer()) {
@ -312,8 +361,8 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::C
_midnam_layout->set_text (note.name);
set_source_rgba(cr, textc);
cr->move_to(2.f, y);
set_source_rgba (cr, textc);
cr->move_to (2.f, y);
if (!_mini_map_display) {
_midnam_layout->show_in_cairo_context (cr);
@ -342,13 +391,13 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::C
gradient_ptr->add_color_stop_rgba (0,r,g,b,0);
gradient_ptr->add_color_stop_rgba (1,r,g,b,1);
cr->set_source (gradient_ptr);
cr->rectangle (_scroomer_size - fade_width, 0, _scroomer_size, _alloc.height () );
cr->rectangle (_scroomer_size - fade_width, 0, _scroomer_size, get().height () );
cr->fill();
}
/* Now draw the semi-transparent scroomer over the top */
render_scroomer(cr);
render_scroomer (cr);
/* Done with clip region */
@ -450,6 +499,9 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::C
}
}
}
/* Done with translation for item->window */
cr->restore ();
}
void
@ -558,15 +610,17 @@ PianoRollHeader::get_note_name (int note)
bool
PianoRollHeader::motion_handler (GdkEventMotion* ev)
{
Duple evd (canvas_to_item (Duple (ev->x, ev->y)));
if (!_scroomer_drag && ev->x < _scroomer_size){
Gdk::Cursor m_Cursor;
double scroomer_top = max(1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * _alloc.height());
double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * _alloc.height();
if (ev->y > scroomer_top - 5 && ev->y < scroomer_top + 5){
double scroomer_top = max(1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * get().height());
double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * get().height();
if (evd.y > scroomer_top - 5 && evd.y < scroomer_top + 5){
m_Cursor = Gdk::Cursor (Gdk::TOP_SIDE);
// XXX _canvas->set_cursor(m_Cursor);
_scroomer_state = TOP;
}else if (ev->y > scroomer_bottom - 5 && ev->y < scroomer_bottom + 5){
}else if (evd.y > scroomer_bottom - 5 && evd.y < scroomer_bottom + 5){
m_Cursor = Gdk::Cursor (Gdk::BOTTOM_SIDE);
// XXXX _canvas->set_cursor(m_Cursor);
_scroomer_state = BOTTOM;
@ -577,10 +631,10 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev)
}
if (_scroomer_drag){
double pixel2val = 127.0 / _alloc.height();
double delta = _old_y - ev->y;
double pixel2val = 127.0 / get().height();
double delta = _old_y - evd.y;
double val_at_pointer = (delta * pixel2val);
double real_val_at_pointer = 127.0 - (ev->y * pixel2val);
double real_val_at_pointer = 127.0 - (evd.y * pixel2val);
double note_range = _adj.get_page_size ();
switch (_scroomer_button_state){
@ -616,7 +670,7 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev)
break;
}
}else{
int note = _view.midi_context().y_to_note(ev->y);
int note = _view.midi_context().y_to_note(evd.y);
set_note_highlight (note);
if (_dragging) {
@ -648,7 +702,7 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev)
}
_adj.value_changed ();
redraw ();
_old_y = ev->y;
_old_y = evd.y;
//win->process_updates(false);
return true;
@ -657,28 +711,25 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev)
bool
PianoRollHeader::button_press_handler (GdkEventButton* ev)
{
Duple evd (canvas_to_item (Duple (ev->x, ev->y)));
_scroomer_button_state = _scroomer_state;
if (ev->button == 1 && ev->x <= _scroomer_size){
if (ev->type == GDK_2BUTTON_PRESS) {
#if 0
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&_view.trackview());
if (mtv) {
mtv->set_note_range (MidiStreamView::ContentsRange, false);
}
#endif
// _view.set_note_range (MidiStreamView::ContentsRange, false);
return true;
}
_scroomer_drag = true;
_old_y = ev->y;
_old_y = evd.y;
_fract = _adj.get_value();
_fract_top = _adj.get_value() + _adj.get_page_size();
return true;
} else {
int note = _view.midi_context().y_to_note(ev->y);
int note = _view.midi_context().y_to_note(evd.y);
bool tertiary = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
bool primary = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
@ -721,10 +772,12 @@ PianoRollHeader::button_press_handler (GdkEventButton* ev)
bool
PianoRollHeader::button_release_handler (GdkEventButton* ev)
{
Duple evd (canvas_to_item (Duple (ev->x, ev->y)));
if (_scroomer_drag){
_scroomer_drag = false;
}
int note = _view.midi_context().y_to_note(ev->y);
int note = _view.midi_context().y_to_note(evd.y);
if (false /*editor().current_mouse_mode() == Editing::MouseRange*/) { //Todo: this mode is buggy, and of questionable utility anyway
@ -775,7 +828,8 @@ PianoRollHeader::set_note_highlight (uint8_t note)
bool
PianoRollHeader::enter_handler (GdkEventCrossing* ev)
{
set_note_highlight (_view.midi_context().y_to_note (ev->y));
Duple evd (canvas_to_item (Duple (ev->x, ev->y)));
set_note_highlight (_view.midi_context().y_to_note (evd.y));
entered = true;
redraw ();
return true;
@ -816,7 +870,7 @@ PianoRollHeader::invalidate_note_range (int lowest, int highest)
double y = _view.midi_context().note_to_y (highest);
double height = _view.midi_context().note_to_y (lowest - 1) - y;
dynamic_cast<ArdourCanvas::GtkCanvas*>(_canvas)->queue_draw_area (0., floor (y), _alloc.width(), floor (height));
dynamic_cast<ArdourCanvas::GtkCanvas*>(_canvas)->queue_draw_area (0., floor (y), get().width(), floor (height));
}
bool

View File

@ -41,6 +41,8 @@ class PianoRollHeader : public ArdourCanvas::Rectangle {
public:
PianoRollHeader (ArdourCanvas::Item* parent, MidiView&);
void size_request (double& w, double& h) const;
void render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
void size_allocate (ArdourCanvas::Rect const &);
@ -56,6 +58,7 @@ public:
private:
bool event_handler (GdkEvent*);
bool motion_handler (GdkEventMotion*);
bool button_press_handler (GdkEventButton*);
bool button_release_handler (GdkEventButton*);