Compare commits

...

23 Commits

Author SHA1 Message Date
Robin Gareus 754f789aa4
Fix region-gain envelope auto-enable when dragging
When using freehand drawing of automation, the line itself
is not notified (no ::start_drag(), ::end_drag() calls).

This adds a end_draw() call which allows the AutomatioLine
to perform additional actions.
2024-04-15 00:41:34 +02:00
Robin Gareus c7a06fa22d
Add custom color for region effect automation 2024-04-15 00:41:34 +02:00
Robin Gareus bde791df34
Add support for Region Fx Automation 2024-04-15 00:41:34 +02:00
Robin Gareus 373b70818c
GUI support for Region Fx (in Region Properties) 2024-04-15 00:41:33 +02:00
Robin Gareus 97c231c6e7
Per [Audio] Region Fx
* apply effects during region-read in non-rt context
* Add multi-channel audioregion read cache
  to process stereo effects
2024-04-15 00:41:33 +02:00
Robin Gareus 19fe9aee7b
Add custom RegionFxPlugin
Less than a PluginInsert but more than an IOPlug.
2024-04-15 00:41:33 +02:00
Robin Gareus e423222fee
NO-OP: sort debug bits 2024-04-15 00:41:33 +02:00
Robin Gareus 63c9658ae6
I/O and region plugins: hide automation controls in generic UI 2024-04-15 00:41:33 +02:00
Robin Gareus 0cfabacddb
Remove unused gain-buffer for master-read (NOOP) 2024-04-15 00:41:33 +02:00
Robin Gareus 8391f009ce
Fix redeclaration of DnDVbox 2024-04-15 00:41:33 +02:00
Robin Gareus 474c1724cd
Separate PluginWindowProxy into public class 2024-04-15 00:41:33 +02:00
Robin Gareus 6909c84a51
Plugins: Ignore offset for scratch/silent buffers
This allows to process buffers at an offset, as long
as the number of processed samples is less or equal to the
current buffersize.
2024-04-15 00:41:33 +02:00
Robin Gareus 737db323bb
NO-OP: whitespace 2024-04-15 00:41:32 +02:00
Robin Gareus 5d635fb294
Overhaul and optimize thread-buffer allocation
Every route calls Session::ensure_buffers when configuring
processors. Previously that unconditionally invoked the
BufferManager, even if no change was required.

This also fixes a potential issue when bouncing tracks.
::write_one_track() increases the buffersize to 8k, but only for
the ChanCount required to bounce. This was never properly reset.

Furthermore this is in preparation for RegionFX which may
need to increase the ChanCount of Threadbuffers.
2024-04-15 00:41:32 +02:00
Robin Gareus 58b3da7274
Automatable find_next/prev_ac_event requires no context 2024-04-15 00:41:32 +02:00
Robin Gareus 1838523db9
Fix signal analysis when buffer-size changes
PI::signal-analysis buffers were not updated when a user
changes the buffersize.

This also remove a single use Session API.
2024-04-15 00:41:32 +02:00
Robin Gareus 2e873f32dd
PluginInsert match I/O: skip div by zero 2024-04-15 00:41:32 +02:00
Robin Gareus 102cf764a8
IOPlug: various small fixes 2024-04-15 00:41:32 +02:00
Robin Gareus 67d881fcb8
Refactor and consolidate setting and copying plugin state 2024-04-15 00:41:32 +02:00
Robin Gareus 46af8a67d7
Consolidate PluginInsert Match, move to parent class 2024-04-15 00:41:32 +02:00
Robin Gareus 374d7e89e1
Consolidate plugin_factory, move to parent class 2024-04-15 00:41:32 +02:00
Robin Gareus 337ec53287
Consolidate PluginControl Code
This code was (for the most part) duplicated, and with
preparation for Region FX, a third copy motivated this
consolidation.
2024-04-15 00:41:31 +02:00
Robin Gareus 4b6e372ce7
Fix deleting the same point multiple times (#9689)
When iterating over automation tracks, previously it was
possible that the same point was added multiple times to
the selection.
2024-04-15 00:39:20 +02:00
75 changed files with 3684 additions and 886 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 ("region automation line");
} else if (audio_region()->envelope_active()) {
_fx_line->set_line_color ("gain line");
} else {
_fx_line->set_line_color ("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

@ -92,6 +92,7 @@ AutomationLine::AutomationLine (const string& name,
: trackview (tv)
, _name (name)
, _height (0)
, _line_color ("automation line")
, _view_index_offset (0)
, alist (al)
, _visible (Line)
@ -264,14 +265,23 @@ AutomationLine::set_height (guint32 h)
}
void
AutomationLine::set_line_color (uint32_t color)
AutomationLine::set_line_color (string color_name, std::string color_mod)
{
_line_color = color;
_line_color = color_name;
_line_color_mod = color_mod;
uint32_t color = UIConfiguration::instance().color (color_name);
line->set_outline_color (color);
Gtkmm2ext::SVAModifier mod = UIConfiguration::instance().modifier ("automation line fill");
Gtkmm2ext::SVAModifier mod = UIConfiguration::instance().modifier (color_mod.empty () ? "automation line fill" : color_mod);
line->set_fill_color ((color & 0xffffff00) + mod.a()*255);
line->set_fill_color ((color & 0xffffff00) + mod.a() * 255);
}
uint32_t
AutomationLine::get_line_color() const
{
return UIConfiguration::instance().color (_line_color);
}
ControlPoint*
@ -996,7 +1006,7 @@ AutomationLine::set_selected_points (PointSelection const & points)
void
AutomationLine::set_colors ()
{
set_line_color (UIConfiguration::instance().color ("automation line"));
set_line_color (_line_color, _line_color_mod);
for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
(*i)->set_color ();
}

View File

@ -94,6 +94,7 @@ public:
virtual void start_drag_multiple (std::list<ControlPoint*>, float, XMLNode *);
virtual std::pair<float, float> drag_motion (Temporal::timecnt_t const &, float, bool, bool with_push, uint32_t& final_index);
virtual void end_drag (bool with_push, uint32_t final_index);
virtual void end_draw_merge () {}
ControlPoint* nth (uint32_t);
ControlPoint const * nth (uint32_t) const;
@ -103,8 +104,8 @@ public:
bool visible() const { return _visible != VisibleAspects(0); }
guint32 height() const { return _height; }
void set_line_color (uint32_t);
uint32_t get_line_color() const { return _line_color; }
void set_line_color (std::string color, std::string mod = "");
uint32_t get_line_color() const;
void set_visibility (VisibleAspects);
void add_visibility (VisibleAspects);
@ -173,7 +174,8 @@ protected:
std::string _name;
guint32 _height;
uint32_t _line_color;
std::string _line_color;
std::string _line_color_mod;
uint32_t _view_index_offset;
std::shared_ptr<ARDOUR::AutomationList> alist;

View File

@ -350,7 +350,7 @@ AutomationTimeAxisView::add_contents (bool show_regions)
)
);
line->set_line_color (UIConfiguration::instance().color ("processor automation line"));
line->set_line_color ("processor automation line");
line->set_fill (true);
line->queue_reset ();
add_line (line);

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 ());
@ -7267,7 +7267,7 @@ FreehandLineDrag<OrderedPointList,OrderedPoint>::motion (GdkEvent* ev, bool firs
dragging_line = new ArdourCanvas::PolyLine (parent ? parent : item());
dragging_line->set_ignore_events (true);
dragging_line->set_outline_width (2.0);
dragging_line->set_outline_color (UIConfiguration::instance().color ("automation line"));
dragging_line->set_outline_color (UIConfiguration::instance().color ("automation line")); // XXX -> get color from AutomationLine
dragging_line->raise_to_top ();
/* for freehand drawing, we only support left->right direction, for now. */

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

@ -1015,7 +1015,10 @@ GenericPluginUI::build_control_ui (const Evoral::Parameter& param,
}
if (!_pi || mcontrol->flags () & Controllable::NotAutomatable) {
if (!_pi) {
control_ui->automate_button.set_no_show_all ();
control_ui->automate_button.hide ();
} else if (mcontrol->flags () & Controllable::NotAutomatable) {
control_ui->automate_button.set_sensitive (false);
set_tooltip(control_ui->automate_button, _("This control cannot be automated"));
} else {

View File

@ -42,6 +42,7 @@
#include "mixer_ui.h"
#include "plugin_selector.h"
#include "plugin_ui.h"
#include "plugin_window_proxy.h"
#include "ui_config.h"
#include "pbd/i18n.h"
@ -299,7 +300,7 @@ IOPluginWindow::IOPlugUI::IOPlugUI (std::shared_ptr<ARDOUR::IOPlug> iop)
_window_proxy = dynamic_cast<PluginWindowProxy*> (iop->window_proxy ());
assert (_window_proxy);
} else {
_window_proxy = new PluginWindowProxy (string_compose ("IOP-%1", _iop->id ()), _iop);
_window_proxy = new PluginWindowProxy (string_compose ("IOP-%1", _iop->id ()), "I/O", _iop);
const XMLNode* ui_xml = _iop->session ().extra_xml (X_("UI"));
if (ui_xml) {
@ -373,113 +374,6 @@ IOPluginWindow::IOPlugUI::button_resized (Gtk::Allocation& alloc)
_btn_ioplug.set_layout_ellipsize_width (alloc.get_width () * PANGO_SCALE);
}
/* ****************************************************************************/
IOPluginWindow::PluginWindowProxy::PluginWindowProxy (std::string const& name, std::weak_ptr<PlugInsertBase> plugin)
: WM::ProxyBase (name, std::string ())
, _pib (plugin)
, _is_custom (true)
, _want_custom (true)
{
std::shared_ptr<PlugInsertBase> p = _pib.lock ();
if (!p) {
return;
}
p->DropReferences.connect (_going_away_connection, MISSING_INVALIDATOR, boost::bind (&IOPluginWindow::PluginWindowProxy::plugin_going_away, this), gui_context ());
}
IOPluginWindow::PluginWindowProxy::~PluginWindowProxy ()
{
_window = 0;
}
Gtk::Window*
IOPluginWindow::PluginWindowProxy::get (bool create)
{
std::shared_ptr<PlugInsertBase> p = _pib.lock ();
if (!p) {
return 0;
}
if (_window && (_is_custom != _want_custom)) {
set_state_mask (WindowProxy::StateMask (state_mask () & ~WindowProxy::Size));
drop_window ();
}
if (!_window) {
if (!create) {
return 0;
}
_is_custom = _want_custom;
_window = new PluginUIWindow (p, false, _is_custom);
if (_window) {
std::shared_ptr<ARDOUR::IOPlug> iop = std::dynamic_pointer_cast<ARDOUR::IOPlug> (p);
assert (iop);
_window->set_title (iop->name ());
setup ();
_window->show_all ();
}
}
return _window;
}
void
IOPluginWindow::PluginWindowProxy::show_the_right_window ()
{
if (_window && (_is_custom != _want_custom)) {
set_state_mask (WindowProxy::StateMask (state_mask () & ~WindowProxy::Size));
drop_window ();
}
if (_window) {
_window->unset_transient_for ();
}
toggle ();
}
int
IOPluginWindow::PluginWindowProxy::set_state (const XMLNode& node, int)
{
XMLNodeList children = node.children ();
XMLNodeList::const_iterator i = children.begin ();
while (i != children.end ()) {
std::string name;
if ((*i)->name () == X_("Window") && (*i)->get_property (X_("name"), name) && name == _name) {
break;
}
++i;
}
if (i != children.end ()) {
(*i)->get_property (X_("custom-ui"), _want_custom);
}
return ProxyBase::set_state (node, 0);
}
XMLNode&
IOPluginWindow::PluginWindowProxy::get_state () const
{
XMLNode* node;
node = &ProxyBase::get_state ();
node->set_property (X_("custom-ui"), _is_custom);
return *node;
}
void
IOPluginWindow::PluginWindowProxy::plugin_going_away ()
{
delete _window;
_window = 0;
WM::Manager::instance ().remove (this);
_going_away_connection.disconnect ();
delete this;
}
/* ****************************************************************************/
IOPluginWindow::IOButton::IOButton (std::shared_ptr<ARDOUR::IO> io, bool pre)
: _io (io)
, _pre (pre)

View File

@ -35,11 +35,11 @@ namespace ARDOUR
{
class IO;
class IOPlug;
class PlugInsertBase;
class Port;
}
class IOSelectorWindow;
class PluginWindowProxy;
class IOPluginWindow : public ArdourWindow
{
@ -48,39 +48,6 @@ public:
void set_session (ARDOUR::Session*);
class PluginWindowProxy : public WM::ProxyBase
{
public:
PluginWindowProxy (std::string const&, std::weak_ptr<ARDOUR::PlugInsertBase>);
~PluginWindowProxy ();
Gtk::Window* get (bool create = false);
void show_the_right_window ();
ARDOUR::SessionHandlePtr* session_handle ()
{
return 0;
}
void set_custom_ui_mode (bool use_custom)
{
_want_custom = use_custom;
}
int set_state (const XMLNode&, int);
XMLNode& get_state () const;
private:
void plugin_going_away ();
std::weak_ptr<ARDOUR::PlugInsertBase> _pib;
bool _is_custom;
bool _want_custom;
PBD::ScopedConnection _going_away_connection;
};
protected:
void on_show ();
void on_hide ();
@ -147,7 +114,7 @@ private:
IOButton _btn_output;
ArdourWidgets::ArdourButton _btn_ioplug;
PluginWindowProxy* _window_proxy;
std::shared_ptr<ARDOUR::IOPlug> _iop;
std::shared_ptr<ARDOUR::IOPlug> _iop;
PBD::ScopedConnection _going_away_connection;
};

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,6 +827,14 @@ 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")

View File

@ -103,6 +103,8 @@ MergeableLine::merge_drawn_line (Editor& e, Session& s, Evoral::ControlList::Ord
e.begin_reversible_command (_("draw automation"));
s.add_command (new MementoCommand<ARDOUR::AutomationList> (*list.get (), &before, &after));
_line->end_draw_merge ();
_line->get_selectables (earliest, latest, 0.0, 1.0, results);
e.get_selection ().set (results);

View File

@ -2548,13 +2548,13 @@ MidiRegionView::update_drag_selection(timepos_t const & start, timepos_t const &
/* Add control points to selection. */
const ATracks& atracks = midi_view()->automation_tracks();
Selectables selectables;
editor.get_selection().clear_points();
timepos_t st (start);
timepos_t et (end);
for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
Selectables selectables;
a->second->get_selectables (st, et, gy0, gy1, selectables);
for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);

View File

@ -0,0 +1,154 @@
/*
* Copyright (C) 2022 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 "ardour/plug_insert_base.h"
#include "ardour/plugin_manager.h"
#include "gui_thread.h"
#include "plugin_ui.h"
#include "plugin_window_proxy.h"
#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace Gtk;
using namespace Gtkmm2ext;
PluginWindowProxy::PluginWindowProxy (std::string const& name, std::string const& title, std::weak_ptr<PlugInsertBase> plugin)
: WM::ProxyBase (name, std::string ())
, _pib (plugin)
, _title (title)
, _is_custom (true)
, _want_custom (true)
{
std::shared_ptr<PlugInsertBase> p = _pib.lock ();
if (!p) {
return;
}
p->DropReferences.connect (*this, MISSING_INVALIDATOR, boost::bind (&PluginWindowProxy::plugin_going_away, this), gui_context ());
}
PluginWindowProxy::~PluginWindowProxy ()
{
_window = 0;
}
Gtk::Window*
PluginWindowProxy::get (bool create)
{
std::shared_ptr<PlugInsertBase> p = _pib.lock ();
if (!p) {
return 0;
}
if (_window && (_is_custom != _want_custom)) {
set_state_mask (WindowProxy::StateMask (state_mask () & ~WindowProxy::Size));
drop_window ();
}
if (!_window) {
if (!create) {
return 0;
}
_is_custom = _want_custom;
_window = new PluginUIWindow (p, false, _is_custom);
if (_window) {
_window->set_title (generate_processor_title (p));
setup ();
_window->show_all ();
}
}
return _window;
}
void
PluginWindowProxy::show_the_right_window ()
{
if (_window && (_is_custom != _want_custom)) {
set_state_mask (WindowProxy::StateMask (state_mask () & ~WindowProxy::Size));
drop_window ();
}
if (_window) {
_window->unset_transient_for ();
}
toggle ();
}
int
PluginWindowProxy::set_state (const XMLNode& node, int)
{
XMLNodeList children = node.children ();
XMLNodeList::const_iterator i = children.begin ();
while (i != children.end ()) {
std::string name;
if ((*i)->name () == X_("Window") && (*i)->get_property (X_("name"), name) && name == _name) {
break;
}
++i;
}
if (i != children.end ()) {
(*i)->get_property (X_("custom-ui"), _want_custom);
}
return ProxyBase::set_state (node, 0);
}
XMLNode&
PluginWindowProxy::get_state () const
{
XMLNode* node;
node = &ProxyBase::get_state ();
node->set_property (X_("custom-ui"), _is_custom);
return *node;
}
void
PluginWindowProxy::plugin_going_away ()
{
delete _window;
_window = 0;
WM::Manager::instance ().remove (this);
drop_connections ();
delete this;
}
std::string
PluginWindowProxy::generate_processor_title (std::shared_ptr<PlugInsertBase> p)
{
std::string maker = p->plugin()->maker() ? p->plugin()->maker() : "";
std::string::size_type email_pos;
if ((email_pos = maker.find_first_of ('<')) != std::string::npos) {
maker = maker.substr (0, email_pos - 1);
}
if (maker.length() > 32) {
maker = maker.substr (0, 32);
maker += " ...";
}
std::string type = PluginManager::plugin_type_name (p->type ());
auto so = std::dynamic_pointer_cast<SessionObject> (p);
assert (so);
return string_compose(_("%1: %2 (by %3) [%4]"), _title, so->name(), maker, type);
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2022,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 _gtkardour_plugin_window_proxy_h_
#define _gtkardour_plugin_window_proxy_h_
#include "ardour_window.h"
#include "window_manager.h"
#include "pbd/signals.h"
namespace Gtk
{
class Window;
}
namespace ARDOUR
{
class PlugInsertBase;
}
class PluginWindowProxy : public WM::ProxyBase, public PBD::ScopedConnectionList
{
public:
PluginWindowProxy (std::string const&, std::string const&, std::weak_ptr<ARDOUR::PlugInsertBase>);
~PluginWindowProxy ();
Gtk::Window* get (bool create = false);
void show_the_right_window ();
ARDOUR::SessionHandlePtr* session_handle ()
{
return 0;
}
void set_custom_ui_mode (bool use_custom)
{
_want_custom = use_custom;
}
int set_state (const XMLNode&, int);
XMLNode& get_state () const;
std::string generate_processor_title (std::shared_ptr<ARDOUR::PlugInsertBase>);
private:
void plugin_going_away ();
std::weak_ptr<ARDOUR::PlugInsertBase> _pib;
std::string _title;
bool _is_custom;
bool _want_custom;
};
#endif

View File

@ -28,18 +28,31 @@
#include "pbd/memento_command.h"
#include "pbd/stateful_diff_command.h"
#include "pbd/unwind.h"
#include "gtkmm2ext/dndtreeview.h"
#include "widgets/tooltips.h"
#include "ardour/plugin_manager.h"
#include "ardour/region.h"
#include "ardour/region_fx_plugin.h"
#include "ardour/session.h"
#include "ardour/source.h"
#include "ardour_message.h"
#include "ardour_ui.h"
#include "clock_group.h"
#include "main_clock.h"
#include "context_menu_helper.h"
#include "gui_thread.h"
#include "keyboard.h"
#include "main_clock.h"
#include "mixer_ui.h"
#include "new_plugin_preset_dialog.h"
#include "region_editor.h"
#include "region_view.h"
#include "plugin_selector.h"
#include "plugin_window_proxy.h"
#include "public_editor.h"
#include "pbd/i18n.h"
@ -49,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, 2)
, _table (9, 3)
, _table_row (0)
, _region (r)
, _region (rv->region ())
, name_label (_("Name:"))
, audition_button (_("Audition"))
, _clock_group (new ClockGroup)
@ -64,11 +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 (_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);
@ -113,6 +127,8 @@ RegionEditor::RegionEditor (Session* s, std::shared_ptr<Region> r)
start_label.set_name ("RegionEditorLabel");
start_label.set_text (_("File start:"));
_sources_label.set_name ("RegionEditorLabel");
region_fx_label.set_text (_("Region Effects"));
region_fx_label.set_name ("RegionEditorLabel");
if (_region->sources().size() > 1) {
_sources_label.set_text (_("Sources:"));
@ -170,6 +186,9 @@ RegionEditor::RegionEditor (Session* s, std::shared_ptr<Region> r)
_table.attach (_sources, 1, 2, _table_row, _table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
++_table_row;
_table.attach (region_fx_label, 2, 3, 0, 1, Gtk::FILL, Gtk::FILL);
_table.attach (_region_fx_box, 2, 3, 1, _table_row + 2, Gtk::FILL, Gtk::FILL);
get_vbox()->pack_start (_table, true, true);
add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_ACCEPT);
@ -190,6 +209,9 @@ RegionEditor::RegionEditor (Session* s, std::shared_ptr<Region> r)
assert (t);
t->property_ellipsize() = Pango::ELLIPSIZE_END;
region_fx_label.set_no_show_all ();
_region_fx_box.set_no_show_all ();
show_all();
name_changed ();
@ -203,9 +225,16 @@ RegionEditor::RegionEditor (Session* s, std::shared_ptr<Region> r)
bounds_changed (change);
_region->PropertyChanged.connect (state_connection, invalidator (*this), boost::bind (&RegionEditor::region_changed, this, _1), gui_context());
_region->RegionFxChanged.connect (region_connection, invalidator (*this), boost::bind (&RegionEditor::region_fx_changed, this), gui_context ());
spin_arrow_grab = false;
/* for now only audio region effects are supported */
if (std::dynamic_pointer_cast<AudioRegion> (_region)) {
region_fx_label.show ();
_region_fx_box.show ();
}
connect_editor_events ();
}
@ -238,6 +267,12 @@ RegionEditor::region_changed (const PBD::PropertyChange& what_changed)
}
}
void
RegionEditor::region_fx_changed ()
{
_region_fx_box.redisplay_plugins ();
}
gint
RegionEditor::bpressed (GdkEventButton* ev, Gtk::SpinButton* /*but*/, void (RegionEditor::*/*pmf*/)())
{
@ -469,3 +504,541 @@ RegionEditor::handle_response (int)
{
hide ();
}
/* ****************************************************************************/
static std::list<Gtk::TargetEntry>
drop_targets ()
{
std::list<Gtk::TargetEntry> tmp;
tmp.push_back (Gtk::TargetEntry ("x-ardour/region-fx", Gtk::TARGET_SAME_APP)); // re-order
tmp.push_back (Gtk::TargetEntry ("x-ardour/plugin.info", Gtk::TARGET_SAME_APP)); // from plugin-manager
tmp.push_back (Gtk::TargetEntry ("x-ardour/plugin.favorite", Gtk::TARGET_SAME_APP)); // from sidebar
return tmp;
}
static std::list<Gtk::TargetEntry>
drag_targets ()
{
std::list<Gtk::TargetEntry> tmp;
tmp.push_back (Gtk::TargetEntry ("x-ardour/region-fx", Gtk::TARGET_SAME_APP)); // re-order
tmp.push_back (Gtk::TargetEntry ("x-ardour/plugin.preset", Gtk::TARGET_SAME_APP)); // to sidebar (optional preset)
return tmp;
}
RegionEditor::RegionFxBox::RegionFxBox (std::shared_ptr<ARDOUR::Region> r)
: _region (r)
, _display (drop_targets ())
, _no_redisplay (false)
, _placement (-1)
{
_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
_scroller.set_name ("ProcessorScroller");
_scroller.add (_display);
pack_start (_scroller, true, true);
_display.set_can_focus ();
_display.set_name ("ProcessorList");
_display.set_data ("regionfxbox", this);
_display.set_size_request (104, -1); // TODO UI scale
_display.set_spacing (0);
_display.ButtonPress.connect (sigc::mem_fun (*this, &RegionFxBox::fxe_button_press_event));
_display.ButtonRelease.connect (sigc::mem_fun (*this, &RegionFxBox::fxe_button_release_event));
_display.Reordered.connect (sigc::mem_fun (*this, &RegionFxBox::reordered));
_display.DropFromAnotherBox.connect (sigc::mem_fun (*this, &RegionFxBox::object_drop));
_display.DropFromExternal.connect (sigc::mem_fun (*this, &RegionFxBox::plugin_drop));
_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &RegionFxBox::on_key_press), false);
_scroller.show ();
_display.show ();
redisplay_plugins ();
}
bool
RegionEditor::RegionFxBox::use_plugins (SelectedPlugins const& plugins)
{
int errors = 0;
{
PBD::Unwinder<bool> uw (_no_redisplay, true);
for (auto const& p : plugins) {
std::shared_ptr<RegionFxPlugin> pos;
if (_placement >= 0) {
pos = _region->nth_plugin (_placement++);
}
if (!_region->add_plugin (std::shared_ptr<RegionFxPlugin> (new RegionFxPlugin (_region->session (), _region->time_domain (), p)), pos)) {
++errors;
}
}
}
redisplay_plugins ();
if (errors) {
notify_plugin_load_fail (errors);
}
return false;
}
void
RegionEditor::RegionFxBox::redisplay_plugins ()
{
if (_no_redisplay) {
return;
}
_display.clear ();
_region->foreach_plugin (sigc::mem_fun (*this, &RegionFxBox::add_fx_to_display));
}
void
RegionEditor::RegionFxBox::add_fx_to_display (std::weak_ptr<RegionFxPlugin> wfx)
{
std::shared_ptr<RegionFxPlugin> fx (wfx.lock ());
if (!fx) {
return;
}
RegionFxEntry* e = new RegionFxEntry (fx);
_display.add_child (e, drag_targets ());
}
bool
RegionEditor::RegionFxBox::fxe_button_press_event (GdkEventButton* ev, RegionFxEntry* child)
{
if (child) {
std::weak_ptr<RegionFxPlugin> wfx (std::weak_ptr<RegionFxPlugin> (child->region_fx_plugin ()));
if (Keyboard::is_edit_event (ev) || (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS)) {
if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
show_plugin_gui (wfx, false);
} else {
show_plugin_gui (wfx, true);
}
return true;
}
if (Keyboard::is_context_menu_event (ev)) {
using namespace Gtk::Menu_Helpers;
PluginSelector* ps = Mixer_UI::instance ()->plugin_selector ();
ps->set_interested_object (*this);
Gtk::Menu* m = ARDOUR_UI_UTILS::shared_popup_menu ();
MenuList& items = m->items ();
items.push_back (MenuElem (_("New Plugin")));
Gtk::MenuItem& npm = items.back ();
npm.set_submenu (*ps->plugin_menu ());
std::shared_ptr<Plugin> plugin = child->region_fx_plugin ()->plugin ();
items.push_back (SeparatorElem ());
items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &RegionFxBox::show_plugin_gui), wfx, true)));
items.back ().set_sensitive (plugin->has_editor ());
items.push_back (MenuElem (_("Edit with generic controls..."), sigc::bind (sigc::mem_fun (*this, &RegionFxBox::show_plugin_gui), wfx, false)));
Gtk::Menu* automation_menu = manage (new Gtk::Menu);
MenuList& ac_items (automation_menu->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> (child->region_fx_plugin ()->control (param)));
if (c && c->flags () & (Controllable::HiddenControl | Controllable::NotAutomatable)) {
continue;
}
std::weak_ptr<ARDOUR::AutomationControl> wac (c);
bool play = c->automation_state () == Play;
ac_items.push_back (CheckMenuElem (label));
Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*> (&ac_items.back ());
cmi->set_active (play);
cmi->signal_activate ().connect ([wac, play] () {
std::shared_ptr<ARDOUR::AutomationControl> ac = wac.lock ();
if (ac) {
ac->set_automation_state (play ? ARDOUR::Off : Play);
}
});
}
if (!ac_items.empty ()) {
items.push_back (SeparatorElem ());
items.push_back (MenuElem ("Automation Enable", *automation_menu));
} else {
delete automation_menu;
}
items.push_back (SeparatorElem ());
items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &RegionFxBox::queue_delete_region_fx), wfx)));
m->signal_unmap ().connect ([this, &npm] () { npm.remove_submenu (); _display.remove_placeholder (); });
m->popup (ev->button, ev->time);
int x, y;
_display.get_pointer (x, y);
_placement = _display.add_placeholder (y);
return true;
}
return false;
}
if (Keyboard::is_context_menu_event (ev)) {
_placement = -1;
using namespace Gtk::Menu_Helpers;
PluginSelector* ps = Mixer_UI::instance ()->plugin_selector ();
ps->set_interested_object (*this);
Gtk::Menu* m = ARDOUR_UI_UTILS::shared_popup_menu ();
MenuList& items = m->items ();
items.push_back (MenuElem (_("New Plugin")));
Gtk::MenuItem& npm = items.back ();
npm.set_submenu (*ps->plugin_menu ());
m->signal_unmap ().connect ([&npm] () { npm.remove_submenu (); });
m->popup (ev->button, ev->time);
return true;
} else if (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS) {
_placement = -1;
PluginSelector* ps = Mixer_UI::instance ()->plugin_selector ();
ps->set_interested_object (*this);
ps->show_manager ();
return true;
}
return false;
}
bool
RegionEditor::RegionFxBox::fxe_button_release_event (GdkEventButton* ev, RegionFxEntry* child)
{
if (child && Keyboard::is_delete_event (ev)) {
queue_delete_region_fx (std::weak_ptr<RegionFxPlugin> (child->region_fx_plugin ()));
}
return false;
}
bool
RegionEditor::RegionFxBox::on_key_press (GdkEventKey* ev)
{
switch (ev->keyval) {
case GDK_KEY_Delete:
break;
case GDK_KEY_BackSpace:
break;
default:
return false;
}
for (auto const& i : _display.selection (true)) {
queue_delete_region_fx (std::weak_ptr<RegionFxPlugin> (i->region_fx_plugin ()));
}
return true;
}
void
RegionEditor::RegionFxBox::reordered ()
{
Region::RegionFxList fxl;
for (auto const& i : _display.children ()) {
fxl.push_back (i->region_fx_plugin ());
}
_region->reorder_plugins (fxl);
}
void
RegionEditor::RegionFxBox::queue_delete_region_fx (std::weak_ptr<ARDOUR::RegionFxPlugin> wfx)
{
Glib::signal_idle ().connect (sigc::bind (sigc::mem_fun (*this, &RegionFxBox::idle_delete_region_fx), wfx));
}
bool
RegionEditor::RegionFxBox::idle_delete_region_fx (std::weak_ptr<RegionFxPlugin> wfx)
{
std::shared_ptr<RegionFxPlugin> fx (wfx.lock ());
if (!fx) {
return false;
}
_region->remove_plugin (fx);
return false;
}
void
RegionEditor::RegionFxBox::notify_plugin_load_fail (uint32_t cnt)
{
assert (cnt > 0);
ArdourMessageDialog (_("Failed to load Region Effect Plugin"), false, Gtk::MESSAGE_ERROR).run ();
}
std::shared_ptr<RegionFxPlugin>
RegionEditor::RegionFxBox::find_drop_position (RegionFxEntry* pos)
{
std::shared_ptr<RegionFxPlugin> rv;
if (pos) {
rv = pos->region_fx_plugin ();
if (!rv) {
rv = _display.children ().front ()->region_fx_plugin ();
}
}
return rv;
}
void
RegionEditor::RegionFxBox::plugin_drop (Gtk::SelectionData const& data, RegionFxEntry* pos, Glib::RefPtr<Gdk::DragContext> const& context)
{
uint32_t errors = 0;
std::shared_ptr<RegionFxPlugin> at = find_drop_position (pos);
if (data.get_target () == "x-ardour/plugin.info") {
const void* d = data.get_data ();
const Gtkmm2ext::DnDTreeView<ARDOUR::PluginInfoPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginInfoPtr>*> (d);
PluginInfoList nfos;
Gtk::TreeView* source;
tv->get_object_drag_data (nfos, &source);
for (auto const& i : nfos) {
PluginPtr p = (i)->load (_region->session ());
if (!_region->add_plugin (std::shared_ptr<RegionFxPlugin> (new RegionFxPlugin (_region->session (), _region->time_domain (), p)), at)) {
++errors;
}
}
} else if (data.get_target () == "x-ardour/plugin.favorite") {
const void* d = data.get_data ();
const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*> (d);
PluginPresetList nfos;
Gtk::TreeView* source;
tv->get_object_drag_data (nfos, &source);
for (auto const& i : nfos) {
PluginPresetPtr ppp (i);
PluginInfoPtr pip = ppp->_pip;
PluginPtr p = pip->load (_region->session ());
if (!p) {
continue;
}
if (ppp->_preset.valid) {
p->load_preset (ppp->_preset);
}
if (!_region->add_plugin (std::shared_ptr<RegionFxPlugin> (new RegionFxPlugin (_region->session (), _region->time_domain (), p)), at)) {
++errors;
}
}
}
if (errors) {
notify_plugin_load_fail (errors);
}
}
void
RegionEditor::RegionFxBox::delete_dragged_plugins (Region::RegionFxList const& fxl)
{
{
PBD::Unwinder<bool> uw (_no_redisplay, true);
for (auto const& fx : fxl) {
_region->remove_plugin (fx);
}
}
redisplay_plugins ();
}
void
RegionEditor::RegionFxBox::object_drop (Gtkmm2ext::DnDVBox<RegionFxEntry>* source, RegionFxEntry* pos, Glib::RefPtr<Gdk::DragContext> const& context)
{
if (Gdk::ACTION_LINK == context->get_selected_action ()) {
std::list<RegionFxEntry*> children = source->selection ();
assert (children.size () == 1);
RegionFxEntry* other = *children.begin ();
assert (other->can_copy_state (pos));
std::shared_ptr<ARDOUR::RegionFxPlugin> othr = other->region_fx_plugin ();
std::shared_ptr<ARDOUR::RegionFxPlugin> self = pos->region_fx_plugin ();
PBD::ID id = self->id ();
XMLNode& state = othr->get_state ();
state.remove_property ("count");
/* Controllable and automation IDs should not be copied */
PBD::Stateful::ForceIDRegeneration force_ids;
self->set_state (state, Stateful::current_state_version);
self->update_id (id);
return;
}
std::shared_ptr<RegionFxPlugin> at = find_drop_position (pos);
uint32_t errors = 0;
Region::RegionFxList fxl;
for (auto const& i : source->selection (true)) {
fxl.push_back (i->region_fx_plugin ());
}
for (auto const& i : fxl) {
XMLNode& state = i->get_state ();
state.remove_property ("count");
PBD::Stateful::ForceIDRegeneration force_ids;
std::shared_ptr<RegionFxPlugin> rfx (new RegionFxPlugin (_region->session (), _region->time_domain ()));
rfx->set_state (state, Stateful::current_state_version);
if (!_region->add_plugin (rfx, at)) {
++errors;
}
delete &state;
}
if ((context->get_suggested_action () == Gdk::ACTION_MOVE) && source) {
RegionFxBox* other = reinterpret_cast<RegionFxBox*> (source->get_data ("regionfxbox"));
if (other) {
other->delete_dragged_plugins (fxl);
}
}
if (errors) {
notify_plugin_load_fail (errors);
}
}
void
RegionEditor::RegionFxBox::show_plugin_gui (std::weak_ptr<RegionFxPlugin> wfx, bool custom_ui)
{
std::shared_ptr<RegionFxPlugin> rfx (wfx.lock ());
if (!rfx) {
return;
}
PluginWindowProxy* pwp;
if (rfx->window_proxy ()) {
pwp = dynamic_cast<PluginWindowProxy*> (rfx->window_proxy ());
} else {
pwp = new PluginWindowProxy (string_compose ("RFX-%1", rfx->id ()), _region->name (), rfx);
const XMLNode* ui_xml = rfx->session ().extra_xml (X_("UI"));
if (ui_xml) {
pwp->set_state (*ui_xml, 0);
}
rfx->set_window_proxy (pwp);
WM::Manager::instance ().register_window (pwp);
RegionView* rv = PublicEditor::instance ().regionview_from_region (_region);
rv->RegionViewGoingAway.connect_same_thread (*pwp, [pwp] (RegionView*) { pwp->hide (); });
}
pwp->set_custom_ui_mode (custom_ui);
pwp->show_the_right_window ();
Gtk::Window* tlw = PublicEditor::instance ().current_toplevel ();
if (tlw) {
pwp->set_transient_for (*tlw);
}
}
/* ****************************************************************************/
RegionEditor::RegionFxEntry::RegionFxEntry (std::shared_ptr<RegionFxPlugin> rfx)
: _fx_btn (ArdourWidgets::ArdourButton::default_elements)
, _rfx (rfx)
{
_box.pack_start (_fx_btn, true, true);
_plugin_preset_pointer = PluginPresetPtr (new PluginPreset (rfx->plugin ()->get_info ()));
_fx_btn.set_fallthrough_to_parent (true);
_fx_btn.set_text (name ());
_fx_btn.set_active (true);
_fx_btn.set_name ("processor postfader");
if (rfx->plugin ()->has_editor ()) {
set_tooltip (_fx_btn, string_compose (_("<b>%1</b>\nDouble-click to show GUI.\n%2+double-click to show generic GUI."), name (), Keyboard::secondary_modifier_name ()));
} else {
set_tooltip (_fx_btn, string_compose (_("<b>%1</b>\nDouble-click to show generic GUI."), name ()));
}
_box.show ();
_fx_btn.show ();
}
std::string
RegionEditor::RegionFxEntry::name () const
{
return _rfx->name ();
}
bool
RegionEditor::RegionFxEntry::can_copy_state (Gtkmm2ext::DnDVBoxChild* o) const
{
RegionFxEntry* other = dynamic_cast<RegionFxEntry*> (o);
if (!other) {
return false;
}
std::shared_ptr<ARDOUR::RegionFxPlugin> othr = other->region_fx_plugin ();
std::shared_ptr<ARDOUR::RegionFxPlugin> self = region_fx_plugin ();
if (self->type () != othr->type ()) {
return false;
}
std::shared_ptr<Plugin> my_p = self->plugin ();
std::shared_ptr<Plugin> ot_p = othr->plugin ();
return my_p->unique_id () == ot_p->unique_id ();
}
void
RegionEditor::RegionFxEntry::set_visual_state (Gtkmm2ext::VisualState s, bool yn)
{
if (yn) {
_fx_btn.set_visual_state (Gtkmm2ext::VisualState (_fx_btn.visual_state () | s));
} else {
_fx_btn.set_visual_state (Gtkmm2ext::VisualState (_fx_btn.visual_state () & ~s));
}
}
bool
RegionEditor::RegionFxEntry::drag_data_get (Glib::RefPtr<Gdk::DragContext> const, Gtk::SelectionData& data)
{
/* compare to ProcessorEntry::drag_data_get */
if (data.get_target () != "x-ardour/plugin.preset") {
return false;
}
std::shared_ptr<Plugin> plugin = _rfx->plugin ();
assert (plugin);
PluginManager& manager (PluginManager::instance ());
bool fav = manager.get_status (_plugin_preset_pointer->_pip) == PluginManager::Favorite;
NewPluginPresetDialog d (plugin, string_compose (_("New Favorite Preset for \"%1\""), _plugin_preset_pointer->_pip->name), !fav);
_plugin_preset_pointer->_preset.valid = false;
switch (d.run ()) {
default:
case Gtk::RESPONSE_CANCEL:
data.set (data.get_target (), 8, NULL, 0);
return true;
break;
case Gtk::RESPONSE_NO:
break;
case Gtk::RESPONSE_ACCEPT:
if (d.name ().empty ()) {
break;
}
if (d.replace ()) {
plugin->remove_preset (d.name ());
}
Plugin::PresetRecord const r = plugin->save_preset (d.name ());
if (!r.uri.empty ()) {
_plugin_preset_pointer->_preset.uri = r.uri;
_plugin_preset_pointer->_preset.label = r.label;
_plugin_preset_pointer->_preset.user = r.user;
_plugin_preset_pointer->_preset.valid = r.valid;
}
}
data.set (data.get_target (), 8, (const guchar*)&_plugin_preset_pointer, sizeof (PluginPresetPtr));
return true;
}

View File

@ -27,6 +27,7 @@
#include <gtkmm/label.h>
#include <gtkmm/entry.h>
#include <gtkmm/box.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/togglebutton.h>
#include <gtkmm/button.h>
#include <gtkmm/arrow.h>
@ -36,34 +37,100 @@
#include <gtkmm/separator.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/listviewtext.h>
#include <gtkmm/scrolledwindow.h>
#include "gtkmm2ext/dndtreeview.h"
#include "gtkmm2ext/dndvbox.h"
#include "pbd/signals.h"
#include "audio_clock.h"
#include "ardour_dialog.h"
#include "plugin_interest.h"
#include "region_editor.h"
namespace ARDOUR {
class Region;
class Session;
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:
virtual void region_changed (const PBD::PropertyChange&);
virtual void region_fx_changed ();
Gtk::Table _table;
int _table_row;
private:
class RegionFxEntry : public Gtkmm2ext::DnDVBoxChild, public sigc::trackable
{
public:
RegionFxEntry (std::shared_ptr<ARDOUR::RegionFxPlugin>);
Gtk::EventBox& action_widget () { return _fx_btn; }
Gtk::Widget& widget () { return _box; }
std::string drag_text () const { return name (); }
bool is_selectable() const { return true; }
bool can_copy_state (Gtkmm2ext::DnDVBoxChild*) const;
void set_visual_state (Gtkmm2ext::VisualState, bool);
bool drag_data_get (Glib::RefPtr<Gdk::DragContext> const, Gtk::SelectionData &);
std::shared_ptr<ARDOUR::RegionFxPlugin> region_fx_plugin () const { return _rfx; }
private:
std::string name () const;
Gtk::VBox _box;
ArdourWidgets::ArdourButton _fx_btn;
std::shared_ptr<ARDOUR::RegionFxPlugin> _rfx;
ARDOUR::PluginPresetPtr _plugin_preset_pointer;
};
class RegionFxBox : public Gtk::VBox, public PluginInterestedObject //, public ARDOUR::SessionHandlePtr
{
public:
RegionFxBox (std::shared_ptr<ARDOUR::Region>);
void redisplay_plugins ();
private:
void add_fx_to_display (std::weak_ptr<ARDOUR::RegionFxPlugin>);
void show_plugin_gui (std::weak_ptr<ARDOUR::RegionFxPlugin>, bool custom_ui = true);
void queue_delete_region_fx (std::weak_ptr<ARDOUR::RegionFxPlugin>);
bool idle_delete_region_fx (std::weak_ptr<ARDOUR::RegionFxPlugin>);
void notify_plugin_load_fail (uint32_t cnt = 1);
bool on_key_press (GdkEventKey*);
/* PluginInterestedObject */
bool use_plugins (SelectedPlugins const&);
/* DNDVbox signal handlers */
bool fxe_button_press_event (GdkEventButton*, RegionFxEntry*);
bool fxe_button_release_event (GdkEventButton*, RegionFxEntry*);
void reordered ();
void plugin_drop (Gtk::SelectionData const&, RegionFxEntry*, Glib::RefPtr<Gdk::DragContext> const&);
void object_drop (Gtkmm2ext::DnDVBox<RegionFxEntry>*, RegionFxEntry*, Glib::RefPtr<Gdk::DragContext> const&);
void delete_dragged_plugins (std::list<std::shared_ptr<ARDOUR::RegionFxPlugin>> const&);
std::shared_ptr<ARDOUR::RegionFxPlugin> find_drop_position (RegionFxEntry*);
std::shared_ptr<ARDOUR::Region> _region;
Gtkmm2ext::DnDVBox<RegionFxEntry> _display;
Gtk::ScrolledWindow _scroller;
Gtk::EventBox _base;
bool _no_redisplay;
int _placement;
};
std::shared_ptr<ARDOUR::Region> _region;
void connect_editor_events ();
@ -78,6 +145,7 @@ private:
Gtk::Label sync_relative_label;
Gtk::Label sync_absolute_label;
Gtk::Label start_label;
Gtk::Label region_fx_label;
ClockGroup* _clock_group;
@ -88,8 +156,11 @@ private:
AudioClock sync_offset_absolute_clock; ///< sync offset relative to the start of the timeline
AudioClock start_clock;
RegionFxBox _region_fx_box;
PBD::ScopedConnection state_connection;
PBD::ScopedConnection audition_connection;
PBD::ScopedConnection region_connection;
void bounds_changed (const PBD::PropertyChange&);
void name_changed ();

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,31 @@ 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);
}
}
void
AudioRegionGainLine::start_drag_line (uint32_t i1, uint32_t i2, float fraction)
{
AutomationLine::start_drag_line (i1, i2, fraction);
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);
}
}
void
AudioRegionGainLine::start_drag_multiple (list<ControlPoint*> cp, float fraction, XMLNode* state)
{
AutomationLine::start_drag_multiple (cp, fraction, state);
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 +94,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,14 +111,26 @@ 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);
}
void
AudioRegionGainLine::end_draw_merge ()
{
if (!arv.audio_region()->envelope_active()) {
XMLNode& before = arv.audio_region()->get_state();
arv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(arv.audio_region().get()), &before, &arv.audio_region()->get_state()));
}
AutomationLine::end_draw_merge ();
}
void
AudioRegionGainLine::region_changed (const PropertyChange& what_changed)
{

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,24 @@ 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 start_drag_line (uint32_t, uint32_t, float);
void start_drag_multiple (std::list<ControlPoint*>, float, XMLNode*);
void end_drag (bool with_push, uint32_t final_index);
void end_draw_merge ();
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

@ -237,6 +237,7 @@ RegionView::init (bool wfd)
//set_height (trackview.current_height());
_region->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RegionView::region_changed, this, _1), gui_context());
_region->RegionFxChanged.connect (*this, invalidator (*this), boost::bind (&RegionView::region_renamed, this), gui_context());
/* derived class calls set_colors () including RegionView::set_colors() in ::init() */
//set_colors ();
@ -749,7 +750,7 @@ void
RegionView::show_region_editor ()
{
if (!editor) {
editor = new RegionEditor (trackview.session(), region());
editor = new RegionEditor (trackview.session(), this);
}
editor->present ();
@ -791,6 +792,9 @@ RegionView::make_name () const
if (_region->muted()) {
str = std::string(u8"\U0001F507") + str; // SPEAKER WITH CANCELLATION STROKE
}
if (_region->has_region_fx()) {
str = str + " (Fx)";
}
return str;
}

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

@ -365,6 +365,7 @@
<ColorAlias name="recording rect" alias="alert:ruddy"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:foregroundest"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -356,6 +356,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -357,6 +357,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -357,6 +357,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -357,6 +357,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -320,7 +320,7 @@
<ColorAlias name="pluginui toggle: fill" alias="widget:bg"/>
<ColorAlias name="pluginui toggle: fill active" alias="widget:blue"/>
<ColorAlias name="preference highlight" alias="alert:yellow"/>
<ColorAlias name="processor automation line" alias="theme:bg1"/>
<ColorAlias name="processor automation line" alias="alert:green"/>
<ColorAlias name="processor auxfeedback: fill" alias="theme:contrasting alt"/>
<ColorAlias name="processor auxfeedback: led active" alias="alert:green"/>
<ColorAlias name="processor control button: fill" alias="neutral:background"/>
@ -359,6 +359,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -358,6 +358,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -358,6 +358,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -356,6 +356,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:background2"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -357,6 +357,7 @@
<ColorAlias name="recording note" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform fill" alias="neutral:foregroundest"/>
<ColorAlias name="recording waveform outline" alias="neutral:foregroundest"/>
<ColorAlias name="region automation line" alias="alert:blue"/>
<ColorAlias name="region list automatic" alias="theme:contrasting less"/>
<ColorAlias name="region list missing source" alias="alert:red"/>
<ColorAlias name="region list whole file" alias="neutral:foreground"/>

View File

@ -219,6 +219,7 @@ gtk2_ardour_sources = [
'plugin_ui.cc',
'plugin_dspload_ui.cc',
'plugin_dspload_window.cc',
'plugin_window_proxy.cc',
'port_group.cc',
'port_insert_ui.cc',
'port_matrix.cc',
@ -240,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',

View File

@ -23,6 +23,7 @@
#ifndef __ardour_audio_region_h__
#define __ardour_audio_region_h__
#include <atomic>
#include <vector>
#include <list>
@ -32,6 +33,7 @@
#include "ardour/ardour.h"
#include "ardour/automatable.h"
#include "ardour/automation_list.h"
#include "ardour/buffer_set.h"
#include "ardour/interthread_info.h"
#include "ardour/logcurve.h"
#include "ardour/region.h"
@ -60,7 +62,8 @@ class Playlist;
class Session;
class Filter;
class AudioSource;
class RegionFxPlugin;
class PlugInsertBase;
class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
{
@ -116,14 +119,17 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
samplecnt_t readable_length_samples() const { return length_samples(); }
uint32_t n_channels() const { return _sources.size(); }
samplecnt_t read_at (Sample *buf, Sample *mixdown_buf, float *gain_buf,
samplepos_t position,
samplecnt_t cnt,
uint32_t chan_n = 0) const;
samplecnt_t read_at (Sample* buf,
Sample* mixdown_buf,
gain_t* gain_buf,
samplepos_t position,
samplecnt_t cnt,
uint32_t chan_n = 0) const;
samplecnt_t master_read_at (Sample *buf, Sample *mixdown_buf, float *gain_buf,
samplepos_t position, samplecnt_t cnt,
uint32_t chan_n=0) const;
samplecnt_t master_read_at (Sample* buf,
samplepos_t position,
samplecnt_t cnt,
uint32_t chan_n=0) const;
samplecnt_t read_raw_internal (Sample*, samplepos_t, samplecnt_t, int channel) const;
@ -158,6 +164,9 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
int separate_by_channel (std::vector<std::shared_ptr<Region> >&) const;
bool remove_plugin (std::shared_ptr<RegionFxPlugin>);
void reorder_plugins (RegionFxList const&);
/* automation */
std::shared_ptr<Evoral::Control>
@ -248,11 +257,27 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
std::shared_ptr<ARDOUR::Region> get_single_other_xfade_region (bool start) const;
void apply_region_fx (BufferSet&, samplepos_t, samplepos_t, samplecnt_t);
void fx_latency_changed (bool no_emit);
void copy_plugin_state (std::shared_ptr<const AudioRegion>);
mutable samplepos_t _fx_pos;
pframes_t _fx_block_size;
mutable bool _fx_latent_read;
mutable Glib::Threads::Mutex _cache_lock;
mutable BufferSet _readcache;
mutable samplepos_t _cache_start;
mutable samplepos_t _cache_end;
mutable std::atomic<bool> _invalidated;
protected:
/* default constructor for derived (compound) types */
AudioRegion (Session& s, timepos_t const &, timecnt_t const &, std::string name);
bool _add_plugin (std::shared_ptr<RegionFxPlugin>, std::shared_ptr<RegionFxPlugin>, bool);
int _set_state (const XMLNode&, int version, PBD::PropertyChange& what_changed, bool send_signal);
};

View File

@ -122,6 +122,9 @@ public:
void start_domain_bounce (Temporal::DomainBounceInfo&);
void finish_domain_bounce (Temporal::DomainBounceInfo&);
static void find_next_ac_event (std::shared_ptr<AutomationControl>, Temporal::timepos_t const & start, Temporal::timepos_t const & end, Evoral::ControlEvent& ev);
static void find_prev_ac_event (std::shared_ptr<AutomationControl>, Temporal::timepos_t const & start, Temporal::timepos_t const & end, Evoral::ControlEvent& ev);
protected:
Session& _a_session;
@ -139,9 +142,6 @@ protected:
SlavableAutomationControlList slavables () const { return SlavableAutomationControlList(); }
void find_next_ac_event (std::shared_ptr<AutomationControl>, Temporal::timepos_t const & start, Temporal::timepos_t const & end, Evoral::ControlEvent& ev) const;
void find_prev_ac_event (std::shared_ptr<AutomationControl>, Temporal::timepos_t const & start, Temporal::timepos_t const & end, Evoral::ControlEvent& ev) const;
private:
PBD::ScopedConnectionList _control_connections; ///< connections to our controls' signals
};

View File

@ -42,7 +42,7 @@ public:
static void ensure_buffers (ChanCount howmany = ChanCount::ZERO, size_t custom = 0);
private:
static Glib::Threads::Mutex rb_mutex;
static Glib::Threads::Mutex rb_mutex;
typedef PBD::RingBufferNPT<ThreadBuffers*> ThreadBufferFIFO;
typedef std::list<ThreadBuffers*> ThreadBufferList;

View File

@ -66,7 +66,9 @@ namespace PBD {
LIBARDOUR_API extern DebugBits LatencyIO;
LIBARDOUR_API extern DebugBits LatencyRoute;
LIBARDOUR_API extern DebugBits LaunchControlXL;
LIBARDOUR_API extern DebugBits Launchpad;
LIBARDOUR_API extern DebugBits Layering;
LIBARDOUR_API extern DebugBits MIDISurface;
LIBARDOUR_API extern DebugBits MTC;
LIBARDOUR_API extern DebugBits MackieControl;
LIBARDOUR_API extern DebugBits MidiClock;
@ -88,8 +90,7 @@ namespace PBD {
LIBARDOUR_API extern DebugBits ProcessThreads;
LIBARDOUR_API extern DebugBits Processors;
LIBARDOUR_API extern DebugBits Push2;
LIBARDOUR_API extern DebugBits Launchpad;
LIBARDOUR_API extern DebugBits MIDISurface;
LIBARDOUR_API extern DebugBits RegionFx;
LIBARDOUR_API extern DebugBits Selection;
LIBARDOUR_API extern DebugBits SessionEvents;
LIBARDOUR_API extern DebugBits Slave;

View File

@ -108,37 +108,6 @@ public:
protected:
std::string describe_parameter (Evoral::Parameter);
/** A control that manipulates a plugin parameter (control port). */
struct PluginControl : public AutomationControl
{
PluginControl (IOPlug* p,
Evoral::Parameter const& param,
ParameterDescriptor const& desc);
double get_value () const;
void catch_up_with_external_value (double val);
XMLNode& get_state() const;
std::string get_user_string() const;
private:
void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
IOPlug* _iop;
};
/** A control that manipulates a plugin property (message). */
struct PluginPropertyControl : public AutomationControl
{
PluginPropertyControl (IOPlug* p,
Evoral::Parameter const& param,
ParameterDescriptor const& desc);
double get_value () const;
XMLNode& get_state() const;
private:
void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
IOPlug* _iop;
Variant _value;
};
private:
/* disallow copy construction */
IOPlug (IOPlug const&);

View File

@ -24,15 +24,16 @@
#include "evoral/ControlSet.h"
#include "ardour/ardour.h"
#include "ardour/automation_control.h"
#include "ardour/plugin.h"
#include "ardour/plugin_types.h"
namespace ARDOUR {
class Session;
class Plugin;
class ReadOnlyControl;
class Route;
class Plugin;
class Session;
class LIBARDOUR_API PlugInsertBase : virtual public Evoral::ControlSet, virtual public PBD::Destructible
{
@ -61,18 +62,86 @@ public:
virtual bool can_reset_all_parameters () = 0;
virtual bool reset_parameters_to_default () = 0;
virtual std::string describe_parameter (Evoral::Parameter param) = 0;
virtual bool provides_stats () const = 0;
virtual bool get_stats (PBD::microseconds_t&, PBD::microseconds_t&, double&, double&) const = 0;
virtual void clear_stats () = 0;
/** A control that manipulates a plugin parameter (control port). */
struct PluginControl : public AutomationControl {
PluginControl (Session& s,
PlugInsertBase* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list = std::shared_ptr<AutomationList> ());
double get_value (void) const;
void catch_up_with_external_value (double val);
XMLNode& get_state () const;
std::string get_user_string () const;
protected:
virtual void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
PlugInsertBase* _pib;
};
/** A control that manipulates a plugin property (message). */
struct PluginPropertyControl : public AutomationControl {
PluginPropertyControl (Session& s,
PlugInsertBase* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list = std::shared_ptr<AutomationList> ());
double get_value (void) const;
XMLNode& get_state () const;
protected:
virtual void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
PlugInsertBase* _pib;
Variant _value;
};
/** Enumeration of the ways in which we can match our insert's
* IO to that of the plugin(s).
*/
enum MatchingMethod {
Impossible, ///< we can't
Delegate, ///< we are delegating to the plugin, and it can handle it
NoInputs, ///< plugin has no inputs, so anything goes
ExactMatch, ///< our insert's inputs are the same as the plugin's
Replicate, ///< we have multiple instances of the plugin
Split, ///< we copy one of our insert's inputs to multiple plugin inputs
Hide, ///< we `hide' some of the plugin's inputs by feeding them silence
};
/** Description of how we can match our plugin's IO to our own insert IO */
struct Match {
Match () : method (Impossible), plugins (0), strict_io (false), custom_cfg (false) {}
Match (MatchingMethod m, int32_t p,
bool strict = false, bool custom = false, ChanCount h = ChanCount ())
: method (m), plugins (p), hide (h), strict_io (strict), custom_cfg (custom) {}
MatchingMethod method; ///< method to employ
int32_t plugins; ///< number of copies of the plugin that we need
ChanCount hide; ///< number of channels to hide
bool strict_io; ///< force in == out
bool custom_cfg; ///< custom config (if not strict)
};
protected:
static std::shared_ptr<Plugin> plugin_factory (std::shared_ptr<Plugin>);
bool parse_plugin_type (XMLNode const&, PluginType&, std::string&) const;
std::shared_ptr<Plugin> find_and_load_plugin (Session&, XMLNode const&, PluginType&, std::string const&, bool& any_vst);
void set_control_ids (const XMLNode&, int version);
void set_control_ids (const XMLNode&, int version, bool by_value = false);
void preset_load_set_value (uint32_t, float);
};
} // namespace ARDOUR
std::ostream& operator<<(std::ostream& o, const ARDOUR::PlugInsertBase::Match& m);
#endif

View File

@ -53,9 +53,11 @@ class AudioEngine;
class Session;
class BufferSet;
class IOPlug;
class PlugInsertBase;
class PluginInsert;
class Plugin;
class PluginInfo;
class RegionFxPlugin;
class AutomationControl;
class SessionObject;
@ -373,8 +375,9 @@ public:
PBD::Signal1<void, uint32_t> EndTouch;
protected:
friend class IOPlug;
friend class PluginInsert;
friend class PlugInsertBase;
friend class RegionFxPlugin;
friend class Session;
/* Called when a parameter of the plugin is changed outside of this

View File

@ -44,7 +44,6 @@
#include "ardour/processor.h"
#include "ardour/readonly_control.h"
#include "ardour/sidechain.h"
#include "ardour/automation_control.h"
class XMLNode;
@ -209,42 +208,18 @@ public:
bool get_stats (PBD::microseconds_t& min, PBD::microseconds_t& max, double& avg, double& dev) const;
void clear_stats ();
/** A control that manipulates a plugin parameter (control port). */
struct PluginControl : public AutomationControl
struct PIControl : public PluginControl
{
PluginControl (PluginInsert* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list=std::shared_ptr<AutomationList>());
double get_value (void) const;
void catch_up_with_external_value (double val);
XMLNode& get_state() const;
std::string get_user_string() const;
PIControl (Session& s,
PlugInsertBase* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list = std::shared_ptr<AutomationList>())
: PluginControl (s, p, param, desc, list) {}
private:
PluginInsert* _plugin;
void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
};
/** A control that manipulates a plugin property (message). */
struct PluginPropertyControl : public AutomationControl
{
PluginPropertyControl (PluginInsert* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list=std::shared_ptr<AutomationList>());
double get_value (void) const;
XMLNode& get_state() const;
protected:
void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
private:
PluginInsert* _plugin;
Variant _value;
};
std::shared_ptr<Plugin> plugin(uint32_t num=0) const {
if (num < _plugins.size()) {
return _plugins[num];
@ -295,34 +270,6 @@ public:
PBD::Signal0<void> PluginIoReConfigure;
PBD::Signal0<void> PluginMapChanged;
PBD::Signal0<void> PluginConfigChanged;
/** Enumeration of the ways in which we can match our insert's
* IO to that of the plugin(s).
*/
enum MatchingMethod {
Impossible, ///< we can't
Delegate, ///< we are delegating to the plugin, and it can handle it
NoInputs, ///< plugin has no inputs, so anything goes
ExactMatch, ///< our insert's inputs are the same as the plugin's
Replicate, ///< we have multiple instances of the plugin
Split, ///< we copy one of our insert's inputs to multiple plugin inputs
Hide, ///< we `hide' some of the plugin's inputs by feeding them silence
};
/** Description of how we can match our plugin's IO to our own insert IO */
struct Match {
Match () : method (Impossible), plugins (0), strict_io (false), custom_cfg (false) {}
Match (MatchingMethod m, int32_t p,
bool strict = false, bool custom = false, ChanCount h = ChanCount ())
: method (m), plugins (p), hide (h), strict_io (strict), custom_cfg (custom) {}
MatchingMethod method; ///< method to employ
int32_t plugins; ///< number of copies of the plugin that we need
ChanCount hide; ///< number of channels to hide
bool strict_io; ///< force in == out
bool custom_cfg; ///< custom config (if not strict)
};
protected:
XMLNode& state () const;
@ -420,7 +367,6 @@ private:
void create_automatable_parameters ();
void control_list_automation_state_changed (Evoral::Parameter, AutoState);
void set_parameter_state_2X (const XMLNode& node, int version);
void update_control_values (const XMLNode&, int version);
void enable_changed ();
void bypassable_changed ();
@ -429,7 +375,6 @@ private:
bool check_inplace ();
void mapping_changed ();
std::shared_ptr<Plugin> plugin_factory (std::shared_ptr<Plugin>);
void add_plugin (std::shared_ptr<Plugin>);
void plugin_removed (std::weak_ptr<Plugin>);
@ -453,6 +398,4 @@ private:
} // namespace ARDOUR
std::ostream& operator<<(std::ostream& o, const ARDOUR::PluginInsert::Match& m);
#endif /* __ardour_plugin_insert_h__ */

View File

@ -79,11 +79,14 @@ namespace Properties {
LIBARDOUR_API extern PBD::PropertyDescriptor<std::string> tags;
LIBARDOUR_API extern PBD::PropertyDescriptor<uint64_t> reg_group;
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> contents; // type doesn't matter here, used for signal only
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> region_fx; // type doesn't matter here, used for signal only
};
class Playlist;
class Filter;
class ExportSpecification;
class Plugin;
class RegionFxPlugin;
enum LIBARDOUR_API RegionEditState {
EditChangesNothing = 0,
@ -107,11 +110,14 @@ class LIBARDOUR_API Region
{
public:
typedef std::vector<std::shared_ptr<Source> > SourceList;
typedef std::list<std::shared_ptr<RegionFxPlugin>> RegionFxList;
static void make_property_quarks ();
static PBD::Signal2<void,std::shared_ptr<RegionList>, const PBD::PropertyChange&> RegionsPropertyChanged;
PBD::Signal0<void> RegionFxChanged;
typedef std::map <PBD::PropertyChange, RegionList> ChangeMap;
virtual ~Region();
@ -503,6 +509,34 @@ public:
void move_cue_marker (CueMarker const &, timepos_t const & region_relative_position);
void rename_cue_marker (CueMarker&, std::string const &);
/* Region Fx */
bool load_plugin (ARDOUR::PluginType type, std::string const& name);
bool add_plugin (std::shared_ptr<RegionFxPlugin>, std::shared_ptr<RegionFxPlugin> pos = std::shared_ptr<RegionFxPlugin> ());
virtual bool remove_plugin (std::shared_ptr<RegionFxPlugin>) { return false; }
virtual void reorder_plugins (RegionFxList const&);
bool has_region_fx () const {
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
return !_plugins.empty ();
}
std::shared_ptr<RegionFxPlugin> nth_plugin (uint32_t n) const {
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
for (auto const& i : _plugins) {
if (0 == n--) {
return i;
}
}
return std::shared_ptr<RegionFxPlugin> ();
}
void foreach_plugin (boost::function<void(std::weak_ptr<RegionFxPlugin>)> method) const {
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
for (auto const& i : _plugins) {
method (std::weak_ptr<RegionFxPlugin> (i));
}
}
protected:
virtual XMLNode& state () const;
@ -528,6 +562,8 @@ protected:
}
protected:
virtual bool _add_plugin (std::shared_ptr<RegionFxPlugin>, std::shared_ptr<RegionFxPlugin>, bool) { return false; }
virtual void fx_latency_changed (bool no_emit);
void send_change (const PBD::PropertyChange&);
virtual int _set_state (const XMLNode&, int version, PBD::PropertyChange& what_changed, bool send_signal);
@ -546,6 +582,10 @@ protected:
DataType _type;
mutable Glib::Threads::RWLock _fx_lock;
uint32_t _fx_latency;
RegionFxList _plugins;
PBD::Property<bool> _sync_marked;
PBD::Property<bool> _left_of_split;
PBD::Property<bool> _right_of_split;

View File

@ -0,0 +1,185 @@
/*
* 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_region_fx_plugin_h__
#define __ardour_region_fx_plugin_h__
#include <atomic>
#include "temporal/domain_provider.h"
#include "ardour/ardour.h"
#include "ardour/automation_control.h"
#include "ardour/chan_mapping.h"
#include "ardour/libardour_visibility.h"
#include "ardour/parameter_descriptor.h"
#include "ardour/plug_insert_base.h"
#include "ardour/plugin.h"
#include "ardour/types.h"
namespace Gtkmm2ext
{
class WindowProxy;
}
class XMLNode;
namespace ARDOUR
{
class ReadOnlyControl;
class LIBARDOUR_API RegionFxPlugin : public SessionObject, public PlugInsertBase, public Latent, public Temporal::TimeDomainProvider
{
public:
RegionFxPlugin (Session&, Temporal::TimeDomain const, std::shared_ptr<Plugin> = std::shared_ptr<Plugin> ());
~RegionFxPlugin ();
/* UI Proxy */
Gtkmm2ext::WindowProxy* window_proxy () const
{
return _window_proxy;
}
void set_window_proxy (Gtkmm2ext::WindowProxy* wp)
{
_window_proxy = wp;
}
/* Latent */
samplecnt_t signal_latency () const;
/* PlugInsertBase */
uint32_t get_count () const
{
return _plugins.size ();
}
PluginType type () const
{
return plugin ()->get_info ()->type;
}
std::shared_ptr<Plugin> plugin (uint32_t num = 0) const
{
if (num < _plugins.size ()) {
return _plugins[num];
} else {
return _plugins[0];
}
}
UIElements ui_elements () const;
std::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
bool write_immediate_event (Evoral::EventType event_type, size_t size, const uint8_t* buf);
bool load_preset (Plugin::PresetRecord);
std::shared_ptr<ReadOnlyControl> control_output (uint32_t) const;
bool reset_parameters_to_default ();
bool can_reset_all_parameters ();
std::string describe_parameter (Evoral::Parameter param);
bool provides_stats () const
{
return false;
}
bool get_stats (PBD::microseconds_t&, PBD::microseconds_t&, double&, double&) const
{
return false;
}
void clear_stats () {}
/* Stateful */
XMLNode& get_state (void) const;
int set_state (const XMLNode&, int version);
void drop_references ();
void update_id (PBD::ID);
/* Fx */
bool run (BufferSet&, samplepos_t start, samplepos_t end, samplepos_t region_pos, pframes_t nframes, sampleoffset_t off);
void flush ();
int set_block_size (pframes_t nframes);
void automatables (PBD::ControllableSet&) const;
void set_default_automation (timepos_t);
void truncate_automation_start (timecnt_t);
void truncate_automation_end (timepos_t);
bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
bool configure_io (ChanCount in, ChanCount out);
ChanCount input_streams () const
{
return _configured_in;
}
ChanCount output_streams () const
{
return _configured_out;
}
ChanCount required_buffers () const
{
return _required_buffers;
}
private:
/* disallow copy construction */
RegionFxPlugin (RegionFxPlugin const&);
void add_plugin (std::shared_ptr<Plugin>);
void plugin_removed (std::weak_ptr<Plugin>);
bool set_count (uint32_t num);
bool check_inplace ();
void create_parameters ();
void parameter_changed_externally (uint32_t, float);
void automation_run (samplepos_t start, pframes_t nframes);
bool find_next_event (timepos_t const& start, timepos_t const& end, Evoral::ControlEvent& next_event) const;
void start_touch (uint32_t param_id);
void end_touch (uint32_t param_id);
bool connect_and_run (BufferSet&, samplepos_t start, samplepos_t end, samplepos_t region_pos, pframes_t nframes, sampleoffset_t buf_off, sampleoffset_t cycle_off);
Match private_can_support_io_configuration (ChanCount const&, ChanCount&) const;
/** details of the match currently being used */
Match _match;
uint32_t _plugin_signal_latency;
typedef std::vector<std::shared_ptr<Plugin>> Plugins;
Plugins _plugins;
ChanCount _configured_in;
ChanCount _configured_out;
ChanCount _required_buffers;
std::map <uint32_t, ARDOUR::ChanMapping> _in_map;
std::map <uint32_t, ARDOUR::ChanMapping> _out_map;
bool _configured;
bool _no_inplace;
typedef std::map<uint32_t, std::shared_ptr<ReadOnlyControl>> CtrlOutMap;
CtrlOutMap _control_outputs;
Gtkmm2ext::WindowProxy* _window_proxy;
std::atomic<int> _flush;
};
} // namespace ARDOUR
#endif

View File

@ -311,6 +311,8 @@ public:
BufferSet& get_route_buffers (ChanCount count = ChanCount::ZERO, bool silence = true);
BufferSet& get_mix_buffers (ChanCount count = ChanCount::ZERO);
void ensure_buffers_unlocked (ChanCount howmany);
bool have_rec_enabled_track () const;
bool have_rec_disabled_track () const;
@ -1195,8 +1197,6 @@ public:
gain_t* scratch_automation_buffer () const;
pan_t** pan_automation_buffer () const;
void ensure_buffer_set (BufferSet& buffers, const ChanCount& howmany);
/* VST support */
static int vst_current_loading_id;
@ -1515,6 +1515,8 @@ private:
void setup_engine_resampling ();
void ensure_buffers (ChanCount howmany = ChanCount::ZERO);
ChanCount _required_thread_buffers;
size_t _required_thread_buffersize;
void process_without_events (pframes_t);
void process_with_events (pframes_t);

View File

@ -1417,7 +1417,7 @@ AUPlugin::render_callback(AudioUnitRenderActionFlags*,
if (valid) {
ioData->mBuffers[i].mData = input_buffers->get_audio (idx).data (cb_offsets[bus] + input_offset);
} else {
ioData->mBuffers[i].mData = silent_bufs.get_audio(0).data (cb_offsets[bus] + input_offset);
ioData->mBuffers[i].mData = silent_bufs.get_audio(0).data (0);
}
}
cb_offsets[bus] += inNumberSamples;
@ -1536,7 +1536,7 @@ AUPlugin::connect_and_run (BufferSet& bufs,
if (valid) {
buffers->mBuffers[i].mData = bufs.get_audio (idx).data (offset);
} else {
buffers->mBuffers[i].mData = scratch_bufs.get_audio(0).data(offset);
buffers->mBuffers[i].mData = scratch_bufs.get_audio(0).data(0);
}
}
}

View File

@ -46,6 +46,7 @@
#include "ardour/audioengine.h"
#include "ardour/analysis_graph.h"
#include "ardour/audioregion.h"
#include "ardour/buffer_manager.h"
#include "ardour/session.h"
#include "ardour/dB.h"
#include "ardour/debug.h"
@ -53,6 +54,7 @@
#include "ardour/playlist.h"
#include "ardour/audiofilesource.h"
#include "ardour/region_factory.h"
#include "ardour/region_fx_plugin.h"
#include "ardour/runtime_functions.h"
#include "ardour/sndfilesource.h"
#include "ardour/transient_detector.h"
@ -243,6 +245,33 @@ AudioRegion::init ()
listen_to_my_curves ();
connect_to_analysis_changed ();
connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1;
_fx_block_size = 0;
_fx_latent_read = false;
}
void
AudioRegion::copy_plugin_state (std::shared_ptr<const AudioRegion> other)
{
/* state cannot copied in Region, because when running Region's c'tor
* the AudioRegion does not yet exist, and virtual _add_plugin
* of the parent class is called
*/
Glib::Threads::RWLock::ReaderLock lm (other->_fx_lock);
for (auto const& i : other->_plugins) {
XMLNode& state = i->get_state ();
state.remove_property ("count");
PBD::Stateful::ForceIDRegeneration force_ids;
std::shared_ptr<RegionFxPlugin> rfx (new RegionFxPlugin (_session, Temporal::AudioTime));
rfx->set_state (state, Stateful::current_state_version);
if (!_add_plugin (rfx, std::shared_ptr<RegionFxPlugin>(), true)) {
continue;
}
_plugins.push_back (rfx);
delete &state;
}
fx_latency_changed (true);
}
/** Constructor for use by derived types only */
@ -289,6 +318,12 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other)
connect_to_analysis_changed ();
connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1;
_fx_block_size = 0;
_fx_latent_read = false;
copy_plugin_state (other);
assert(_type == DataType::AUDIO);
assert (_sources.size() == _master_sources.size());
}
@ -311,6 +346,12 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, timecnt_t co
connect_to_analysis_changed ();
connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1;
_fx_block_size = 0;
_fx_latent_read = false;
copy_plugin_state (other);
assert(_type == DataType::AUDIO);
assert (_sources.size() == _master_sources.size());
}
@ -331,6 +372,12 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, const Source
connect_to_analysis_changed ();
connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1;
_fx_block_size = 0;
_fx_latent_read = false;
copy_plugin_state (other);
assert (_sources.size() == _master_sources.size());
}
@ -350,6 +397,9 @@ AudioRegion::AudioRegion (SourceList& srcs)
AudioRegion::~AudioRegion ()
{
for (auto const& rfx : _plugins) {
rfx->drop_references ();
}
}
void
@ -475,8 +525,7 @@ AudioRegion::read (Sample* buf, samplepos_t pos, samplecnt_t cnt, int channel) c
}
samplecnt_t
AudioRegion::master_read_at (Sample* buf, Sample* /*mixdown_buffer*/, float* /*gain_buffer*/,
samplepos_t position, samplecnt_t cnt, uint32_t chan_n) const
AudioRegion::master_read_at (Sample* buf, samplepos_t position, samplecnt_t cnt, uint32_t chan_n) const
{
/* do not read gain/scaling/fades and do not count this disk i/o in statistics */
@ -492,7 +541,9 @@ AudioRegion::master_read_at (Sample* buf, Sample* /*mixdown_buffer*/, float* /*g
* @param chan_n Channel number to read.
*/
samplecnt_t
AudioRegion::read_at (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
AudioRegion::read_at (Sample* buf,
Sample* mixdown_buffer,
gain_t* gain_buffer,
samplepos_t pos,
samplecnt_t cnt,
uint32_t chan_n) const
@ -506,8 +557,9 @@ AudioRegion::read_at (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
*/
assert (cnt >= 0);
uint32_t const n_chn = n_channels ();
if (n_channels() == 0) {
if (n_chn == 0) {
return 0;
}
@ -524,7 +576,10 @@ AudioRegion::read_at (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
return 0; /* read nothing */
}
if ((to_read = min (cnt, lsamples - internal_offset)) == 0) {
const samplecnt_t esamples = lsamples - internal_offset;
assert (esamples >= 0);
if ((to_read = min (cnt, esamples)) == 0) {
return 0; /* read nothing */
}
@ -594,32 +649,117 @@ AudioRegion::read_at (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
}
}
/* READ DATA FROM THE SOURCE INTO mixdown_buffer.
We can never read directly into buf, since it may contain data
from a region `below' this one in the stack, and our fades (if they exist)
may need to mix with the existing data.
*/
if (read_from_sources (_sources, lsamples, mixdown_buffer, pos, to_read, chan_n) != to_read) {
return 0;
Glib::Threads::Mutex::Lock cl (_cache_lock);
if (chan_n == 0 && _invalidated.exchange (false)) {
_cache_start = _cache_end = -1;
}
/* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */
boost::scoped_array<gain_t> gain_array;
boost::scoped_array<Sample> mixdown_array;
if (envelope_active()) {
_envelope->curve().get_vector (timepos_t (internal_offset), timepos_t (internal_offset + to_read), gain_buffer, to_read);
// TODO optimize mono reader, w/o plugins -> old code
if (n_chn > 1 && _cache_start < _cache_end && internal_offset >= _cache_start && internal_offset + to_read <= _cache_end) {
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 copy from cache %3 - %4 to_read: %5\n",
name(), chan_n, internal_offset, internal_offset + to_read, to_read));
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (internal_offset - _cache_start), to_read);
cl.release ();
} else {
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
bool have_fx = !_plugins.empty ();
uint32_t fx_latency = _fx_latency;
lm.release ();
if (_scale_amplitude != 1.0f) {
for (samplecnt_t n = 0; n < to_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read at %3 - %4 to_read: %5 with fx: %6\n",
name(), chan_n, internal_offset, internal_offset + to_read, to_read, have_fx));
ChanCount cc (DataType::AUDIO, n_channels ());
_readcache.ensure_buffers (cc, to_read + _fx_latency);
samplecnt_t n_read = to_read; //< data to read from disk
samplecnt_t n_proc = to_read; //< silence pad data to process
samplepos_t readat = pos;
sampleoffset_t offset = internal_offset;
//printf ("READ Cache end %ld pos %ld\n", _cache_end, readat);
if (_cache_end != readat && fx_latency > 0) {
_fx_latent_read = true;
n_proc += fx_latency;
n_read = min (to_read + fx_latency, esamples);
mixdown_array.reset (new Sample[n_proc]);
mixdown_buffer = mixdown_array.get ();
gain_array.reset (new gain_t[n_proc]);
gain_buffer = gain_array.get ();
}
if (!_fx_latent_read && fx_latency > 0) {
offset += fx_latency;
readat += fx_latency;
n_read = max<samplecnt_t> (0, min (to_read, lsamples - offset));
}
_readcache.ensure_buffers (cc, n_proc);
if (n_read < n_proc) {
//printf ("SILENCE PAD rd: %ld proc: %ld\n", n_read, n_proc);
/* silence pad, process tail of latent effects */
memset (&mixdown_buffer[n_read], 0, sizeof (Sample)* (n_proc - n_read));
_readcache.silence (n_proc - n_read, n_read);
}
/* reset in case read fails we return early */
_cache_start = _cache_end = -1;
for (uint32_t chn = 0; chn < n_chn; ++chn) {
/* READ DATA FROM THE SOURCE INTO mixdown_buffer.
* We can never read directly into buf, since it may contain data
* from a region `below' this one in the stack, and our fades (if they exist)
* may need to mix with the existing data.
*/
if (read_from_sources (_sources, lsamples, mixdown_buffer, readat, n_read, chn) != n_read) {
return 0; // XXX
}
} else {
for (samplecnt_t n = 0; n < to_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n];
/* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */
if (envelope_active()) {
_envelope->curve().get_vector (timepos_t (offset), timepos_t (offset + n_read), gain_buffer, n_read);
if (_scale_amplitude != 1.0f) {
for (samplecnt_t n = 0; n < n_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
}
} else {
for (samplecnt_t n = 0; n < n_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n];
}
}
} else if (_scale_amplitude != 1.0f) {
apply_gain_to_buffer (mixdown_buffer, n_read, _scale_amplitude);
}
/* for mono regions no cache is required, unless there are
* regionFX, which use the _readcache BufferSet.
*/
if (n_chn > 1 || have_fx) {
_readcache.get_audio (chn).read_from (mixdown_buffer, n_proc);
}
}
} else if (_scale_amplitude != 1.0f) {
apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
/* apply region FX to all channels */
if (have_fx) {
const_cast<AudioRegion*>(this)->apply_region_fx (_readcache, offset, offset + n_proc, n_proc);
}
/* for mono regions without plugins, mixdown_buffer is valid as-is */
if (n_chn > 1 || have_fx) {
/* copy data for current channel */
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read);
}
_cache_start = internal_offset;
_cache_end = internal_offset + to_read;
cl.release ();
}
/* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO
@ -724,7 +864,7 @@ AudioRegion::read_at (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
if (is_opaque) {
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region %1 memcpy into buf @ %2 + %3, from mixdown buffer @ %4 + %5, len = %6 cnt was %7\n",
name(), buf, fade_in_limit, mixdown_buffer, fade_in_limit, N, cnt));
memcpy (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N * sizeof (Sample));
copy_vector (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N);
} else {
mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N);
}
@ -1314,10 +1454,20 @@ AudioRegion::recompute_at_end ()
based on the the existing curve.
*/
timepos_t tend (len_as_tpos ());
_envelope->freeze ();
_envelope->truncate_end (len_as_tpos ());
_envelope->truncate_end (tend);
_envelope->thaw ();
foreach_plugin ([tend](std::weak_ptr<RegionFxPlugin> wfx)
{
shared_ptr<RegionFxPlugin> rfx = wfx.lock ();
if (rfx) {
rfx->truncate_automation_end (tend);
}
});
suspend_property_changes();
if (_left_of_split) {
@ -1341,7 +1491,16 @@ AudioRegion::recompute_at_start ()
{
/* as above, but the shift was from the front */
_envelope->truncate_start (timecnt_t::from_samples (length().samples ()));
timecnt_t tas (timecnt_t::from_samples (length().samples ()));
_envelope->truncate_start (tas);
foreach_plugin ([tas](std::weak_ptr<RegionFxPlugin> wfx)
{
shared_ptr<RegionFxPlugin> rfx = wfx.lock ();
if (rfx) {
rfx->truncate_automation_start (tas);
}
});
suspend_property_changes();
@ -2113,3 +2272,204 @@ errout:
return to_read == 0;
}
bool
AudioRegion::_add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<RegionFxPlugin> before, bool from_set_state)
{
ChanCount in (DataType::AUDIO, n_channels ());
ChanCount out (in);
if (!rfx->can_support_io_configuration (in, out)) {
return false;
}
if (in.n_audio () > out.n_audio ()) {
return false;
}
if (!rfx->configure_io (in, out)) {
return false;
}
ChanCount fx_cc;
{
Glib::Threads::RWLock::ReaderLock lm (_fx_lock, Glib::Threads::NOT_LOCK);
if (!from_set_state) {
lm.acquire();
}
ChanCount cc (DataType::AUDIO, n_channels ());
fx_cc = ChanCount::max (in, out);
fx_cc = ChanCount::max (fx_cc, rfx->required_buffers ());
for (auto const& i : _plugins) {
fx_cc = ChanCount::max (fx_cc, i->required_buffers ());
}
}
DEBUG_TRACE (DEBUG::RegionFx, string_compose ("Audio Region Fx required ChanCount: %1\n", fx_cc));
_session.ensure_buffers_unlocked (fx_cc);
/* subscribe to parameter changes */
ControllableSet acs;
rfx->automatables (acs);
for (auto& ec : acs) {
std::shared_ptr<AutomationControl> ac (std::dynamic_pointer_cast<AutomationControl>(ec));
std::weak_ptr<AutomationControl> wc (ac);
ec->Changed.connect_same_thread (*this, [this, wc] (bool, PBD::Controllable::GroupControlDisposition)
{
std::shared_ptr<AutomationControl> ac = wc.lock ();
if (ac && ac->automation_playback ()) {
return;
}
if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
}
});
if (!ac->alist ()) {
continue;
}
ac->alist()->StateChanged.connect_same_thread (*this, [this] ()
{
if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
}
});
}
rfx->LatencyChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fx_latency_changed, this, false));
rfx->set_block_size (_session.get_block_size ());
if (from_set_state) {
return true;
}
{
Glib::Threads::RWLock::WriterLock lm (_fx_lock);
RegionFxList::iterator loc = _plugins.end ();
if (before) {
loc = find (_plugins.begin (), _plugins.end (), before);
}
_plugins.insert (loc, rfx);
}
rfx->set_default_automation (len_as_tpos ());
fx_latency_changed (true);
if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
}
RegionFxChanged (); /* EMIT SIGNAL */
return true;
}
bool
AudioRegion::remove_plugin (std::shared_ptr<RegionFxPlugin> fx)
{
Glib::Threads::RWLock::WriterLock lm (_fx_lock);
auto i = find (_plugins.begin(), _plugins.end(), fx);
if (i == _plugins.end ()) {
return false;
}
_plugins.erase (i);
lm.release ();
fx->drop_references ();
fx_latency_changed (true);
if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
}
RegionFxChanged (); /* EMIT SIGNAL */
return true;
}
void
AudioRegion::reorder_plugins (RegionFxList const& new_order)
{
Region::reorder_plugins (new_order);
if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
}
RegionFxChanged (); /* EMIT SIGNAL */
}
void
AudioRegion::fx_latency_changed (bool no_emit)
{
uint32_t l = 0;
for (auto const& rfx : _plugins) {
l += rfx->effective_latency ();
}
if (l == _fx_latency) {
return;
}
_fx_latency = l;
if (no_emit) {
return;
}
if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
}
}
void
AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, samplecnt_t n_samples)
{
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
if (_plugins.empty ()) {
return;
}
pframes_t block_size = _session.get_block_size ();
if (_fx_block_size != block_size) {
_fx_block_size = block_size;
for (auto const& rfx : _plugins) {
rfx->set_block_size (_session.get_block_size ());
}
}
ARDOUR::ProcessThread* pt = new ProcessThread (); // TODO -> move to butler ?
pt->get_buffers ();
samplecnt_t latency_offset = 0;
for (auto const& rfx : _plugins) {
if (_fx_pos != start_sample) {
rfx->flush ();
}
samplecnt_t remain = n_samples;
samplecnt_t offset = 0;
samplecnt_t latency = rfx->effective_latency ();
while (remain > 0) {
pframes_t run = std::min <pframes_t> (remain, block_size);
if (!rfx->run (bufs, start_sample + offset - latency_offset, end_sample + offset - latency_offset, position().samples(), run, offset)) {
lm.release ();
/* this triggers a re-read */
const_cast<AudioRegion*>(this)->remove_plugin (rfx);
return;
}
remain -= run;
offset += run;
}
if (_fx_latent_read && latency > 0) {
for (uint32_t c = 0; c < n_channels (); ++c) {
Sample* to = _readcache.get_audio (c).data();
Sample* from = _readcache.get_audio (c).data(latency);
// XXX can left to right copy_vector() work here?
memmove (to, from, (n_samples - latency) * sizeof(Sample));
}
n_samples -= latency;
}
if (!_fx_latent_read) {
latency_offset += latency;
}
}
_fx_pos = end_sample;
_fx_latent_read = false;
pt->drop_buffers ();
delete pt;
}

View File

@ -569,7 +569,7 @@ Automatable::control_factory(const Evoral::Parameter& param)
PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
if (pi) {
pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
control = new PluginInsert::PluginControl(pi, param, desc);
control = new PluginInsert::PIControl (_a_session, pi, param, desc);
} else {
warning << "PluginAutomation for non-Plugin" << endl;
}
@ -583,7 +583,7 @@ Automatable::control_factory(const Evoral::Parameter& param)
} else {
list = std::shared_ptr<AutomationList>(new AutomationList(param, desc, Temporal::TimeDomainProvider (Temporal::AudioTime)));
}
control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
control = new PluginInsert::PluginPropertyControl (_a_session, pi, param, desc, list);
}
} else {
warning << "PluginPropertyAutomation for non-Plugin" << endl;
@ -722,7 +722,7 @@ Automatable::find_next_event (timepos_t const & start, timepos_t const & end, Ev
}
void
Automatable::find_next_ac_event (std::shared_ptr<AutomationControl> c, timepos_t const & start, timepos_t const & end, Evoral::ControlEvent& next_event) const
Automatable::find_next_ac_event (std::shared_ptr<AutomationControl> c, timepos_t const & start, timepos_t const & end, Evoral::ControlEvent& next_event)
{
assert (start <= end);
@ -749,7 +749,7 @@ Automatable::find_next_ac_event (std::shared_ptr<AutomationControl> c, timepos_t
}
void
Automatable::find_prev_ac_event (std::shared_ptr<AutomationControl> c, timepos_t const & start, timepos_t const & end, Evoral::ControlEvent& next_event) const
Automatable::find_prev_ac_event (std::shared_ptr<AutomationControl> c, timepos_t const & start, timepos_t const & end, Evoral::ControlEvent& next_event)
{
assert (start > end);
std::shared_ptr<SlavableAutomationControl> sc

View File

@ -61,7 +61,9 @@ PBD::DebugBits PBD::DEBUG::LatencyDelayLine = PBD::new_debug_bit ("latencydelayl
PBD::DebugBits PBD::DEBUG::LatencyIO = PBD::new_debug_bit ("latencyio");
PBD::DebugBits PBD::DEBUG::LatencyRoute = PBD::new_debug_bit ("latencyroute");
PBD::DebugBits PBD::DEBUG::LaunchControlXL = PBD::new_debug_bit("launchcontrolxl");
PBD::DebugBits PBD::DEBUG::Launchpad = PBD::new_debug_bit ("launchpad");
PBD::DebugBits PBD::DEBUG::Layering = PBD::new_debug_bit ("layering");
PBD::DebugBits PBD::DEBUG::MIDISurface = PBD::new_debug_bit ("midisurface");
PBD::DebugBits PBD::DEBUG::MTC = PBD::new_debug_bit ("mtc");
PBD::DebugBits PBD::DEBUG::MackieControl = PBD::new_debug_bit ("mackiecontrol");
PBD::DebugBits PBD::DEBUG::MidiClock = PBD::new_debug_bit ("midiclock");
@ -83,8 +85,7 @@ PBD::DebugBits PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
PBD::DebugBits PBD::DEBUG::ProcessThreads = PBD::new_debug_bit ("processthreads");
PBD::DebugBits PBD::DEBUG::Processors = PBD::new_debug_bit ("processors");
PBD::DebugBits PBD::DEBUG::Push2 = PBD::new_debug_bit ("push2");
PBD::DebugBits PBD::DEBUG::Launchpad = PBD::new_debug_bit ("launchpad");
PBD::DebugBits PBD::DEBUG::MIDISurface = PBD::new_debug_bit ("midisurface");
PBD::DebugBits PBD::DEBUG::RegionFx = PBD::new_debug_bit ("regionfx");
PBD::DebugBits PBD::DEBUG::Selection = PBD::new_debug_bit ("selection");
PBD::DebugBits PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents");
PBD::DebugBits PBD::DEBUG::Slave = PBD::new_debug_bit ("slave");

View File

@ -715,19 +715,17 @@ DiskReader::overwrite_existing_audio ()
boost::scoped_array<float> gain_buffer (new float[to_overwrite]);
uint32_t n = 0;
bool ret = true;
samplepos_t start;
samplepos_t start = overwrite_sample;
for (auto const& chan : *c) {
Sample* buf = chan->rbuf->buffer ();
ReaderChannelInfo* rci = dynamic_cast<ReaderChannelInfo*> (chan);
if (chunk1_cnt) {
for (auto const& chan : *c) {
Sample* buf = chan->rbuf->buffer ();
ReaderChannelInfo* rci = dynamic_cast<ReaderChannelInfo*> (chan);
/* Note that @p start is passed by reference and will be
* updated by the ::audio_read() call
*/
start = overwrite_sample;
if (chunk1_cnt) {
/* Note that @p start is passed by reference and will be
* updated by the ::audio_read() call
*/
start = overwrite_sample;
if (audio_read (sum_buffer.get (), mixdown_buffer.get (), gain_buffer.get (), start, chunk1_cnt, rci, n, reversed) != (samplecnt_t)chunk1_cnt) {
error << string_compose (_("DiskReader %1: when overwriting(1), cannot read %2 from playlist at sample %3"), id (), chunk1_cnt, overwrite_sample) << endmsg;
ret = false;
@ -735,9 +733,21 @@ DiskReader::overwrite_existing_audio ()
continue;
}
memcpy (buf + chunk1_offset, sum_buffer.get (), sizeof (float) * chunk1_cnt);
++n;
}
}
overwrite_sample = start;
/* sequence read chunks. first read data at same position for all channels */
n = 0;
for (auto const& chan : *c) {
Sample* buf = chan->rbuf->buffer ();
ReaderChannelInfo* rci = dynamic_cast<ReaderChannelInfo*> (chan);
if (chunk2_cnt) {
start = overwrite_sample;
if (audio_read (sum_buffer.get (), mixdown_buffer.get (), gain_buffer.get (), start, chunk2_cnt, rci, n, reversed) != (samplecnt_t)chunk2_cnt) {
error << string_compose (_("DiskReader %1: when overwriting(2), cannot read %2 from playlist at sample %3"), id (), chunk2_cnt, overwrite_sample) << endmsg;
ret = false;

View File

@ -726,10 +726,11 @@ ARDOUR::init (bool try_optimization, const char* localedir, bool with_gui)
* each cycle). Session Export uses one, and the GUI requires
* buffers (for plugin-analysis, auditioner updates) but not
* concurrently.
* Last but not least, the butler needs one for RegionFX.
*
* In theory (hw + 3) should be sufficient, let's add one for luck.
* In theory (hw + 4) should be sufficient, let's add one for luck.
*/
BufferManager::init (hardware_concurrency () + 4);
BufferManager::init (hardware_concurrency () + 5);
PannerManager::instance ().discover_panners ();

View File

@ -170,7 +170,9 @@ IOPlug::set_state (const XMLNode& node, int version)
XMLNodeIterator niter;
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() == _plugin->state_node_name ()) {
if ((*niter)->name() == _plugin->state_node_name ()
|| (any_vst && ((*niter)->name() == "lxvst" || (*niter)->name() == "windows-vst" || (*niter)->name() == "mac-vst"))
) {
_plugin->set_state (**niter, version);
break;
}
@ -336,7 +338,7 @@ IOPlug::create_parameters ()
Evoral::Parameter param (PluginAutomation, 0, i);
std::shared_ptr<AutomationControl> c (new PluginControl(this, param, desc));
std::shared_ptr<AutomationControl> c (new PluginControl (_session, this, param, desc));
c->set_flag (Controllable::NotAutomatable);
add_control (c);
@ -351,7 +353,7 @@ IOPlug::create_parameters ()
if (desc.datatype == Variant::NOTHING) {
continue;
}
std::shared_ptr<AutomationControl> c (new PluginPropertyControl (this, param, desc));
std::shared_ptr<AutomationControl> c (new PluginPropertyControl (_session, this, param, desc));
c->set_flag (Controllable::NotAutomatable);
add_control (c);
}
@ -481,7 +483,6 @@ IOPlug::connect_and_run (samplepos_t start, pframes_t n_samples)
}
}
PortSet& ports (_output->ports());
if (_pre) {
canderef = 1;
const bool reset = _reset_meters.compare_exchange_strong (canderef, 0);
@ -588,7 +589,7 @@ std::string
IOPlug::describe_parameter (Evoral::Parameter param)
{
if (param.type() == PluginAutomation) {
_plugin->describe_parameter (param);
return _plugin->describe_parameter (param);
} else if (param.type() == PluginPropertyAutomation) {
return string_compose ("Property %1", URIMap::instance ().id_to_uri (param.id()));
}
@ -655,104 +656,3 @@ IOPlug::reset_parameters_to_default ()
}
return all;
}
/* ****************************************************************************/
IOPlug::PluginControl::PluginControl (IOPlug* p,
Evoral::Parameter const& param,
ParameterDescriptor const& desc)
: AutomationControl (p->session (), param, desc, std::shared_ptr<AutomationList> (), p->describe_parameter (param))
, _iop (p)
{
}
void
IOPlug::PluginControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
{
_iop->plugin ()->set_parameter (parameter().id(), user_val, 0);
AutomationControl::actually_set_value (user_val, group_override);
}
void
IOPlug::PluginControl::catch_up_with_external_value (double user_val)
{
AutomationControl::actually_set_value (user_val, Controllable::NoGroup);
}
XMLNode&
IOPlug::PluginControl::get_state () const
{
XMLNode& node (AutomationControl::get_state());
node.set_property ("parameter", parameter().id());
std::shared_ptr<LV2Plugin> lv2plugin = std::dynamic_pointer_cast<LV2Plugin> (_iop->plugin ());
if (lv2plugin) {
node.set_property ("symbol", lv2plugin->port_symbol (parameter().id()));
}
return node;
}
double
IOPlug::PluginControl::get_value () const
{
std::shared_ptr<Plugin> plugin = _iop->plugin ();
if (!plugin) {
return 0.0;
}
return plugin->get_parameter (parameter().id());
}
std::string
IOPlug::PluginControl::get_user_string () const
{
std::shared_ptr<Plugin> plugin = _iop->plugin (0);
if (plugin) {
std::string pp;
if (plugin->print_parameter (parameter().id(), pp) && pp.size () > 0) {
return pp;
}
}
return AutomationControl::get_user_string ();
}
IOPlug::PluginPropertyControl::PluginPropertyControl (IOPlug* p,
Evoral::Parameter const& param,
ParameterDescriptor const& desc)
: AutomationControl (p->session(), param, desc )
, _iop (p)
{
}
void
IOPlug::PluginPropertyControl::actually_set_value (double user_val, Controllable::GroupControlDisposition gcd)
{
const Variant value(_desc.datatype, user_val);
if (value.type() == Variant::NOTHING) {
return;
}
_iop->plugin ()->set_property (parameter().id(), value);
_value = value;
AutomationControl::actually_set_value (user_val, gcd);
}
XMLNode&
IOPlug::PluginPropertyControl::get_state () const
{
XMLNode& node (AutomationControl::get_state());
node.set_property ("property", parameter ().id ());
node.remove_property ("value");
return node;
}
double
IOPlug::PluginPropertyControl::get_value () const
{
return _value.to_double();
}

View File

@ -588,12 +588,12 @@ LadspaPlugin::connect_and_run (BufferSet& bufs,
const uint32_t buf_index = in_map.get(DataType::AUDIO, audio_in_index++, &valid);
connect_port(port_index,
valid ? bufs.get_audio(buf_index).data(offset)
: silent_bufs.get_audio(0).data(offset));
: silent_bufs.get_audio(0).data(0));
} else if (LADSPA_IS_PORT_OUTPUT(port_descriptor(port_index))) {
const uint32_t buf_index = out_map.get(DataType::AUDIO, audio_out_index++, &valid);
connect_port(port_index,
valid ? bufs.get_audio(buf_index).data(offset)
: scratch_bufs.get_audio(0).data(offset));
: scratch_bufs.get_audio(0).data(0));
}
}
}

View File

@ -1216,7 +1216,7 @@ LuaAPI::Rubberband::set_mapping (luabridge::LuaRef tbl)
samplecnt_t
LuaAPI::Rubberband::read (Sample* buf, samplepos_t pos, samplecnt_t cnt, int channel) const
{
return _region->master_read_at (buf, NULL, NULL, _read_offset + pos, cnt, channel);
return _region->master_read_at (buf, _read_offset + pos, cnt, channel);
}
static void null_deleter (LuaAPI::Rubberband*) {}

View File

@ -85,6 +85,7 @@
#include "ardour/runtime_functions.h"
#include "ardour/region.h"
#include "ardour/region_factory.h"
#include "ardour/region_fx_plugin.h"
#include "ardour/return.h"
#include "ardour/revision.h"
#include "ardour/route_group.h"
@ -340,6 +341,7 @@ CLASSKEYS(std::shared_ptr<ARDOUR::MidiRegion>);
CLASSKEYS(std::shared_ptr<ARDOUR::MidiSource>);
CLASSKEYS(std::shared_ptr<ARDOUR::PluginInfo>);
CLASSKEYS(std::shared_ptr<ARDOUR::PluginInsert>);
CLASSKEYS(std::shared_ptr<ARDOUR::RegionFxPlugin>);
CLASSKEYS(std::shared_ptr<ARDOUR::Processor>);
CLASSKEYS(std::shared_ptr<ARDOUR::AudioReadable>);
CLASSKEYS(std::shared_ptr<ARDOUR::Region>);
@ -1635,6 +1637,11 @@ LuaBindings::common (lua_State* L)
.addFunction ("has_transients", &Region::has_transients)
.addFunction ("transients", (AnalysisFeatureList (Region::*)())&Region::transients)
.addFunction ("load_plugin", &Region::load_plugin)
.addFunction ("add_plugin", &Region::add_plugin)
.addFunction ("remove_plugin", &Region::add_plugin)
.addFunction ("nth_plugin", &Region::nth_plugin)
/* editing operations */
.addFunction ("set_length", &Region::set_length)
.addFunction ("set_start", &Region::set_start)
@ -2030,6 +2037,14 @@ LuaBindings::common (lua_State* L)
.addRefFunction ("get_stats", &PluginInsert::get_stats)
.endClass ()
.deriveWSPtrClass <RegionFxPlugin, SessionObject> ("RegionFxPlugin")
.addFunction ("plugin", &RegionFxPlugin::plugin)
.addFunction ("signal_latency", &RegionFxPlugin::signal_latency)
.addFunction ("get_count", &RegionFxPlugin::get_count)
.addFunction ("type", &RegionFxPlugin::type)
.addFunction ("reset_parameters_to_default", &RegionFxPlugin::reset_parameters_to_default)
.endClass ()
.deriveWSPtrClass <MPControl<gain_t>, PBD::Controllable> ("MPGainControl")
.addFunction ("set_value", &MPControl<gain_t>::set_value)
.addFunction ("get_value", &MPControl<gain_t>::get_value)
@ -2168,7 +2183,13 @@ LuaBindings::common (lua_State* L)
.addFunction ("delay", &DelayLine::delay)
.endClass ()
.deriveWSPtrClass <PluginInsert::PluginControl, AutomationControl> ("PluginControl")
.deriveWSPtrClass <PlugInsertBase::PluginControl, AutomationControl> ("PluginControl")
.endClass ()
.deriveWSPtrClass <PlugInsertBase::PluginPropertyControl, AutomationControl> ("PluginPropertyControl")
.endClass ()
.deriveWSPtrClass <PluginInsert::PIControl, PlugInsertBase::PluginControl> ("PIControl")
.endClass ()
.beginClass <RawMidiParser> ("RawMidiParser")

View File

@ -813,7 +813,7 @@ LuaProc::connect_and_run (BufferSet& bufs,
if (valid) {
in_map[ap + 1] = bufs.get_audio (buf_index).data (offset);
} else {
in_map[ap + 1] = silent_bufs.get_audio (0).data (offset);
in_map[ap + 1] = silent_bufs.get_audio (0).data (0);
}
}
for (uint32_t ap = 0; ap < audio_out; ++ap) {
@ -822,7 +822,7 @@ LuaProc::connect_and_run (BufferSet& bufs,
if (valid) {
out_map[ap + 1] = bufs.get_audio (buf_index).data (offset);
} else {
out_map[ap + 1] = scratch_bufs.get_audio (0).data (offset);
out_map[ap + 1] = scratch_bufs.get_audio (0).data (0);
}
}

View File

@ -2845,12 +2845,12 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
index = in_map.get(DataType::AUDIO, audio_in_index++, &valid);
buf = (valid)
? bufs.get_audio(index).data(offset)
: silent_bufs.get_audio(0).data(offset);
: silent_bufs.get_audio(0).data(0);
} else {
index = out_map.get(DataType::AUDIO, audio_out_index++, &valid);
buf = (valid)
? bufs.get_audio(index).data(offset)
: scratch_bufs.get_audio(0).data(offset);
: scratch_bufs.get_audio(0).data(0);
}
} else if (flags & PORT_SEQUENCE) {
/* FIXME: The checks here for bufs.count().n_midi() > index shouldn't

View File

@ -1700,6 +1700,7 @@ Playlist::region_changed (const PropertyChange& what_changed, std::shared_ptr<Re
our_interests.add (Properties::opaque);
our_interests.add (Properties::contents);
our_interests.add (Properties::time_domain);
our_interests.add (Properties::region_fx);
bounds.add (Properties::start);
bounds.add (Properties::length);

View File

@ -16,11 +16,32 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "ardour/plug_insert_base.h"
#include "ardour/ardour.h"
#include "ardour/automation_control.h"
#include "ardour/lv2_plugin.h"
#include "ardour/ladspa_plugin.h"
#include "ardour/luaproc.h"
#include "ardour/plug_insert_base.h"
#include "ardour/lv2_plugin.h"
#ifdef WINDOWS_VST_SUPPORT
#include "ardour/windows_vst_plugin.h"
#endif
#ifdef LXVST_SUPPORT
#include "ardour/lxvst_plugin.h"
#endif
#ifdef MACVST_SUPPORT
#include "ardour/mac_vst_plugin.h"
#endif
#ifdef VST3_SUPPORT
#include "ardour/vst3_plugin.h"
#endif
#ifdef AUDIOUNIT_SUPPORT
#include "ardour/audio_unit.h"
#endif
#include "pbd/i18n.h"
@ -145,7 +166,7 @@ PlugInsertBase::find_and_load_plugin (Session& s, XMLNode const& node, PluginTyp
}
void
PlugInsertBase::set_control_ids (const XMLNode& node, int version)
PlugInsertBase::set_control_ids (const XMLNode& node, int version, bool by_value)
{
const XMLNodeList& nlist = node.children();
for (XMLNodeConstIterator iter = nlist.begin(); iter != nlist.end(); ++iter) {
@ -169,14 +190,21 @@ PlugInsertBase::set_control_ids (const XMLNode& node, int version)
continue;
}
/* this may create the new controllable */
std::shared_ptr<Evoral::Control> c = control (Evoral::Parameter (PluginAutomation, 0, p));
if (!c) {
continue;
}
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl> (c);
if (ac) {
if (!ac) {
continue;
}
if (by_value) {
float val;
if ((*iter)->get_property (X_("value"), val)) {
ac->set_value (val, Controllable::NoGroup);
}
} else {
ac->set_state (**iter, version);
}
}
@ -198,3 +226,205 @@ PlugInsertBase::preset_load_set_value (uint32_t p, float v)
ac->set_value (v, Controllable::NoGroup);
ac->stop_touch (timepos_t (ac->session ().audible_sample()));
}
/* ****************************************************************************/
std::shared_ptr<Plugin>
PlugInsertBase::plugin_factory (std::shared_ptr<Plugin> other)
{
std::shared_ptr<LadspaPlugin> lp;
std::shared_ptr<LuaProc> lua;
std::shared_ptr<LV2Plugin> lv2p;
#ifdef WINDOWS_VST_SUPPORT
std::shared_ptr<WindowsVSTPlugin> vp;
#endif
#ifdef LXVST_SUPPORT
std::shared_ptr<LXVSTPlugin> lxvp;
#endif
#ifdef MACVST_SUPPORT
std::shared_ptr<MacVSTPlugin> mvp;
#endif
#ifdef VST3_SUPPORT
std::shared_ptr<VST3Plugin> vst3;
#endif
#ifdef AUDIOUNIT_SUPPORT
std::shared_ptr<AUPlugin> ap;
#endif
if ((lp = std::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new LadspaPlugin (*lp));
} else if ((lua = std::dynamic_pointer_cast<LuaProc> (other)) != 0) {
return std::shared_ptr<Plugin> (new LuaProc (*lua));
} else if ((lv2p = std::dynamic_pointer_cast<LV2Plugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new LV2Plugin (*lv2p));
#ifdef WINDOWS_VST_SUPPORT
} else if ((vp = std::dynamic_pointer_cast<WindowsVSTPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new WindowsVSTPlugin (*vp));
#endif
#ifdef LXVST_SUPPORT
} else if ((lxvp = std::dynamic_pointer_cast<LXVSTPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new LXVSTPlugin (*lxvp));
#endif
#ifdef MACVST_SUPPORT
} else if ((mvp = std::dynamic_pointer_cast<MacVSTPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new MacVSTPlugin (*mvp));
#endif
#ifdef VST3_SUPPORT
} else if ((vst3 = std::dynamic_pointer_cast<VST3Plugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new VST3Plugin (*vst3));
#endif
#ifdef AUDIOUNIT_SUPPORT
} else if ((ap = std::dynamic_pointer_cast<AUPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new AUPlugin (*ap));
#endif
}
fatal << string_compose (_("programming error: %1"),
X_("unknown plugin type in PlugInsertBase::plugin_factory"))
<< endmsg;
abort(); /*NOTREACHED*/
return std::shared_ptr<Plugin> ((Plugin*) 0);
}
/* ****************************************************************************/
PlugInsertBase::PluginControl::PluginControl (Session& s,
PlugInsertBase* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list)
: AutomationControl (s, param, desc, list, p->describe_parameter (param))
, _pib (p)
{
if (alist ()) {
if (desc.toggled) {
list->set_interpolation (Evoral::ControlList::Discrete);
}
}
}
/** @param val `user' value */
void
PlugInsertBase::PluginControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
{
for (uint32_t i = 0; i < _pib->get_count (); ++i) {
_pib->plugin (i)->set_parameter (parameter ().id (), user_val, 0);
}
AutomationControl::actually_set_value (user_val, group_override);
}
void
PlugInsertBase::PluginControl::catch_up_with_external_value (double user_val)
{
AutomationControl::actually_set_value (user_val, Controllable::NoGroup);
}
XMLNode&
PlugInsertBase::PluginControl::get_state () const
{
XMLNode& node (AutomationControl::get_state ());
node.set_property (X_("parameter"), parameter ().id ());
std::shared_ptr<LV2Plugin> lv2plugin = std::dynamic_pointer_cast<LV2Plugin> (_pib->plugin (0));
if (lv2plugin) {
node.set_property (X_("symbol"), lv2plugin->port_symbol (parameter ().id ()));
}
return node;
}
/** @return `user' val */
double
PlugInsertBase::PluginControl::get_value () const
{
std::shared_ptr<Plugin> plugin = _pib->plugin ();
if (!plugin) {
return 0.0;
}
return plugin->get_parameter (parameter ().id ());
}
std::string
PlugInsertBase::PluginControl::get_user_string () const
{
std::shared_ptr<Plugin> plugin = _pib->plugin ();
if (plugin) {
std::string pp;
if (plugin->print_parameter (parameter ().id (), pp) && pp.size () > 0) {
return pp;
}
}
return AutomationControl::get_user_string ();
}
PlugInsertBase::PluginPropertyControl::PluginPropertyControl (Session& s,
PlugInsertBase* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list)
: AutomationControl (s, param, desc, list)
, _pib (p)
{
}
void
PlugInsertBase::PluginPropertyControl::actually_set_value (double user_val, Controllable::GroupControlDisposition gcd)
{
/* Old numeric set_value(), coerce to appropriate datatype if possible.
* This is lossy, but better than nothing until Ardour's automation system
* can handle various datatypes all the way down.
*/
const Variant value (_desc.datatype, user_val);
if (value.type () == Variant::NOTHING) {
error << "set_value(double) called for non-numeric property" << endmsg;
return;
}
for (uint32_t i = 0; i < _pib->get_count (); ++i) {
_pib->plugin (i)->set_property (parameter ().id (), value);
}
_value = value;
AutomationControl::actually_set_value (user_val, gcd);
}
XMLNode&
PlugInsertBase::PluginPropertyControl::get_state () const
{
XMLNode& node (AutomationControl::get_state ());
node.set_property (X_("property"), parameter ().id ());
node.remove_property (X_("value"));
return node;
}
double
PlugInsertBase::PluginPropertyControl::get_value () const
{
return _value.to_double ();
}
std::ostream& operator<<(std::ostream& o, const ARDOUR::PlugInsertBase::Match& m)
{
switch (m.method) {
case PlugInsertBase::Impossible: o << "Impossible"; break;
case PlugInsertBase::Delegate: o << "Delegate"; break;
case PlugInsertBase::NoInputs: o << "NoInputs"; break;
case PlugInsertBase::ExactMatch: o << "ExactMatch"; break;
case PlugInsertBase::Replicate: o << "Replicate"; break;
case PlugInsertBase::Split: o << "Split"; break;
case PlugInsertBase::Hide: o << "Hide"; break;
}
o << " cnt: " << m.plugins
<< (m.strict_io ? " strict-io" : "")
<< (m.custom_cfg ? " custom-cfg" : "");
if (m.method == PlugInsertBase::Hide) {
o << " hide: " << m.hide;
}
o << "\n";
return o;
}

View File

@ -44,27 +44,6 @@
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#ifdef WINDOWS_VST_SUPPORT
#include "ardour/windows_vst_plugin.h"
#endif
#ifdef LXVST_SUPPORT
#include "ardour/lxvst_plugin.h"
#endif
#ifdef MACVST_SUPPORT
#include "ardour/mac_vst_plugin.h"
#endif
#ifdef VST3_SUPPORT
#include "ardour/vst3_plugin.h"
#endif
#ifdef AUDIOUNIT_SUPPORT
#include "ardour/audio_unit.h"
#endif
#include "ardour/session.h"
#include "ardour/types.h"
@ -569,7 +548,7 @@ PluginInsert::create_automatable_parameters ()
const bool automatable = a.find(param) != a.end();
std::shared_ptr<AutomationList> list(new AutomationList(param, desc, *this));
std::shared_ptr<AutomationControl> c (new PluginControl(this, param, desc, list));
std::shared_ptr<AutomationControl> c (new PIControl(_session, this, param, desc, list));
if (!automatable || (limit_automatables > 0 && what_can_be_automated ().size() > limit_automatables)) {
c->set_flag (Controllable::NotAutomatable);
}
@ -590,7 +569,7 @@ PluginInsert::create_automatable_parameters ()
if (Variant::type_is_numeric(desc.datatype)) {
list = std::shared_ptr<AutomationList>(new AutomationList(param, desc, *this));
}
std::shared_ptr<AutomationControl> c (new PluginPropertyControl(this, param, desc, list));
std::shared_ptr<AutomationControl> c (new PluginPropertyControl(_session, this, param, desc, list));
if (!Variant::type_is_numeric(desc.datatype)) {
c->set_flag (Controllable::NotAutomatable);
}
@ -612,7 +591,7 @@ PluginInsert::create_automatable_parameters ()
desc.upper = 1;
std::shared_ptr<AutomationList> list(new AutomationList(param, desc, *this));
std::shared_ptr<AutomationControl> c (new PluginControl(this, param, desc, list));
std::shared_ptr<AutomationControl> c (new PluginControl(_session, this, param, desc, list));
add_control (c);
}
@ -1557,62 +1536,6 @@ PluginInsert::reset_parameters_to_default ()
return all;
}
std::shared_ptr<Plugin>
PluginInsert::plugin_factory (std::shared_ptr<Plugin> other)
{
std::shared_ptr<LadspaPlugin> lp;
std::shared_ptr<LuaProc> lua;
std::shared_ptr<LV2Plugin> lv2p;
#ifdef WINDOWS_VST_SUPPORT
std::shared_ptr<WindowsVSTPlugin> vp;
#endif
#ifdef LXVST_SUPPORT
std::shared_ptr<LXVSTPlugin> lxvp;
#endif
#ifdef MACVST_SUPPORT
std::shared_ptr<MacVSTPlugin> mvp;
#endif
#ifdef VST3_SUPPORT
std::shared_ptr<VST3Plugin> vst3;
#endif
#ifdef AUDIOUNIT_SUPPORT
std::shared_ptr<AUPlugin> ap;
#endif
if ((lp = std::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new LadspaPlugin (*lp));
} else if ((lua = std::dynamic_pointer_cast<LuaProc> (other)) != 0) {
return std::shared_ptr<Plugin> (new LuaProc (*lua));
} else if ((lv2p = std::dynamic_pointer_cast<LV2Plugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new LV2Plugin (*lv2p));
#ifdef WINDOWS_VST_SUPPORT
} else if ((vp = std::dynamic_pointer_cast<WindowsVSTPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new WindowsVSTPlugin (*vp));
#endif
#ifdef LXVST_SUPPORT
} else if ((lxvp = std::dynamic_pointer_cast<LXVSTPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new LXVSTPlugin (*lxvp));
#endif
#ifdef MACVST_SUPPORT
} else if ((mvp = std::dynamic_pointer_cast<MacVSTPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new MacVSTPlugin (*mvp));
#endif
#ifdef VST3_SUPPORT
} else if ((vst3 = std::dynamic_pointer_cast<VST3Plugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new VST3Plugin (*vst3));
#endif
#ifdef AUDIOUNIT_SUPPORT
} else if ((ap = std::dynamic_pointer_cast<AUPlugin> (other)) != 0) {
return std::shared_ptr<Plugin> (new AUPlugin (*ap));
#endif
}
fatal << string_compose (_("programming error: %1"),
X_("unknown plugin type in PluginInsert::plugin_factory"))
<< endmsg;
abort(); /*NOTREACHED*/
return std::shared_ptr<Plugin> ((Plugin*) 0);
}
void
PluginInsert::set_input_map (uint32_t num, ChanMapping m) {
@ -2307,10 +2230,10 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
ChanCount cc_analysis_in (DataType::AUDIO, in.n_audio());
ChanCount cc_analysis_out (DataType::AUDIO, out.n_audio());
session().ensure_buffer_set (_signal_analysis_inputs, cc_analysis_in);
_signal_analysis_inputs.ensure_buffers (cc_analysis_in, 8192);
_signal_analysis_inputs.set_count (cc_analysis_in);
session().ensure_buffer_set (_signal_analysis_outputs, cc_analysis_out);
_signal_analysis_outputs.ensure_buffers (cc_analysis_out, 8192);
_signal_analysis_outputs.set_count (cc_analysis_out);
// std::cerr << "set counts to i" << in.n_audio() << "/o" << out.n_audio() << std::endl;
@ -2336,7 +2259,7 @@ PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out)
return private_can_support_io_configuration (in, out).method != Impossible;
}
PluginInsert::Match
PlugInsertBase::Match
PluginInsert::private_can_support_io_configuration (ChanCount const& in, ChanCount& out) const
{
if (!_custom_cfg && _preset_out.n_audio () > 0) {
@ -2411,7 +2334,7 @@ PluginInsert::internal_can_support_io_configuration (ChanCount const & inx, Chan
uint32_t f = 1; // at least one. e.g. control data filters, no in, no out.
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
uint32_t nout = outputs.get (*t);
if (nout == 0 || inx.get(*t) == 0) { continue; }
if (nout == 0 || inx.get(*t) == 0 || nout == 0) { continue; }
f = max (f, (uint32_t) ceil (inx.get(*t) / (float)nout));
}
out = inx;
@ -2463,7 +2386,7 @@ PluginInsert::internal_can_support_io_configuration (ChanCount const & inx, Chan
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
uint32_t nin = ns_inputs.get (*t);
uint32_t nout = outputs.get (*t);
if (nin == 0 || inx.get(*t) == 0) { continue; }
if (nin == 0 || inx.get(*t) == 0 || nout == 0) { continue; }
// prefer floor() so the count won't overly increase IFF (nin < nout)
f = max (f, (uint32_t) floor (inx.get(*t) / (float)nout));
}
@ -2708,50 +2631,6 @@ PluginInsert::state () const
return node;
}
void
PluginInsert::update_control_values (const XMLNode& node, int version)
{
const XMLNodeList& nlist = node.children();
for (XMLNodeConstIterator iter = nlist.begin(); iter != nlist.end(); ++iter) {
if ((*iter)->name() != Controllable::xml_node_name) {
continue;
}
float val;
if (!(*iter)->get_property (X_("value"), val)) {
continue;
}
uint32_t p = (uint32_t)-1;
std::string str;
if ((*iter)->get_property (X_("symbol"), str)) {
std::shared_ptr<LV2Plugin> lv2plugin = std::dynamic_pointer_cast<LV2Plugin> (_plugins[0]);
if (lv2plugin) {
p = lv2plugin->port_index(str.c_str());
}
}
if (p == (uint32_t)-1) {
(*iter)->get_property (X_("parameter"), p);
}
if (p == (uint32_t)-1) {
continue;
}
/* lookup controllable */
std::shared_ptr<Evoral::Control> c = control (Evoral::Parameter (PluginAutomation, 0, p), false);
if (!c) {
continue;
}
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl> (c);
if (ac) {
ac->set_value (val, Controllable::NoGroup);
}
}
}
int
PluginInsert::set_state(const XMLNode& node, int version)
{
@ -2802,7 +2681,7 @@ PluginInsert::set_state(const XMLNode& node, int version)
assert (_plugins[0]->unique_id() == unique_id);
/* update controllable value only (copy plugin state) */
set_id (node);
update_control_values (node, version);
set_control_ids (node, version, true);
}
Processor::set_state (node, version);
@ -3069,129 +2948,15 @@ PluginInsert::type () const
return plugin()->get_info()->type;
}
PluginInsert::PluginControl::PluginControl (PluginInsert* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list)
: AutomationControl (p->session(), param, desc, list, p->describe_parameter(param))
, _plugin (p)
{
if (alist()) {
if (desc.toggled) {
list->set_interpolation(Evoral::ControlList::Discrete);
}
}
}
/** @param val `user' value */
void
PluginInsert::PluginControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
PluginInsert::PIControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
{
/* FIXME: probably should be taking out some lock here.. */
for (Plugins::iterator i = _plugin->_plugins.begin(); i != _plugin->_plugins.end(); ++i) {
(*i)->set_parameter (_list->parameter().id(), user_val, 0);
}
std::shared_ptr<Plugin> iasp = _plugin->_impulseAnalysisPlugin.lock();
std::shared_ptr<Plugin> iasp = dynamic_cast<PluginInsert*>(_pib)->_impulseAnalysisPlugin.lock();
if (iasp) {
iasp->set_parameter (_list->parameter().id(), user_val, 0);
}
AutomationControl::actually_set_value (user_val, group_override);
}
void
PluginInsert::PluginControl::catch_up_with_external_value (double user_val)
{
AutomationControl::actually_set_value (user_val, Controllable::NoGroup);
}
XMLNode&
PluginInsert::PluginControl::get_state () const
{
XMLNode& node (AutomationControl::get_state());
node.set_property (X_("parameter"), parameter().id());
std::shared_ptr<LV2Plugin> lv2plugin = std::dynamic_pointer_cast<LV2Plugin> (_plugin->_plugins[0]);
if (lv2plugin) {
node.set_property (X_("symbol"), lv2plugin->port_symbol (parameter().id()));
}
return node;
}
/** @return `user' val */
double
PluginInsert::PluginControl::get_value () const
{
std::shared_ptr<Plugin> plugin = _plugin->plugin (0);
if (!plugin) {
return 0.0;
}
return plugin->get_parameter (_list->parameter().id());
}
std::string
PluginInsert::PluginControl::get_user_string () const
{
std::shared_ptr<Plugin> plugin = _plugin->plugin (0);
if (plugin) {
std::string pp;
if (plugin->print_parameter (parameter().id(), pp) && pp.size () > 0) {
return pp;
}
}
return AutomationControl::get_user_string ();
}
PluginInsert::PluginPropertyControl::PluginPropertyControl (PluginInsert* p,
const Evoral::Parameter& param,
const ParameterDescriptor& desc,
std::shared_ptr<AutomationList> list)
: AutomationControl (p->session(), param, desc, list)
, _plugin (p)
{
}
void
PluginInsert::PluginPropertyControl::actually_set_value (double user_val, Controllable::GroupControlDisposition gcd)
{
/* Old numeric set_value(), coerce to appropriate datatype if possible.
This is lossy, but better than nothing until Ardour's automation system
can handle various datatypes all the way down. */
const Variant value(_desc.datatype, user_val);
if (value.type() == Variant::NOTHING) {
error << "set_value(double) called for non-numeric property" << endmsg;
return;
}
for (Plugins::iterator i = _plugin->_plugins.begin(); i != _plugin->_plugins.end(); ++i) {
(*i)->set_property(_list->parameter().id(), value);
}
_value = value;
AutomationControl::actually_set_value (user_val, gcd);
}
XMLNode&
PluginInsert::PluginPropertyControl::get_state () const
{
XMLNode& node (AutomationControl::get_state());
node.set_property (X_("property"), parameter().id());
node.remove_property (X_("value"));
return node;
}
double
PluginInsert::PluginPropertyControl::get_value () const
{
return _value.to_double();
PluginControl::actually_set_value (user_val, group_override);
}
std::shared_ptr<Plugin>
@ -3427,24 +3192,3 @@ PluginInsert::clear_stats ()
{
_stat_reset.store (1);
}
std::ostream& operator<<(std::ostream& o, const ARDOUR::PluginInsert::Match& m)
{
switch (m.method) {
case PluginInsert::Impossible: o << "Impossible"; break;
case PluginInsert::Delegate: o << "Delegate"; break;
case PluginInsert::NoInputs: o << "NoInputs"; break;
case PluginInsert::ExactMatch: o << "ExactMatch"; break;
case PluginInsert::Replicate: o << "Replicate"; break;
case PluginInsert::Split: o << "Split"; break;
case PluginInsert::Hide: o << "Hide"; break;
}
o << " cnt: " << m.plugins
<< (m.strict_io ? " strict-io" : "")
<< (m.custom_cfg ? " custom-cfg" : "");
if (m.method == PluginInsert::Hide) {
o << " hide: " << m.hide;
}
o << "\n";
return o;
}

View File

@ -77,7 +77,6 @@ RBEffect::run (std::shared_ptr<Region> r, Progress* progress)
SourceList nsrcs;
int ret = -1;
const samplecnt_t bufsize = 8192;
gain_t* gain_buffer = 0;
Sample** buffers = 0;
char suffix[32];
string new_name;
@ -201,8 +200,7 @@ RBEffect::run (std::shared_ptr<Region> r, Progress* progress)
goto out;
}
gain_buffer = new gain_t[bufsize];
buffers = new float*[channels];
buffers = new float*[channels];
for (uint32_t i = 0; i < channels; ++i) {
buffers[i] = new float[bufsize];
@ -225,8 +223,6 @@ RBEffect::run (std::shared_ptr<Region> r, Progress* progress)
region->start_sample () + region->position_sample ();
this_read = region->master_read_at (buffers[i],
buffers[i],
gain_buffer,
this_position,
this_time,
i);
@ -262,8 +258,6 @@ RBEffect::run (std::shared_ptr<Region> r, Progress* progress)
region->start_sample () + region->position_sample ();
this_read = region->master_read_at (buffers[i],
buffers[i],
gain_buffer,
this_position,
this_time,
i);
@ -377,8 +371,6 @@ RBEffect::run (std::shared_ptr<Region> r, Progress* progress)
out:
delete[] gain_buffer;
if (buffers) {
for (uint32_t i = 0; i < channels; ++i) {
delete[] buffers[i];

View File

@ -36,11 +36,13 @@
#include "ardour/audioregion.h"
#include "ardour/debug.h"
#include "ardour/filter.h"
#include "ardour/lua_api.h"
#include "ardour/playlist.h"
#include "ardour/playlist_source.h"
#include "ardour/profile.h"
#include "ardour/region.h"
#include "ardour/region_factory.h"
#include "ardour/region_fx_plugin.h"
#include "ardour/session.h"
#include "ardour/source.h"
#include "ardour/tempo.h"
@ -82,6 +84,7 @@ namespace ARDOUR {
PBD::PropertyDescriptor<std::string> tags;
PBD::PropertyDescriptor<uint64_t> reg_group;
PBD::PropertyDescriptor<bool> contents;
PBD::PropertyDescriptor<bool> region_fx;
/* these properties are used as a convenience for announcing changes to state, but aren't stored as properties */
PBD::PropertyDescriptor<Temporal::TimeDomain> time_domain;
@ -184,6 +187,8 @@ Region::make_property_quarks ()
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for tags = %1\n", Properties::tags.property_id));
Properties::contents.property_id = g_quark_from_static_string (X_("contents"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for contents = %1\n", Properties::contents.property_id));
Properties::region_fx.property_id = g_quark_from_static_string (X_("region-fx"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for region-fx = %1\n", Properties::region_fx.property_id));
Properties::time_domain.property_id = g_quark_from_static_string (X_("time_domain"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for time_domain = %1\n", Properties::time_domain.property_id));
Properties::reg_group.property_id = g_quark_from_static_string (X_("rgroup"));
@ -290,6 +295,7 @@ Region::register_properties ()
Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, const string& name, DataType type)
: SessionObject(s, name)
, _type (type)
, _fx_latency (0)
, REGION_DEFAULT_STATE (start,length)
, _last_length (length)
, _first_edit (EditChangesNothing)
@ -305,6 +311,7 @@ Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, c
Region::Region (const SourceList& srcs)
: SessionObject(srcs.front()->session(), "toBeRenamed")
, _type (srcs.front()->type())
, _fx_latency (0)
, REGION_DEFAULT_STATE(_type == DataType::MIDI ? timepos_t (Temporal::Beats()) : timepos_t::from_superclock (0),
_type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0))
, _last_length (_type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0))
@ -326,6 +333,7 @@ Region::Region (const SourceList& srcs)
Region::Region (std::shared_ptr<const Region> other)
: SessionObject(other->session(), other->name())
, _type (other->data_type())
, _fx_latency (0)
, REGION_COPY_STATE (other)
, _last_length (other->_last_length)
, _first_edit (EditChangesNothing)
@ -384,6 +392,7 @@ Region::Region (std::shared_ptr<const Region> other)
Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
: SessionObject(other->session(), other->name())
, _type (other->data_type())
, _fx_latency (0)
, REGION_COPY_STATE (other)
, _last_length (other->_last_length)
, _first_edit (EditChangesNothing)
@ -429,6 +438,7 @@ Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
Region::Region (std::shared_ptr<const Region> other, const SourceList& srcs)
: SessionObject (other->session(), other->name())
, _type (srcs.front()->type())
, _fx_latency (0)
, REGION_COPY_STATE (other)
, _last_length (other->_last_length)
, _first_edit (EditChangesID)
@ -1447,6 +1457,13 @@ Region::state () const
node->add_child_copy (*_extra_xml);
}
{
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
for (auto const & p : _plugins) {
node->add_child_nocopy (p->get_state ());
}
}
return *node;
}
@ -1552,6 +1569,30 @@ Region::_set_state (const XMLNode& node, int version, PropertyChange& what_chang
_valid_transients = false;
}
{
Glib::Threads::RWLock::WriterLock lm (_fx_lock);
bool changed = !_plugins.empty ();
_plugins.clear ();
for (auto const& child : node.children ()) {
if (child->name() == X_("RegionFXPlugin")) {
std::shared_ptr<RegionFxPlugin> rfx (new RegionFxPlugin (_session, time_domain ()));
rfx->set_state (*child, version);
if (!_add_plugin (rfx, std::shared_ptr<RegionFxPlugin>(), true)) {
continue;
}
_plugins.push_back (rfx);
changed = true;
}
}
if (changed) {
fx_latency_changed (true);
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
RegionFxChanged (); /* EMIT SIGNAL */
}
}
return 0;
}
@ -2347,3 +2388,63 @@ Region::finish_domain_bounce (Temporal::DomainBounceInfo& cmd)
send_change (Properties::length);
}
bool
Region::load_plugin (ARDOUR::PluginType type, std::string const& name)
{
PluginInfoPtr pip = LuaAPI::new_plugin_info (name, type);
if (!pip) {
return false;
}
PluginPtr p (pip->load (_session));
if (!p) {
return false;
}
std::shared_ptr<RegionFxPlugin> rfx (new RegionFxPlugin (_session, time_domain (), p));
return add_plugin (rfx);
}
bool
Region::add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<RegionFxPlugin> pos)
{
return _add_plugin (rfx, pos, false);
}
void
Region::reorder_plugins (RegionFxList const& new_order)
{
Glib::Threads::RWLock::WriterLock lm (_fx_lock);
RegionFxList as_it_will_be;
RegionFxList::iterator oiter = _plugins.begin ();
RegionFxList::const_iterator niter = new_order.begin ();
while (niter != new_order.end ()) {
if (oiter == _plugins.end ()) {
as_it_will_be.insert (as_it_will_be.end (), niter, new_order.end ());
while (niter != new_order.end ()) {
++niter;
}
break;
}
if (find (new_order.begin (), new_order.end (), *oiter) != new_order.end ()) {
as_it_will_be.push_back (*niter);
++niter;
}
oiter = _plugins.erase (oiter);
}
_plugins.insert (oiter, as_it_will_be.begin (), as_it_will_be.end ());
}
void
Region::fx_latency_changed (bool)
{
uint32_t l = 0;
for (auto const& rfx : _plugins) {
l += rfx->effective_latency ();
}
if (l == _fx_latency) {
return;
}
_fx_latency = l;
}

File diff suppressed because it is too large Load Diff

View File

@ -230,6 +230,7 @@ Session::Session (AudioEngine &eng,
, _writable (false)
, _under_nsm_control (false)
, _xrun_count (0)
, _required_thread_buffersize (0)
, master_wait_end (0)
, post_export_sync (false)
, post_export_position (0)
@ -2407,6 +2408,7 @@ Session::set_block_size (pframes_t nframes)
* here.
*/
current_block_size = nframes;
_required_thread_buffersize = -1;
ensure_buffers ();
@ -5839,21 +5841,33 @@ Session::tempo_map_changed ()
set_dirty ();
}
void
Session::ensure_buffers_unlocked (ChanCount howmany)
{
if (_required_thread_buffers >= howmany) {
return;
}
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
ensure_buffers (howmany);
}
/** Ensures that all buffers (scratch, send, silent, etc) are allocated for
* the given count with the current block size.
* Must be called with the process-lock held
*/
void
Session::ensure_buffers (ChanCount howmany)
{
BufferManager::ensure_buffers (howmany, bounce_processing() ? bounce_chunk_size : 0);
}
void
Session::ensure_buffer_set(BufferSet& buffers, const ChanCount& count)
{
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
buffers.ensure_buffers(*t, count.get(*t), _engine.raw_buffer_size(*t));
size_t want_size = bounce_processing() ? bounce_chunk_size : 0;
if (howmany.n_total () == 0) {
howmany = _required_thread_buffers;
}
if (_required_thread_buffers >= howmany && _required_thread_buffersize == want_size) {
return;
}
_required_thread_buffers = ChanCount::max (_required_thread_buffers, howmany);
_required_thread_buffersize = want_size;
BufferManager::ensure_buffers (_required_thread_buffers, _required_thread_buffersize);
}
uint32_t

View File

@ -60,7 +60,6 @@ STStretch::run (std::shared_ptr<Region> r, Progress* progress)
SourceList nsrcs;
int ret = -1;
const samplecnt_t bufsize = 8192;
gain_t* gain_buffer = 0;
Sample** buffers = 0;
char suffix[32];
string new_name;
@ -181,8 +180,7 @@ STStretch::run (std::shared_ptr<Region> r, Progress* progress)
goto out;
}
gain_buffer = new gain_t[bufsize];
buffers = new float*[channels];
buffers = new float*[channels];
for (uint32_t i = 0; i < channels; ++i) {
buffers[i] = new float[bufsize];
@ -208,8 +206,6 @@ STStretch::run (std::shared_ptr<Region> r, Progress* progress)
region->start () + region->position ();
this_read = region->master_read_at (buffers[i],
buffers[i],
gain_buffer,
this_position,
this_time,
i);
@ -319,8 +315,6 @@ STStretch::run (std::shared_ptr<Region> r, Progress* progress)
out:
delete[] gain_buffer;
if (buffers) {
for (uint32_t i = 0; i < channels; ++i) {
delete[] buffers[i];

View File

@ -790,7 +790,7 @@ VST3Plugin::connect_and_run (BufferSet& bufs,
index = in_map.get (DataType::AUDIO, in_index++, &valid);
ins[i] = (valid)
? bufs.get_audio (index).data (offset)
: silent_bufs.get_audio (0).data (offset);
: silent_bufs.get_audio (0).data (0);
_connected_inputs[i] = valid;
}
@ -801,7 +801,7 @@ VST3Plugin::connect_and_run (BufferSet& bufs,
index = out_map.get (DataType::AUDIO, out_index++, &valid);
outs[i] = (valid)
? bufs.get_audio (index).data (offset)
: scratch_bufs.get_audio (0).data (offset);
: scratch_bufs.get_audio (0).data (0);
_connected_outputs[i] = valid;
}

View File

@ -724,7 +724,7 @@ VSTPlugin::connect_and_run (BufferSet& bufs,
index = in_map.get(DataType::AUDIO, in_index++, &valid);
ins[i] = (valid)
? bufs.get_audio(index).data(offset)
: silent_bufs.get_audio(0).data(offset);
: silent_bufs.get_audio(0).data(0);
}
uint32_t out_index = 0;
@ -734,7 +734,7 @@ VSTPlugin::connect_and_run (BufferSet& bufs,
index = out_map.get(DataType::AUDIO, out_index++, &valid);
outs[i] = (valid)
? bufs.get_audio(index).data(offset)
: scratch_bufs.get_audio(0).data(offset);
: scratch_bufs.get_audio(0).data(0);
}
if (bufs.count().n_midi() > 0) {

View File

@ -193,6 +193,7 @@ libardour_sources = [
'record_enable_control.cc',
'record_safe_control.cc',
'region_factory.cc',
'region_fx_plugin.cc',
'resampled_source.cc',
'region.cc',
'return.cc',

View File

@ -19,6 +19,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <gtkmm/window.h>
#include <gtkmm/box.h>