2023-11-18 22:45:48 -05:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2023 Paul Davis <paul@linuxaudiosystems.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
2024-01-26 22:45:22 -05:00
|
|
|
#include "ardour/midi_region.h"
|
2024-02-02 12:32:04 -05:00
|
|
|
#include "ardour/smf_source.h"
|
2024-01-26 22:45:22 -05:00
|
|
|
|
2023-11-18 22:45:48 -05:00
|
|
|
#include "canvas/canvas.h"
|
|
|
|
#include "canvas/container.h"
|
|
|
|
#include "canvas/debug.h"
|
|
|
|
#include "canvas/scroll_group.h"
|
|
|
|
#include "canvas/rectangle.h"
|
|
|
|
|
2024-01-31 20:08:44 -05:00
|
|
|
#include "gtkmm2ext/actions.h"
|
|
|
|
|
2024-02-02 12:32:04 -05:00
|
|
|
|
2024-01-31 20:08:44 -05:00
|
|
|
#include "ardour_ui.h"
|
2024-01-26 22:45:22 -05:00
|
|
|
#include "editor_cursors.h"
|
2024-01-30 14:40:30 -05:00
|
|
|
#include "editor_drag.h"
|
|
|
|
#include "keyboard.h"
|
2024-01-08 16:35:15 -05:00
|
|
|
#include "midi_cue_background.h"
|
2023-11-18 22:45:48 -05:00
|
|
|
#include "midi_cue_editor.h"
|
2024-01-26 22:45:22 -05:00
|
|
|
#include "midi_cue_view.h"
|
2024-01-30 14:40:30 -05:00
|
|
|
#include "note_base.h"
|
2023-11-18 22:45:48 -05:00
|
|
|
#include "ui_config.h"
|
2023-11-25 17:25:29 -05:00
|
|
|
#include "verbose_cursor.h"
|
2023-11-18 22:45:48 -05:00
|
|
|
|
2024-01-26 22:45:22 -05:00
|
|
|
#include "pbd/i18n.h"
|
|
|
|
|
2023-11-19 22:43:29 -05:00
|
|
|
using namespace ARDOUR;
|
2023-11-18 22:45:48 -05:00
|
|
|
using namespace ArdourCanvas;
|
2024-01-30 14:40:30 -05:00
|
|
|
using namespace Gtkmm2ext;
|
2023-11-19 22:43:29 -05:00
|
|
|
using namespace Temporal;
|
2023-11-18 22:45:48 -05:00
|
|
|
|
|
|
|
MidiCueEditor::MidiCueEditor()
|
|
|
|
: vertical_adjustment (0.0, 0.0, 10.0, 400.0)
|
|
|
|
, horizontal_adjustment (0.0, 0.0, 1e16)
|
2024-01-16 14:16:46 -05:00
|
|
|
, view (nullptr)
|
2024-01-30 14:40:30 -05:00
|
|
|
, mouse_mode (Editing::MouseDraw)
|
2024-02-01 23:24:20 -05:00
|
|
|
, bbt_metric (*this)
|
|
|
|
, timebar_height (15.)
|
|
|
|
, n_timebars (1)
|
2023-11-18 22:45:48 -05:00
|
|
|
{
|
|
|
|
build_canvas ();
|
2023-11-25 17:25:29 -05:00
|
|
|
|
|
|
|
_verbose_cursor = new VerboseCursor (*this);
|
2024-01-26 22:45:22 -05:00
|
|
|
|
|
|
|
// _playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event, X_("playhead"));
|
|
|
|
_playhead_cursor = new EditorCursor (*this, X_("playhead"));
|
|
|
|
_playhead_cursor->set_sensitive (UIConfiguration::instance().get_sensitize_playhead());
|
|
|
|
|
|
|
|
_snapped_cursor = new EditorCursor (*this, X_("snapped"));
|
2023-11-18 22:45:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
MidiCueEditor::~MidiCueEditor ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiCueEditor::build_canvas ()
|
|
|
|
{
|
|
|
|
_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
|
|
|
|
|
2023-11-25 17:25:29 -05:00
|
|
|
_canvas = _canvas_viewport->canvas ();
|
2024-01-31 20:08:44 -05:00
|
|
|
_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
|
2023-11-25 17:25:29 -05:00
|
|
|
dynamic_cast<ArdourCanvas::GtkCanvas*>(_canvas)->use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes);
|
2023-11-18 22:45:48 -05:00
|
|
|
|
|
|
|
/* scroll group for items that should not automatically scroll
|
|
|
|
* (e.g verbose cursor). It shares the canvas coordinate space.
|
|
|
|
*/
|
|
|
|
no_scroll_group = new ArdourCanvas::Container (_canvas->root());
|
|
|
|
|
|
|
|
ArdourCanvas::ScrollGroup* hsg;
|
|
|
|
ArdourCanvas::ScrollGroup* hg;
|
|
|
|
ArdourCanvas::ScrollGroup* cg;
|
|
|
|
|
|
|
|
h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
|
|
|
|
CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
|
|
|
|
_canvas->add_scroller (*hg);
|
|
|
|
|
|
|
|
hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_canvas->root(),
|
|
|
|
ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
|
|
|
|
ArdourCanvas::ScrollGroup::ScrollsHorizontally));
|
2024-01-31 20:08:44 -05:00
|
|
|
CANVAS_DEBUG_NAME (hv_scroll_group, "cue canvas hv scroll");
|
2023-11-18 22:45:48 -05:00
|
|
|
_canvas->add_scroller (*hsg);
|
|
|
|
|
|
|
|
cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
|
2024-01-31 20:08:44 -05:00
|
|
|
CANVAS_DEBUG_NAME (cursor_scroll_group, "cue canvas cursor scroll");
|
2023-11-18 22:45:48 -05:00
|
|
|
_canvas->add_scroller (*cg);
|
|
|
|
|
|
|
|
/*a group to hold global rects like punch/loop indicators */
|
|
|
|
global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
|
2024-01-31 20:08:44 -05:00
|
|
|
CANVAS_DEBUG_NAME (global_rect_group, "cue global rect group");
|
2023-11-18 22:45:48 -05:00
|
|
|
|
|
|
|
transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
|
2024-01-31 20:08:44 -05:00
|
|
|
CANVAS_DEBUG_NAME (transport_loop_range_rect, "cue loop rect");
|
2023-11-18 22:45:48 -05:00
|
|
|
transport_loop_range_rect->hide();
|
|
|
|
|
|
|
|
/*a group to hold time (measure) lines */
|
|
|
|
time_line_group = new ArdourCanvas::Container (h_scroll_group);
|
2024-01-31 20:08:44 -05:00
|
|
|
CANVAS_DEBUG_NAME (time_line_group, "cue time line group");
|
2023-11-18 22:45:48 -05:00
|
|
|
|
|
|
|
// used as rubberband rect
|
|
|
|
rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
|
|
|
|
rubberband_rect->hide();
|
2024-01-28 17:26:38 -05:00
|
|
|
rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
|
|
|
|
rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
|
2024-01-31 20:08:44 -05:00
|
|
|
CANVAS_DEBUG_NAME (rubberband_rect, X_("cue rubberband rect"));
|
2024-01-08 16:35:15 -05:00
|
|
|
|
2024-02-01 23:24:20 -05:00
|
|
|
|
|
|
|
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);
|
2024-01-08 16:38:16 -05:00
|
|
|
_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &MidiCueEditor::canvas_allocate));
|
2024-01-31 20:08:44 -05:00
|
|
|
|
|
|
|
_canvas->set_name ("MidiCueCanvas");
|
|
|
|
_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
|
|
|
|
_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &MidiCueEditor::canvas_enter_leave), false);
|
|
|
|
_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &MidiCueEditor::canvas_enter_leave), false);
|
|
|
|
_canvas->set_can_focus ();
|
|
|
|
|
|
|
|
Bindings* midi_bindings = Bindings::get_bindings (X_("MIDI"));
|
|
|
|
_canvas->set_data (X_("ardour-bindings"), midi_bindings);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::canvas_enter_leave (GdkEventCrossing* ev)
|
|
|
|
{
|
|
|
|
switch (ev->type) {
|
|
|
|
case GDK_ENTER_NOTIFY:
|
|
|
|
if (ev->detail != GDK_NOTIFY_INFERIOR) {
|
|
|
|
_canvas_viewport->canvas()->grab_focus ();
|
|
|
|
ActionManager::set_sensitive (_midi_actions, true);
|
|
|
|
EditingContext::push_editing_context (this);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GDK_LEAVE_NOTIFY:
|
|
|
|
if (ev->detail != GDK_NOTIFY_INFERIOR) {
|
|
|
|
ActionManager::set_sensitive (_midi_actions, false);
|
|
|
|
ARDOUR_UI::instance()->reset_focus (_canvas_viewport);
|
|
|
|
EditingContext::pop_editing_context ();
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
2023-11-18 22:45:48 -05:00
|
|
|
}
|
|
|
|
|
2024-01-08 16:38:16 -05:00
|
|
|
void
|
|
|
|
MidiCueEditor::canvas_allocate (Gtk::Allocation alloc)
|
|
|
|
{
|
|
|
|
bg->set_size (alloc.get_width(), alloc.get_height());
|
2024-02-01 23:24:20 -05:00
|
|
|
|
2024-01-28 17:26:38 -05:00
|
|
|
if (view) {
|
2024-02-01 23:24:20 -05:00
|
|
|
view->set_size (alloc.get_width(), alloc.get_height() - (timebar_height * n_timebars));
|
2024-01-28 17:26:38 -05:00
|
|
|
}
|
2024-01-08 16:38:16 -05:00
|
|
|
}
|
2023-11-19 22:43:29 -05:00
|
|
|
|
|
|
|
timepos_t
|
2024-01-04 21:02:59 -05:00
|
|
|
MidiCueEditor::snap_to_grid (timepos_t const & presnap, Temporal::RoundMode direction, SnapPref gpref) const
|
2023-11-19 22:43:29 -05:00
|
|
|
{
|
|
|
|
/* BBT time only */
|
|
|
|
return snap_to_bbt (presnap, direction, gpref);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2024-01-04 21:02:59 -05:00
|
|
|
MidiCueEditor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapPref pref, bool ensure_snap) const
|
2023-11-19 22:43:29 -05:00
|
|
|
{
|
|
|
|
UIConfiguration const& uic (UIConfiguration::instance ());
|
|
|
|
const timepos_t presnap = start;
|
|
|
|
|
|
|
|
|
|
|
|
timepos_t dist = timepos_t::max (start.time_domain()); // this records the distance of the best snap result we've found so far
|
|
|
|
timepos_t best = timepos_t::max (start.time_domain()); // this records the best snap-result we've found so far
|
|
|
|
|
|
|
|
timepos_t pre (presnap);
|
|
|
|
timepos_t post (snap_to_grid (pre, direction, pref));
|
|
|
|
|
|
|
|
check_best_snap (presnap, post, dist, best);
|
|
|
|
|
|
|
|
if (timepos_t::max (start.time_domain()) == best) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
|
|
|
|
* this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
|
|
|
|
* ToDo: Perhaps this should only occur if EditPointMouse?
|
|
|
|
*/
|
|
|
|
samplecnt_t snap_threshold_s = pixel_to_sample (uic.get_snap_threshold ());
|
|
|
|
|
|
|
|
if (!ensure_snap && ::llabs (best.distance (presnap).samples()) > snap_threshold_s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = best;
|
|
|
|
}
|
|
|
|
|
2024-01-26 22:45:22 -05:00
|
|
|
void
|
|
|
|
MidiCueEditor::reset_zoom (samplecnt_t spp)
|
|
|
|
{
|
|
|
|
CueEditor::reset_zoom (spp);
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
view->set_samples_per_pixel (spp);
|
|
|
|
}
|
2024-02-01 23:24:20 -05:00
|
|
|
|
|
|
|
bbt_ruler->set_range (0, current_page_samples());
|
2024-01-26 22:45:22 -05:00
|
|
|
}
|
|
|
|
|
2023-11-19 22:43:29 -05:00
|
|
|
samplecnt_t
|
|
|
|
MidiCueEditor::current_page_samples() const
|
|
|
|
{
|
|
|
|
return (samplecnt_t) _visible_canvas_width* samples_per_pixel;
|
|
|
|
}
|
|
|
|
|
2023-11-25 17:25:29 -05:00
|
|
|
void
|
|
|
|
MidiCueEditor::apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
PBD::Command*
|
|
|
|
MidiCueEditor::apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
Evoral::Sequence<Temporal::Beats>::Notes selected;
|
|
|
|
mrv.selection_as_notelist (selected, true);
|
|
|
|
|
|
|
|
if (selected.empty()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
|
|
|
|
v.push_back (selected);
|
|
|
|
|
|
|
|
timepos_t pos = mrv.midi_region()->source_position();
|
|
|
|
|
|
|
|
return op (mrv.midi_region()->model(), pos.beats(), v);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2024-01-04 21:07:52 -05:00
|
|
|
|
|
|
|
bool
|
2024-01-31 20:08:44 -05:00
|
|
|
MidiCueEditor::canvas_note_event (GdkEvent* event, ArdourCanvas::Item* item)
|
2024-01-04 21:07:52 -05:00
|
|
|
{
|
2024-01-31 20:08:44 -05:00
|
|
|
return typed_event (item, event, NoteItem);
|
2024-01-04 21:07:52 -05:00
|
|
|
}
|
2024-01-08 16:35:15 -05:00
|
|
|
|
|
|
|
Gtk::Widget&
|
|
|
|
MidiCueEditor::viewport()
|
|
|
|
{
|
|
|
|
return *_canvas_viewport;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2024-01-11 20:00:07 -05:00
|
|
|
MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, std::shared_ptr<ARDOUR::MidiRegion> r)
|
2024-01-08 16:35:15 -05:00
|
|
|
{
|
2024-01-16 14:16:46 -05:00
|
|
|
delete view;
|
|
|
|
view = nullptr;
|
2024-01-08 16:35:15 -05:00
|
|
|
|
|
|
|
if (!t || !r) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-02-01 23:24:20 -05:00
|
|
|
view = new MidiCueView (t, *data_group, *this, *bg, 0xff0000ff);
|
2024-01-11 20:00:07 -05:00
|
|
|
view->set_region (r);
|
2024-01-12 14:51:13 -05:00
|
|
|
|
|
|
|
bg->set_view (view);
|
2024-01-26 22:45:22 -05:00
|
|
|
|
|
|
|
/* Compute zoom level to show entire source plus some margin if possible */
|
|
|
|
|
2024-02-02 12:32:04 -05:00
|
|
|
Temporal::timecnt_t duration = Temporal::timecnt_t (r->midi_source()->length().beats());
|
|
|
|
|
|
|
|
bool provided = false;
|
2024-01-26 22:45:22 -05:00
|
|
|
std::shared_ptr<Temporal::TempoMap> map;
|
2024-02-02 12:32:04 -05:00
|
|
|
std::shared_ptr<SMFSource> smf (std::dynamic_pointer_cast<SMFSource> (r->midi_source()));
|
2024-01-26 22:45:22 -05:00
|
|
|
|
2024-02-02 12:32:04 -05:00
|
|
|
if (smf) {
|
|
|
|
map = smf->tempo_map (provided);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!provided) {
|
|
|
|
map.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4)));
|
|
|
|
}
|
2024-01-26 22:45:22 -05:00
|
|
|
|
|
|
|
{
|
|
|
|
EditingContext::TempoMapScope tms (*this, map);
|
|
|
|
double width = bg->width();
|
|
|
|
samplecnt_t samples = duration.samples();
|
|
|
|
|
|
|
|
samplecnt_t spp = floor (samples / width);
|
|
|
|
reset_zoom (spp);
|
|
|
|
}
|
2024-01-08 16:35:15 -05:00
|
|
|
}
|
2024-01-26 22:45:22 -05:00
|
|
|
|
2024-01-29 16:32:18 -05:00
|
|
|
bool
|
2024-01-30 14:40:30 -05:00
|
|
|
MidiCueEditor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
|
2024-01-29 16:32:18 -05:00
|
|
|
{
|
2024-01-30 14:40:30 -05:00
|
|
|
switch (event->button.button) {
|
|
|
|
case 1:
|
|
|
|
return button_press_handler_1 (item, event, item_type);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
return button_press_handler_2 (item, event, item_type);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return button_press_dispatch (&event->button);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-01-31 20:08:44 -05:00
|
|
|
return false;
|
2024-01-29 16:32:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2024-01-30 14:40:30 -05:00
|
|
|
MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
|
2024-01-29 16:32:18 -05:00
|
|
|
{
|
2024-01-30 14:40:30 -05:00
|
|
|
NoteBase* note = nullptr;
|
|
|
|
|
|
|
|
if (mouse_mode == Editing::MouseContent) {
|
|
|
|
switch (item_type) {
|
|
|
|
case NoteItem:
|
|
|
|
/* Existing note: allow trimming/motion */
|
|
|
|
if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
|
|
|
|
if (note->big_enough_to_trim() && note->mouse_near_ends()) {
|
|
|
|
_drags->set (new NoteResizeDrag (*this, item), event, get_canvas_cursor());
|
|
|
|
} else {
|
|
|
|
_drags->set (new NoteDrag (*this, item), event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-31 20:08:44 -05:00
|
|
|
return false;
|
2024-01-29 16:32:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::button_press_handler_2 (ArdourCanvas::Item*, GdkEvent*, ItemType)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2024-01-30 14:40:30 -05:00
|
|
|
MidiCueEditor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
|
2024-01-29 16:32:18 -05:00
|
|
|
{
|
2024-01-31 20:08:44 -05:00
|
|
|
std::cerr << "hey\n";
|
|
|
|
|
2024-01-30 14:40:30 -05:00
|
|
|
if (Keyboard::is_context_menu_event (&event->button)) {
|
|
|
|
switch (item_type) {
|
|
|
|
case NoteItem:
|
|
|
|
if (internal_editing()) {
|
|
|
|
popup_note_context_menu (item, event);
|
2024-01-31 20:08:44 -05:00
|
|
|
return true;
|
2024-01-30 14:40:30 -05:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-31 20:08:44 -05:00
|
|
|
return false;
|
2024-01-29 16:32:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2024-01-30 14:40:30 -05:00
|
|
|
MidiCueEditor::button_press_dispatch (GdkEventButton* ev)
|
2024-01-29 16:32:18 -05:00
|
|
|
{
|
2024-01-30 14:40:30 -05:00
|
|
|
/* this function is intended only for buttons 4 and above. */
|
|
|
|
|
|
|
|
Gtkmm2ext::MouseButton b (ev->state, ev->button);
|
|
|
|
return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
|
2024-01-29 16:32:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2024-01-30 14:40:30 -05:00
|
|
|
MidiCueEditor::button_release_dispatch (GdkEventButton* ev)
|
2024-01-29 16:32:18 -05:00
|
|
|
{
|
2024-01-30 14:40:30 -05:00
|
|
|
/* this function is intended only for buttons 4 and above. */
|
|
|
|
|
|
|
|
Gtkmm2ext::MouseButton b (ev->state, ev->button);
|
|
|
|
return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
|
2024-01-29 16:32:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::motion_handler (ArdourCanvas::Item*, GdkEvent*, bool from_autoscroll)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::enter_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::leave_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2024-01-31 20:08:44 -05:00
|
|
|
MidiCueEditor::key_press_handler (ArdourCanvas::Item*, GdkEvent* ev, ItemType)
|
2024-01-29 16:32:18 -05:00
|
|
|
{
|
2024-01-31 20:08:44 -05:00
|
|
|
|
|
|
|
switch (ev->key.keyval) {
|
|
|
|
case GDK_d:
|
|
|
|
set_mouse_mode (Editing::MouseDraw);
|
|
|
|
std::cerr << "draw\n";
|
|
|
|
break;
|
|
|
|
case GDK_e:
|
|
|
|
set_mouse_mode (Editing::MouseContent);
|
|
|
|
std::cerr << "content/edit\n";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-01-29 16:32:18 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-30 14:40:30 -05:00
|
|
|
void
|
|
|
|
MidiCueEditor::set_mouse_mode (Editing::MouseMode m, bool force)
|
|
|
|
{
|
|
|
|
if (m != Editing::MouseDraw && m != Editing::MouseContent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mouse_mode = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiCueEditor::step_mouse_mode (bool next)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Editing::MouseMode
|
|
|
|
MidiCueEditor::current_mouse_mode () const
|
|
|
|
{
|
|
|
|
return mouse_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MidiCueEditor::internal_editing() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
RegionSelection
|
|
|
|
MidiCueEditor::region_selection()
|
|
|
|
{
|
|
|
|
RegionSelection rs;
|
|
|
|
return rs;
|
|
|
|
}
|
2024-02-01 23:24:20 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-02 12:32:04 -05:00
|
|
|
bool provided = false;
|
2024-02-01 23:24:20 -05:00
|
|
|
std::shared_ptr<Temporal::TempoMap> tmap;
|
|
|
|
|
|
|
|
if (view) {
|
2024-02-02 12:32:04 -05:00
|
|
|
std::shared_ptr<SMFSource> smf (std::dynamic_pointer_cast<SMFSource> (view->midi_region()->midi_source()));
|
|
|
|
|
|
|
|
if (smf) {
|
|
|
|
tmap = smf->tempo_map (provided);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!provided) {
|
2024-02-01 23:24:20 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|