Add support for Region Fx Automation

This commit is contained in:
Robin Gareus 2024-04-08 03:03:16 +02:00
parent 373b70818c
commit bde791df34
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
20 changed files with 371 additions and 150 deletions

View File

@ -33,6 +33,7 @@
#include "ardour/audioregion.h"
#include "ardour/session_event.h"
#include "ardour/dB.h"
#include "ardour/region_fx_plugin.h"
#include "audio_region_editor.h"
#include "audio_region_view.h"
@ -52,9 +53,10 @@ _peak_amplitude_thread (void* arg)
return 0;
}
AudioRegionEditor::AudioRegionEditor (Session* s, std::shared_ptr<AudioRegion> r)
: RegionEditor (s, r)
, _audio_region (r)
AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv)
: RegionEditor (s, arv)
, _arv (arv)
, _audio_region (arv->audio_region ())
, gain_adjustment(accurate_coefficient_to_dB(fabsf (_audio_region->scale_amplitude())), -40.0, +40.0, 0.1, 1.0, 0)
, _polarity_toggle (_("Invert"))
, _peak_channel (false)
@ -87,15 +89,26 @@ AudioRegionEditor::AudioRegionEditor (Session* s, std::shared_ptr<AudioRegion> r
_polarity_label.set_name ("AudioRegionEditorLabel");
_polarity_label.set_text (_("Polarity:"));
_polarity_label.set_alignment (1, 0.5);
_table.attach (_polarity_label, 0, 1, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL);
_table.attach (_polarity_toggle, 1, 2, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL);
++_table_row;
_region_line_label.set_name ("AudioRegionEditorLabel");
_region_line_label.set_text (_("Region Line:"));
_region_line_label.set_alignment (1, 0.5);
_table.attach (_region_line_label, 0, 1, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL);
_table.attach (_region_line, 1, 2, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL);
++_table_row;
gain_changed ();
refill_region_line ();
gain_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &AudioRegionEditor::gain_adjustment_changed));
_polarity_toggle.signal_toggled().connect (sigc::mem_fun (*this, &AudioRegionEditor::gain_adjustment_changed));
arv->region_line_changed.connect ((sigc::mem_fun (*this, &AudioRegionEditor::refill_region_line)));
_peak_amplitude.property_editable() = false;
_peak_amplitude.set_text (_("Calculating..."));
@ -128,6 +141,14 @@ AudioRegionEditor::region_changed (const PBD::PropertyChange& what_changed)
signal_peak_thread ();
}
}
void
AudioRegionEditor::region_fx_changed ()
{
RegionEditor::region_fx_changed ();
refill_region_line ();
}
void
AudioRegionEditor::gain_changed ()
{
@ -189,3 +210,74 @@ AudioRegionEditor::peak_amplitude_found (double p)
_peak_amplitude.set_text (s.str ());
}
void
AudioRegionEditor::refill_region_line ()
{
using namespace Gtk::Menu_Helpers;
_region_line.clear_items ();
MenuList& rm_items (_region_line.items ());
int nth = 0;
PBD::ID rfx_id (0);
uint32_t param_id = 0;
string active_text = _("Gain Envelope");
_arv->get_region_fx_line (rfx_id, param_id);
_arv->set_ignore_line_change (true);
Gtk::RadioMenuItem::Group grp;
AudioRegionView* arv = _arv;
rm_items.push_back (RadioMenuElem (grp, _("Gain Envelope")));
Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*> (&rm_items.back ());
cmi->set_active (rfx_id == 0 || param_id == UINT32_MAX);
cmi->signal_activate ().connect ([cmi, arv] () { if (cmi->get_active ()) {arv->set_region_gain_line (); }});
_audio_region->foreach_plugin ([&rm_items, arv, &nth, &grp, &active_text, rfx_id, param_id](std::weak_ptr<RegionFxPlugin> wfx)
{
std::shared_ptr<RegionFxPlugin> fx (wfx.lock ());
if (!fx) {
return;
}
std::shared_ptr<Plugin> plugin = fx->plugin ();
Gtk::Menu* acm = manage (new Gtk::Menu);
MenuList& acm_items (acm->items ());
for (size_t i = 0; i < plugin->parameter_count (); ++i) {
if (!plugin->parameter_is_control (i) || !plugin->parameter_is_input (i)) {
continue;
}
const Evoral::Parameter param (PluginAutomation, 0, i);
std::string label = plugin->describe_parameter (param);
if (label == X_("latency") || label == X_("hidden")) {
continue;
}
std::shared_ptr<ARDOUR::AutomationControl> c (std::dynamic_pointer_cast<ARDOUR::AutomationControl> (fx->control (param)));
if (c && c->flags () & (Controllable::HiddenControl | Controllable::NotAutomatable)) {
continue;
}
bool active = fx->id () == rfx_id && param_id == i;
acm_items.push_back (RadioMenuElem (grp, label));
Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*> (&acm_items.back ());
cmi->set_active (active);
cmi->signal_activate ().connect ([cmi, arv, nth, i] () { if (cmi->get_active ()) {arv->set_region_fx_line (nth, i); }});
if (active) {
active_text = fx->name () + ": " + label;
}
}
if (!acm_items.empty ()) {
rm_items.push_back (MenuElem (fx->name (), *acm));
} else {
delete acm;
}
++nth;
});
_region_line.set_text (active_text);
_arv->set_ignore_line_change (false);
}

View File

@ -37,6 +37,7 @@
#include <gtkmm/separator.h>
#include <gtkmm/spinbutton.h>
#include "widgets/ardour_dropdown.h"
#include "pbd/signals.h"
#include "pbd/crossthread.h"
@ -55,7 +56,7 @@ class AudioRegionView;
class AudioRegionEditor : public RegionEditor
{
public:
AudioRegionEditor (ARDOUR::Session*, std::shared_ptr<ARDOUR::AudioRegion>);
AudioRegionEditor (ARDOUR::Session*, AudioRegionView*);
~AudioRegionEditor ();
void peak_amplitude_thread ();
@ -63,10 +64,14 @@ public:
private:
void region_changed (PBD::PropertyChange const &);
void region_fx_changed ();
void gain_changed ();
void gain_adjustment_changed ();
void refill_region_line ();
AudioRegionView* _arv;
std::shared_ptr<ARDOUR::AudioRegion> _audio_region;
Gtk::Label gain_label;
@ -79,6 +84,9 @@ private:
Gtk::Label _peak_amplitude_label;
Gtk::Entry _peak_amplitude;
Gtk::Label _region_line_label;
ArdourWidgets::ArdourDropdown _region_line;
void signal_peak_thread ();
pthread_t _peak_amplitude_thread_handle;
void peak_amplitude_found (double);

View File

@ -36,6 +36,7 @@
#include "ardour/audioregion.h"
#include "ardour/audiosource.h"
#include "ardour/profile.h"
#include "ardour/region_fx_plugin.h"
#include "ardour/session.h"
#include "pbd/memento_command.h"
@ -112,8 +113,7 @@ static Cairo::RefPtr<Cairo::Pattern> create_pending_peak_pattern() {
return p;
}
AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, std::shared_ptr<AudioRegion> r, double spu,
uint32_t basic_color)
AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, std::shared_ptr<AudioRegion> r, double spu, uint32_t basic_color)
: RegionView (parent, tv, r, spu, basic_color)
, fade_in_handle(0)
, fade_out_handle(0)
@ -129,6 +129,9 @@ AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxis
, _amplitude_above_axis(1.0)
, trim_fade_in_drag_active(false)
, trim_fade_out_drag_active(false)
, _rfx_id (0)
, _rdx_param (UINT32_MAX)
, _ignore_line_change (false)
{
}
@ -149,6 +152,9 @@ AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxis
, _amplitude_above_axis(1.0)
, trim_fade_in_drag_active(false)
, trim_fade_out_drag_active(false)
, _rfx_id (0)
, _rdx_param (UINT32_MAX)
, _ignore_line_change (false)
{
}
@ -168,6 +174,9 @@ AudioRegionView::AudioRegionView (const AudioRegionView& other, std::shared_ptr<
, _amplitude_above_axis (other._amplitude_above_axis)
, trim_fade_in_drag_active(false)
, trim_fade_out_drag_active(false)
, _rfx_id (0)
, _rdx_param (UINT32_MAX)
, _ignore_line_change (false)
{
init (true);
}
@ -232,12 +241,7 @@ AudioRegionView::init (bool wfd)
set_fade_visibility (false);
}
const string line_name = _region->name() + ":gain";
gain_line.reset (new AudioRegionGainLine (line_name, *this, *group, audio_region()->envelope()));
update_envelope_visibility ();
gain_line->reset ();
set_region_gain_line ();
/* streamview will call set_height() */
//set_height (trackview.current_height()); // XXX not correct for Layered mode, but set_height() will fix later.
@ -278,7 +282,7 @@ AudioRegionView::init (bool wfd)
setup_waveform_visibility ();
get_canvas_frame()->set_data ("linemerger", (LineMerger*) this);
gain_line->canvas_group().raise_to_top ();
_fx_line->canvas_group().raise_to_top ();
/* XXX sync mark drag? */
}
@ -606,15 +610,15 @@ AudioRegionView::set_height (gdouble height)
}
}
if (gain_line) {
if (_fx_line) {
if ((height / nchans) < NAME_HIGHLIGHT_THRESH) {
gain_line->hide ();
_fx_line->hide ();
} else {
update_envelope_visibility ();
}
gain_line->set_height ((uint32_t) rint (height - NAME_HIGHLIGHT_SIZE) - 2);
_fx_line->set_height ((uint32_t) rint (height - NAME_HIGHLIGHT_SIZE) - 2);
}
reset_fade_shapes ();
@ -1081,8 +1085,8 @@ AudioRegionView::set_samples_per_pixel (gdouble fpp)
}
}
if (gain_line) {
gain_line->reset ();
if (_fx_line) {
_fx_line->reset ();
}
reset_fade_shapes ();
@ -1101,12 +1105,9 @@ AudioRegionView::set_colors ()
{
RegionView::set_colors();
if (gain_line) {
gain_line->set_line_color (audio_region()->envelope_active() ?
UIConfiguration::instance().color ("gain line") :
UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
}
assert (_fx_line);
set_fx_line_colors ();
set_waveform_colors ();
if (start_xfade_curve) {
@ -1148,8 +1149,8 @@ AudioRegionView::setup_waveform_visibility ()
void
AudioRegionView::temporarily_hide_envelope ()
{
if (gain_line) {
gain_line->hide ();
if (_fx_line) {
_fx_line->hide ();
}
}
@ -1160,20 +1161,80 @@ AudioRegionView::unhide_envelope ()
}
void
AudioRegionView::update_envelope_visibility ()
AudioRegionView::set_region_gain_line ()
{
if (!gain_line) {
if (_ignore_line_change) {
return;
}
const string line_name = _region->name() + ":gain";
_fx_line.reset (new AudioRegionGainLine (line_name, *this, *group, audio_region()->envelope()));
_fx_line->set_height ((uint32_t) rint (height() - NAME_HIGHLIGHT_SIZE) - 2);
_fx_line->reset ();
_region_fx_connection.disconnect ();
bool changed = _rfx_id != PBD::ID (0) || _rdx_param != UINT32_MAX;
_rfx_id = PBD::ID (0);
_rdx_param = UINT32_MAX;
envelope_active_changed ();
if (changed) {
region_line_changed (); /* EMIT SIGNAL */
}
}
bool
AudioRegionView::set_region_fx_line (uint32_t plugin_id, uint32_t param_id)
{
if (_ignore_line_change) {
return false;
}
std::shared_ptr<RegionFxPlugin> rfx = _region->nth_plugin (plugin_id);
if (rfx) {
std::shared_ptr<Evoral::Control> c = rfx->control (Evoral::Parameter (PluginAutomation, 0, param_id));
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl> (c);
if (ac) {
ac->set_automation_state (Play);
const string line_name = _region->name () + ":" + rfx->describe_parameter (Evoral::Parameter (PluginAutomation, 0, param_id));
_fx_line.reset (new RegionFxLine (line_name, *this, *group, ac->alist (), ac->desc ()));
_fx_line->set_height ((uint32_t) rint (height() - NAME_HIGHLIGHT_SIZE) - 2);
_fx_line->reset ();
rfx->DropReferences.connect (_region_fx_connection, invalidator (*this), boost::bind (&AudioRegionView::set_region_gain_line, this), gui_context ());
bool changed = _rfx_id != rfx->id () || _rdx_param != param_id;
_rfx_id = rfx->id ();
_rdx_param = param_id;
envelope_active_changed ();
if (changed) {
region_line_changed (); /* EMIT SIGNAL */
}
return true;
}
}
return false;
}
bool
AudioRegionView::get_region_fx_line (PBD::ID& id, uint32_t& param_id)
{
id = _rfx_id;
param_id = _rdx_param;
return _rdx_param != UINT32_MAX && _rfx_id != 0;
}
void
AudioRegionView::update_envelope_visibility ()
{
assert (_fx_line);
if (trackview.editor().current_mouse_mode() == Editing::MouseDraw || trackview.editor().current_mouse_mode() == Editing::MouseContent ) {
gain_line->set_visibility (AutomationLine::VisibleAspects(AutomationLine::ControlPoints|AutomationLine::Line));
gain_line->canvas_group().raise_to_top ();
_fx_line->set_visibility (AutomationLine::VisibleAspects(AutomationLine::ControlPoints|AutomationLine::Line));
_fx_line->canvas_group().raise_to_top ();
} else if (UIConfiguration::instance().get_show_region_gain() || trackview.editor().current_mouse_mode() == Editing::MouseRange ) {
gain_line->set_visibility (AutomationLine::VisibleAspects(AutomationLine::Line));
gain_line->canvas_group().raise_to_top ();
_fx_line->set_visibility (AutomationLine::VisibleAspects(AutomationLine::Line));
_fx_line->canvas_group().raise_to_top ();
} else {
gain_line->set_visibility (AutomationLine::VisibleAspects(0));
_fx_line->set_visibility (AutomationLine::VisibleAspects(0));
}
}
@ -1374,7 +1435,7 @@ AudioRegionView::peaks_ready_handler (uint32_t which)
void
AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, bool with_guard_points)
{
if (!gain_line) {
if (!_fx_line) {
return;
}
@ -1386,18 +1447,17 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, b
samplecnt_t const sample_within_region = (samplecnt_t) floor (mx * samples_per_pixel);
if (!gain_line->control_points_adjacent (sample_within_region, before_p, after_p)) {
/* no adjacent points */
return;
double y = my;
if (_fx_line->control_points_adjacent (sample_within_region, before_p, after_p)) {
/* y is in item frame */
double const bx = _fx_line->nth (before_p)->get_x();
double const ax = _fx_line->nth (after_p)->get_x();
double const click_ratio = (ax - mx) / (ax - bx);
y = ((_fx_line->nth (before_p)->get_y() * click_ratio) + (_fx_line->nth (after_p)->get_y() * (1 - click_ratio)));
}
/* y is in item frame */
double const bx = gain_line->nth (before_p)->get_x();
double const ax = gain_line->nth (after_p)->get_x();
double const click_ratio = (ax - mx) / (ax - bx);
double y = ((gain_line->nth (before_p)->get_y() * click_ratio) + (gain_line->nth (after_p)->get_y() * (1 - click_ratio)));
/* don't create points that can't be seen */
update_envelope_visibility ();
@ -1412,28 +1472,28 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, b
/* compute vertical fractional position */
y = 1.0 - (y / (gain_line->height()));
y = 1.0 - (y / (_fx_line->height()));
/* map using gain line */
gain_line->view_to_model_coord_y (y);
_fx_line->view_to_model_coord_y (y);
/* XXX STATEFUL: can't convert to stateful diff until we
can represent automation data with it.
*/
XMLNode &before = audio_region()->envelope()->get_state();
XMLNode &before = _fx_line->the_list()->get_state();
MementoCommand<AudioRegion>* region_memento = 0;
if (!audio_region()->envelope_active()) {
if (!audio_region()->envelope_active() && _fx_line->the_list() == audio_region()->envelope()) {
XMLNode &region_before = audio_region()->get_state();
audio_region()->set_envelope_active(true);
XMLNode &region_after = audio_region()->get_state();
region_memento = new MementoCommand<AudioRegion>(*(audio_region().get()), &region_before, &region_after);
}
if (audio_region()->envelope()->editor_add (timepos_t (fx), y, with_guard_points)) {
XMLNode &after = audio_region()->envelope()->get_state();
if (_fx_line->the_list()->editor_add (timepos_t (fx), y, with_guard_points)) {
XMLNode &after = _fx_line->the_list()->get_state();
std::list<Selectable*> results;
trackview.editor().begin_reversible_command (_("add gain control point"));
@ -1442,9 +1502,9 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, b
trackview.session()->add_command (region_memento);
}
trackview.session()->add_command (new MementoCommand<AutomationList>(*audio_region()->envelope().get(), &before, &after));
trackview.session()->add_command (new MementoCommand<AutomationList>(*_fx_line->the_list(), &before, &after));
gain_line->get_selectables (region ()->position () + timecnt_t (fx), region ()->position () + timecnt_t (fx), 0.0, 1.0, results);
_fx_line->get_selectables (region ()->position () + timecnt_t (fx), region ()->position () + timecnt_t (fx), 0.0, 1.0, results);
trackview.editor ().get_selection ().set (results);
trackview.editor ().commit_reversible_command ();
@ -1454,12 +1514,14 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, b
}
}
#if 0 // unused
void
AudioRegionView::remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent* /*ev*/)
{
ControlPoint *cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
audio_region()->envelope()->erase (cp->model());
_fx_line->the_list()->erase (cp->model());
}
#endif
GhostRegion*
AudioRegionView::add_ghost (TimeAxisView& tv)
@ -1557,10 +1619,6 @@ AudioRegionView::exited ()
trackview.editor().set_current_trimmable (std::shared_ptr<Trimmable>());
trackview.editor().set_current_movable (std::shared_ptr<Movable>());
// if (gain_line) {
// gain_line->remove_visibility (AutomationLine::ControlPoints);
// }
if (fade_in_handle) { fade_in_handle->hide(); }
if (fade_out_handle) { fade_out_handle->hide(); }
if (fade_in_trim_handle) { fade_in_trim_handle->hide(); }
@ -1572,12 +1630,8 @@ AudioRegionView::exited ()
void
AudioRegionView::envelope_active_changed ()
{
if (gain_line) {
gain_line->set_line_color (audio_region()->envelope_active() ?
UIConfiguration::instance().color ("gain line") :
UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
update_envelope_visibility ();
}
set_fx_line_colors ();
update_envelope_visibility ();
}
void
@ -1595,6 +1649,20 @@ AudioRegionView::color_handler ()
}
void
AudioRegionView::set_fx_line_colors ()
{
assert (_fx_line);
if (_rdx_param != UINT32_MAX && _rfx_id != 0) {
_fx_line->set_line_color (UIConfiguration::instance().color ("processor automation line"));
} else {
_fx_line->set_line_color (audio_region()->envelope_active()
? UIConfiguration::instance().color ("gain line")
: UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
}
}
void
AudioRegionView::set_waveform_colors ()
{
@ -1696,7 +1764,7 @@ void
AudioRegionView::show_region_editor ()
{
if (editor == 0) {
editor = new AudioRegionEditor (trackview.session(), audio_region());
editor = new AudioRegionEditor (trackview.session(), this);
}
editor->present ();
@ -1858,7 +1926,7 @@ AudioRegionView::parameter_changed (string const & p)
MergeableLine*
AudioRegionView::make_merger ()
{
return new MergeableLine (gain_line, std::shared_ptr<AutomationControl>(),
return new MergeableLine (_fx_line, std::shared_ptr<AutomationControl>(),
[this](timepos_t const& t) { return timepos_t (_region->position().distance (t)); },
nullptr, nullptr);
}

View File

@ -50,9 +50,9 @@ namespace ARDOUR {
};
class AudioTimeAxisView;
class AudioRegionGainLine;
class GhostRegion;
class AutomationTimeAxisView;
class RegionFxLine;
class RouteTimeAxisView;
class AudioRegionView : public RegionView, public LineMerger
@ -91,12 +91,18 @@ public:
void temporarily_hide_envelope (); ///< Dangerous!
void unhide_envelope (); ///< Dangerous!
void set_region_gain_line ();
void set_ignore_line_change (bool v) { _ignore_line_change = v; };
bool set_region_fx_line (uint32_t, uint32_t);
bool get_region_fx_line (PBD::ID&, uint32_t&);
void update_envelope_visibility ();
sigc::signal<void> region_line_changed;
void add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *event, bool with_guard_points);
void remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent *event);
std::shared_ptr<AudioRegionGainLine> get_gain_line() const { return gain_line; }
std::shared_ptr<RegionFxLine> fx_line() const { return _fx_line; }
void region_changed (const PBD::PropertyChange&);
void envelope_active_changed ();
@ -184,7 +190,7 @@ protected:
ArdourCanvas::Rectangle* end_xfade_rect;
bool _end_xfade_visible;
std::shared_ptr<AudioRegionGainLine> gain_line;
std::shared_ptr<RegionFxLine> _fx_line;
double _amplitude_above_axis;
@ -206,6 +212,7 @@ protected:
void set_colors ();
void set_waveform_colors ();
void set_fx_line_colors ();
void reset_width_dependent_items (double pixel_width);
void color_handler ();
@ -234,6 +241,12 @@ private:
bool trim_fade_in_drag_active;
bool trim_fade_out_drag_active;
PBD::ID _rfx_id;
uint32_t _rdx_param;
bool _ignore_line_change;
PBD::ScopedConnection _region_fx_connection;
};
#endif /* __gtk_ardour_audio_region_view_h__ */

View File

@ -470,8 +470,8 @@ AudioStreamView::set_selected_points (PointSelection& points)
{
for (list<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
if (arv && arv->get_gain_line ()) {
arv->get_gain_line ()->set_selected_points (points);
if (arv && arv->fx_line ()) {
arv->fx_line ()->set_selected_points (points);
}
}
}

View File

@ -707,11 +707,11 @@ bool
Editor::canvas_line_event (GdkEvent *event, ArdourCanvas::Item* item, AutomationLine* al)
{
ItemType type;
AudioRegionGainLine* gl;
if ((gl = dynamic_cast<AudioRegionGainLine*> (al)) != 0) {
RegionFxLine* rfl;
if ((rfl = dynamic_cast<RegionFxLine*> (al)) != 0) {
type = GainLineItem;
if (event->type == GDK_BUTTON_PRESS) {
clicked_regionview = &gl->region_view ();
clicked_regionview = &rfl->region_view ();
}
} else {
type = AutomationLineItem;

View File

@ -5039,7 +5039,7 @@ LineDrag::finished (GdkEvent* event, bool movement_occurred)
AudioRegionView* arv;
if ((arv = dynamic_cast<AudioRegionView*> (_editor->clicked_regionview)) != 0) {
arv->add_gain_point_event (&arv->get_gain_line ()->grab_item (), event, false);
arv->add_gain_point_event (&arv->fx_line ()->grab_item (), event, false);
}
}
}
@ -6367,7 +6367,7 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, list<RegionView*> cons
for (list<RegionView*>::const_iterator i = v.begin (); i != v.end (); ++i) {
if (AudioRegionView* audio_view = dynamic_cast<AudioRegionView*> (*i)) {
lines.push_back (audio_view->get_gain_line ());
lines.push_back (audio_view->fx_line ());
} else if (AutomationRegionView* automation_view = dynamic_cast<AutomationRegionView*> (*i)) {
lines.push_back (automation_view->line ());
_integral = true;
@ -6394,8 +6394,8 @@ AutomationRangeDrag::setup (list<std::shared_ptr<AutomationLine>> const& lines)
/* need a special detection for automation lanes (not region gain line) */
// TODO: if we implement automation regions, this check can probably be removed
AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> ((*i).get ());
if (!argl) {
RegionFxLine* fxl = dynamic_cast<RegionFxLine*> ((*i).get ());
if (!fxl) {
/* in automation lanes, the EFFECTIVE range should be considered 0->max_position (even if there is no line) */
r.first = Temporal::timepos_t ((*i)->the_list ()->time_domain ());
r.second = Temporal::timepos_t::max ((*i)->the_list ()->time_domain ());

View File

@ -2340,7 +2340,7 @@ Editor::can_remove_control_point (ArdourCanvas::Item* item)
}
AutomationLine& line = control_point->line ();
if (dynamic_cast<AudioRegionGainLine*> (&line)) {
if (dynamic_cast<RegionFxLine*> (&line)) {
/* we shouldn't remove the first or last gain point in region gain lines */
if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
return false;

View File

@ -4833,7 +4833,7 @@ Editor::cut_copy_points (Editing::CutCopyOp op, timepos_t const & earliest_time)
bool erase = true;
if (dynamic_cast<AudioRegionGainLine*> (&line)) {
if (dynamic_cast<RegionFxLine*> (&line)) {
/* removing of first and last gain point in region gain lines is prohibited*/
if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
erase = false;

View File

@ -42,6 +42,7 @@
#include "ardour_http.h"
#include "ardour_ui.h"
#include "audio_region_view.h"
#include "public_editor.h"
#include "region_selection.h"
#include "luadialog.h"
@ -826,10 +827,16 @@ LuaInstance::register_classes (lua_State* L, bool sandbox)
.endClass ()
.deriveClass <RegionView, TimeAxisViewItem> ("RegionView")
.addCast<AudioRegionView> ("to_audioregionview")
.addFunction ("show_region_editor", &RegionView::show_region_editor)
.addFunction ("hide_region_editor", &RegionView::hide_region_editor)
.endClass ()
.deriveClass <AudioRegionView, RegionView> ("RegionView")
.addFunction ("set_region_gain_line", &AudioRegionView::set_region_gain_line)
.addFunction ("set_region_fx_line", &AudioRegionView::set_region_fx_line)
.endClass ()
.deriveClass <RouteUI, Selectable> ("RouteUI")
.endClass ()

View File

@ -62,11 +62,11 @@ using namespace PBD;
using namespace std;
using namespace Gtkmm2ext;
RegionEditor::RegionEditor (Session* s, std::shared_ptr<Region> r)
RegionEditor::RegionEditor (Session* s, RegionView* rv)
: ArdourDialog (_("Region"))
, _table (9, 3)
, _table_row (0)
, _region (r)
, _region (rv->region ())
, name_label (_("Name:"))
, audition_button (_("Audition"))
, _clock_group (new ClockGroup)
@ -77,12 +77,12 @@ RegionEditor::RegionEditor (Session* s, std::shared_ptr<Region> r)
, sync_offset_absolute_clock (X_("regionsyncoffsetabsolute"), true, "", true, false)
/* XXX cannot file start yet */
, start_clock (X_("regionstart"), true, "", false, false)
, _region_fx_box (r)
, _region_fx_box (_region)
, _sources (1)
{
set_session (s);
switch (r->time_domain()) {
switch (_region->time_domain()) {
case Temporal::AudioTime:
/* XXX check length of region and choose samples or minsec */
_clock_group->set_clock_mode (AudioClock::MinSec);

View File

@ -55,12 +55,13 @@ namespace ARDOUR {
class RegionFxPlugin;
}
class RegionView;
class ClockGroup;
class RegionEditor : public ArdourDialog
{
public:
RegionEditor (ARDOUR::Session*, std::shared_ptr<ARDOUR::Region>);
RegionEditor (ARDOUR::Session*, RegionView*);
virtual ~RegionEditor ();
protected:

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2024 Robin Gareus <robin@gareus.org>
*
* 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 "audio_region_view.h"
#include "region_fx_line.h"
#include "pbd/i18n.h"
RegionFxLine::RegionFxLine (std::string const& name, RegionView& r, ArdourCanvas::Container& parent, std::shared_ptr<ARDOUR::AutomationList> l, ARDOUR::ParameterDescriptor const& d)
: AutomationLine (name, r.get_time_axis_view(), parent, l, d)
, rv (r)
{
group->raise_to_top ();
group->set_y_position (2);
}
Temporal::timepos_t
RegionFxLine::get_origin() const
{
return rv.region()->position();
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2024 Robin Gareus <robin@gareus.org>
*
* 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.
*/
#ifndef __ardour_gtk_region_fx_line_h__
#define __ardour_gtk_region_fx_line_h__
#include "automation_line.h"
class RegionView;
class RegionFxLine : public AutomationLine
{
public:
RegionFxLine (std::string const&, RegionView&, ArdourCanvas::Container&, std::shared_ptr<ARDOUR::AutomationList>, ARDOUR::ParameterDescriptor const&);
Temporal::timepos_t get_origin() const;
RegionView& region_view () { return rv; }
protected:
RegionView& rv;
};
#endif

View File

@ -44,26 +44,14 @@ using namespace ARDOUR;
using namespace PBD;
AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView& r, ArdourCanvas::Container& parent, std::shared_ptr<AutomationList> l)
: AutomationLine (name, r.get_time_axis_view(), parent, l, l->parameter())
, rv (r)
: RegionFxLine (name, r, parent, l, l->parameter ())
, arv (r)
{
// If this isn't true something is horribly wrong, and we'll get catastrophic gain values
assert(l->parameter().type() == EnvelopeAutomation);
r.region()->PropertyChanged.connect (_region_changed_connection, invalidator (*this), boost::bind (&AudioRegionGainLine::region_changed, this, _1), gui_context());
group->raise_to_top ();
group->set_y_position (2);
terminal_points_can_slide = false;
}
timepos_t
AudioRegionGainLine::get_origin() const
{
return rv.region()->position();
}
void
AudioRegionGainLine::start_drag_single (ControlPoint* cp, double x, float fraction)
{
@ -71,9 +59,9 @@ AudioRegionGainLine::start_drag_single (ControlPoint* cp, double x, float fracti
// XXX Stateful need to capture automation curve data
if (!rv.audio_region()->envelope_active()) {
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0));
rv.audio_region()->set_envelope_active(false);
if (!arv.audio_region()->envelope_active()) {
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(arv.audio_region().get()), &arv.audio_region()->get_state(), 0));
arv.audio_region()->set_envelope_active(false);
}
}
@ -84,10 +72,10 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
trackview.editor().begin_reversible_command (_("remove control point"));
XMLNode &before = alist->get_state();
if (!rv.audio_region()->envelope_active()) {
rv.audio_region()->clear_changes ();
rv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new StatefulDiffCommand (rv.audio_region()));
if (!arv.audio_region()->envelope_active()) {
arv.audio_region()->clear_changes ();
arv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new StatefulDiffCommand (arv.audio_region()));
}
trackview.editor ().get_selection ().clear_points ();
@ -101,9 +89,9 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
void
AudioRegionGainLine::end_drag (bool with_push, uint32_t final_index)
{
if (!rv.audio_region()->envelope_active()) {
rv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state()));
if (!arv.audio_region()->envelope_active()) {
arv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(arv.audio_region().get()), 0, &arv.audio_region()->get_state()));
}
AutomationLine::end_drag (with_push, final_index);

View File

@ -27,8 +27,7 @@
#include "ardour/ardour.h"
#include "automation_line.h"
#include "region_fx_line.h"
namespace ARDOUR {
class Session;
@ -37,23 +36,21 @@ namespace ARDOUR {
class TimeAxisView;
class AudioRegionView;
class AudioRegionGainLine : public AutomationLine
class AudioRegionGainLine : public RegionFxLine
{
public:
AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Container& parent, std::shared_ptr<ARDOUR::AutomationList>);
Temporal::timepos_t get_origin() const;
void start_drag_single (ControlPoint*, double, float);
void end_drag (bool with_push, uint32_t final_index);
void remove_point (ControlPoint&);
AudioRegionView& region_view () { return rv; }
private:
PBD::ScopedConnection _region_changed_connection;
void region_changed (const PBD::PropertyChange& what_changed);
AudioRegionView& rv;
AudioRegionView& arv;
PBD::ScopedConnection _region_changed_connection;
};
#endif /* __ardour_gtk_region_gain_line_h__ */

View File

@ -750,7 +750,7 @@ void
RegionView::show_region_editor ()
{
if (!editor) {
editor = new RegionEditor (trackview.session(), region());
editor = new RegionEditor (trackview.session(), this);
}
editor->present ();

View File

@ -1176,11 +1176,11 @@ Selection::get_state () const
continue;
}
AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> (&(*i)->line());
if (argl) {
RegionFxLine* fxl = dynamic_cast<RegionFxLine*> (&(*i)->line());
if (fxl) {
XMLNode* r = node->add_child (X_("ControlPoint"));
r->set_property (X_("type"), "region");
r->set_property (X_("region-id"), argl->region_view ().region ()->id ());
r->set_property (X_("region-id"), fxl->region_view ().region ()->id ());
r->set_property (X_("view-index"), (*i)->view_index());
}
@ -1324,35 +1324,6 @@ Selection::set_state (XMLNode const & node, int)
if (!cps.empty()) {
add (cps);
}
} else if (prop_type->value () == "region") {
PBD::ID region_id;
uint32_t view_index;
if (!(*i)->get_property (X_("region-id"), region_id) ||
!(*i)->get_property (X_("view-index"), view_index)) {
continue;
}
RegionSelection rs;
editor->get_regionviews_by_id (region_id, rs);
if (!rs.empty ()) {
vector <ControlPoint *> cps;
for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*rsi);
if (arv) {
std::shared_ptr<AudioRegionGainLine> gl = arv->get_gain_line ();
ControlPoint* cp = gl->nth(view_index);
if (cp) {
cps.push_back (cp);
cp->show();
}
}
}
if (!cps.empty()) {
add (cps);
}
}
}
} else if ((*i)->name() == X_("TimelineRange")) {

View File

@ -622,7 +622,7 @@ StreamView::get_selectables (timepos_t const & start, timepos_t const & end, dou
|| (!within && (*i)->region()->coverage (start, end) != Temporal::OverlapNone)) {
if (_trackview.editor().internal_editing()) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
if (arv && arv->get_gain_line ()) {
if (arv && arv->fx_line ()) {
/* Note: AutomationLine::get_selectables() uses trackview.current_height (),
* disregarding Stacked layer display height
*/
@ -630,7 +630,7 @@ StreamView::get_selectables (timepos_t const & start, timepos_t const & end, dou
double const y = (*i)->get_canvas_group ()->position().y;
double t = 1.0 - std::min (1.0, std::max (0., (top - _trackview.y_position () - y) / c));
double b = 1.0 - std::min (1.0, std::max (0., (bottom - _trackview.y_position () - y) / c));
arv->get_gain_line()->get_selectables (start, end, b, t, results);
arv->fx_line()->get_selectables (start, end, b, t, results);
}
} else {
results.push_back (*i);

View File

@ -241,6 +241,7 @@ gtk2_ardour_sources = [
'recorder_group_tabs.cc',
'recorder_ui.cc',
'region_editor.cc',
'region_fx_line.cc',
'region_gain_line.cc',
'region_layering_order_editor.cc',
'region_list_base.cc',