steps toward rulers for the MIDI cue editor

This commit is contained in:
Paul Davis 2024-02-01 21:24:20 -07:00
parent 7836418ebf
commit 5509db3b24
3 changed files with 401 additions and 10 deletions

View File

@ -168,6 +168,12 @@ CueEditor::reset_zoom (samplecnt_t n)
ZoomChanged(); /* EMIT SIGNAL */
}
samplecnt_t
CueEditor::get_current_zoom () const
{
return samples_per_pixel;
}
void
CueEditor::reposition_and_zoom (samplepos_t, double)
{

View File

@ -50,6 +50,9 @@ MidiCueEditor::MidiCueEditor()
, horizontal_adjustment (0.0, 0.0, 1e16)
, view (nullptr)
, mouse_mode (Editing::MouseDraw)
, bbt_metric (*this)
, timebar_height (15.)
, n_timebars (1)
{
build_canvas ();
@ -117,7 +120,23 @@ MidiCueEditor::build_canvas ()
rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
CANVAS_DEBUG_NAME (rubberband_rect, X_("cue rubberband rect"));
bg = new CueMidiBackground (hv_scroll_group);
Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
Pango::FontDescription larger_font (UIConfiguration::instance().get_SmallBoldFont());
bbt_ruler = new ArdourCanvas::Ruler (time_line_group, &bbt_metric, ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
bbt_ruler->set_font_description (font);
bbt_ruler->set_second_font_description (larger_font);
Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
bbt_ruler->set_fill_color (base);
bbt_ruler->set_outline_color (text);
CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
data_group = new ArdourCanvas::Container (hv_scroll_group);
data_group->move (ArdourCanvas::Duple (0., timebar_height * n_timebars));
CANVAS_DEBUG_NAME (data_group, "cue data group");
bg = new CueMidiBackground (data_group);
_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &MidiCueEditor::canvas_allocate));
_canvas->set_name ("MidiCueCanvas");
@ -139,7 +158,6 @@ MidiCueEditor::canvas_enter_leave (GdkEventCrossing* ev)
_canvas_viewport->canvas()->grab_focus ();
ActionManager::set_sensitive (_midi_actions, true);
EditingContext::push_editing_context (this);
std::cerr << "GRAB FOCUS\n";
}
break;
case GDK_LEAVE_NOTIFY:
@ -147,7 +165,6 @@ MidiCueEditor::canvas_enter_leave (GdkEventCrossing* ev)
ActionManager::set_sensitive (_midi_actions, false);
ARDOUR_UI::instance()->reset_focus (_canvas_viewport);
EditingContext::pop_editing_context ();
std::cerr << "DROP FOCUS\n";
}
default:
break;
@ -159,8 +176,9 @@ void
MidiCueEditor::canvas_allocate (Gtk::Allocation alloc)
{
bg->set_size (alloc.get_width(), alloc.get_height());
if (view) {
view->set_size (alloc.get_width(), alloc.get_height());
view->set_size (alloc.get_width(), alloc.get_height() - (timebar_height * n_timebars));
}
}
@ -211,6 +229,8 @@ MidiCueEditor::reset_zoom (samplecnt_t spp)
if (view) {
view->set_samples_per_pixel (spp);
}
bbt_ruler->set_range (0, current_page_samples());
}
samplecnt_t
@ -268,7 +288,7 @@ MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, std::shared_ptr
return;
}
view = new MidiCueView (t, *hv_scroll_group, *this, *bg, 0xff0000ff);
view = new MidiCueView (t, *data_group, *this, *bg, 0xff0000ff);
view->set_region (r);
bg->set_view (view);
@ -460,3 +480,349 @@ MidiCueEditor::region_selection()
RegionSelection rs;
return rs;
}
static void
edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
{
ArdourCanvas::Ruler::Mark copy = marks.back();
copy.label = newlabel;
marks.pop_back ();
marks.push_back (copy);
}
void
MidiCueEditor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, samplepos_t leftmost, samplepos_t rightmost, gint /*maxchars*/)
{
if (_session == 0) {
return;
}
std::shared_ptr<Temporal::TempoMap> tmap;
if (view) {
/* XXX MAKE MAP FROM REGION->SOURCE */
tmap.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4)));
} else {
tmap.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4)));
}
EditingContext::TempoMapScope tms (*this, tmap);
Temporal::TempoMapPoints::const_iterator i;
char buf[64];
Temporal::BBT_Time next_beat;
double bbt_position_of_helper;
bool helper_active = false;
ArdourCanvas::Ruler::Mark mark;
const samplecnt_t sr (_session->sample_rate());
Temporal::TempoMapPoints grid;
grid.reserve (4096);
/* prevent negative values of leftmost from creeping into tempomap
*/
const Beats left = tmap->quarters_at_sample (leftmost).round_down_to_beat();
const Beats lower_beat = (left < Beats() ? Beats() : left);
using std::max;
switch (bbt_ruler_scale) {
case bbt_show_quarters:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 1);
break;
case bbt_show_eighths:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 2);
break;
case bbt_show_sixteenths:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 4);
break;
case bbt_show_thirtyseconds:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 8);
break;
case bbt_show_sixtyfourths:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 16);
break;
case bbt_show_onetwentyeighths:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 32);
break;
case bbt_show_1:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 1);
break;
case bbt_show_4:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 4);
break;
case bbt_show_16:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 16);
break;
case bbt_show_64:
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 64);
break;
default:
/* bbt_show_many */
tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 128);
break;
}
#if 0 // DEBUG GRID
for (auto const& g : grid) {
std::cout << "Grid " << g.time() << " Beats: " << g.beats() << " BBT: " << g.bbt() << " sample: " << g.sample(_session->nominal_sample_rate ()) << "\n";
}
#endif
if (distance (grid.begin(), grid.end()) == 0) {
return;
}
/* we can accent certain lines depending on the user's Grid choice */
/* for example, even in a 4/4 meter we can draw a grid with triplet-feel */
/* and in this case you will want the accents on '3s' not '2s' */
uint32_t bbt_divisor = 2;
using namespace Editing;
switch (_grid_type) {
case GridTypeBeatDiv3:
bbt_divisor = 3;
break;
case GridTypeBeatDiv5:
bbt_divisor = 5;
break;
case GridTypeBeatDiv6:
bbt_divisor = 3;
break;
case GridTypeBeatDiv7:
bbt_divisor = 7;
break;
case GridTypeBeatDiv10:
bbt_divisor = 5;
break;
case GridTypeBeatDiv12:
bbt_divisor = 3;
break;
case GridTypeBeatDiv14:
bbt_divisor = 7;
break;
case GridTypeBeatDiv16:
break;
case GridTypeBeatDiv20:
bbt_divisor = 5;
break;
case GridTypeBeatDiv24:
bbt_divisor = 6;
break;
case GridTypeBeatDiv28:
bbt_divisor = 7;
break;
case GridTypeBeatDiv32:
break;
default:
bbt_divisor = 2;
break;
}
uint32_t bbt_beat_subdivision = 1;
switch (bbt_ruler_scale) {
case bbt_show_quarters:
bbt_beat_subdivision = 1;
break;
case bbt_show_eighths:
bbt_beat_subdivision = 1;
break;
case bbt_show_sixteenths:
bbt_beat_subdivision = 2;
break;
case bbt_show_thirtyseconds:
bbt_beat_subdivision = 4;
break;
case bbt_show_sixtyfourths:
bbt_beat_subdivision = 8;
break;
case bbt_show_onetwentyeighths:
bbt_beat_subdivision = 16;
break;
default:
bbt_beat_subdivision = 1;
break;
}
bbt_beat_subdivision *= bbt_divisor;
switch (bbt_ruler_scale) {
case bbt_show_many:
snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
mark.style = ArdourCanvas::Ruler::Mark::Major;
mark.label = buf;
mark.position = leftmost;
marks.push_back (mark);
break;
case bbt_show_64:
for (i = grid.begin(); i != grid.end(); i++) {
BBT_Time bbt ((*i).bbt());
if (bbt.is_bar()) {
if (bbt.bars % 64 == 1) {
if (bbt.bars % 256 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars);
mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
buf[0] = '\0';
if (bbt.bars % 256 == 129) {
mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
}
mark.label = buf;
mark.position = (*i).sample (sr);
marks.push_back (mark);
}
}
}
break;
case bbt_show_16:
for (i = grid.begin(); i != grid.end(); i++) {
BBT_Time bbt ((*i).bbt());
if (bbt.is_bar()) {
if (bbt.bars % 16 == 1) {
if (bbt.bars % 64 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars);
mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
buf[0] = '\0';
if (bbt.bars % 64 == 33) {
mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
}
mark.label = buf;
mark.position = (*i).sample(sr);
marks.push_back (mark);
}
}
}
break;
case bbt_show_4:
for (i = grid.begin(); i != grid.end(); ++i) {
BBT_Time bbt ((*i).bbt());
if (bbt.is_bar()) {
if (bbt.bars % 4 == 1) {
if (bbt.bars % 16 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars);
mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
buf[0] = '\0';
mark.style = ArdourCanvas::Ruler::Mark::Minor;
}
mark.label = buf;
mark.position = (*i).sample (sr);
marks.push_back (mark);
}
}
}
break;
case bbt_show_1:
for (i = grid.begin(); i != grid.end(); ++i) {
BBT_Time bbt ((*i).bbt());
if (bbt.is_bar()) {
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars);
mark.style = ArdourCanvas::Ruler::Mark::Major;
mark.label = buf;
mark.position = (*i).sample (sr);
marks.push_back (mark);
}
}
break;
case bbt_show_quarters:
mark.label = "";
mark.position = leftmost;
mark.style = ArdourCanvas::Ruler::Mark::Micro;
marks.push_back (mark);
for (i = grid.begin(); i != grid.end(); ++i) {
BBT_Time bbt ((*i).bbt());
if ((*i).sample (sr) < leftmost && (bbt_bar_helper_on)) {
snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats);
edit_last_mark_label (marks, buf);
} else {
if (bbt.is_bar()) {
mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars);
} else if ((bbt.beats % 2) == 1) {
mark.style = ArdourCanvas::Ruler::Mark::Minor;
buf[0] = '\0';
} else {
mark.style = ArdourCanvas::Ruler::Mark::Micro;
buf[0] = '\0';
}
mark.label = buf;
mark.position = (*i).sample (sr);
marks.push_back (mark);
}
}
break;
case bbt_show_eighths:
case bbt_show_sixteenths:
case bbt_show_thirtyseconds:
case bbt_show_sixtyfourths:
case bbt_show_onetwentyeighths:
bbt_position_of_helper = leftmost + (3 * get_current_zoom ());
mark.label = "";
mark.position = leftmost;
mark.style = ArdourCanvas::Ruler::Mark::Micro;
marks.push_back (mark);
for (i = grid.begin(); i != grid.end(); ++i) {
BBT_Time bbt ((*i).bbt());
if ((*i).sample (sr) < leftmost && (bbt_bar_helper_on)) {
snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats);
edit_last_mark_label (marks, buf);
helper_active = true;
} else {
if (bbt.is_bar()) {
mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars);
} else if (bbt.ticks == 0) {
mark.style = ArdourCanvas::Ruler::Mark::Minor;
snprintf (buf, sizeof(buf), "%" PRIu32, bbt.beats);
} else {
mark.style = ArdourCanvas::Ruler::Mark::Micro;
buf[0] = '\0';
}
if (((*i).sample(sr) < bbt_position_of_helper) && helper_active) {
buf[0] = '\0';
}
mark.label = buf;
mark.position = (*i).sample (sr);
marks.push_back (mark);
}
}
break;
}
}

View File

@ -21,6 +21,8 @@
#include <gtkmm/adjustment.h>
#include "canvas/ruler.h"
#include "cue_editor.h"
namespace Gtk {
@ -102,8 +104,6 @@ class MidiCueEditor : public CueEditor
ArdourCanvas::GtkCanvasViewport* _canvas_viewport;
ArdourCanvas::GtkCanvas* _canvas;
ArdourCanvas::Container* tempo_group;
/* The group containing all other groups that are scrolled vertically
and horizontally.
*/
@ -117,11 +117,11 @@ class MidiCueEditor : public CueEditor
*/
ArdourCanvas::ScrollGroup* cursor_scroll_group;
/* The group containing all trackviews. */
ArdourCanvas::Container* no_scroll_group;
ArdourCanvas::Container* global_rect_group;
ArdourCanvas::Container* no_scroll_group;
ArdourCanvas::Container* data_group;
ArdourCanvas::Container* time_line_group;
ArdourCanvas::Ruler* bbt_ruler;
ArdourCanvas::Rectangle* transport_loop_range_rect;
@ -136,6 +136,25 @@ class MidiCueEditor : public CueEditor
RegionSelection region_selection();
bool canvas_enter_leave (GdkEventCrossing* ev);
void metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>&, samplepos_t, samplepos_t, gint);
class BBTMetric : public ArdourCanvas::Ruler::Metric
{
public:
BBTMetric (MidiCueEditor& ec) : context (&ec) {}
void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, int64_t lower, int64_t upper, int maxchars) const {
context->metric_get_bbt (marks, lower, upper, maxchars);
}
private:
MidiCueEditor* context;
};
BBTMetric bbt_metric;
double timebar_height;
size_t n_timebars;
};