refactor pianoroll/cueeditor/audioclipeditor to share code and do the right stuff (compile success stage)

This commit is contained in:
Paul Davis 2025-07-17 22:34:35 -06:00
parent 8922b6d438
commit 01beb00a5f
20 changed files with 1340 additions and 1239 deletions

View file

@ -1852,17 +1852,6 @@ ARDOUR_UI::get_smart_mode() const
void
ARDOUR_UI::spacebar_action (bool with_abort, bool roll_out_of_bounded_mode)
{
if (!_session) {
return;
}
std::shared_ptr<TriggerBox> armed_tb = _session->armed_triggerbox();
if (armed_tb && _session->transport_rolling()) {
armed_tb->disarm_all ();
return;
}
toggle_roll (with_abort, roll_out_of_bounded_mode);
}
@ -1892,7 +1881,10 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
if (rolling) {
std::cerr << "TR rolling\n";
if (roll_out_of_bounded_mode) {
std::cerr << "roobm\n";
/* drop out of loop/range playback but leave transport rolling */
if (_session->get_play_loop()) {
@ -1914,6 +1906,7 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
}
} else {
std::cerr << "stip\n";
_session->request_stop (with_abort, true);
}

View file

@ -88,7 +88,6 @@ ARDOUR_UI::we_have_dependents ()
mixer->monitor_section().use_others_actions ();
StepEntry::setup_actions_and_bindings ();
ClipEditorBox::init ();
RegionEditor::setup_actions_and_bindings ();
setup_action_tooltips ();

View file

@ -41,12 +41,15 @@
#include "widgets/ardour_button.h"
#include "widgets/ardour_icon.h"
#include "ardour_ui.h"
#include "audio_clip_editor.h"
#include "audio_clock.h"
#include "editor_automation_line.h"
#include "editor_cursors.h"
#include "control_point.h"
#include "editor.h"
#include "region_view.h"
#include "verbose_cursor.h"
#include "ui_config.h"
#include "pbd/i18n.h"
@ -60,27 +63,6 @@ using namespace ArdourWidgets;
using std::max;
using std::min;
Glib::RefPtr<Gtk::ActionGroup> ClipEditorBox::clip_editor_actions;
void
ClipEditorBox::init ()
{
Bindings* bindings = Bindings::get_bindings (X_("Clip Editing"));
register_clip_editor_actions (bindings);
}
void
ClipEditorBox::register_clip_editor_actions (Bindings* clip_editor_bindings)
{
clip_editor_actions = ActionManager::create_action_group (clip_editor_bindings, X_("ClipEditing"));
/* two versions to allow same action for Delete and Backspace */
// ActionManager::register_action (clip_editor_actions, X_("zoom-in"), _("Zoom In"), sigc::mem_fun (*this, &ClipEditorBox::zoom_in));
// ActionManager::register_action (clip_editor_actions, X_("zoom-in"), _("Zoom In"), sigc::mem_fun (*this, &ClipEditorBox::zoom_out));
}
void
AudioClipEditor::ClipBBTMetric::get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, int64_t lower, int64_t upper, int maxchars) const
{
@ -121,46 +103,133 @@ AudioClipEditor::ClipBBTMetric::get_marks (std::vector<ArdourCanvas::Ruler::Mark
}
}
AudioClipEditor::AudioClipEditor ()
: clip_metric (0)
, _spp (0)
AudioClipEditor::AudioClipEditor (std::string const & name, bool with_transport)
: CueEditor (name, with_transport)
, _viewport (horizontal_adjustment, vertical_adjustment)
, canvas (*_viewport.canvas())
, clip_metric (nullptr)
, scroll_fraction (0)
, current_line_drag (0)
, current_scroll_drag (0)
{
const double scale = UIConfiguration::instance ().get_ui_scale ();
build_upper_toolbar ();
build_canvas ();
}
frame = new ArdourCanvas::Rectangle (root ());
frame->name = "audio clip editor frame";
frame->set_fill (false);
frame->Event.connect (sigc::mem_fun (*this, &AudioClipEditor::event_handler));
void
AudioClipEditor::pack_inner (Gtk::Box& box)
{
box.pack_start (snap_box, false, false);
box.pack_start (grid_box, false, false);
box.pack_start (draw_box, false, false);
}
/* Scroll bar does not scroll and it outside the frame */
void
AudioClipEditor::pack_outer (Gtk::Box& box)
{
if (with_transport_controls) {
box.pack_start (play_box, false, false);
}
scroll_bar_trough = new ArdourCanvas::Rectangle (root ());
scroll_bar_handle = new ArdourCanvas::Rectangle (scroll_bar_trough);
scroll_bar_handle->set_outline (false);
scroll_bar_handle->set_corner_radius (5.);
scroll_bar_handle->Event.connect (sigc::mem_fun (*this, &AudioClipEditor::scroll_event_handler));
scroll_bar_handle->disable_scroll_translation ();
box.pack_start (rec_box, false, false);
box.pack_start (follow_playhead_button, false, false);
}
/* A scrolling container for our waves and lines etc. */
void
AudioClipEditor::build_lower_toolbar ()
{
horizontal_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &CueEditor::scrolled));
_canvas_hscrollbar = manage (new Gtk::HScrollbar (horizontal_adjustment));
waves_container = new ArdourCanvas::ScrollGroup (frame, ScrollGroup::ScrollsHorizontally);
add_scroller (*waves_container);
_toolbox.pack_start (*_canvas_hscrollbar, false, false);
_toolbox.pack_start (button_bar, false, false);
}
ruler_container = new ArdourCanvas::Container (waves_container);
ruler = new ArdourCanvas::Ruler (ruler_container, 0);
ruler->name = "Clip Editor";
ruler->set_font_description (UIConfiguration::instance ().get_SmallerFont ());
ruler->set_fill_color (UIConfiguration::instance().color (X_("theme:bg1")));
ruler->set_outline_color (UIConfiguration::instance().color (X_("theme:contrasting less")));
void
AudioClipEditor::build_canvas ()
{
canvas.set_background_color (UIConfiguration::instance().color ("arrange base"));
canvas.signal_event().connect (sigc::mem_fun (*this, &CueEditor::canvas_pre_event), false);
canvas.use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes);
canvas.PreRender.connect (sigc::mem_fun(*this, &EditingContext::pre_render));
/* 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());
h_scroll_group = new ArdourCanvas::ScrollGroup (canvas.root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
CANVAS_DEBUG_NAME (h_scroll_group, "audioclip 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, "audioclip 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));
CANVAS_DEBUG_NAME (hv_scroll_group, "audioclip hv scroll");
canvas.add_scroller (*hv_scroll_group);
cursor_scroll_group = new ArdourCanvas::ScrollGroup (canvas.root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
CANVAS_DEBUG_NAME (cursor_scroll_group, "audioclip cursor scroll");
canvas.add_scroller (*cursor_scroll_group);
/*a group to hold global rects like punch/loop indicators */
global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
CANVAS_DEBUG_NAME (global_rect_group, "audioclip global rect group");
transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
CANVAS_DEBUG_NAME (transport_loop_range_rect, "audioclip loop rect");
transport_loop_range_rect->hide();
/*a group to hold time (measure) lines */
time_line_group = new ArdourCanvas::Container (h_scroll_group);
CANVAS_DEBUG_NAME (time_line_group, "audioclip time line group");
n_timebars = 0;
minsec_ruler = new ArdourCanvas::Ruler (time_line_group, clip_metric, ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
// minsec_ruler->set_name ("audio clip editor ruler");
minsec_ruler->set_font_description (UIConfiguration::instance ().get_SmallerFont ());
minsec_ruler->set_fill_color (UIConfiguration::instance().color (X_("theme:bg1")));
minsec_ruler->set_outline_color (UIConfiguration::instance().color (X_("theme:contrasting less")));
n_timebars++;
minsec_ruler->Event.connect (sigc::mem_fun (*this, &AudioClipEditor::minsec_ruler_event));
data_group = new ArdourCanvas::Container (hv_scroll_group);
CANVAS_DEBUG_NAME (data_group, "cue data group");
data_group->set_position (ArdourCanvas::Duple (_timeline_origin, timebar_height * n_timebars));
no_scroll_group->set_position (ArdourCanvas::Duple (_timeline_origin, timebar_height * n_timebars));
cursor_scroll_group->set_position (ArdourCanvas::Duple (_timeline_origin, timebar_height * n_timebars));
h_scroll_group->set_position (Duple (_timeline_origin, 0.));
_verbose_cursor = new VerboseCursor (*this);
// _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());
_playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
_playhead_cursor->canvas_item().raise_to_top();
h_scroll_group->raise_to_top ();
canvas.set_name ("AudioClipCanvas");
canvas.add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
canvas.set_can_focus ();
canvas.signal_show().connect (sigc::mem_fun (*this, &CueEditor::catch_pending_show_region));
_viewport.signal_size_allocate().connect (sigc::mem_fun(*this, &AudioClipEditor::canvas_allocate), false);
_toolbox.pack_start (_viewport, true, true);
/* the lines */
line_container = new ArdourCanvas::Container (waves_container);
line_container = new ArdourCanvas::Container (data_group);
const double line_width = 3.;
double scale = UIConfiguration::instance().get_ui_scale();
start_line = new Line (line_container);
start_line->set_outline_width (line_width * scale);
@ -222,41 +291,6 @@ AudioClipEditor::line_event_handler (GdkEvent* ev, ArdourCanvas::Line* l)
return false;
}
bool
AudioClipEditor::scroll_event_handler (GdkEvent* ev)
{
switch (ev->type) {
case GDK_BUTTON_PRESS:
current_scroll_drag = new ScrollDrag (*this);
current_scroll_drag->begin (&ev->button);
return true;
case GDK_BUTTON_RELEASE:
if (current_scroll_drag) {
current_scroll_drag->end (&ev->button);
delete current_scroll_drag;
current_scroll_drag = 0;
return true;
}
break;
case GDK_MOTION_NOTIFY:
if (current_scroll_drag) {
current_scroll_drag->motion (&ev->motion);
return true;
}
break;
case GDK_KEY_PRESS:
return key_press (&ev->key);
default:
break;
}
return false;
}
bool
AudioClipEditor::key_press (GdkEventKey* ev)
{
@ -277,18 +311,6 @@ AudioClipEditor::position_lines ()
end_line->set_x1 (sample_to_pixel (audio_region->end ().samples ()));
}
double
AudioClipEditor::sample_to_pixel (samplepos_t s)
{
return round (s / _spp);
}
samplepos_t
AudioClipEditor::pixel_to_sample (double p)
{
return round (p * _spp);
}
AudioClipEditor::LineDrag::LineDrag (AudioClipEditor& ed, ArdourCanvas::Line& l)
: editor (ed)
, line (l)
@ -317,78 +339,15 @@ AudioClipEditor::LineDrag::motion (GdkEventMotion* ev)
void
AudioClipEditor::set_colors ()
{
set_background_color (UIConfiguration::instance ().color (X_("theme:bg")));
frame->set_outline_color (UIConfiguration::instance ().color (X_("neutral:midground")));
canvas.set_background_color (UIConfiguration::instance ().color (X_("theme:bg")));
start_line->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting clock")));
end_line->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting alt")));
loop_line->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting selection")));
scroll_bar_trough->set_fill_color (UIConfiguration::instance ().color (X_("theme:bg")));
scroll_bar_trough->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting less")));
scroll_bar_handle->set_fill_color (UIConfiguration::instance ().color (X_("theme:contrasting clock")));
set_waveform_colors ();
}
void
AudioClipEditor::scroll_changed ()
{
if (!audio_region) {
return;
}
const double right_edge = scroll_bar_handle->get ().x0;
const double avail_width = scroll_bar_trough->get ().width () - scroll_bar_handle->get ().width ();
scroll_fraction = right_edge / avail_width;
scroll_fraction = std::min (1., std::max (0., scroll_fraction));
const samplepos_t s = llrintf (audio_region->source (0)->length ().samples () * scroll_fraction);
ruler->set_range (s, s + pixel_to_sample (frame->get().width() - 2.));
scroll_to (sample_to_pixel (s), 0);
queue_draw ();
}
AudioClipEditor::ScrollDrag::ScrollDrag (AudioClipEditor& e)
: editor (e)
{
e.scroll_bar_handle->grab ();
}
void
AudioClipEditor::ScrollDrag::begin (GdkEventButton* ev)
{
last_x = ev->x;
}
void
AudioClipEditor::ScrollDrag::end (GdkEventButton* ev)
{
editor.scroll_bar_handle->ungrab ();
editor.scroll_changed ();
}
void
AudioClipEditor::ScrollDrag::motion (GdkEventMotion* ev)
{
ArdourCanvas::Rectangle& r (*editor.scroll_bar_handle);
const double xdelta = ev->x - last_x;
ArdourCanvas::Rect n (r.get ());
const double handle_width = n.width ();
const double avail_width = editor.scroll_bar_trough->get ().width () - handle_width;
n.x0 = std::max (0., std::min (avail_width, n.x0 + xdelta));
n.x1 = n.x0 + handle_width;
r.set (n);
last_x = ev->x;
editor.scroll_changed ();
}
void
AudioClipEditor::drop_waves ()
{
@ -400,12 +359,50 @@ AudioClipEditor::drop_waves ()
}
void
AudioClipEditor::set_region (std::shared_ptr<AudioRegion> r, TriggerReference tr)
AudioClipEditor::set_region (std::shared_ptr<ARDOUR::Region> r)
{
set_region (std::dynamic_pointer_cast<ARDOUR::AudioRegion> (r));
}
void
AudioClipEditor::set_trigger (TriggerReference& tr)
{
TriggerPtr trigger (tr.trigger());
if (trigger == ref.trigger()) {
return;
}
ref = tr;
std::shared_ptr<AudioTrigger> at = std::dynamic_pointer_cast<AudioTrigger> (trigger);
if (at) {
minsec_ruler->show ();
minsec_ruler->set_range (0, pixel_to_sample (_visible_canvas_width - 2.));
} else {
minsec_ruler->hide ();
}
if (ref.trigger()->the_region()) {
std::shared_ptr<AudioRegion> ar = std::dynamic_pointer_cast<AudioRegion> (ref.trigger()->the_region());
if (ar) {
set_region (ar);
}
}
}
void
AudioClipEditor::set_region (std::shared_ptr<AudioRegion> r)
{
drop_waves ();
audio_region = r;
if (!audio_region) {
return;
}
/* Ruler has to reflect tempo of the region, so we have to recreate it
* every time. Note that we retain ownership of the metric, and that
* because the GUI is single-threaded, we can set it and delete it
@ -414,8 +411,8 @@ AudioClipEditor::set_region (std::shared_ptr<AudioRegion> r, TriggerReference tr
*/
delete clip_metric;
clip_metric = new ClipBBTMetric (tr);
ruler->set_metric (clip_metric);
clip_metric = new ClipBBTMetric (ref);
minsec_ruler->set_metric (clip_metric);
uint32_t n_chans = r->n_channels ();
samplecnt_t len;
@ -433,7 +430,7 @@ AudioClipEditor::set_region (std::shared_ptr<AudioRegion> r, TriggerReference tr
continue;
}
WaveView* wv = new WaveView (waves_container, war);
WaveView* wv = new WaveView (data_group, war);
wv->set_channel (n);
wv->set_show_zero_line (false);
wv->set_clip_level (1.0);
@ -442,75 +439,44 @@ AudioClipEditor::set_region (std::shared_ptr<AudioRegion> r, TriggerReference tr
waves.push_back (wv);
}
TriggerPtr trigger (tr.trigger());
std::shared_ptr<AudioTrigger> at = std::dynamic_pointer_cast<AudioTrigger> (trigger);
if (at) {
if (at->segment_tempo() == 0.) {
/* tempo unknown, hide ruler */
ruler->hide ();
} else {
ruler->show ();
ruler->set_range (0, pixel_to_sample (frame->get().width() - 2.));
}
} else {
ruler->hide ();
}
set_spp_from_length (len);
set_wave_heights ();
set_waveform_colors ();
line_container->show ();
line_container->raise_to_top ();
set_session (&r->session ());
state_connection.disconnect ();
PBD::PropertyChange interesting_stuff;
region_changed (interesting_stuff);
audio_region->PropertyChanged.connect (state_connection, invalidator (*this), std::bind (&AudioClipEditor::region_changed, this, _1), gui_context ());
}
void
AudioClipEditor::on_size_allocate (Gtk::Allocation& alloc)
AudioClipEditor::canvas_allocate (Gtk::Allocation& alloc)
{
GtkCanvas::on_size_allocate (alloc);
canvas.size_allocate (alloc);
ArdourCanvas::Rect r (1, 1, alloc.get_width () - 2, alloc.get_height () - 2);
frame->set (r);
_visible_canvas_width = alloc.get_width();
_visible_canvas_height = alloc.get_height();
const double ruler_height = 25.;
ruler->set (ArdourCanvas::Rect (2, 2, alloc.get_width() - 4, ruler_height));
const double scroll_bar_height = 10.;
const double scroll_bar_width = alloc.get_width () - 2;
const double scroll_bar_handle_left = scroll_bar_width * scroll_fraction;
scroll_bar_trough->set (ArdourCanvas::Rect (1, alloc.get_height () - scroll_bar_height, scroll_bar_width, alloc.get_height ()));
scroll_bar_handle->set (ArdourCanvas::Rect (scroll_bar_handle_left, scroll_bar_trough->get ().y0 + 1, scroll_bar_handle_left + 30., scroll_bar_trough->get ().y1 - 1));
minsec_ruler->set (ArdourCanvas::Rect (2, 2, alloc.get_width() - 4, timebar_height));
position_lines ();
start_line->set_y1 (frame->get ().height () - 2. - scroll_bar_height);
end_line->set_y1 (frame->get ().height () - 2. - scroll_bar_height);
loop_line->set_y1 (frame->get ().height () - 2. - scroll_bar_height);
start_line->set_y1 (_visible_canvas_height - 2.);
end_line->set_y1 (_visible_canvas_height - 2.);
loop_line->set_y1 (_visible_canvas_height - 2.);
set_wave_heights ();
}
void
AudioClipEditor::set_spp (double samples_per_pixel)
{
_spp = samples_per_pixel;
clip_metric->units_per_pixel = _spp;
position_lines ();
for (auto& wave : waves) {
wave->set_samples_per_pixel (_spp);
}
}
void
AudioClipEditor::set_spp_from_length (samplecnt_t len)
{
double available_width = frame->get ().width ();
double s = floor (len / available_width);
set_spp (s);
set_samples_per_pixel (floor (len / _visible_canvas_width));
}
void
@ -521,12 +487,12 @@ AudioClipEditor::set_wave_heights ()
}
uint32_t n = 0;
const Distance w = frame->get ().height () - scroll_bar_trough->get ().height () - 2. - ruler->get().height();
const Distance w = _visible_canvas_height - (n_timebars * timebar_height);
Distance ht = w / waves.size ();
for (auto& wave : waves) {
wave->set_height (ht);
wave->set_y_position (ruler->get ().height () + (n * ht));
wave->set_y_position ((n_timebars * timebar_height) + (n * ht));
++n;
}
}
@ -547,86 +513,69 @@ AudioClipEditor::set_waveform_colors ()
}
}
Gtk::Widget&
AudioClipEditor::viewport()
{
return _viewport;
}
Gtk::Widget&
AudioClipEditor::contents ()
{
return _contents;
}
void
AudioClipEditor::region_changed (const PBD::PropertyChange& what_changed)
{
}
void
AudioClipEditor::set_samples_per_pixel (samplecnt_t spp)
{
CueEditor::set_samples_per_pixel (spp);
clip_metric->units_per_pixel = samples_per_pixel;
position_lines ();
for (auto& wave : waves) {
wave->set_samples_per_pixel (samples_per_pixel);
}
horizontal_adjustment.set_upper (max_zoom_extent().second.samples() / samples_per_pixel);
horizontal_adjustment.set_page_size (current_page_samples()/ samples_per_pixel / 10);
horizontal_adjustment.set_page_increment (current_page_samples()/ samples_per_pixel / 20);
horizontal_adjustment.set_step_increment (current_page_samples() / samples_per_pixel / 100);
}
samplecnt_t
AudioClipEditor::current_page_samples() const
{
return (samplecnt_t) _track_canvas_width * samples_per_pixel;
}
bool
AudioClipEditor::event_handler (GdkEvent* ev)
AudioClipEditor::canvas_enter_leave (GdkEventCrossing* ev)
{
switch (ev->type) {
case GDK_BUTTON_PRESS:
break;
case GDK_ENTER_NOTIFY:
if (ev->detail != GDK_NOTIFY_INFERIOR) {
canvas.grab_focus ();
// ActionManager::set_sensitive (_midi_actions, true);
within_track_canvas = true;
}
break;
case GDK_LEAVE_NOTIFY:
break;
if (ev->detail != GDK_NOTIFY_INFERIOR) {
// ActionManager::set_sensitive (_midi_actions, false);
within_track_canvas = false;
ARDOUR_UI::instance()->reset_focus (&_viewport);
gdk_window_set_cursor (_viewport.get_window()->gobj(), nullptr);
}
default:
break;
}
return false;
}
AudioClipEditorBox::AudioClipEditorBox ()
{
_header_label.set_text (_("AUDIO Region Trimmer:"));
_header_label.set_alignment (0.0, 0.5);
zoom_in_button.set_icon (ArdourIcon::ZoomIn);
zoom_out_button.set_icon (ArdourIcon::ZoomOut);
zoom_in_button.signal_clicked.connect (sigc::mem_fun (*this, &AudioClipEditorBox::zoom_in_click));
zoom_out_button.signal_clicked.connect (sigc::mem_fun (*this, &AudioClipEditorBox::zoom_out_click));
header_box.pack_start (_header_label, false, false);
header_box.pack_start (zoom_in_button, false, false);
header_box.pack_start (zoom_out_button, false, false);
pack_start (header_box, false, false, 6);
editor = manage (new AudioClipEditor);
editor->set_size_request (600, 120);
pack_start (*editor, true, true);
editor->show ();
}
AudioClipEditorBox::~AudioClipEditorBox ()
{
delete editor;
}
void
AudioClipEditorBox::zoom_in_click ()
{
editor->set_spp (editor->spp () / 2.);
}
void
AudioClipEditorBox::zoom_out_click ()
{
editor->set_spp (editor->spp () * 2.);
}
void
AudioClipEditorBox::set_region (std::shared_ptr<Region> r, TriggerReference tref)
{
std::shared_ptr<AudioRegion> ar = std::dynamic_pointer_cast<AudioRegion> (r);
if (!ar) {
return;
}
set_session (&r->session ());
state_connection.disconnect ();
_region = r;
editor->set_region (ar, tref);
PBD::PropertyChange interesting_stuff;
region_changed (interesting_stuff);
_region->PropertyChanged.connect (state_connection, invalidator (*this), std::bind (&AudioClipEditorBox::region_changed, this, _1), gui_context ());
}
void
AudioClipEditorBox::region_changed (const PBD::PropertyChange& what_changed)
{
}

View file

@ -25,6 +25,8 @@
#include <ytkmm/label.h>
#include <ytkmm/table.h>
#include "pbd/history_owner.h"
#include "ardour/ardour.h"
#include "ardour/session_handle.h"
#include "ardour/triggerbox.h"
@ -44,6 +46,7 @@
#include "canvas/scroll_group.h"
#include "audio_clock.h"
#include "cue_editor.h"
namespace ARDOUR
{
@ -63,59 +66,104 @@ namespace ArdourWaveView
class WaveView;
}
class ClipEditorBox : public Gtk::VBox, public ARDOUR::SessionHandlePtr
class AudioClipEditor : public CueEditor
{
public:
ClipEditorBox () {}
~ClipEditorBox () {}
virtual void set_region (std::shared_ptr<ARDOUR::Region>, ARDOUR::TriggerReference) = 0;
static void init ();
static void register_clip_editor_actions (Gtkmm2ext::Bindings*);
static Glib::RefPtr<Gtk::ActionGroup> clip_editor_actions;
};
class ClipEditor
{
public:
virtual ~ClipEditor () {}
virtual void zoom_in () = 0;
virtual void zoom_out () = 0;
};
class AudioClipEditor : public ArdourCanvas::GtkCanvas
{
public:
AudioClipEditor ();
AudioClipEditor (std::string const &, bool with_transport);
~AudioClipEditor ();
void set_region (std::shared_ptr<ARDOUR::AudioRegion>, ARDOUR::TriggerReference);
void on_size_allocate (Gtk::Allocation&);
void canvas_allocate (Gtk::Allocation&);
Gtk::Widget& viewport();
Gtk::Widget& contents ();
void set_trigger (ARDOUR::TriggerReference&);
void set_region (std::shared_ptr<ARDOUR::AudioRegion>);
void set_region (std::shared_ptr<ARDOUR::Region> r);
void region_changed (const PBD::PropertyChange& what_changed);
double sample_to_pixel (ARDOUR::samplepos_t);
samplepos_t pixel_to_sample (double);
void set_spp (double);
double spp () const
{
return _spp;
}
bool key_press (GdkEventKey*);
bool minsec_ruler_event (GdkEvent*);
/* EditingContext API. As of July 2025, we do not implement most of
* these
*/
bool button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool button_press_handler_1 (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool button_press_handler_2 (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool button_press_dispatch (GdkEventButton*) { return true; }
bool button_release_dispatch (GdkEventButton*) { return true; }
bool motion_handler (ArdourCanvas::Item*, GdkEvent*, bool from_autoscroll = false) { return true; }
bool enter_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool leave_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; }
bool canvas_note_event (GdkEvent* event, ArdourCanvas::Item*) { return true; }
bool canvas_velocity_base_event (GdkEvent* event, ArdourCanvas::Item*) { return true; }
bool canvas_velocity_event (GdkEvent* event, ArdourCanvas::Item*) { return true; }
bool canvas_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) { return true; }
bool canvas_bg_event (GdkEvent* event, ArdourCanvas::Item*) { return true; }
ArdourCanvas::Container* get_trackview_group () const;
ArdourCanvas::Container* get_noscroll_group() const;
ArdourCanvas::ScrollGroup* get_hscroll_group () const;
ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const;
samplecnt_t current_page_samples() const;
double visible_canvas_width() const;
void set_samples_per_pixel (samplecnt_t);
ArdourCanvas::GtkCanvasViewport* get_canvas_viewport() const { return const_cast<ArdourCanvas::GtkCanvasViewport*> (&_viewport); }
ArdourCanvas::GtkCanvas* get_canvas() const { return &canvas; }
std::pair<Temporal::timepos_t,Temporal::timepos_t> max_zoom_extent() const;
Gdk::Cursor* which_track_cursor () const { return nullptr; }
Gdk::Cursor* which_mode_cursor () const { return nullptr; }
Gdk::Cursor* which_trim_cursor (bool left_side) const { return nullptr; }
Gdk::Cursor* which_canvas_cursor (ItemType type) const { return nullptr; }
RegionSelection region_selection();
Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, Temporal::RoundMode direction, ARDOUR::SnapPref gpref) const { return start; }
void snap_to_internal (Temporal::timepos_t& first, Temporal::RoundMode direction = Temporal::RoundNearest, ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, bool ensure_snap = false) const {}
void select_all_within (Temporal::timepos_t const &, Temporal::timepos_t const &, double, double, std::list<SelectableOwner*> const &, ARDOUR::SelectionOperation, bool) {}
void get_per_region_note_selection (std::list<std::pair<PBD::ID, std::set<std::shared_ptr<Evoral::Note<Temporal::Beats> > > > >&) const {}
void get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const {}
void maybe_autoscroll (bool, bool, bool from_headers) {};
void stop_canvas_autoscroll () {}
void redisplay_grid (bool immediate_redraw) {};
void instant_save() {};
void point_selection_changed () {}
void step_mouse_mode (bool next);
void mouse_mode_toggled (Editing::MouseMode);
void delete_ () {}
void paste (float times, bool from_context_menu) {}
void keyboard_paste () {}
void cut_copy (Editing::CutCopyOp) {}
void register_actions() {}
void visual_changer (const VisualChange&) {}
void build_zoom_focus_menu () {}
private:
ArdourCanvas::Rectangle* frame;
ArdourCanvas::ScrollGroup* waves_container;
ArdourCanvas::GtkCanvasViewport _viewport;
ArdourCanvas::GtkCanvas& canvas;
ArdourCanvas::Container* line_container;
ArdourCanvas::Line* start_line;
ArdourCanvas::Line* end_line;
ArdourCanvas::Line* loop_line;
ArdourCanvas::Rectangle* scroll_bar_trough;
ArdourCanvas::Rectangle* scroll_bar_handle;
ArdourCanvas::Container* ruler_container;
ArdourCanvas::Ruler* ruler;
ArdourCanvas::Ruler* minsec_ruler;
class ClipBBTMetric : public ArdourCanvas::Ruler::Metric
{
@ -135,7 +183,6 @@ private:
std::vector<ArdourWaveView::WaveView*> waves;
double non_wave_height;
samplepos_t left_origin;
double _spp;
double scroll_fraction;
std::shared_ptr<ARDOUR::AudioRegion> audio_region;
@ -150,7 +197,6 @@ private:
bool event_handler (GdkEvent* ev);
bool line_event_handler (GdkEvent* ev, ArdourCanvas::Line*);
bool scroll_event_handler (GdkEvent* ev);
void drop_waves ();
void set_wave_heights ();
void set_spp_from_length (ARDOUR::samplecnt_t);
@ -176,47 +222,12 @@ private:
friend class LineDrag;
LineDrag* current_line_drag;
class ScrollDrag
{
public:
ScrollDrag (AudioClipEditor&);
void begin (GdkEventButton*);
void end (GdkEventButton*);
void motion (GdkEventMotion*);
private:
AudioClipEditor& editor;
double last_x;
};
friend class ScrollDrag;
ScrollDrag* current_scroll_drag;
};
class AudioClipEditorBox : public ClipEditorBox
{
public:
AudioClipEditorBox ();
~AudioClipEditorBox ();
void set_region (std::shared_ptr<ARDOUR::Region>, ARDOUR::TriggerReference);
void region_changed (const PBD::PropertyChange& what_changed);
private:
Gtk::HBox header_box;
ArdourWidgets::ArdourButton zoom_in_button;
ArdourWidgets::ArdourButton zoom_out_button;
Gtk::Label _header_label;
Gtk::Table table;
AudioClipEditor* editor;
PBD::ScopedConnection state_connection;
std::shared_ptr<ARDOUR::Region> _region;
void build_canvas ();
void build_lower_toolbar ();
void pack_inner (Gtk::Box&);
void pack_outer (Gtk::Box&);
void zoom_in_click ();
void zoom_out_click ();
bool canvas_enter_leave (GdkEventCrossing* ev);
};

View file

@ -112,29 +112,31 @@ AudioTriggerPropertiesBox::AudioTriggerPropertiesBox ()
eTempoBox->show_all();
Gtk::Table* audio_t = manage (new Gtk::Table ());
audio_t->set_homogeneous (true);
audio_t->set_spacings (4);
/* -------------- Clip start&length (redundant with the trimmer gui handles?) ----------*/
row = 0;
label = manage (new Gtk::Label (_("Start:")));
label->set_alignment (1.0, 0.5);
_table.attach (*label, 0, 1, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
_table.attach (_start_clock, 1, 2, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
audio_t->attach (*label, 0, 1, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
audio_t->attach (_start_clock, 1, 2, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
row++;
label = manage (new Gtk::Label (_("Clip Length:")));
label->set_alignment (1.0, 0.5);
_table.attach (*label, 0, 1, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
_table.attach (_length_clock, 1, 2, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
audio_t->attach (*label, 0, 1, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
audio_t->attach (_length_clock, 1, 2, row, row + 1, Gtk::SHRINK, Gtk::SHRINK);
row++;
_table.set_homogeneous (false);
_table.set_spacings (4);
_table.set_border_width (2);
audio_t->set_homogeneous (false);
audio_t->set_spacings (4);
audio_t->set_border_width (2);
attach (*eTempoBox, 0,1, 0,1, Gtk::FILL, Gtk::EXPAND | Gtk::FILL);
#if 0
attach (_table, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
#endif
attach (*audio_t, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
_start_clock.ValueChanged.connect (sigc::mem_fun (*this, &AudioTriggerPropertiesBox::start_clock_changed));
_length_clock.ValueChanged.connect (sigc::mem_fun (*this, &AudioTriggerPropertiesBox::length_clock_changed));

View file

@ -17,8 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _gtk_ardour_audio_trigger_properties_box_h_
#define _gtk_ardour_audio_trigger_properties_box_h_
#pragma once
#include <ytkmm/box.h>
#include <ytkmm/label.h>
@ -34,18 +33,7 @@
#include "audio_clock.h"
#include "trigger_ui.h"
class TriggerPropertiesBox : public Gtk::Table, public ARDOUR::SessionHandlePtr, public TriggerUI
{
public:
TriggerPropertiesBox () {}
~TriggerPropertiesBox () {}
protected:
Gtk::Label _header_label;
PBD::ScopedConnection _state_connection;
};
#include "trigger_properties_box.h"
class AudioTriggerPropertiesBox : public TriggerPropertiesBox
{
@ -72,8 +60,6 @@ private:
void MultiplyTempo(float mult);
Gtk::Table _table;
AudioClock _length_clock;
AudioClock _start_clock;
@ -98,5 +84,3 @@ private:
bool _ignore_changes;
};
#endif

View file

@ -1,15 +1,49 @@
/*
* 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.
*/
#include "widgets/ardour_icon.h"
#include "widgets/tooltips.h"
#include "ardour/types.h"
#include "canvas/canvas.h"
#include "cue_editor.h"
#include "editor_drag.h"
#include "gui_thread.h"
#include "ui_config.h"
#include "pbd/i18n.h"
CueEditor::CueEditor (std::string const & name)
using namespace ARDOUR;
using namespace ArdourWidgets;
CueEditor::CueEditor (std::string const & name, bool with_transport)
: EditingContext (name)
, HistoryOwner (X_("cue-editor"))
, HistoryOwner (name)
, with_transport_controls (with_transport)
, length_label (X_("Record:"))
, solo_button (S_("Solo|S"))
, zoom_in_allocate (false)
, timebar_height (15.)
, n_timebars (0)
{
_history.Changed.connect (history_connection, invalidator (*this), std::bind (&CueEditor::history_changed, this), gui_context());
set_zoom_focus (Editing::ZoomFocusLeft);
}
@ -71,22 +105,6 @@ CueEditor::find_marker_for_meter (Temporal::MeterPoint const &)
return nullptr;
}
void
CueEditor::maybe_autoscroll (bool, bool, bool from_headers)
{
}
void
CueEditor::stop_canvas_autoscroll ()
{
}
bool
CueEditor::autoscroll_active() const
{
return false;
}
void
CueEditor::redisplay_grid (bool immediate_redraw)
{
@ -263,3 +281,695 @@ CueEditor::_get_preferred_edit_position (Editing::EditIgnoreOption ignore, bool
return Temporal::timepos_t (where);
}
void
CueEditor::build_upper_toolbar ()
{
using namespace Gtk::Menu_Helpers;
Gtk::HBox* mode_box = manage(new Gtk::HBox);
mode_box->set_border_width (2);
mode_box->set_spacing(2);
Gtk::HBox* mouse_mode_box = manage (new Gtk::HBox);
Gtk::HBox* mouse_mode_hbox = manage (new Gtk::HBox);
Gtk::VBox* mouse_mode_vbox = manage (new Gtk::VBox);
Gtk::Alignment* mouse_mode_align = manage (new Gtk::Alignment);
Glib::RefPtr<Gtk::SizeGroup> mouse_mode_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_VERTICAL);
mouse_mode_size_group->add_widget (mouse_draw_button);
mouse_mode_size_group->add_widget (mouse_content_button);
mouse_mode_size_group->add_widget (grid_type_selector);
mouse_mode_size_group->add_widget (draw_length_selector);
mouse_mode_size_group->add_widget (draw_velocity_selector);
mouse_mode_size_group->add_widget (draw_channel_selector);
mouse_mode_size_group->add_widget (snap_mode_button);
mouse_mode_hbox->set_spacing (2);
mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
mouse_mode_hbox->pack_start (mouse_content_button, false, false);
mouse_mode_vbox->pack_start (*mouse_mode_hbox);
mouse_mode_align->add (*mouse_mode_vbox);
mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
mouse_mode_box->pack_start (*mouse_mode_align, false, false);
pack_snap_box ();
pack_draw_box (false);
Gtk::HBox* _toolbar_inner = manage (new Gtk::HBox);
Gtk::HBox* _toolbar_outer = manage (new Gtk::HBox);
Gtk::HBox* _toolbar_left = manage (new Gtk::HBox);
_toolbar_inner->pack_start (*mouse_mode_box, false, false);
pack_inner (*_toolbar_inner);
set_tooltip (full_zoom_button, _("Zoom to full clip"));
set_tooltip (note_mode_button, _("Toggle between drum and regular note drawing"));
play_button.set_icon (ArdourIcon::TransportPlay);
play_button.set_name ("transport button");
play_button.show();
if (with_transport_controls) {
loop_button.set_icon (ArdourIcon::TransportLoop);
loop_button.set_name ("transport button");
solo_button.set_name ("solo button");
play_box.set_spacing (8);
play_box.pack_start (play_button, false, false);
play_box.pack_start (loop_button, false, false);
play_box.pack_start (solo_button, false, false);
loop_button.show();
solo_button.show();
play_box.set_no_show_all (true);
play_box.show ();
play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::play_button_press), false);
solo_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::solo_button_press), false);
loop_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::loop_button_press), false);
} else {
rec_box.pack_start (play_button, false, false);
play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::bang_button_press), false);
}
rec_enable_button.set_icon (ArdourIcon::RecButton);
rec_enable_button.set_sensitive (false);
rec_enable_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::rec_button_press), false);
rec_enable_button.set_name ("record enable button");
length_selector.add_menu_elem (MenuElem (_("Until Stopped"), sigc::bind (sigc::mem_fun (*this, &CueEditor::set_recording_length), Temporal::BBT_Offset ())));
length_selector.add_menu_elem (MenuElem (_("1 Bar"), sigc::bind (sigc::mem_fun (*this, &CueEditor::set_recording_length), Temporal::BBT_Offset (1, 0, 0))));
std::vector<int> b ({ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 24, 32 });
for (auto & n : b) {
length_selector.add_menu_elem (MenuElem (string_compose (_("%1 Bars"), n), sigc::bind (sigc::mem_fun (*this, &CueEditor::set_recording_length), Temporal::BBT_Offset (n, 0, 0))));
}
length_selector.set_active (_("Until Stopped"));
rec_box.set_spacing (12);
rec_box.pack_start (rec_enable_button, false, false);
rec_box.pack_start (length_label, false, false);
rec_box.pack_start (length_selector, false, false);
rec_enable_button.show();
length_label.show ();
length_selector.show ();
rec_box.set_no_show_all (true);
/* rec box not shown */
_toolbar_outer->set_border_width (6);
_toolbar_outer->set_spacing (12);
pack_outer (*_toolbar_outer);
_toolbar_outer->pack_start (*_toolbar_inner, true, false);
build_zoom_focus_menu ();
zoom_focus_selector.set_text (zoom_focus_strings[(int)_zoom_focus]);
_toolbar_left->pack_start (zoom_in_button, false, false);
_toolbar_left->pack_start (zoom_out_button, false, false);
_toolbar_left->pack_start (full_zoom_button, false, false);
_toolbar_left->pack_start (zoom_focus_selector, false, false);
_toolbar_outer->pack_start (*_toolbar_left, true, false);
_toolbox.pack_start (*_toolbar_outer, false, false);
_contents.add (_toolbox);
_contents.signal_unmap().connect ([this]() {viewport().unmap (); }, false);
_contents.signal_map().connect ([this]() { viewport().map (); }, false);
}
void
CueEditor::build_zoom_focus_menu ()
{
using namespace Gtk::Menu_Helpers;
using namespace Editing;
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
zoom_focus_selector.set_sizing_texts (zoom_focus_strings);
}
bool
CueEditor::play_button_press (GdkEventButton* ev)
{
#warning paul fix lookup region via CueEditor 87
#if 0
if (_session) {
_session->request_locate (view->midi_region()->position().samples());
_session->request_roll ();
}
#endif
return true;
}
bool
CueEditor::loop_button_press (GdkEventButton* ev)
{
#warning paul fix region lookup via CueEditor 1
#if 0
if (!view) {
return true;
}
if (!view->midi_region()) {
return true;
}
if (_session->get_play_loop()) {
_session->request_play_loop (false);
} else {
set_loop_range (view->midi_region()->position(), view->midi_region()->end(), _("loop region"));
_session->request_play_loop (true);
}
#endif
return true;
}
bool
CueEditor::solo_button_press (GdkEventButton* ev)
{
#warning paul fix region lookup via CueEditor 2
#if 0
if (!view) {
return true;
}
if (!view->midi_track()) {
return true;
}
view->midi_track()->solo_control()->set_value (!view->midi_track()->solo_control()->get_value(), Controllable::NoGroup);
#endif
return true;
}
bool
CueEditor::rec_button_press (GdkEventButton* ev)
{
#warning paul fix trigger lookup via CueEditor 1
#if 0
if (ev->button != 1) {
return false;
}
TriggerPtr trigger (ref.trigger());
if (!trigger) {
return true;
}
if (trigger->armed()) {
trigger->disarm ();
} else {
trigger->arm (rec_length);
}
#endif
return true;
}
void
CueEditor::blink_rec_enable (bool onoff)
{
if (onoff) {
rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive);
} else {
rec_enable_button.set_active_state (Gtkmm2ext::Off);
}
}
void
CueEditor::trigger_arm_change ()
{
#warning paul fix trigger lookup via CueEditor 1
#if 0
if (!ref.trigger()) {
return;
}
if (!ref.trigger()->armed()) {
view->end_write ();
} else {
maybe_set_count_in ();
}
#endif
rec_enable_change ();
}
void
CueEditor::rec_enable_change ()
{
#warning paul fix trigger lookup via CueEditor 1
#if 0
if (!ref.box()) {
std::cerr << "no box!\n";
return;
}
rec_blink_connection.disconnect ();
count_in_connection.disconnect ();
std::cerr << "REC, state " << ref.box()->record_enabled() << std::endl;
switch (ref.box()->record_enabled()) {
case Recording:
rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive);
rec_blink_connection.disconnect ();
if (view) {
view->begin_write ();
}
break;
case Enabled:
if (!UIConfiguration::instance().get_no_strobe() && ref.trigger()->armed()) {
rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &CueEditor::blink_rec_enable));
} else {
rec_enable_button.set_active_state (Gtkmm2ext::Off);
}
maybe_set_count_in ();
break;
case Disabled:
rec_enable_button.set_active_state (Gtkmm2ext::Off);
break;
}
#endif
}
void
CueEditor::set_recording_length (Temporal::BBT_Offset dur)
{
rec_length = dur;
}
bool
CueEditor::bang_button_press (GdkEventButton* ev)
{
#warning paul fix trigger look from CueEditor 93
#if 0
if (!ref.trigger()) {
return true;
}
ref.trigger()->bang ();
#endif
return true;
}
void
CueEditor::scrolled ()
{
pending_visual_change.add (VisualChange::TimeOrigin);
pending_visual_change.time_origin = horizontal_adjustment.get_value() * samples_per_pixel;
ensure_visual_change_idle_handler ();
}
bool
CueEditor::canvas_pre_event (GdkEvent* ev)
{
switch (ev->type) {
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
if (canvas_enter_leave (&ev->crossing)) {
return true;
}
break;
default:
break;
}
return false;
}
bool
CueEditor::autoscroll_active () const
{
return autoscroll_connection.connected ();
}
/** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
*
* @param allow_vert true to allow vertical autoscroll, otherwise false.
*
*/
void
CueEditor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
{
if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
return;
}
/* define a rectangular boundary for scrolling. If the mouse moves
* outside of this area and/or continue to be outside of this area,
* then we will continuously auto-scroll the canvas in the appropriate
* direction(s)
*
* the boundary is defined in coordinates relative to canvas' own
* window since that is what we're going to call ::get_pointer() on
* during autoscrolling to determine if we're still outside the
* boundary or not.
*/
ArdourCanvas::Rect scrolling_boundary;
Gtk::Allocation alloc;
alloc = get_canvas()->get_allocation ();
alloc.set_x (0);
alloc.set_y (0);
if (allow_vert) {
/* reduce height by the height of the timebars, which happens
to correspond to the position of the data_group.
*/
alloc.set_height (alloc.get_height() - data_group->position().y);
alloc.set_y (alloc.get_y() + data_group->position().y);
/* now reduce it again so that we start autoscrolling before we
* move off the top or bottom of the canvas
*/
alloc.set_height (alloc.get_height() - 20);
alloc.set_y (alloc.get_y() + 10);
}
if (allow_horiz && (alloc.get_width() > 20)) {
#warning paul fix use of PRH in CueEditor context
#if 0
if (prh) {
double w, h;
prh->size_request (w, h);
alloc.set_width (alloc.get_width() - w);
alloc.set_x (alloc.get_x() + w);
}
#endif
/* the effective width of the autoscroll boundary so
that we start scrolling before we hit the edge.
this helps when the window is slammed up against the
right edge of the screen, making it hard to scroll
effectively.
*/
alloc.set_width (alloc.get_width() - 20);
alloc.set_x (alloc.get_x() + 10);
}
scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
int x, y;
Gdk::ModifierType mask;
get_canvas()->get_window()->get_pointer (x, y, mask);
if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) ||
(allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
}
}
bool
CueEditor::autoscroll_canvas ()
{
using std::max;
using std::min;
int x, y;
Gdk::ModifierType mask;
sampleoffset_t dx = 0;
bool no_stop = false;
Gtk::Window* toplevel = dynamic_cast<Gtk::Window*> (viewport().get_toplevel());
if (!toplevel) {
return false;
}
get_canvas()->get_window()->get_pointer (x, y, mask);
VisualChange vc;
bool vertical_motion = false;
if (autoscroll_horizontal_allowed) {
samplepos_t new_sample = _leftmost_sample;
/* horizontal */
if (x > autoscroll_boundary.x1) {
/* bring it back into view */
dx = x - autoscroll_boundary.x1;
dx += 10 + (2 * (autoscroll_cnt/2));
dx = pixel_to_sample (dx);
dx *= UIConfiguration::instance().get_draggable_playhead_speed();
if (_leftmost_sample < max_samplepos - dx) {
new_sample = _leftmost_sample + dx;
} else {
new_sample = max_samplepos;
}
no_stop = true;
} else if (x < autoscroll_boundary.x0) {
dx = autoscroll_boundary.x0 - x;
dx += 10 + (2 * (autoscroll_cnt/2));
dx = pixel_to_sample (dx);
dx *= UIConfiguration::instance().get_draggable_playhead_speed();
if (_leftmost_sample >= dx) {
new_sample = _leftmost_sample - dx;
} else {
new_sample = 0;
}
no_stop = true;
}
if (new_sample != _leftmost_sample) {
vc.time_origin = new_sample;
vc.add (VisualChange::TimeOrigin);
}
}
if (autoscroll_vertical_allowed) {
// const double vertical_pos = vertical_adjustment.get_value();
const int speed_factor = 10;
/* vertical */
if (y < autoscroll_boundary.y0) {
/* scroll to make higher tracks visible */
if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
// XXX SCROLL UP
vertical_motion = true;
}
no_stop = true;
} else if (y > autoscroll_boundary.y1) {
if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
// XXX SCROLL DOWN
vertical_motion = true;
}
no_stop = true;
}
}
if (vc.pending || vertical_motion) {
/* change horizontal first */
if (vc.pending) {
visual_changer (vc);
}
/* now send a motion event to notify anyone who cares
that we have moved to a new location (because we scrolled)
*/
GdkEventMotion ev;
ev.type = GDK_MOTION_NOTIFY;
ev.state = Gdk::BUTTON1_MASK;
/* the motion handler expects events in canvas coordinate space */
/* we asked for the mouse position above (::get_pointer()) via
* our own top level window (we being the Editor). Convert into
* coordinates within the canvas window.
*/
int cx;
int cy;
//toplevel->translate_coordinates (*get_canvas(), x, y, cx,
//cy);
cx = x;
cy = y;
/* clamp x and y to remain within the autoscroll boundary,
* which is defined in window coordinates
*/
x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
/* now convert from Editor window coordinates to canvas
* window coordinates
*/
ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy));
ev.x = d.x;
ev.y = d.y;
ev.state = mask;
motion_handler (0, (GdkEvent*) &ev, true);
} else if (no_stop) {
/* not changing visual state but pointer is outside the scrolling boundary
* so we still need to deliver a fake motion event
*/
GdkEventMotion ev;
ev.type = GDK_MOTION_NOTIFY;
ev.state = Gdk::BUTTON1_MASK;
/* the motion handler expects events in canvas coordinate space */
/* first convert from Editor window coordinates to canvas
* window coordinates
*/
int cx;
int cy;
/* clamp x and y to remain within the visible area. except
* .. if horizontal scrolling is allowed, always allow us to
* move back to zero
*/
if (autoscroll_horizontal_allowed) {
x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
} else {
x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
}
y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
// toplevel->translate_coordinates (*get_canvas_viewport(), x,
// y, cx, cy);
cx = x;
cy = y;
ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy));
ev.x = d.x;
ev.y = d.y;
ev.state = mask;
motion_handler (0, (GdkEvent*) &ev, true);
} else {
stop_canvas_autoscroll ();
return false;
}
autoscroll_cnt++;
return true; /* call me again */
}
void
CueEditor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
{
if (!_session) {
return;
}
stop_canvas_autoscroll ();
autoscroll_horizontal_allowed = allow_horiz;
autoscroll_vertical_allowed = allow_vert;
autoscroll_boundary = boundary;
/* do the first scroll right now
*/
autoscroll_canvas ();
/* scroll again at very very roughly 30FPS */
autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &CueEditor::autoscroll_canvas), 30);
}
void
CueEditor::stop_canvas_autoscroll ()
{
autoscroll_connection.disconnect ();
autoscroll_cnt = 0;
}
void
CueEditor::visual_changer (const VisualChange& vc)
{
/**
* Changed first so the correct horizontal canvas position is calculated in
* EditingContext::set_horizontal_position
*/
if (vc.pending & VisualChange::ZoomLevel) {
set_samples_per_pixel (vc.samples_per_pixel);
}
if (vc.pending & VisualChange::TimeOrigin) {
double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
set_horizontal_position (new_time_origin);
update_rulers ();
}
if (vc.pending & VisualChange::YOrigin) {
vertical_adjustment.set_value (vc.y_origin);
}
if (vc.pending & VisualChange::ZoomLevel) {
if (!(vc.pending & VisualChange::TimeOrigin)) {
update_rulers ();
}
} else {
/* If the canvas is not being zoomed then the canvas items will not change
* and cause Item::prepare_for_render to be called so do it here manually.
* Not ideal, but I can't think of a better solution atm.
*/
get_canvas()->prepare_for_render();
}
/* If we are only scrolling vertically there is no need to update these */
if (vc.pending != VisualChange::YOrigin) {
// XXX update_fixed_rulers ();
redisplay_grid (true);
}
}
void
CueEditor::catch_pending_show_region ()
{
if (_visible_pending_region) {
set_region (_visible_pending_region);
_visible_pending_region.reset ();
}
}

View file

@ -20,15 +20,25 @@
#include "pbd/history_owner.h"
#include "widgets/ardour_button.h"
#include "widgets/eventboxext.h"
#include "editing.h"
#include "editing_context.h"
namespace Gtk {
class HScrollbar;
}
class CueEditor : public EditingContext, public PBD::HistoryOwner
{
public:
CueEditor (std::string const & name);
CueEditor (std::string const & name, bool with_transport_controls);
~CueEditor ();
virtual Gtk::Widget& viewport() = 0;
virtual Gtk::Widget& contents () = 0;
void get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const;
StripableTimeAxisView* get_stripable_time_axis_by_id (const PBD::ID& id) const;
TrackViewList axis_views_from_routes (std::shared_ptr<ARDOUR::RouteList>) const;
@ -46,6 +56,7 @@ class CueEditor : public EditingContext, public PBD::HistoryOwner
void redisplay_grid (bool immediate_redraw);
Temporal::timecnt_t get_nudge_distance (Temporal::timepos_t const & pos, Temporal::timecnt_t& next) const;
std::list<SelectableOwner*> selectable_owners() { return std::list<SelectableOwner*>(); }
void instant_save();
@ -97,10 +108,89 @@ class CueEditor : public EditingContext, public PBD::HistoryOwner
std::shared_ptr<Temporal::TempoMap const> start_local_tempo_map (std::shared_ptr<Temporal::TempoMap>);
void end_local_tempo_map (std::shared_ptr<Temporal::TempoMap const>);
void scrolled ();
bool canvas_pre_event (GdkEvent*);
void catch_pending_show_region ();
protected:
ARDOUR::TriggerReference ref;
std::shared_ptr<ARDOUR::MidiTrack> _track;
bool with_transport_controls;
ArdourWidgets::EventBoxExt _contents;
Gtk::VBox _toolbox;
Gtk::HBox button_bar;
Gtk::HScrollbar* _canvas_hscrollbar;
/* The group containing all other groups that are scrolled vertically
and horizontally.
*/
ArdourCanvas::ScrollGroup* hv_scroll_group;
/* 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
*/
ArdourCanvas::ScrollGroup* cursor_scroll_group;
ArdourCanvas::Container* global_rect_group;
ArdourCanvas::Container* no_scroll_group;
ArdourCanvas::Container* data_group;
Gtk::Label length_label;
Gtk::HBox rec_box;
Gtk::HBox play_box;
virtual void pack_inner (Gtk::Box&) = 0;
virtual void pack_outer (Gtk::Box&) = 0;
void build_zoom_focus_menu ();
virtual void update_rulers() {}
virtual bool canvas_enter_leave (GdkEventCrossing* ev) = 0;
void build_upper_toolbar ();
void do_undo (uint32_t n);
void do_redo (uint32_t n);
Temporal::timepos_t _get_preferred_edit_position (Editing::EditIgnoreOption, bool use_context_click, bool from_outside_canvas);
};
ArdourWidgets::ArdourButton rec_enable_button;
ArdourWidgets::ArdourButton play_button;
ArdourWidgets::ArdourButton solo_button;
ArdourWidgets::ArdourButton loop_button;
ArdourCanvas::Rectangle* transport_loop_range_rect;
bool play_button_press (GdkEventButton*);
bool solo_button_press (GdkEventButton*);
bool bang_button_press (GdkEventButton*);
bool loop_button_press (GdkEventButton*);
ArdourWidgets::ArdourDropdown length_selector;
Temporal::BBT_Offset rec_length;
bool zoom_in_allocate;
void set_recording_length (Temporal::BBT_Offset bars);
virtual void set_region (std::shared_ptr<ARDOUR::Region>) = 0;
bool rec_button_press (GdkEventButton*);
void rec_enable_change ();
void blink_rec_enable (bool);
sigc::connection rec_blink_connection;
void trigger_arm_change ();
double timebar_height;
size_t n_timebars;
/* autoscrolling */
bool autoscroll_canvas ();
void start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary);
void visual_changer (const VisualChange&);
std::shared_ptr<ARDOUR::Region> _visible_pending_region;
};

View file

@ -258,6 +258,9 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
double timeline_to_canvas (double p) const { return p + _timeline_origin; }
double canvas_to_timeline (double p) const { return p - _timeline_origin; }
double visible_canvas_width () const { return _visible_canvas_width; }
double visible_canvas_height () const { return _visible_canvas_height; }
/** computes the timeline position for an event whose coordinates
* are in canvas units (pixels, scroll offset included). The time
* domain used by the return value will match ::default_time_domain()
@ -633,7 +636,6 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
bool ensure_snap = false) const = 0;
void check_best_snap (Temporal::timepos_t const & presnap, Temporal::timepos_t &test, Temporal::timepos_t &dist, Temporal::timepos_t &best) const;
virtual double visible_canvas_width() const = 0;
enum BBTRulerScale {
bbt_show_many,

View file

@ -177,9 +177,6 @@ public:
return (samplecnt_t) _visible_canvas_width* samples_per_pixel;
}
double visible_canvas_height () const {
return _visible_canvas_height;
}
double trackviews_height () const;
XMLNode& get_state () const;
@ -2125,8 +2122,6 @@ private:
Temporal::timepos_t snap_to_marker (Temporal::timepos_t const & presnap,
Temporal::RoundMode direction = Temporal::RoundNearest) const;
double visible_canvas_width() const { return _visible_canvas_width; }
RhythmFerret* rhythm_ferret;
void fit_tracks (TrackViewList &);

View file

@ -25,7 +25,7 @@
#include "ardour/ardour.h"
#include "ardour/triggerbox.h"
#include "audio_trigger_properties_box.h"
#include "trigger_properties_box.h"
class MidiTriggerPropertiesBox : public TriggerPropertiesBox
{

View file

@ -66,19 +66,13 @@ using namespace Gtkmm2ext;
using namespace Temporal;
Pianoroll::Pianoroll (std::string const & name, bool with_transport)
: CueEditor (name)
, timebar_height (15.)
, n_timebars (0)
: CueEditor (name, with_transport)
, prh (nullptr)
, bg (nullptr)
, view (nullptr)
, bbt_metric (*this)
, _note_mode (Sustained)
, zoom_in_allocate (false)
, solo_button (S_("Solo|S"))
, length_label (X_("Record:"))
, ignore_channel_changes (false)
, with_transport_controls (with_transport)
, show_source (false)
{
mouse_mode = Editing::MouseContent;
@ -148,23 +142,6 @@ Pianoroll::get_canvas() const
return _canvas;
}
bool
Pianoroll::canvas_pre_event (GdkEvent* ev)
{
switch (ev->type) {
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
if (canvas_enter_leave (&ev->crossing)) {
return true;
}
break;
default:
break;
}
return false;
}
void
Pianoroll::rebuild_parameter_button_map()
{
@ -274,15 +251,6 @@ Pianoroll::add_multi_controller_item (Gtk::Menu_Helpers::MenuList&,
mb->add_item (name, menu_text, *chn_menu, [](){});
}
void
Pianoroll::scrolled ()
{
pending_visual_change.add (VisualChange::TimeOrigin);
pending_visual_change.time_origin = horizontal_adjustment.get_value() * samples_per_pixel;
ensure_visual_change_idle_handler ();
}
void
Pianoroll::build_lower_toolbar ()
{
@ -355,145 +323,24 @@ Pianoroll::build_lower_toolbar ()
}
void
Pianoroll::build_upper_toolbar ()
Pianoroll::pack_inner (Gtk::Box& box)
{
using namespace Gtk::Menu_Helpers;
Gtk::HBox* mode_box = manage(new Gtk::HBox);
mode_box->set_border_width (2);
mode_box->set_spacing(2);
Gtk::HBox* mouse_mode_box = manage (new Gtk::HBox);
Gtk::HBox* mouse_mode_hbox = manage (new Gtk::HBox);
Gtk::VBox* mouse_mode_vbox = manage (new Gtk::VBox);
Gtk::Alignment* mouse_mode_align = manage (new Gtk::Alignment);
Glib::RefPtr<Gtk::SizeGroup> mouse_mode_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_VERTICAL);
mouse_mode_size_group->add_widget (mouse_draw_button);
mouse_mode_size_group->add_widget (mouse_content_button);
mouse_mode_size_group->add_widget (grid_type_selector);
mouse_mode_size_group->add_widget (draw_length_selector);
mouse_mode_size_group->add_widget (draw_velocity_selector);
mouse_mode_size_group->add_widget (draw_channel_selector);
mouse_mode_size_group->add_widget (snap_mode_button);
mouse_mode_hbox->set_spacing (2);
mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
mouse_mode_hbox->pack_start (mouse_content_button, false, false);
mouse_mode_vbox->pack_start (*mouse_mode_hbox);
mouse_mode_align->add (*mouse_mode_vbox);
mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
mouse_mode_box->pack_start (*mouse_mode_align, false, false);
pack_snap_box ();
pack_draw_box (false);
Gtk::HBox* _toolbar_inner = manage (new Gtk::HBox);
Gtk::HBox* _toolbar_outer = manage (new Gtk::HBox);
Gtk::HBox* _toolbar_left = manage (new Gtk::HBox);
_toolbar_inner->pack_start (*mouse_mode_box, false, false);
_toolbar_inner->pack_start (snap_box, false, false);
_toolbar_inner->pack_start (grid_box, false, false);
_toolbar_inner->pack_start (draw_box, false, false);
set_tooltip (full_zoom_button, _("Zoom to full clip"));
set_tooltip (note_mode_button, _("Toggle between drum and regular note drawing"));
note_mode_button.set_icon (ArdourIcon::Drum);
play_note_selection_button.set_icon (ArdourIcon::ToolAudition);
#define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
note_mode_button.set_size_request (PX_SCALE(50), -1);
note_mode_button.set_active_color (UIConfiguration::instance().color ("alert:yellow"));
play_button.set_icon (ArdourIcon::TransportPlay);
play_button.set_name ("transport button");
play_button.show();
if (with_transport_controls) {
loop_button.set_icon (ArdourIcon::TransportLoop);
loop_button.set_name ("transport button");
solo_button.set_name ("solo button");
play_box.set_spacing (8);
play_box.pack_start (play_button, false, false);
play_box.pack_start (loop_button, false, false);
play_box.pack_start (solo_button, false, false);
loop_button.show();
solo_button.show();
play_box.set_no_show_all (true);
play_box.show ();
play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::play_button_press), false);
solo_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::solo_button_press), false);
loop_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::loop_button_press), false);
} else {
rec_box.pack_start (play_button, false, false);
play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::bang_button_press), false);
}
rec_enable_button.set_icon (ArdourIcon::RecButton);
rec_enable_button.set_sensitive (false);
rec_enable_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::rec_button_press), false);
rec_enable_button.set_name ("record enable button");
length_selector.add_menu_elem (MenuElem (_("Until Stopped"), sigc::bind (sigc::mem_fun (*this, &Pianoroll::set_recording_length), Temporal::BBT_Offset ())));
length_selector.add_menu_elem (MenuElem (_("1 Bar"), sigc::bind (sigc::mem_fun (*this, &Pianoroll::set_recording_length), Temporal::BBT_Offset (1, 0, 0))));
std::vector<int> b ({ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 24, 32 });
for (auto & n : b) {
length_selector.add_menu_elem (MenuElem (string_compose (_("%1 Bars"), n), sigc::bind (sigc::mem_fun (*this, &Pianoroll::set_recording_length), Temporal::BBT_Offset (n, 0, 0))));
}
length_selector.set_active (_("Until Stopped"));
rec_box.set_spacing (12);
rec_box.pack_start (rec_enable_button, false, false);
rec_box.pack_start (length_label, false, false);
rec_box.pack_start (length_selector, false, false);
rec_enable_button.show();
length_label.show ();
length_selector.show ();
rec_box.set_no_show_all (true);
/* rec box not shown */
_toolbar_outer->set_border_width (6);
_toolbar_outer->set_spacing (12);
if (with_transport_controls) {
_toolbar_outer->pack_start (play_box, false, false);
}
_toolbar_outer->pack_start (rec_box, false, false);
_toolbar_outer->pack_start (visible_channel_label, false, false);
_toolbar_outer->pack_start (visible_channel_selector, false, false);
_toolbar_outer->pack_start (play_note_selection_button, false, false);
_toolbar_outer->pack_start (note_mode_button, false, false);
_toolbar_outer->pack_start (follow_playhead_button, false, false);
_toolbar_outer->pack_start (*_toolbar_inner, true, false);
build_zoom_focus_menu ();
zoom_focus_selector.set_text (zoom_focus_strings[(int)_zoom_focus]);
_toolbar_left->pack_start (zoom_in_button, false, false);
_toolbar_left->pack_start (zoom_out_button, false, false);
_toolbar_left->pack_start (full_zoom_button, false, false);
_toolbar_left->pack_start (zoom_focus_selector, false, false);
_toolbar_outer->pack_start (*_toolbar_left, true, false);
_toolbox.pack_start (*_toolbar_outer, false, false);
_contents.add (_toolbox);
_contents.signal_unmap().connect ([this]() {_canvas_viewport->unmap ();}, false);
_contents.signal_map().connect ([this]() {_canvas_viewport->map ();}, false);
box.pack_start (snap_box, false, false);
box.pack_start (grid_box, false, false);
box.pack_start (draw_box, false, false);
}
void
Pianoroll::set_recording_length (Temporal::BBT_Offset dur)
Pianoroll::pack_outer (Gtk::Box& box)
{
rec_length = dur;
if (with_transport_controls) {
box.pack_start (play_box, false, false);
}
box.pack_start (rec_box, false, false);
box.pack_start (visible_channel_label, false, false);
box.pack_start (visible_channel_selector, false, false);
box.pack_start (follow_playhead_button, false, false);
}
void
@ -657,15 +504,6 @@ Pianoroll::build_canvas ()
_toolbox.pack_start (*_canvas_viewport, true, true);
}
void
Pianoroll::catch_pending_show_region ()
{
if (_visible_pending_region) {
set_region (_visible_pending_region);
_visible_pending_region.reset ();
}
}
bool
Pianoroll::bbt_ruler_event (GdkEvent* ev)
{
@ -1700,365 +1538,6 @@ Pianoroll::get_state () const
get_common_editing_state (*node);
return *node;
}
/** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
*
* @param allow_vert true to allow vertical autoscroll, otherwise false.
*
*/
void
Pianoroll::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
{
if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
return;
}
/* define a rectangular boundary for scrolling. If the mouse moves
* outside of this area and/or continue to be outside of this area,
* then we will continuously auto-scroll the canvas in the appropriate
* direction(s)
*
* the boundary is defined in coordinates relative to canvas' own
* window since that is what we're going to call ::get_pointer() on
* during autoscrolling to determine if we're still outside the
* boundary or not.
*/
ArdourCanvas::Rect scrolling_boundary;
Gtk::Allocation alloc;
alloc = get_canvas()->get_allocation ();
alloc.set_x (0);
alloc.set_y (0);
if (allow_vert) {
/* reduce height by the height of the timebars, which happens
to correspond to the position of the data_group.
*/
alloc.set_height (alloc.get_height() - data_group->position().y);
alloc.set_y (alloc.get_y() + data_group->position().y);
/* now reduce it again so that we start autoscrolling before we
* move off the top or bottom of the canvas
*/
alloc.set_height (alloc.get_height() - 20);
alloc.set_y (alloc.get_y() + 10);
}
if (allow_horiz && (alloc.get_width() > 20)) {
if (prh) {
double w, h;
prh->size_request (w, h);
alloc.set_width (alloc.get_width() - w);
alloc.set_x (alloc.get_x() + w);
}
/* the effective width of the autoscroll boundary so
that we start scrolling before we hit the edge.
this helps when the window is slammed up against the
right edge of the screen, making it hard to scroll
effectively.
*/
alloc.set_width (alloc.get_width() - 20);
alloc.set_x (alloc.get_x() + 10);
}
scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
int x, y;
Gdk::ModifierType mask;
get_canvas()->get_window()->get_pointer (x, y, mask);
if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) ||
(allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
}
}
bool
Pianoroll::autoscroll_active () const
{
return autoscroll_connection.connected ();
}
bool
Pianoroll::autoscroll_canvas ()
{
using std::max;
using std::min;
int x, y;
Gdk::ModifierType mask;
sampleoffset_t dx = 0;
bool no_stop = false;
Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(_canvas_viewport->get_toplevel());
if (!toplevel) {
return false;
}
get_canvas()->get_window()->get_pointer (x, y, mask);
VisualChange vc;
bool vertical_motion = false;
if (autoscroll_horizontal_allowed) {
samplepos_t new_sample = _leftmost_sample;
/* horizontal */
if (x > autoscroll_boundary.x1) {
/* bring it back into view */
dx = x - autoscroll_boundary.x1;
dx += 10 + (2 * (autoscroll_cnt/2));
dx = pixel_to_sample (dx);
dx *= UIConfiguration::instance().get_draggable_playhead_speed();
if (_leftmost_sample < max_samplepos - dx) {
new_sample = _leftmost_sample + dx;
} else {
new_sample = max_samplepos;
}
no_stop = true;
} else if (x < autoscroll_boundary.x0) {
dx = autoscroll_boundary.x0 - x;
dx += 10 + (2 * (autoscroll_cnt/2));
dx = pixel_to_sample (dx);
dx *= UIConfiguration::instance().get_draggable_playhead_speed();
if (_leftmost_sample >= dx) {
new_sample = _leftmost_sample - dx;
} else {
new_sample = 0;
}
no_stop = true;
}
if (new_sample != _leftmost_sample) {
vc.time_origin = new_sample;
vc.add (VisualChange::TimeOrigin);
}
}
if (autoscroll_vertical_allowed) {
// const double vertical_pos = vertical_adjustment.get_value();
const int speed_factor = 10;
/* vertical */
if (y < autoscroll_boundary.y0) {
/* scroll to make higher tracks visible */
if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
// XXX SCROLL UP
vertical_motion = true;
}
no_stop = true;
} else if (y > autoscroll_boundary.y1) {
if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
// XXX SCROLL DOWN
vertical_motion = true;
}
no_stop = true;
}
}
if (vc.pending || vertical_motion) {
/* change horizontal first */
if (vc.pending) {
visual_changer (vc);
}
/* now send a motion event to notify anyone who cares
that we have moved to a new location (because we scrolled)
*/
GdkEventMotion ev;
ev.type = GDK_MOTION_NOTIFY;
ev.state = Gdk::BUTTON1_MASK;
/* the motion handler expects events in canvas coordinate space */
/* we asked for the mouse position above (::get_pointer()) via
* our own top level window (we being the Editor). Convert into
* coordinates within the canvas window.
*/
int cx;
int cy;
//toplevel->translate_coordinates (*get_canvas(), x, y, cx,
//cy);
cx = x;
cy = y;
/* clamp x and y to remain within the autoscroll boundary,
* which is defined in window coordinates
*/
x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
/* now convert from Editor window coordinates to canvas
* window coordinates
*/
ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy));
ev.x = d.x;
ev.y = d.y;
ev.state = mask;
motion_handler (0, (GdkEvent*) &ev, true);
} else if (no_stop) {
/* not changing visual state but pointer is outside the scrolling boundary
* so we still need to deliver a fake motion event
*/
GdkEventMotion ev;
ev.type = GDK_MOTION_NOTIFY;
ev.state = Gdk::BUTTON1_MASK;
/* the motion handler expects events in canvas coordinate space */
/* first convert from Editor window coordinates to canvas
* window coordinates
*/
int cx;
int cy;
/* clamp x and y to remain within the visible area. except
* .. if horizontal scrolling is allowed, always allow us to
* move back to zero
*/
if (autoscroll_horizontal_allowed) {
x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
} else {
x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
}
y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
// toplevel->translate_coordinates (*get_canvas_viewport(), x,
// y, cx, cy);
cx = x;
cy = y;
ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy));
ev.x = d.x;
ev.y = d.y;
ev.state = mask;
motion_handler (0, (GdkEvent*) &ev, true);
} else {
stop_canvas_autoscroll ();
return false;
}
autoscroll_cnt++;
return true; /* call me again */
}
void
Pianoroll::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
{
if (!_session) {
return;
}
stop_canvas_autoscroll ();
autoscroll_horizontal_allowed = allow_horiz;
autoscroll_vertical_allowed = allow_vert;
autoscroll_boundary = boundary;
/* do the first scroll right now
*/
autoscroll_canvas ();
/* scroll again at very very roughly 30FPS */
autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Pianoroll::autoscroll_canvas), 30);
}
void
Pianoroll::stop_canvas_autoscroll ()
{
autoscroll_connection.disconnect ();
autoscroll_cnt = 0;
}
void
Pianoroll::visual_changer (const VisualChange& vc)
{
/**
* Changed first so the correct horizontal canvas position is calculated in
* EditingContext::set_horizontal_position
*/
if (vc.pending & VisualChange::ZoomLevel) {
set_samples_per_pixel (vc.samples_per_pixel);
}
if (vc.pending & VisualChange::TimeOrigin) {
double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
set_horizontal_position (new_time_origin);
update_tempo_based_rulers ();
}
if (vc.pending & VisualChange::YOrigin) {
vertical_adjustment.set_value (vc.y_origin);
}
if (vc.pending & VisualChange::ZoomLevel) {
if (!(vc.pending & VisualChange::TimeOrigin)) {
update_tempo_based_rulers ();
}
} else {
/* If the canvas is not being zoomed then the canvas items will not change
* and cause Item::prepare_for_render to be called so do it here manually.
* Not ideal, but I can't think of a better solution atm.
*/
get_canvas()->prepare_for_render();
}
/* If we are only scrolling vertically there is no need to update these */
if (vc.pending != VisualChange::YOrigin) {
// XXX update_fixed_rulers ();
redisplay_grid (true);
}
}
void
Pianoroll::midi_action (void (MidiView::*method)())
{
@ -2295,66 +1774,16 @@ Pianoroll::region_prop_change (PBD::PropertyChange const & what_changed)
}
}
void
Pianoroll::blink_rec_enable (bool onoff)
{
if (onoff) {
rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive);
} else {
rec_enable_button.set_active_state (Gtkmm2ext::Off);
}
}
void
Pianoroll::trigger_arm_change ()
{
if (!ref.trigger()) {
return;
}
if (!ref.trigger()->armed()) {
view->end_write ();
}
rec_enable_change ();
}
void
Pianoroll::rec_enable_change ()
{
if (!ref.box()) {
return;
}
rec_blink_connection.disconnect ();
count_in_connection.disconnect ();
switch (ref.box()->record_enabled()) {
case Recording:
rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive);
rec_blink_connection.disconnect ();
if (view) {
view->begin_write ();
}
break;
case Enabled:
if (!UIConfiguration::instance().get_no_strobe() && ref.trigger()->armed()) {
rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &Pianoroll::blink_rec_enable));
} else {
rec_enable_button.set_active_state (Gtkmm2ext::Off);
}
maybe_set_count_in ();
break;
case Disabled:
rec_enable_button.set_active_state (Gtkmm2ext::Off);
break;
}
}
void
Pianoroll::maybe_set_count_in ()
{
if (!ref.box()) {
std::cerr << "msci no box\n";
return;
}
if (ref.box()->record_enabled() == Disabled) {
std::cerr << "msci RE\n";
return;
}
@ -2365,6 +1794,7 @@ Pianoroll::maybe_set_count_in ()
count_in_to = ref.box()->start_time (valid);
if (!valid) {
std::cerr << "no start time\n";
return;
}
@ -2372,10 +1802,12 @@ Pianoroll::maybe_set_count_in ()
Temporal::Beats const & a_q (tmap->quarters_at_sample (audible));
if ((count_in_to - a_q).get_beats() == 0) {
std::cerr << "not enough time\n";
return;
}
count_in_connection = ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::count_in), ARDOUR_UI::clock_signal_interval()));
std::cerr << "count in started, with view " << view << std::endl;
}
void
@ -2418,92 +1850,23 @@ Pianoroll::count_in (Temporal::timepos_t audible, unsigned int clock_interval_ms
}
std::string str (string_compose ("%1", current_delta.get_beats()));
std::cerr << str << std::endl;
view->set_overlay_text (str);
}
}
bool
Pianoroll::play_button_press (GdkEventButton* ev)
void
Pianoroll::set_region (std::shared_ptr<ARDOUR::Region> r)
{
_session->request_locate (view->midi_region()->position().samples());
_session->request_roll ();
return true;
}
bool
Pianoroll::bang_button_press (GdkEventButton* ev)
{
if (!ref.trigger()) {
return true;
}
ref.trigger()->bang ();
return true;
}
bool
Pianoroll::loop_button_press (GdkEventButton* ev)
{
if (!view) {
return true;
}
if (!view->midi_region()) {
return true;
}
if (_session->get_play_loop()) {
_session->request_play_loop (false);
} else {
set_loop_range (view->midi_region()->position(), view->midi_region()->end(), _("loop region"));
_session->request_play_loop (true);
}
return true;
}
bool
Pianoroll::solo_button_press (GdkEventButton* ev)
{
if (!view) {
return true;
}
if (!view->midi_track()) {
return true;
}
view->midi_track()->solo_control()->set_value (!view->midi_track()->solo_control()->get_value(), Controllable::NoGroup);
return true;
}
bool
Pianoroll::rec_button_press (GdkEventButton* ev)
{
if (ev->button != 1) {
return false;
}
TriggerPtr trigger (ref.trigger());
if (!trigger) {
return true;
}
if (trigger->armed()) {
trigger->disarm ();
} else {
trigger->arm (rec_length);
}
return true;
set_region (std::dynamic_pointer_cast<ARDOUR::MidiRegion> (r));
}
void
Pianoroll::set_trigger (TriggerReference & tref)
{
std::cerr << "set trigger\n";
PBD::stacktrace (std::cerr, 17);
if (tref.trigger() == ref.trigger()) {
return;
}
@ -2522,6 +1885,8 @@ Pianoroll::set_trigger (TriggerReference & tref)
/* Don't bind a shared_ptr<TriggerBox> within the lambda */
TriggerBox* tb (ref.box().get());
tb->RecEnableChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::rec_enable_change, this), gui_context());
std::cerr << "connected to box " << tb->order() << std::endl;
maybe_set_count_in ();
Stripable* st = dynamic_cast<Stripable*> (ref.box()->owner());
assert (st);
@ -2577,6 +1942,7 @@ Pianoroll::unset (bool trigger_too)
_history.clear ();
_update_connection.disconnect();
object_connections.drop_connections ();
std::cerr << "disconnected\n";
_track.reset ();
view->set_region (nullptr);
if (trigger_too) {
@ -2631,6 +1997,9 @@ Pianoroll::set_region (std::shared_ptr<ARDOUR::MidiRegion> r)
return;
}
std::cerr << editor_name() << " set region to " << r << std::endl;
PBD::stacktrace (std::cerr, 19);
unset (false);
if (!r) {
@ -2826,20 +2195,6 @@ Pianoroll::set_note_mode (NoteMode nm)
}
}
void
Pianoroll::build_zoom_focus_menu ()
{
using namespace Gtk::Menu_Helpers;
using namespace Editing;
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
zoom_focus_selector.set_sizing_texts (zoom_focus_strings);
}
std::pair<Temporal::timepos_t,Temporal::timepos_t>
Pianoroll::max_zoom_extent() const
{

View file

@ -63,7 +63,6 @@ class Pianoroll : public CueEditor
Gtk::Widget& viewport();
Gtk::Widget& contents ();
double visible_canvas_width() const { return _visible_canvas_width; }
samplecnt_t current_page_samples() const;
void get_per_region_note_selection (std::list<std::pair<PBD::ID, std::set<std::shared_ptr<Evoral::Note<Temporal::Beats> > > > >&) const {}
@ -84,6 +83,7 @@ class Pianoroll : public CueEditor
void set_trigger (ARDOUR::TriggerReference&);
void set_region (std::shared_ptr<ARDOUR::MidiRegion>);
void set_region (std::shared_ptr<ARDOUR::Region> r);
void set_track (std::shared_ptr<ARDOUR::MidiTrack>);
ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; }
@ -97,20 +97,12 @@ class Pianoroll : public CueEditor
Editing::MouseMode current_mouse_mode () const;
bool internal_editing() const;
void trigger_arm_change ();
double timebar_height;
size_t n_timebars;
ArdourCanvas::GtkCanvasViewport* get_canvas_viewport() const;
ArdourCanvas::GtkCanvas* get_canvas() const;
int set_state (const XMLNode&, int version);
XMLNode& get_state () const;
void maybe_autoscroll (bool, bool, bool);
bool autoscroll_active() const;
void midi_action (void (MidiView::*method)());
std::list<SelectableOwner*> selectable_owners();
@ -183,41 +175,14 @@ class Pianoroll : public CueEditor
void escape ();
private:
ARDOUR::TriggerReference ref;
std::shared_ptr<ARDOUR::MidiTrack> _track;
ArdourCanvas::GtkCanvasViewport* _canvas_viewport;
ArdourCanvas::GtkCanvas* _canvas;
Gtk::HScrollbar* _canvas_hscrollbar;
/* The group containing all other groups that are scrolled vertically
and horizontally.
*/
ArdourCanvas::ScrollGroup* hv_scroll_group;
/* 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
*/
ArdourCanvas::ScrollGroup* cursor_scroll_group;
ArdourCanvas::Container* global_rect_group;
ArdourCanvas::Container* no_scroll_group;
ArdourCanvas::Container* data_group;
ArdourCanvas::Ruler* bbt_ruler;
ArdourCanvas::Rectangle* tempo_bar;
ArdourCanvas::Rectangle* meter_bar;
ArdourCanvas::PianoRollHeader* prh;
ArdourCanvas::Rectangle* transport_loop_range_rect;
ArdourWidgets::EventBoxExt _contents;
Gtk::VBox _toolbox;
Gtk::HBox button_bar;
ArdourWidgets::ArdourButton* velocity_button;
ArdourWidgets::ArdourButton* bender_button;
ArdourWidgets::ArdourButton* pressure_button;
@ -260,14 +225,6 @@ class Pianoroll : public CueEditor
BBTMetric bbt_metric;
bool canvas_pre_event (GdkEvent*);
/* autoscrolling */
bool autoscroll_canvas ();
void start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary);
void stop_canvas_autoscroll ();
sigc::connection _update_connection;
PBD::ScopedConnectionList object_connections;
PBD::ScopedConnectionList view_connections;
@ -277,7 +234,6 @@ class Pianoroll : public CueEditor
void unset (bool trigger_too);
void visual_changer (const VisualChange&);
void bindings_changed ();
void data_captured (samplecnt_t);
@ -299,37 +255,10 @@ class Pianoroll : public CueEditor
void automation_state_changed ();
void build_zoom_focus_menu ();
std::pair<Temporal::timepos_t,Temporal::timepos_t> max_zoom_extent() const;
void point_selection_changed ();
bool zoom_in_allocate;
ArdourWidgets::ArdourButton rec_enable_button;
ArdourWidgets::ArdourButton play_button;
ArdourWidgets::ArdourButton solo_button;
ArdourWidgets::ArdourButton loop_button;
bool play_button_press (GdkEventButton*);
bool bang_button_press (GdkEventButton*);
bool solo_button_press (GdkEventButton*);
bool loop_button_press (GdkEventButton*);
ArdourWidgets::ArdourDropdown length_selector;
Temporal::BBT_Offset rec_length;
Gtk::Label length_label;
Gtk::HBox rec_box;
Gtk::HBox play_box;
void set_recording_length (Temporal::BBT_Offset bars);
bool rec_button_press (GdkEventButton*);
void rec_enable_change ();
void blink_rec_enable (bool);
sigc::connection rec_blink_connection;
void add_single_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name, ArdourWidgets::MetaButton*);
void add_multi_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, uint16_t channels, int ctl, const std::string& name, ArdourWidgets::MetaButton*);
void reset_user_cc_choice (std::string, Evoral::Parameter param, ArdourWidgets::MetaButton*);
@ -349,18 +278,18 @@ class Pianoroll : public CueEditor
bool bbt_ruler_event (GdkEvent*);
void ruler_locate (GdkEventButton*);
void scrolled ();
void update_tempo_based_rulers ();
void update_rulers() { update_tempo_based_rulers (); }
Gtk::Menu _region_context_menu;
void popup_region_context_menu (ArdourCanvas::Item* item, GdkEvent* event);
std::shared_ptr<ARDOUR::MidiRegion> _visible_pending_region;
void catch_pending_show_region ();
bool show_source;
void set_note_selection (uint8_t note);
void add_note_selection (uint8_t note);
void extend_note_selection (uint8_t note);
void toggle_note_selection (uint8_t note);
void pack_inner (Gtk::Box&);
void pack_outer (Gtk::Box&);
};

View file

@ -287,8 +287,6 @@ public:
/** @return true if the playhead is currently being dragged, otherwise false */
virtual bool dragging_playhead () const = 0;
virtual samplepos_t leftmost_sample() const = 0;
virtual samplecnt_t current_page_samples() const = 0;
virtual double visible_canvas_height () const = 0;
virtual void ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top = false) = 0;
virtual void override_visible_track_count () = 0;
virtual void scroll_tracks_down_line () = 0;

View file

@ -144,6 +144,7 @@ TriggerPage::TriggerPage ()
_sidebar_pager2.set_index (3);
_midi_editor = new Pianoroll (X_("MIDICueEditor"));
_audio_editor = new AudioClipEditor (X_("AudioClipEditor"), true);
/* Bottom -- Properties of selected Slot/Region */
@ -475,9 +476,7 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger)
/* hide everything */
_audio_trig_box.hide ();
_midi_trig_box.hide ();
_midi_editor->viewport().hide ();
hide_all ();
Tabbable::showhide_att_bottom (false);
@ -485,10 +484,12 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger)
TriggerReference ref (trigger->boxptr(), trigger->index());
if (box.data_type () == DataType::AUDIO) {
if (trigger->the_region()) {
_audio_trig_box.set_trigger (ref);
_audio_trig_box.show ();
}
// _audio_editor->set_trigger (ref);
_audio_editor->viewport().show ();
} else {
@ -504,20 +505,30 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger)
}
}
void
TriggerPage::hide_all ()
{
_audio_trig_box.hide ();
_audio_editor->viewport().hide ();
_audio_trig_box.hide ();
_midi_trig_box.hide ();
}
void
TriggerPage::selection_changed ()
{
Selection& selection (Editor::instance ().get_selection ());
/* hide everything */
_audio_trig_box.hide ();
_midi_trig_box.hide ();
hide_all ();
if (_midi_editor->contents().get_parent()) {
_midi_editor->contents().get_parent()->remove (_midi_editor->contents());
}
if (_audio_editor->contents().get_parent()) {
_audio_editor->contents().get_parent()->remove (_audio_editor->contents());
}
Tabbable::showhide_att_bottom (false);
if (selection.triggers.empty ()) {

View file

@ -30,6 +30,7 @@
#include "widgets/tabbable.h"
#include "application_bar.h"
#include "audio_clip_editor.h"
#include "audio_region_operations_box.h"
#include "audio_trigger_properties_box.h"
#include "axis_provider.h"
@ -76,7 +77,7 @@ private:
void remove_route (TriggerStrip*);
void clear_selected_slot ();
void hide_all ();
void redisplay_track_list ();
void pi_property_changed (PBD::PropertyChange const&);
void stripable_property_changed (PBD::PropertyChange const&, std::weak_ptr<ARDOUR::Stripable>);
@ -140,10 +141,10 @@ private:
#if REGION_PROPERTIES_BOX_TODO
AudioRegionOperationsBox _audio_ops_box;
AudioClipEditorBox _audio_trim_box;
#endif
Pianoroll* _midi_editor;
AudioClipEditor* _audio_editor;
RouteProcessorSelection _selection;
std::list<TriggerStrip*> _strips;

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2021 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2021 Ben Loftis <ben@harrisonconsoles.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.
*/
#pragma once
#include <ytkmm/label.h>
#include <ytkmm/table.h>
#include "pbd/signals.h"
#include "ardour/session_handle.h"
#include "trigger_ui.h"
class TriggerPropertiesBox : public Gtk::Table, public ARDOUR::SessionHandlePtr, public TriggerUI
{
public:
TriggerPropertiesBox () {}
~TriggerPropertiesBox () {}
protected:
Gtk::Label _header_label;
PBD::ScopedConnection _state_connection;
};

View file

@ -300,7 +300,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void arm (Temporal::BBT_Offset duration = Temporal::BBT_Offset()) {
_arm (duration);
}
virtual void disarm ();
virtual void disarm (bool disarm_box = true);
bool armed() const { return _armed; }
PBD::Signal<void()> ArmChanged;
static PBD::Signal<void(Trigger const *)> TriggerArmChanged;
@ -353,11 +353,11 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
bool compute_quantized_transition (samplepos_t start_sample, Temporal::Beats const & start, Temporal::Beats const & end,
Temporal::BBT_Argument& t_bbt, Temporal::Beats& t_beats, samplepos_t& t_samples,
Temporal::TempoMap::SharedPtr const & tmap, Temporal::BBT_Offset const & q);
Temporal::TempoMap::SharedPtr const & tmap, Temporal::BBT_Offset const & q, int multiple = 0);
pframes_t compute_next_transition (samplepos_t start_sample, Temporal::Beats const & start, Temporal::Beats const & end, pframes_t nframes,
Temporal::BBT_Argument& t_bbt, Temporal::Beats& t_beats, samplepos_t& t_samples,
Temporal::TempoMap::SharedPtr const & tmap);
Temporal::TempoMap::SharedPtr const & tmap, int multiple = 0);
template<typename TriggerType>
@ -616,7 +616,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
bool playable() const { return rt_midibuffer.load() || _region; }
void captured (SlotArmInfo&, BufferSet&);
void disarm ();
void disarm (bool disarm_box);
template<bool actually_run> pframes_t midi_run (BufferSet&, samplepos_t start_sample, samplepos_t end_sample,
Temporal::Beats const & start_beats, Temporal::Beats const & end_beats, pframes_t nframes, pframes_t offset, double bpm, pframes_t& quantize_offset);
@ -849,7 +849,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
void arm_from_another_thread (Trigger& slot, samplepos_t, uint32_t chans, Temporal::BBT_Offset const &);
void disarm ();
void disarm_all();
void disarm_all(bool disarm_box);
bool armed() const { return (bool) _arm_info.load(); }
PBD::Signal<void()> ArmedChanged;

View file

@ -909,6 +909,19 @@ Session::request_stop (bool abort, bool clear_state, TransportRequestSource orig
return;
}
std::shared_ptr<RouteList const> rl (routes.reader());
for (auto & r : *rl) {
std::shared_ptr<TriggerBox> tb = r->triggerbox();
bool was_clip_recording = false;
if (tb && tb->record_enabled() == Recording) {
tb->disarm_all (false);
was_clip_recording = true;
}
if (was_clip_recording) {
return;
}
}
/* clear our solo-selection, if there is one */
if ( solo_selection_active() ) {
solo_selection ( _soloSelection, false );

View file

@ -309,7 +309,7 @@ Trigger::_arm (Temporal::BBT_Offset const & duration)
/* trigger arming is mutually exclusive within a given TriggerBox */
_box.disarm_all ();
_box.disarm_all (true);
int chns;
@ -329,15 +329,19 @@ Trigger::_arm (Temporal::BBT_Offset const & duration)
}
void
Trigger::disarm ()
Trigger::disarm (bool disarm_box)
{
if (_armed) {
_armed = false;
if (disarm_box) {
_box.disarm ();
}
ArmChanged(); /* EMIT SIGNAL */
if (disarm_box) {
TriggerArmChanged (this);
}
}
}
void
Trigger::get_ui_state (Trigger::UIState &state) const
@ -1040,7 +1044,7 @@ Trigger::compute_start (Temporal::TempoMap::SharedPtr const & tmap, samplepos_t
bool
Trigger::compute_quantized_transition (samplepos_t start_sample, Temporal::Beats const & start_beats, Temporal::Beats const & end_beats,
Temporal::BBT_Argument& t_bbt, Temporal::Beats& t_beats, samplepos_t& t_samples,
Temporal::TempoMap::SharedPtr const & tmap, Temporal::BBT_Offset const & q)
Temporal::TempoMap::SharedPtr const & tmap, Temporal::BBT_Offset const & q, int multiplier)
{
/* XXX need to use global grid here is quantization == zero */
@ -1061,7 +1065,9 @@ Trigger::compute_quantized_transition (samplepos_t start_sample, Temporal::Beats
} else if (q.bars == 0) {
possible_beats = start_beats.round_up_to_multiple (Temporal::Beats (q.beats, q.ticks));
Temporal::Beats qb (q.beats, q.ticks);
possible_beats = start_beats.round_up_to_multiple (qb);
possible_beats += (qb * multiplier);
possible_bbt = tmap->bbt_at (possible_beats);
possible_samples = tmap->sample_at (possible_beats);
@ -1078,6 +1084,7 @@ Trigger::compute_quantized_transition (samplepos_t start_sample, Temporal::Beats
if (possible_beats % qb != Temporal::Beats()) {
possible_beats = ((tmap->quarters_at (start) + (qb/2)) / qb) * qb;
}
possible_beats += (qb * multiplier);
possible_bbt = tmap->bbt_at (possible_beats);
possible_samples = tmap->sample_at (possible_beats);
@ -1102,7 +1109,7 @@ Trigger::compute_quantized_transition (samplepos_t start_sample, Temporal::Beats
pframes_t
Trigger::compute_next_transition (samplepos_t start_sample, Temporal::Beats const & start, Temporal::Beats const & end, pframes_t nframes,
Temporal::BBT_Argument& t_bbt, Temporal::Beats& t_beats, samplepos_t& t_samples,
Temporal::TempoMap::SharedPtr const & tmap)
Temporal::TempoMap::SharedPtr const & tmap, int multiple)
{
using namespace Temporal;
@ -1127,7 +1134,7 @@ Trigger::compute_next_transition (samplepos_t start_sample, Temporal::Beats cons
}
if (!compute_quantized_transition (start_sample, start, end, t_bbt, t_beats, t_samples, tmap, q)) {
if (!compute_quantized_transition (start_sample, start, end, t_bbt, t_beats, t_samples, tmap, q, multiple)) {
/* no transition */
return 0;
}
@ -2535,16 +2542,16 @@ MIDITrigger::_arm (Temporal::BBT_Offset const & duration)
}
void
MIDITrigger::disarm ()
MIDITrigger::disarm (bool disarm_box)
{
Trigger::disarm ();
Trigger::disarm (disarm_box);
}
void
MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs)
{
if (ai.midi_buf->size() == 0) {
disarm ();
disarm (true);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 captured but with no MIDI data\n", _box.order(), index()));
return;
}
@ -2573,7 +2580,7 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs)
// std::cerr << "capture done, ask for a source of length " << dur.beats().str() << std::endl;
TriggerBox::worker->request_build_source (this, timecnt_t (dur.beats()));
disarm ();
disarm (true);
}
void
@ -3693,6 +3700,8 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch
{
using namespace Temporal;
std::cerr << "AFAT...\n";
SlotArmInfo* ai = &_the_arm_info;
/* Delete any dangling RTMidiBuffer and Stretcher from previous capture
@ -3720,8 +3729,9 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch
Beats now_beats = tmap->quarters_at (timepos_t (now));
slot.compute_quantized_transition (now, now_beats, std::numeric_limits<Beats>::max(),
t_bbt, t_beats, t_samples, tmap, slot.quantization());
t_bbt, t_beats, t_samples, tmap, slot.quantization(), 2);
std::cerr << " from " << now_beats.str() << " compute start at " << t_beats.str() << std::endl;
ai->start_samples = t_samples;
ai->start_beats = t_beats;
@ -3738,10 +3748,10 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch
}
void
TriggerBox::disarm_all ()
TriggerBox::disarm_all (bool disarm_box)
{
for (auto & t : all_triggers) {
t->disarm ();
t->disarm (disarm_box);
}
}
@ -3750,6 +3760,8 @@ TriggerBox::disarm ()
{
/* This must be called as an alternative to ::finish_recording() */
std::cerr << "disarmed\n";
PBD::stacktrace (std::cerr, 17);
_arm_info = nullptr;
}
@ -3768,6 +3780,7 @@ TriggerBox::finish_recording (BufferSet& bufs)
/* XXX this should likely be dependent on what the post-record action is */
_record_state = Disabled;
std::cerr << "all done!\n";
RecEnableChanged (); /* EMIT SIGNAL */
}
@ -3779,6 +3792,7 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
SlotArmInfo* ai = _arm_info.load();
if (!ai) {
std::cerr << "no AI\n";
return;
}
@ -3786,6 +3800,7 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
bool reached_end = false;
if (!ai->slot->armed()) {
std::cerr << "not armed!\n";
/* since _arm_info is set, we have been capturing for a slot,
but now the slot is no longer armed.
*/
@ -3808,7 +3823,10 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
t_bbt, t_beats, t_samples, tmap, ai->slot->quantization());
ai->end_samples = t_samples;
ai->end_beats = t_beats;
std::cerr << "will stop at " << t_beats.str() << std::endl;
}
} else {
std::cerr << "still armed\n";
}
if (speed <= 0.) {
@ -3924,9 +3942,7 @@ TriggerBox::set_record_enabled (bool yn)
_record_state = yn ? Enabled : Disabled;
if (_record_state == Disabled) {
for (auto & trig : all_triggers) {
trig->disarm ();
}
disarm_all (false);
}
RecEnableChanged (); /* EMIT SIGNAL */
@ -5958,10 +5974,11 @@ TriggerBox::start_time (bool& is_set) const
{
SlotArmInfo* ai = _arm_info.load ();
if (!ai) {
std::cerr << "no slot\n";
is_set = false;
return Temporal::Beats();
}
std::cerr << "have slot\n";
is_set = true;
return ai->start_beats;
}