Compare commits
21 Commits
c120829796
...
0e58cbe767
Author | SHA1 | Date |
---|---|---|
Robin Gareus | 0e58cbe767 | |
Robin Gareus | af68ff3158 | |
Robin Gareus | 9e3c609151 | |
Robin Gareus | f203494672 | |
Robin Gareus | c884d31c73 | |
Robin Gareus | 63e5550e1d | |
Robin Gareus | fdfaeaa70a | |
Robin Gareus | ba971b8fa9 | |
Robin Gareus | 83241aebef | |
Robin Gareus | fa83563187 | |
Robin Gareus | 82e898d916 | |
Robin Gareus | 859d5d9841 | |
Robin Gareus | 17e05f3081 | |
Robin Gareus | da8ec3ff32 | |
Robin Gareus | a4f9b1f64c | |
Robin Gareus | 1acf57d198 | |
Robin Gareus | 4f9f261fc6 | |
Robin Gareus | 1d3b5f2bfd | |
Robin Gareus | cbeaa4b968 | |
Robin Gareus | 3c188f562d | |
Edgar Aichinger | c9c419213f |
|
@ -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; // TODO Fix radio signal duplicate emission, skip inactive one
|
||||
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 ([arv] () { 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 ([arv, nth, i] () { 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,8 +1105,8 @@ AudioRegionView::set_colors ()
|
|||
{
|
||||
RegionView::set_colors();
|
||||
|
||||
if (gain_line) {
|
||||
gain_line->set_line_color (audio_region()->envelope_active() ?
|
||||
if (_fx_line) {
|
||||
_fx_line->set_line_color (audio_region()->envelope_active() ?
|
||||
UIConfiguration::instance().color ("gain line") :
|
||||
UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
|
||||
}
|
||||
|
@ -1148,8 +1152,8 @@ AudioRegionView::setup_waveform_visibility ()
|
|||
void
|
||||
AudioRegionView::temporarily_hide_envelope ()
|
||||
{
|
||||
if (gain_line) {
|
||||
gain_line->hide ();
|
||||
if (_fx_line) {
|
||||
_fx_line->hide ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1160,20 +1164,81 @@ 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);
|
||||
envelope_active_changed ();
|
||||
update_envelope_visibility ();
|
||||
_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;
|
||||
|
||||
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);
|
||||
envelope_active_changed ();
|
||||
update_envelope_visibility ();
|
||||
_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;
|
||||
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 +1439,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 +1451,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 +1476,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 ®ion_before = audio_region()->get_state();
|
||||
audio_region()->set_envelope_active(true);
|
||||
XMLNode ®ion_after = audio_region()->get_state();
|
||||
region_memento = new MementoCommand<AudioRegion>(*(audio_region().get()), ®ion_before, ®ion_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 +1506,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 +1518,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 +1623,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 +1634,15 @@ 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 ();
|
||||
assert (_fx_line);
|
||||
if (_rdx_param != UINT32_MAX && _rfx_id != 0) {
|
||||
_fx_line->set_line_color (UIConfiguration::instance().color ("processor automation line")); // XXX
|
||||
} else {
|
||||
_fx_line->set_line_color (audio_region()->envelope_active()
|
||||
? UIConfiguration::instance().color ("gain line")
|
||||
: UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
|
||||
}
|
||||
update_envelope_visibility ();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1696,7 +1761,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 +1923,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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -234,6 +240,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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
2748
gtk2_ardour/po/de.po
2748
gtk2_ardour/po/de.po
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -44,26 +44,14 @@ using namespace ARDOUR;
|
|||
using namespace PBD;
|
||||
|
||||
AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView& r, ArdourCanvas::Container& parent, std::shared_ptr<AutomationList> l)
|
||||
: AutomationLine (name, r.get_time_axis_view(), parent, l, l->parameter())
|
||||
, rv (r)
|
||||
: RegionFxLine (name, r, parent, l, l->parameter ())
|
||||
, arv (r)
|
||||
{
|
||||
// If this isn't true something is horribly wrong, and we'll get catastrophic gain values
|
||||
assert(l->parameter().type() == EnvelopeAutomation);
|
||||
|
||||
r.region()->PropertyChanged.connect (_region_changed_connection, invalidator (*this), boost::bind (&AudioRegionGainLine::region_changed, this, _1), gui_context());
|
||||
|
||||
group->raise_to_top ();
|
||||
group->set_y_position (2);
|
||||
terminal_points_can_slide = false;
|
||||
}
|
||||
|
||||
timepos_t
|
||||
AudioRegionGainLine::get_origin() const
|
||||
{
|
||||
return rv.region()->position();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioRegionGainLine::start_drag_single (ControlPoint* cp, double x, float fraction)
|
||||
{
|
||||
|
@ -71,9 +59,9 @@ AudioRegionGainLine::start_drag_single (ControlPoint* cp, double x, float fracti
|
|||
|
||||
// XXX Stateful need to capture automation curve data
|
||||
|
||||
if (!rv.audio_region()->envelope_active()) {
|
||||
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0));
|
||||
rv.audio_region()->set_envelope_active(false);
|
||||
if (!arv.audio_region()->envelope_active()) {
|
||||
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(arv.audio_region().get()), &arv.audio_region()->get_state(), 0));
|
||||
arv.audio_region()->set_envelope_active(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,10 +72,10 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
|
|||
trackview.editor().begin_reversible_command (_("remove control point"));
|
||||
XMLNode &before = alist->get_state();
|
||||
|
||||
if (!rv.audio_region()->envelope_active()) {
|
||||
rv.audio_region()->clear_changes ();
|
||||
rv.audio_region()->set_envelope_active(true);
|
||||
trackview.session()->add_command(new StatefulDiffCommand (rv.audio_region()));
|
||||
if (!arv.audio_region()->envelope_active()) {
|
||||
arv.audio_region()->clear_changes ();
|
||||
arv.audio_region()->set_envelope_active(true);
|
||||
trackview.session()->add_command(new StatefulDiffCommand (arv.audio_region()));
|
||||
}
|
||||
|
||||
trackview.editor ().get_selection ().clear_points ();
|
||||
|
@ -101,9 +89,9 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
|
|||
void
|
||||
AudioRegionGainLine::end_drag (bool with_push, uint32_t final_index)
|
||||
{
|
||||
if (!rv.audio_region()->envelope_active()) {
|
||||
rv.audio_region()->set_envelope_active(true);
|
||||
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state()));
|
||||
if (!arv.audio_region()->envelope_active()) {
|
||||
arv.audio_region()->set_envelope_active(true);
|
||||
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(arv.audio_region().get()), 0, &arv.audio_region()->get_state()));
|
||||
}
|
||||
|
||||
AutomationLine::end_drag (with_push, final_index);
|
||||
|
|
|
@ -27,8 +27,7 @@
|
|||
|
||||
#include "ardour/ardour.h"
|
||||
|
||||
|
||||
#include "automation_line.h"
|
||||
#include "region_fx_line.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Session;
|
||||
|
@ -37,23 +36,21 @@ namespace ARDOUR {
|
|||
class TimeAxisView;
|
||||
class AudioRegionView;
|
||||
|
||||
class AudioRegionGainLine : public AutomationLine
|
||||
class AudioRegionGainLine : public RegionFxLine
|
||||
{
|
||||
public:
|
||||
AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Container& parent, std::shared_ptr<ARDOUR::AutomationList>);
|
||||
|
||||
Temporal::timepos_t get_origin() const;
|
||||
|
||||
void start_drag_single (ControlPoint*, double, float);
|
||||
void end_drag (bool with_push, uint32_t final_index);
|
||||
|
||||
void remove_point (ControlPoint&);
|
||||
AudioRegionView& region_view () { return rv; }
|
||||
|
||||
private:
|
||||
PBD::ScopedConnection _region_changed_connection;
|
||||
void region_changed (const PBD::PropertyChange& what_changed);
|
||||
AudioRegionView& rv;
|
||||
|
||||
AudioRegionView& arv;
|
||||
PBD::ScopedConnection _region_changed_connection;
|
||||
};
|
||||
|
||||
#endif /* __ardour_gtk_region_gain_line_h__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -2113,3 +2253,196 @@ 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);
|
||||
}
|
||||
|
||||
fx_latency_changed (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);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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*) {}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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];
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/window.h>
|
||||
#include <gtkmm/box.h>
|
||||
|
||||
|
|
Loading…
Reference in New Issue