Merge branch 'ardour'
This commit is contained in:
commit
544b53f65c
@ -2471,7 +2471,7 @@ CLASS_GRAPH = YES
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
COLLABORATION_GRAPH = YES
|
||||
COLLABORATION_GRAPH = NO
|
||||
|
||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||
# groups, showing the direct groups dependencies.
|
||||
|
@ -19,7 +19,7 @@ export GTK2_RC_FILES=/nonexistent
|
||||
# can find all the components.
|
||||
#
|
||||
|
||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport8:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/us2400:$libs/surfaces/wiimote:$libs/surfaces/push2:$libs/surfaces/maschine2:$libs/surfaces/cc121:$libs/surfaces/launch_control_xl:$libs/surfaces/contourdesign:$libs/surfaces/websockets:$libs/surfaces/console1:$libs/surfaces/launchpad_pro:$libs/surfaces/launchpad_x
|
||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport8:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/us2400:$libs/surfaces/wiimote:$libs/surfaces/push2:$libs/surfaces/maschine2:$libs/surfaces/cc121:$libs/surfaces/launch_control_xl:$libs/surfaces/contourdesign:$libs/surfaces/websockets:$libs/surfaces/console1:$libs/surfaces/launchpad_pro:$libs/surfaces/launchpad_x:$libs/surfaces/launchkey_4
|
||||
export ARDOUR_PANNER_PATH=$libs/panners
|
||||
export ARDOUR_DATA_PATH=$TOP/share:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour
|
||||
export ARDOUR_MIDIMAPS_PATH=$TOP/share/midi_maps
|
||||
|
@ -48,8 +48,6 @@
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "pbd/xml++.h"
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/frame.h>
|
||||
|
@ -60,6 +60,7 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* 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"))
|
||||
, _pre_fade_fx_toggle (_("Pre-Fade Fx"))
|
||||
, _show_on_touch (_("Show on Touch"))
|
||||
, _peak_channel (false)
|
||||
{
|
||||
@ -94,10 +95,10 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv)
|
||||
_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.attach (_pre_fade_fx_toggle, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL);
|
||||
++_table_row;
|
||||
|
||||
#ifndef MIXBUS
|
||||
#ifndef LIVETRAX
|
||||
#ifndef LIVETRAX // no region Fx for Trax
|
||||
_region_line_label.set_name ("AudioRegionEditorLabel");
|
||||
_region_line_label.set_text (_("Region Line:"));
|
||||
_region_line_label.set_alignment (1, 0.5);
|
||||
@ -106,13 +107,18 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv)
|
||||
_table.attach (_show_on_touch, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL);
|
||||
++_table_row;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
UI::instance()->set_tip (_polarity_toggle, _("Invert the signal polarity (180deg phase shift)"));
|
||||
UI::instance()->set_tip (_pre_fade_fx_toggle, _("Apply region effects before the region fades.\nThis is useful if the effect(s) have tail, that would otherwise be faded out by the region fade (e.g. reverb, delay)"));
|
||||
UI::instance()->set_tip (_show_on_touch, _("When touching a control in a region effect plugin UI, the corresponding region-automation line is shown the editor, and edit mode is set to 'draw'."));
|
||||
|
||||
gain_changed ();
|
||||
pre_fade_fx_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));
|
||||
_pre_fade_fx_toggle.signal_toggled().connect (sigc::mem_fun (*this, &AudioRegionEditor::pre_fade_fx_toggle_changed));
|
||||
_show_on_touch.signal_toggled().connect (sigc::mem_fun (*this, &AudioRegionEditor::show_on_touch_changed));
|
||||
|
||||
arv->region_line_changed.connect ((sigc::mem_fun (*this, &AudioRegionEditor::refill_region_line)));
|
||||
@ -127,6 +133,7 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv)
|
||||
pthread_create_and_store (name, &_peak_amplitude_thread_handle, _peak_amplitude_thread, this);
|
||||
signal_peak_thread ();
|
||||
|
||||
|
||||
}
|
||||
|
||||
AudioRegionEditor::~AudioRegionEditor ()
|
||||
@ -145,6 +152,10 @@ AudioRegionEditor::region_changed (const PBD::PropertyChange& what_changed)
|
||||
gain_changed ();
|
||||
}
|
||||
|
||||
if (what_changed.contains (ARDOUR::Properties::fade_before_fx)) {
|
||||
pre_fade_fx_changed ();
|
||||
}
|
||||
|
||||
if (what_changed.contains (ARDOUR::Properties::start) || what_changed.contains (ARDOUR::Properties::length)) {
|
||||
/* ask the peak thread to run again */
|
||||
signal_peak_thread ();
|
||||
@ -182,6 +193,18 @@ AudioRegionEditor::gain_adjustment_changed ()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegionEditor::pre_fade_fx_changed ()
|
||||
{
|
||||
_pre_fade_fx_toggle.set_active (_audio_region->fade_before_fx ());
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegionEditor::pre_fade_fx_toggle_changed ()
|
||||
{
|
||||
_audio_region->set_fade_before_fx (_pre_fade_fx_toggle.get_active ());
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegionEditor::signal_peak_thread ()
|
||||
{
|
||||
@ -281,6 +304,10 @@ AudioRegionEditor::refill_region_line ()
|
||||
}
|
||||
std::shared_ptr<Plugin> plugin = fx->plugin ();
|
||||
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gtk::Menu* acm = manage (new Gtk::Menu);
|
||||
MenuList& acm_items (acm->items ());
|
||||
|
||||
|
@ -74,6 +74,9 @@ private:
|
||||
void show_on_touch_changed ();
|
||||
void show_touched_automation (std::weak_ptr<PBD::Controllable>);
|
||||
|
||||
void pre_fade_fx_changed ();
|
||||
void pre_fade_fx_toggle_changed ();
|
||||
|
||||
AudioRegionView* _arv;
|
||||
std::shared_ptr<ARDOUR::AudioRegion> _audio_region;
|
||||
|
||||
@ -84,6 +87,8 @@ private:
|
||||
Gtk::Label _polarity_label;
|
||||
Gtk::CheckButton _polarity_toggle;
|
||||
|
||||
Gtk::CheckButton _pre_fade_fx_toggle;
|
||||
|
||||
Gtk::Label _peak_amplitude_label;
|
||||
Gtk::Entry _peak_amplitude;
|
||||
|
||||
|
@ -1568,7 +1568,7 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, b
|
||||
|
||||
trackview.editor().begin_reversible_command (_("add gain control point"));
|
||||
|
||||
_fx_line->enable_autoation ();
|
||||
_fx_line->enable_automation ();
|
||||
|
||||
trackview.session()->add_command (new MementoCommand<AutomationList>(*_fx_line->the_list(), &before, &after));
|
||||
|
||||
|
@ -38,8 +38,6 @@
|
||||
#include <climits>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
|
||||
#include "pbd/floating.h"
|
||||
#include "pbd/memento_command.h"
|
||||
#include "pbd/stl_delete.h"
|
||||
|
@ -1042,6 +1042,13 @@ AutomationTimeAxisView::color_handler ()
|
||||
if (_line) {
|
||||
_line->set_colors();
|
||||
}
|
||||
|
||||
if (_base_rect) {
|
||||
const std::string fill_color_name = (dynamic_cast<MidiTimeAxisView*>(get_parent())
|
||||
? "midi automation track fill"
|
||||
: "audio automation track fill");
|
||||
_base_rect->set_fill_color (UIConfiguration::instance().color_mod (fill_color_name, "automation track fill"));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#include <boost/integer/common_factor.hpp>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
|
@ -110,24 +110,30 @@ fixup_bundle_environment (int, char* [], string & localedir)
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ardour_mono_file;
|
||||
static std::string ardour_sans_file;
|
||||
|
||||
static __cdecl void
|
||||
unload_custom_fonts()
|
||||
{
|
||||
std::string font_file;
|
||||
if (find_file (ardour_data_search_path(), "ArdourMono.ttf", font_file)) {
|
||||
RemoveFontResource(font_file.c_str());
|
||||
if (!ardour_mono_file.empty ()) {
|
||||
RemoveFontResource(ardour_mono_file.c_str());
|
||||
}
|
||||
if (find_file (ardour_data_search_path(), "ArdourSans.ttf", font_file)) {
|
||||
RemoveFontResource(font_file.c_str());
|
||||
if (!ardour_sans_file.empty ()) {
|
||||
RemoveFontResource(ardour_sans_file.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static LONG WINAPI
|
||||
unload_font_at_exception (PEXCEPTION_POINTERS pExceptionInfo)
|
||||
{
|
||||
unload_custom_fonts ();
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void
|
||||
load_custom_fonts()
|
||||
{
|
||||
std::string ardour_mono_file;
|
||||
std::string ardour_sans_file;
|
||||
|
||||
if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
|
||||
cerr << _("Cannot find ArdourMono TrueType font") << endl;
|
||||
}
|
||||
@ -144,10 +150,12 @@ load_custom_fonts()
|
||||
FcConfig *config = FcInitLoadConfigAndFonts();
|
||||
|
||||
if (!ardour_mono_file.empty () && FcFalse == FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()))) {
|
||||
ardour_mono_file.clear ();
|
||||
cerr << _("Cannot load ArdourMono TrueType font.") << endl;
|
||||
}
|
||||
|
||||
if (!ardour_sans_file.empty () && FcFalse == FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_sans_file.c_str()))) {
|
||||
ardour_sans_file.clear ();
|
||||
cerr << _("Cannot load ArdourSans TrueType font.") << endl;
|
||||
}
|
||||
|
||||
@ -157,11 +165,14 @@ load_custom_fonts()
|
||||
} else {
|
||||
// pango with win32 backend
|
||||
if (0 == AddFontResource(ardour_mono_file.c_str())) {
|
||||
ardour_mono_file.clear ();
|
||||
cerr << _("Cannot register ArdourMono TrueType font with windows gdi.") << endl;
|
||||
}
|
||||
if (0 == AddFontResource(ardour_sans_file.c_str())) {
|
||||
ardour_sans_file.clear ();
|
||||
cerr << _("Cannot register ArdourSans TrueType font with windows gdi.") << endl;
|
||||
}
|
||||
atexit (&unload_custom_fonts);
|
||||
SetUnhandledExceptionFilter (unload_font_at_exception);
|
||||
}
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ Editor::Editor ()
|
||||
, ignore_gui_changes (false)
|
||||
, _drags (new DragManager (this))
|
||||
, lock_dialog (0)
|
||||
/* , last_event_time { 0, 0 } */ /* this initialization style requires C++11 */
|
||||
, last_event_time { 0, 0 }
|
||||
, _dragging_playhead (false)
|
||||
, ignore_map_change (false)
|
||||
, _follow_playhead (true)
|
||||
@ -486,9 +486,6 @@ Editor::Editor ()
|
||||
|
||||
_have_idled = false;
|
||||
|
||||
last_event_time.tv_sec = 0;
|
||||
last_event_time.tv_usec = 0;
|
||||
|
||||
selection_op_history.clear();
|
||||
before.clear();
|
||||
|
||||
|
@ -1847,8 +1847,12 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
||||
switch (item_type) {
|
||||
case RegionItem:
|
||||
{
|
||||
/* since we have FreehandLineDrag we can only get here after a drag, when no movement has happend */
|
||||
assert (were_dragging);
|
||||
/* since we have FreehandLineDrag we can only get here after a drag, when no movement has happend.
|
||||
* Except when a drag was aborted by pressing Esc.
|
||||
*/
|
||||
if (!were_dragging) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
|
||||
AutomationRegionView* atv = dynamic_cast<AutomationRegionView*> (clicked_regionview);
|
||||
|
@ -202,6 +202,9 @@ Editor::reset_tempo_marks ()
|
||||
TempoPoint const * prev_ts = 0;
|
||||
|
||||
for (auto & t : tempo_marks) {
|
||||
if (entered_marker == t) {
|
||||
entered_marker = 0;
|
||||
}
|
||||
delete t;
|
||||
}
|
||||
|
||||
|
@ -856,7 +856,7 @@ ExportReport::run ()
|
||||
{
|
||||
do {
|
||||
int i = ArdourDialog::run ();
|
||||
if (i == Gtk::RESPONSE_DELETE_EVENT || i == RESPONSE_CLOSE) {
|
||||
if (i == Gtk::RESPONSE_DELETE_EVENT || i == RESPONSE_CLOSE || i == RESPONSE_CANCEL) {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
@ -840,7 +840,7 @@ GenericPluginUI::automation_state_changed (ControlUI* cui)
|
||||
GenericPluginUI::ControlUI*
|
||||
GenericPluginUI::build_control_ui (const Evoral::Parameter& param,
|
||||
const ParameterDescriptor& desc,
|
||||
std::shared_ptr<AutomationControl> mcontrol,
|
||||
std::shared_ptr<AutomationControl> mcontrol,
|
||||
float value,
|
||||
bool is_input,
|
||||
bool use_knob)
|
||||
@ -1273,9 +1273,9 @@ void
|
||||
GenericPluginUI::output_update ()
|
||||
{
|
||||
for (vector<ControlUI*>::iterator i = output_controls.begin(); i != output_controls.end(); ++i) {
|
||||
float val = plugin->get_parameter ((*i)->parameter().id());
|
||||
char buf[32];
|
||||
std::shared_ptr<ReadOnlyControl> c = _pib->control_output ((*i)->parameter().id());
|
||||
float val = c->get_parameter ();
|
||||
const std::string& str = ARDOUR::value_as_string(c->desc(), Variant(val));
|
||||
size_t len = str.copy(buf, 31);
|
||||
buf[len] = '\0';
|
||||
|
BIN
gtk2_ardour/icons/lkmk4.png
Normal file
BIN
gtk2_ardour/icons/lkmk4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
gtk2_ardour/icons/lkmk4mini.png
Normal file
BIN
gtk2_ardour/icons/lkmk4mini.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
@ -16,8 +16,6 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <gtkmm/frame.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/scrolledwindow.h>
|
||||
|
@ -58,7 +58,7 @@ static VSTState * vstfx_first = NULL;
|
||||
|
||||
const char magic[] = "VSTFX Plugin State v002";
|
||||
|
||||
static volatile int gui_quit = 0;
|
||||
static volatile int gui_state = -1;
|
||||
|
||||
/*This will be our connection to X*/
|
||||
|
||||
@ -330,7 +330,7 @@ gui_event_loop (void* ptr)
|
||||
clock1 = g_get_monotonic_time();
|
||||
/*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/
|
||||
|
||||
while (!gui_quit)
|
||||
while (gui_state == 0)
|
||||
{
|
||||
/* handle window creation requests, destroy requests,
|
||||
and run idle callbacks */
|
||||
@ -464,7 +464,7 @@ again:
|
||||
clock1 = g_get_monotonic_time();
|
||||
}
|
||||
|
||||
if (!gui_quit && may_sleep && elapsed_time_ms + 1 < LXVST_sched_timer_interval) {
|
||||
if (0 == gui_state && may_sleep && elapsed_time_ms + 1 < LXVST_sched_timer_interval) {
|
||||
Glib::usleep (1000 * (LXVST_sched_timer_interval - elapsed_time_ms - 1));
|
||||
}
|
||||
}
|
||||
@ -506,7 +506,7 @@ normally started in globals.cc*/
|
||||
|
||||
int vstfx_init (void* ptr)
|
||||
{
|
||||
assert (gui_quit == 0);
|
||||
assert (gui_state == -1);
|
||||
pthread_mutex_init (&plugin_mutex, NULL);
|
||||
|
||||
int thread_create_result;
|
||||
@ -542,6 +542,7 @@ int vstfx_init (void* ptr)
|
||||
}
|
||||
|
||||
/*We have a connection to X - so start the gui event loop*/
|
||||
gui_state = 0;
|
||||
|
||||
/*Create the thread - use default attrs for now, don't think we need anything special*/
|
||||
|
||||
@ -555,7 +556,7 @@ int vstfx_init (void* ptr)
|
||||
|
||||
XCloseDisplay (LXVST_XDisplay);
|
||||
LXVST_XDisplay = 0;
|
||||
gui_quit = 1;
|
||||
gui_state = 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -567,10 +568,10 @@ int vstfx_init (void* ptr)
|
||||
|
||||
void vstfx_exit ()
|
||||
{
|
||||
if (gui_quit) {
|
||||
if (gui_state) {
|
||||
return;
|
||||
}
|
||||
gui_quit = 1;
|
||||
gui_state = 1;
|
||||
|
||||
/*We need to pthread_join the gui_thread here so
|
||||
we know when it has stopped*/
|
||||
@ -782,7 +783,7 @@ vstfx_launch_editor (VSTState* vstfx)
|
||||
void
|
||||
vstfx_destroy_editor (VSTState* vstfx)
|
||||
{
|
||||
assert (!gui_quit);
|
||||
assert (0 == gui_state);
|
||||
pthread_mutex_lock (&vstfx->lock);
|
||||
if (vstfx->linux_window) {
|
||||
vstfx->destroy = TRUE;
|
||||
|
@ -70,9 +70,8 @@ LV2PluginUI::write_from_ui(void* controller,
|
||||
|
||||
std::shared_ptr<AutomationControl> ac = me->_controllables[port_index];
|
||||
|
||||
me->_updates.insert (port_index);
|
||||
|
||||
if (ac) {
|
||||
me->_updates.insert (port_index);
|
||||
ac->set_value(*(const float*)buffer, Controllable::NoGroup);
|
||||
}
|
||||
} else if (format == URIMap::instance().urids.atom_eventTransfer) {
|
||||
@ -208,7 +207,7 @@ void
|
||||
LV2PluginUI::control_changed (uint32_t port_index)
|
||||
{
|
||||
/* Must run in GUI thread because we modify _updates with no lock */
|
||||
if (_lv2->get_parameter (port_index) != _values_last_sent_to_ui[port_index]) {
|
||||
if (_controllables[port_index]->get_value () != _values_last_sent_to_ui[port_index]) {
|
||||
/* current plugin parameter does not match last value received
|
||||
from GUI, so queue an update to push it to the GUI during
|
||||
our regular timeout.
|
||||
@ -239,10 +238,8 @@ LV2PluginUI::queue_port_update()
|
||||
{
|
||||
const uint32_t num_ports = _lv2->num_ports();
|
||||
for (uint32_t i = 0; i < num_ports; ++i) {
|
||||
bool ok;
|
||||
uint32_t port = _lv2->nth_parameter(i, ok);
|
||||
if (ok) {
|
||||
_updates.insert (port);
|
||||
if (_lv2->parameter_is_control (i) && _lv2->parameter_is_input(i)) {
|
||||
_updates.insert (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,10 +271,8 @@ LV2PluginUI::output_update()
|
||||
|
||||
/* output ports (values set by DSP) need propagating to GUI */
|
||||
|
||||
uint32_t nports = _output_ports.size();
|
||||
for (uint32_t i = 0; i < nports; ++i) {
|
||||
uint32_t index = _output_ports[i];
|
||||
float val = _lv2->get_parameter (index);
|
||||
for (auto const& index: _output_ports) {
|
||||
float val = _pib->control_output (index)->get_parameter ();
|
||||
|
||||
if (val != _values_last_sent_to_ui[index]) {
|
||||
/* Send to GUI */
|
||||
@ -288,14 +283,14 @@ LV2PluginUI::output_update()
|
||||
}
|
||||
|
||||
/* Input ports marked for update because the control value changed
|
||||
since the last redisplay.
|
||||
*/
|
||||
* since the last redisplay.
|
||||
*/
|
||||
|
||||
for (Updates::iterator i = _updates.begin(); i != _updates.end(); ++i) {
|
||||
float val = _lv2->get_parameter (*i);
|
||||
for (auto const& i : _updates) {
|
||||
float val = _controllables[i]->get_value ();
|
||||
/* push current value to the GUI */
|
||||
suil_instance_port_event ((SuilInstance*)_inst, (*i), 4, 0, &val);
|
||||
_values_last_sent_to_ui[(*i)] = val;
|
||||
suil_instance_port_event ((SuilInstance*)_inst, i, 4, 0, &val);
|
||||
_values_last_sent_to_ui[i] = val;
|
||||
}
|
||||
|
||||
_updates.clear ();
|
||||
@ -436,15 +431,6 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
|
||||
|
||||
#define GET_WIDGET(inst) suil_instance_get_widget((SuilInstance*)inst);
|
||||
|
||||
const uint32_t num_ports = _lv2->num_ports();
|
||||
for (uint32_t i = 0; i < num_ports; ++i) {
|
||||
if (_lv2->parameter_is_output(i)
|
||||
&& _lv2->parameter_is_control(i)
|
||||
&& is_update_wanted(i)) {
|
||||
_output_ports.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
_external_ui_ptr = NULL;
|
||||
if (!is_external_ui) {
|
||||
GtkWidget* c_widget = (GtkWidget*)GET_WIDGET(_inst);
|
||||
@ -465,29 +451,30 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
|
||||
_external_ui_ptr = (struct lv2_external_ui*)GET_WIDGET(_inst);
|
||||
}
|
||||
|
||||
const uint32_t num_ports = _lv2->num_ports();
|
||||
|
||||
_values_last_sent_to_ui = new float[num_ports];
|
||||
_controllables.resize(num_ports);
|
||||
|
||||
for (uint32_t i = 0; i < num_ports; ++i) {
|
||||
bool ok;
|
||||
uint32_t port = _lv2->nth_parameter(i, ok);
|
||||
if (ok) {
|
||||
/* Cache initial value of the parameter, regardless of
|
||||
whether it is input or output
|
||||
*/
|
||||
if (!_lv2->parameter_is_control (i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_values_last_sent_to_ui[port] = _lv2->get_parameter(port);
|
||||
_controllables[port] = std::dynamic_pointer_cast<ARDOUR::AutomationControl> (
|
||||
_pib->control(Evoral::Parameter(PluginAutomation, 0, port)));
|
||||
/* Cache initial value of the parameter, regardless of whether it is input or output */
|
||||
|
||||
if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) {
|
||||
if (_controllables[port]) {
|
||||
_controllables[port]->Changed.connect (control_connections, invalidator (*this), boost::bind (&LV2PluginUI::control_changed, this, port), gui_context());
|
||||
}
|
||||
}
|
||||
_values_last_sent_to_ui[i] = _lv2->get_parameter(i);
|
||||
_controllables[i] = std::dynamic_pointer_cast<ARDOUR::AutomationControl> (_pib->control(Evoral::Parameter(PluginAutomation, 0, i)));
|
||||
|
||||
if (_lv2->parameter_is_input(i)) {
|
||||
assert (_controllables[i]);
|
||||
_controllables[i]->Changed.connect (control_connections, invalidator (*this), boost::bind (&LV2PluginUI::control_changed, this, i), gui_context());
|
||||
/* queue for first update ("push") to GUI */
|
||||
_updates.insert (port);
|
||||
_updates.insert (i);
|
||||
}
|
||||
|
||||
if (_lv2->parameter_is_output(i) && is_update_wanted(i)) {
|
||||
_output_ports.push_back (i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ private:
|
||||
|
||||
std::shared_ptr<ARDOUR::PlugInsertBase> _pib;
|
||||
std::shared_ptr<ARDOUR::LV2Plugin> _lv2;
|
||||
std::vector<int> _output_ports;
|
||||
std::vector<uint32_t> _output_ports;
|
||||
sigc::connection _screen_update_connection;
|
||||
sigc::connection _message_update_connection;
|
||||
Gtk::Widget* _gui_widget;
|
||||
|
@ -24,7 +24,6 @@
|
||||
#define __ardour_ui_midi_channel_selector_h__
|
||||
|
||||
#include <set>
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include "sigc++/trackable.h"
|
||||
|
||||
#include "gtkmm/table.h"
|
||||
|
@ -1151,8 +1151,6 @@ MixerStrip::build_route_ops_menu ()
|
||||
/* do not allow rename if the track is record-enabled */
|
||||
items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
|
||||
}
|
||||
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
if ((!_route->is_singleton () || !active)
|
||||
@ -1161,11 +1159,18 @@ MixerStrip::build_route_ops_menu ()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (active) {
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
items.push_back (CheckMenuElem (_("Active")));
|
||||
Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
i->set_active (active);
|
||||
i->set_sensitive (!_session->transport_rolling());
|
||||
i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
|
||||
}
|
||||
|
||||
/* Plugin / Processor related */
|
||||
if (active) {
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
@ -1174,18 +1179,6 @@ MixerStrip::build_route_ops_menu ()
|
||||
Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
i->set_active (_route->strict_io());
|
||||
i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
if (active && is_track()) {
|
||||
Gtk::Menu* dio_menu = new Menu;
|
||||
MenuList& dio_items = dio_menu->items();
|
||||
dio_items.push_back (MenuElem (_("Record Pre-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPreFader)));
|
||||
dio_items.push_back (MenuElem (_("Record Post-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPostFader)));
|
||||
dio_items.push_back (MenuElem (_("Custom Record+Playback Positions"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOCustom)));
|
||||
|
||||
items.push_back (MenuElem (_("Disk I/O..."), *dio_menu));
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
uint32_t plugin_insert_cnt = 0;
|
||||
@ -1194,7 +1187,28 @@ MixerStrip::build_route_ops_menu ()
|
||||
items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
|
||||
}
|
||||
|
||||
if (active) {
|
||||
items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
|
||||
denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
denormal_menu_item->set_active (_route->denormal_protection());
|
||||
}
|
||||
|
||||
/* Disk I/O */
|
||||
|
||||
if (active && is_track()) {
|
||||
items.push_back (SeparatorElem());
|
||||
Gtk::Menu* dio_menu = new Menu;
|
||||
MenuList& dio_items = dio_menu->items();
|
||||
dio_items.push_back (MenuElem (_("Record Pre-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPreFader)));
|
||||
dio_items.push_back (MenuElem (_("Record Post-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPostFader)));
|
||||
dio_items.push_back (MenuElem (_("Custom Record+Playback Positions"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOCustom)));
|
||||
items.push_back (MenuElem (_("Disk I/O..."), *dio_menu));
|
||||
}
|
||||
|
||||
/* MIDI */
|
||||
|
||||
if (active && (std::dynamic_pointer_cast<MidiTrack>(_route) || _route->the_instrument ())) {
|
||||
items.push_back (SeparatorElem());
|
||||
items.push_back (MenuElem (_("Patch Selector..."),
|
||||
sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
|
||||
}
|
||||
@ -1203,13 +1217,8 @@ MixerStrip::build_route_ops_menu ()
|
||||
// TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
|
||||
items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
|
||||
items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
|
||||
denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
denormal_menu_item->set_active (_route->denormal_protection());
|
||||
|
||||
/* note that this relies on selection being shared across editor and
|
||||
* mixer (or global to the backend, in the future), which is the only
|
||||
* sane thing for users anyway.
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "ardour/lv2_plugin.h"
|
||||
#include "ardour/plugin.h"
|
||||
#include "ardour/plugin_insert.h"
|
||||
#include "ardour/region_fx_plugin.h"
|
||||
#include "ardour/session.h"
|
||||
#include "lv2_plugin_ui.h"
|
||||
|
||||
@ -85,7 +86,7 @@ extern VST3PluginUI* create_mac_vst3_gui (std::shared_ptr<ARDOUR::PlugInsertBase
|
||||
#include "ardour_window.h"
|
||||
#include "gui_thread.h"
|
||||
#include "keyboard.h"
|
||||
#include "latency_gui.h"
|
||||
#include "timectl_gui.h"
|
||||
#include "new_plugin_preset_dialog.h"
|
||||
#include "plugin_dspload_ui.h"
|
||||
#include "plugin_eq_gui.h"
|
||||
@ -537,6 +538,8 @@ PlugUIBase::PlugUIBase (std::shared_ptr<PlugInsertBase> pib)
|
||||
, cpuload_expander (_("CPU Profile"))
|
||||
, latency_gui (0)
|
||||
, latency_dialog (0)
|
||||
, tailtime_gui (0)
|
||||
, tailtime_dialog (0)
|
||||
, eqgui (0)
|
||||
, stats_gui (0)
|
||||
, preset_gui (0)
|
||||
@ -555,6 +558,7 @@ PlugUIBase::PlugUIBase (std::shared_ptr<PlugInsertBase> pib)
|
||||
set_tooltip (_pin_management_button, _("Show Plugin Pin Management Dialog"));
|
||||
set_tooltip (_bypass_button, _("Disable signal processing by the plugin"));
|
||||
set_tooltip (_latency_button, _("Edit Plugin Delay/Latency Compensation"));
|
||||
set_tooltip (_tailtime_button, _("Edit Plugin tail time"));
|
||||
_no_load_preset = 0;
|
||||
|
||||
update_preset_list ();
|
||||
@ -565,6 +569,11 @@ PlugUIBase::PlugUIBase (std::shared_ptr<PlugInsertBase> pib)
|
||||
_latency_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::latency_button_clicked));
|
||||
set_latency_label ();
|
||||
|
||||
_tailtime_button.set_icon (ArdourIcon::TailTimeClock);
|
||||
_tailtime_button.add_elements (ArdourButton::Text);
|
||||
_tailtime_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::tailtime_button_clicked));
|
||||
set_tailtime_label ();
|
||||
|
||||
_add_button.set_name ("generic button");
|
||||
_add_button.set_icon (ArdourIcon::PsetAdd);
|
||||
_add_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting));
|
||||
@ -636,6 +645,11 @@ PlugUIBase::PlugUIBase (std::shared_ptr<PlugInsertBase> pib)
|
||||
_pi->LatencyChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::set_latency_label, this), gui_context ());
|
||||
automation_state_changed ();
|
||||
}
|
||||
|
||||
shared_ptr<TailTime> tt = std::dynamic_pointer_cast<ARDOUR::TailTime> (_pib);
|
||||
if (tt) {
|
||||
tt->TailTimeChanged.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::set_tailtime_label, this), gui_context ());
|
||||
}
|
||||
}
|
||||
|
||||
PlugUIBase::~PlugUIBase ()
|
||||
@ -645,6 +659,8 @@ PlugUIBase::~PlugUIBase ()
|
||||
delete preset_gui;
|
||||
delete latency_gui;
|
||||
delete latency_dialog;
|
||||
delete tailtime_gui;
|
||||
delete tailtime_dialog;
|
||||
delete preset_dialog;
|
||||
|
||||
delete _focus_out_image;
|
||||
@ -696,6 +712,9 @@ PlugUIBase::add_common_widgets (Gtk::HBox* b, bool with_focus)
|
||||
b->pack_end (_pin_management_button, false, false);
|
||||
b->pack_start (_latency_button, false, false, 4);
|
||||
}
|
||||
else if (std::dynamic_pointer_cast<ARDOUR::RegionFxPlugin> (_pib)) {
|
||||
b->pack_start (_tailtime_button, false, false, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -709,13 +728,26 @@ PlugUIBase::set_latency_label ()
|
||||
|
||||
_latency_button.set_text (samples_as_time_string (l, sr, true));
|
||||
}
|
||||
void
|
||||
|
||||
PlugUIBase::set_tailtime_label ()
|
||||
{
|
||||
auto rfx = std::dynamic_pointer_cast<ARDOUR::RegionFxPlugin> (_pib); /* may be NULL */
|
||||
if (!rfx) {
|
||||
return;
|
||||
}
|
||||
samplecnt_t const l = rfx->effective_tailtime ();
|
||||
float const sr = rfx->session ().sample_rate ();
|
||||
|
||||
_tailtime_button.set_text (samples_as_time_string (l, sr, true));
|
||||
}
|
||||
|
||||
void
|
||||
PlugUIBase::latency_button_clicked ()
|
||||
{
|
||||
assert (_pi);
|
||||
if (!latency_gui) {
|
||||
latency_gui = new LatencyGUI (*(_pi.get ()), _pi->session ().sample_rate (), _pi->session ().get_block_size ());
|
||||
latency_gui = new TimeCtlGUI (*(_pi.get ()), _pi->session ().sample_rate (), _pi->session ().get_block_size ());
|
||||
latency_dialog = new ArdourWindow (_("Edit Latency"));
|
||||
/* use both keep-above and transient for to try cover as many
|
||||
different WM's as possible.
|
||||
@ -732,6 +764,29 @@ PlugUIBase::latency_button_clicked ()
|
||||
latency_dialog->show_all ();
|
||||
}
|
||||
|
||||
void
|
||||
PlugUIBase::tailtime_button_clicked ()
|
||||
{
|
||||
auto rfx = std::dynamic_pointer_cast<ARDOUR::RegionFxPlugin> (_pib); /* may be NULL */
|
||||
assert (rfx);
|
||||
if (!tailtime_gui) {
|
||||
tailtime_gui = new TimeCtlGUI (*dynamic_cast<TailTime*>(rfx.get()), rfx->session ().sample_rate (), rfx->session ().get_block_size ());
|
||||
tailtime_dialog = new ArdourWindow (_("Edit Tail Time"));
|
||||
/* use both keep-above and transient for to try cover as many
|
||||
different WM's as possible.
|
||||
*/
|
||||
tailtime_dialog->set_keep_above (true);
|
||||
Window* win = dynamic_cast<Window*> (_bypass_button.get_toplevel ());
|
||||
if (win) {
|
||||
tailtime_dialog->set_transient_for (*win);
|
||||
}
|
||||
tailtime_dialog->add (*tailtime_gui);
|
||||
}
|
||||
|
||||
tailtime_gui->refresh ();
|
||||
tailtime_dialog->show_all ();
|
||||
}
|
||||
|
||||
void
|
||||
PlugUIBase::processor_active_changed (std::weak_ptr<Processor> weak_p)
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace ArdourWidgets {
|
||||
class FastMeter;
|
||||
}
|
||||
|
||||
class LatencyGUI;
|
||||
class TimeCtlGUI;
|
||||
class ArdourWindow;
|
||||
class PluginEqGui;
|
||||
class PluginLoadStatsGui;
|
||||
@ -110,6 +110,7 @@ public:
|
||||
void update_preset ();
|
||||
|
||||
void latency_button_clicked ();
|
||||
void tailtime_button_clicked ();
|
||||
|
||||
virtual bool on_window_show(const std::string& /*title*/) { return true; }
|
||||
virtual void on_window_hide() {}
|
||||
@ -157,6 +158,8 @@ protected:
|
||||
Gtk::Expander cpuload_expander;
|
||||
/** a button which, when clicked, opens the latency GUI */
|
||||
ArdourWidgets::ArdourButton _latency_button;
|
||||
/** a button which, when clicked, opens the tailtime GUI */
|
||||
ArdourWidgets::ArdourButton _tailtime_button;
|
||||
/** a button which sets all controls' automation setting to Manual */
|
||||
ArdourWidgets::ArdourButton automation_manual_all_button;
|
||||
/** a button which sets all controls' automation setting to Play */
|
||||
@ -169,9 +172,13 @@ protected:
|
||||
ArdourWidgets::ArdourButton automation_latch_all_button;
|
||||
|
||||
void set_latency_label ();
|
||||
LatencyGUI* latency_gui;
|
||||
TimeCtlGUI* latency_gui;
|
||||
ArdourWindow* latency_dialog;
|
||||
|
||||
void set_tailtime_label ();
|
||||
TimeCtlGUI* tailtime_gui;
|
||||
ArdourWindow* tailtime_dialog;
|
||||
|
||||
PluginEqGui* eqgui;
|
||||
PluginLoadStatsGui* stats_gui;
|
||||
PluginPresetsUI* preset_gui;
|
||||
|
1723
gtk2_ardour/po/ru.po
1723
gtk2_ardour/po/ru.po
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,7 @@
|
||||
|
||||
#include "context_menu_helper.h"
|
||||
#include "gui_thread.h"
|
||||
#include "latency_gui.h"
|
||||
#include "timectl_gui.h"
|
||||
#include "port_insert_ui.h"
|
||||
#include "timers.h"
|
||||
#include "utils.h"
|
||||
@ -282,7 +282,7 @@ PortInsertUI::edit_latency_button_clicked ()
|
||||
{
|
||||
assert (_pi);
|
||||
if (!_latency_gui) {
|
||||
_latency_gui = new LatencyGUI (*(_pi.get ()), _pi->session ().sample_rate (), _pi->session ().get_block_size ());
|
||||
_latency_gui = new TimeCtlGUI (*(_pi.get ()), _pi->session ().sample_rate (), _pi->session ().get_block_size ());
|
||||
_latency_dialog = new ArdourWindow (_("Edit Latency"));
|
||||
/* use both keep-above and transient for to try cover as many
|
||||
different WM's as possible.
|
||||
|
@ -31,7 +31,7 @@ namespace ARDOUR
|
||||
class PortInsert;
|
||||
}
|
||||
|
||||
class LatencyGUI;
|
||||
class TimeCtlGUI;
|
||||
class MTDM;
|
||||
|
||||
class PortInsertUI : public Gtk::VBox
|
||||
@ -78,7 +78,7 @@ private:
|
||||
Gtk::HBox _latency_hbox;
|
||||
|
||||
Gtk::Window* _parent;
|
||||
LatencyGUI* _latency_gui;
|
||||
TimeCtlGUI* _latency_gui;
|
||||
ArdourWindow* _latency_dialog;
|
||||
|
||||
sigc::connection _latency_timeout;
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "new_plugin_preset_dialog.h"
|
||||
#include "region_editor.h"
|
||||
#include "region_view.h"
|
||||
#include "timers.h"
|
||||
#include "plugin_selector.h"
|
||||
#include "plugin_window_proxy.h"
|
||||
#include "public_editor.h"
|
||||
@ -188,7 +189,7 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv)
|
||||
_table.attach (_sources, 1, 2, _table_row, _table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
|
||||
++_table_row;
|
||||
|
||||
#ifndef MIXBUS // no region FX
|
||||
#ifndef LIVETRAX // no region FX
|
||||
_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);
|
||||
#endif
|
||||
@ -233,7 +234,7 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv)
|
||||
|
||||
spin_arrow_grab = false;
|
||||
|
||||
#if 0
|
||||
#ifndef LIVETRAX // no region FX
|
||||
/* for now only audio region effects are supported */
|
||||
if (std::dynamic_pointer_cast<AudioRegion> (_region)) {
|
||||
region_fx_label.show ();
|
||||
@ -558,6 +559,8 @@ RegionEditor::RegionFxBox::RegionFxBox (std::shared_ptr<ARDOUR::Region> r)
|
||||
|
||||
_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &RegionFxBox::on_key_press), false);
|
||||
|
||||
screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun (*this, &RegionFxBox::update_controls));
|
||||
|
||||
_scroller.show ();
|
||||
_display.show ();
|
||||
|
||||
@ -604,7 +607,8 @@ RegionEditor::RegionFxBox::add_fx_to_display (std::weak_ptr<RegionFxPlugin> wfx)
|
||||
if (!fx) {
|
||||
return;
|
||||
}
|
||||
RegionFxEntry* e = new RegionFxEntry (fx);
|
||||
std::shared_ptr<AudioRegion> ar = std::dynamic_pointer_cast<AudioRegion> (_region);
|
||||
RegionFxEntry* e = new RegionFxEntry (fx, ar && ar->fade_before_fx ());
|
||||
_display.add_child (e, drag_targets ());
|
||||
}
|
||||
|
||||
@ -638,50 +642,53 @@ RegionEditor::RegionFxBox::fxe_button_press_event (GdkEventButton* ev, RegionFxE
|
||||
|
||||
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 ()) {
|
||||
if (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));
|
||||
items.push_back (MenuElem (_("Clear All Automation"), sigc::bind (sigc::mem_fun (*this, &RegionFxBox::clear_automation), wfx)));
|
||||
} else {
|
||||
delete automation_menu;
|
||||
}
|
||||
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 (); });
|
||||
@ -749,6 +756,60 @@ RegionEditor::RegionFxBox::on_key_press (GdkEventKey* ev)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RegionEditor::RegionFxBox::update_controls ()
|
||||
{
|
||||
for (auto const& i : _display.children ()) {
|
||||
std::shared_ptr<ARDOUR::RegionFxPlugin> rfx = i->region_fx_plugin ();
|
||||
PluginWindowProxy* pwp = dynamic_cast<PluginWindowProxy*> (rfx->window_proxy ());
|
||||
if (!pwp || !pwp->get (false) || !pwp->get (false)->is_mapped ()) {
|
||||
continue;
|
||||
}
|
||||
rfx->maybe_emit_changed_signals ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RegionEditor::RegionFxBox::clear_automation (std::weak_ptr<ARDOUR::RegionFxPlugin> wfx)
|
||||
{
|
||||
std::shared_ptr<RegionFxPlugin> fx (wfx.lock ());
|
||||
if (!fx) {
|
||||
return;
|
||||
}
|
||||
bool in_command = false;
|
||||
|
||||
timepos_t tas ((samplepos_t)_region->length().samples());
|
||||
|
||||
for (auto const& c : fx->controls ()) {
|
||||
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl> (c.second);
|
||||
if (!ac) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<ARDOUR::AutomationList> alist = ac->alist ();
|
||||
if (!alist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
XMLNode& before (alist->get_state());
|
||||
|
||||
alist->freeze ();
|
||||
alist->clear ();
|
||||
fx->set_default_automation (tas);
|
||||
alist->thaw ();
|
||||
alist->set_automation_state (ARDOUR::Off);
|
||||
|
||||
if (!in_command) {
|
||||
_region->session ().begin_reversible_command (_("Clear region fx automation"));
|
||||
in_command = true;
|
||||
}
|
||||
_region->session ().add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
|
||||
}
|
||||
|
||||
if (in_command) {
|
||||
_region->session ().commit_reversible_command ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RegionEditor::RegionFxBox::reordered ()
|
||||
{
|
||||
@ -909,7 +970,7 @@ void
|
||||
RegionEditor::RegionFxBox::show_plugin_gui (std::weak_ptr<RegionFxPlugin> wfx, bool custom_ui)
|
||||
{
|
||||
std::shared_ptr<RegionFxPlugin> rfx (wfx.lock ());
|
||||
if (!rfx) {
|
||||
if (!rfx || !rfx->plugin ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -928,7 +989,7 @@ RegionEditor::RegionFxBox::show_plugin_gui (std::weak_ptr<RegionFxPlugin> wfx, b
|
||||
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 (); });
|
||||
rv->RegionViewGoingAway.connect_same_thread (*pwp, [pwp, rv] (RegionView* srv) { if (rv == srv) { pwp->hide (); }});
|
||||
}
|
||||
|
||||
pwp->set_custom_ui_mode (custom_ui);
|
||||
@ -942,20 +1003,35 @@ RegionEditor::RegionFxBox::show_plugin_gui (std::weak_ptr<RegionFxPlugin> wfx, b
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
RegionEditor::RegionFxEntry::RegionFxEntry (std::shared_ptr<RegionFxPlugin> rfx)
|
||||
RegionEditor::RegionFxEntry::RegionFxEntry (std::shared_ptr<RegionFxPlugin> rfx, bool pre)
|
||||
: _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 ()));
|
||||
if (rfx->plugin ()) {
|
||||
_plugin_preset_pointer = PluginPresetPtr (new PluginPreset (rfx->plugin ()->get_info ()));
|
||||
_selectable = true;
|
||||
} else {
|
||||
_plugin_preset_pointer = 0;
|
||||
_selectable = false;
|
||||
}
|
||||
|
||||
_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 ()) {
|
||||
if (!_selectable) {
|
||||
_fx_btn.set_name ("processor stub");
|
||||
} else if (pre) {
|
||||
_fx_btn.set_name ("processor prefader");
|
||||
} else {
|
||||
_fx_btn.set_name ("processor postfader");
|
||||
}
|
||||
|
||||
if (!rfx->plugin ()) {
|
||||
set_tooltip (_fx_btn, string_compose (_("<b>%1</b>\nThe Plugin is not available on this system\nand has been replaced by a stub."), name ()));
|
||||
} else 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 ()));
|
||||
@ -986,7 +1062,7 @@ RegionEditor::RegionFxEntry::can_copy_state (Gtkmm2ext::DnDVBoxChild* o) const
|
||||
}
|
||||
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 ();
|
||||
return my_p && ot_p && my_p->unique_id () == ot_p->unique_id ();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1008,7 +1084,9 @@ RegionEditor::RegionFxEntry::drag_data_get (Glib::RefPtr<Gdk::DragContext> const
|
||||
}
|
||||
|
||||
std::shared_ptr<Plugin> plugin = _rfx->plugin ();
|
||||
assert (plugin);
|
||||
if (!plugin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginManager& manager (PluginManager::instance ());
|
||||
bool fav = manager.get_status (_plugin_preset_pointer->_pip) == PluginManager::Favorite;
|
||||
|
@ -75,12 +75,12 @@ private:
|
||||
class RegionFxEntry : public Gtkmm2ext::DnDVBoxChild, public sigc::trackable
|
||||
{
|
||||
public:
|
||||
RegionFxEntry (std::shared_ptr<ARDOUR::RegionFxPlugin>);
|
||||
RegionFxEntry (std::shared_ptr<ARDOUR::RegionFxPlugin>, bool pre);
|
||||
|
||||
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 is_selectable() const { return _selectable; }
|
||||
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 &);
|
||||
@ -93,6 +93,7 @@ private:
|
||||
ArdourWidgets::ArdourButton _fx_btn;
|
||||
std::shared_ptr<ARDOUR::RegionFxPlugin> _rfx;
|
||||
ARDOUR::PluginPresetPtr _plugin_preset_pointer;
|
||||
bool _selectable;
|
||||
};
|
||||
|
||||
class RegionFxBox : public Gtk::VBox, public PluginInterestedObject //, public ARDOUR::SessionHandlePtr
|
||||
@ -108,6 +109,8 @@ private:
|
||||
bool idle_delete_region_fx (std::weak_ptr<ARDOUR::RegionFxPlugin>);
|
||||
void notify_plugin_load_fail (uint32_t cnt = 1);
|
||||
bool on_key_press (GdkEventKey*);
|
||||
void clear_automation (std::weak_ptr<ARDOUR::RegionFxPlugin>);
|
||||
void update_controls ();
|
||||
|
||||
/* PluginInterestedObject */
|
||||
bool use_plugins (SelectedPlugins const&);
|
||||
@ -129,6 +132,8 @@ private:
|
||||
Gtk::EventBox _base;
|
||||
bool _no_redisplay;
|
||||
int _placement;
|
||||
|
||||
sigc::connection screen_update_connection;
|
||||
};
|
||||
|
||||
std::shared_ptr<ARDOUR::Region> _region;
|
||||
|
@ -30,6 +30,7 @@ RegionFxLine::RegionFxLine (std::string const& name, RegionView& r, ArdourCanvas
|
||||
: AutomationLine (name, r.get_time_axis_view(), parent, l, d)
|
||||
, _rv (r)
|
||||
{
|
||||
terminal_points_can_slide = false;
|
||||
init ();
|
||||
}
|
||||
|
||||
@ -38,6 +39,7 @@ RegionFxLine::RegionFxLine (std::string const& name, RegionView& r, ArdourCanvas
|
||||
, _rv (r)
|
||||
, _ac (ac)
|
||||
{
|
||||
terminal_points_can_slide = false;
|
||||
init ();
|
||||
}
|
||||
|
||||
@ -56,7 +58,7 @@ RegionFxLine::get_origin() const
|
||||
}
|
||||
|
||||
void
|
||||
RegionFxLine::enable_autoation ()
|
||||
RegionFxLine::enable_automation ()
|
||||
{
|
||||
std::shared_ptr<AutomationControl> ac = _ac.lock ();
|
||||
if (ac) {
|
||||
@ -67,14 +69,14 @@ RegionFxLine::enable_autoation ()
|
||||
void
|
||||
RegionFxLine::end_drag (bool with_push, uint32_t final_index)
|
||||
{
|
||||
enable_autoation ();
|
||||
enable_automation ();
|
||||
AutomationLine::end_drag (with_push, final_index);
|
||||
}
|
||||
|
||||
void
|
||||
RegionFxLine::end_draw_merge ()
|
||||
{
|
||||
enable_autoation ();
|
||||
enable_automation ();
|
||||
AutomationLine::end_draw_merge ();
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
void end_drag (bool with_push, uint32_t final_index);
|
||||
void end_draw_merge ();
|
||||
|
||||
virtual void enable_autoation ();
|
||||
virtual void enable_automation ();
|
||||
|
||||
private:
|
||||
void init ();
|
||||
|
@ -45,8 +45,6 @@ AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView&
|
||||
: RegionFxLine (name, r, parent, l, l->parameter ())
|
||||
, arv (r)
|
||||
{
|
||||
|
||||
terminal_points_can_slide = false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -119,12 +117,12 @@ AudioRegionGainLine::end_drag (bool with_push, uint32_t final_index)
|
||||
void
|
||||
AudioRegionGainLine::end_draw_merge ()
|
||||
{
|
||||
enable_autoation ();
|
||||
enable_automation ();
|
||||
RegionFxLine::end_draw_merge ();
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegionGainLine::enable_autoation ()
|
||||
AudioRegionGainLine::enable_automation ()
|
||||
{
|
||||
if (!arv.audio_region()->envelope_active()) {
|
||||
XMLNode& before = arv.audio_region()->get_state();
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
void start_drag_multiple (std::list<ControlPoint*>, float, XMLNode*);
|
||||
void end_drag (bool with_push, uint32_t final_index);
|
||||
void end_draw_merge ();
|
||||
void enable_autoation ();
|
||||
void enable_automation ();
|
||||
void remove_point (ControlPoint&);
|
||||
|
||||
private:
|
||||
|
@ -346,6 +346,7 @@ RouteTimeAxisView::set_route (std::shared_ptr<Route> rt)
|
||||
plist->add (ARDOUR::Properties::group_mute, true);
|
||||
plist->add (ARDOUR::Properties::group_solo, true);
|
||||
|
||||
delete route_group_menu;
|
||||
route_group_menu = new RouteGroupMenu (_session, plist);
|
||||
|
||||
gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
|
||||
@ -426,6 +427,7 @@ RouteTimeAxisView::route_group_click (GdkEventButton *ev)
|
||||
WeakRouteList r;
|
||||
r.push_back (route ());
|
||||
|
||||
route_group_menu->detach ();
|
||||
route_group_menu->build (r);
|
||||
if (ev->button == 1) {
|
||||
Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
|
||||
@ -650,7 +652,7 @@ RouteTimeAxisView::build_display_menu ()
|
||||
|
||||
TimeAxisView::build_display_menu ();
|
||||
|
||||
/* now fill it with our stuff */
|
||||
bool active = _route->active ();
|
||||
|
||||
MenuList& items = display_menu->items();
|
||||
|
||||
@ -680,24 +682,27 @@ RouteTimeAxisView::build_display_menu ()
|
||||
return;
|
||||
}
|
||||
|
||||
items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
|
||||
/* now fill it with our stuff */
|
||||
if (active) {
|
||||
items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
|
||||
|
||||
items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
|
||||
items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
|
||||
|
||||
items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
|
||||
items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
|
||||
|
||||
items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
|
||||
items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
|
||||
|
||||
items.push_back (SeparatorElem());
|
||||
items.push_back (SeparatorElem());
|
||||
|
||||
build_size_menu ();
|
||||
items.push_back (MenuElem (_("Height"), *_size_menu));
|
||||
items.push_back (SeparatorElem());
|
||||
build_size_menu ();
|
||||
items.push_back (MenuElem (_("Height"), *_size_menu));
|
||||
items.push_back (SeparatorElem());
|
||||
|
||||
// Hook for derived classes to add type specific stuff
|
||||
append_extra_display_menu_items ();
|
||||
/* Hook for derived classes to add type specific stuff */
|
||||
append_extra_display_menu_items ();
|
||||
}
|
||||
|
||||
if (is_track()) {
|
||||
if (active && is_track()) {
|
||||
|
||||
Menu* layers_menu = manage (new Menu);
|
||||
MenuList &layers_items = layers_menu->items();
|
||||
@ -841,32 +846,34 @@ RouteTimeAxisView::build_display_menu ()
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
route_group_menu->detach ();
|
||||
|
||||
WeakRouteList r;
|
||||
for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
|
||||
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
|
||||
if (rtv) {
|
||||
r.push_back (rtv->route ());
|
||||
if (active) {
|
||||
WeakRouteList r;
|
||||
for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
|
||||
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
|
||||
if (rtv) {
|
||||
r.push_back (rtv->route ());
|
||||
}
|
||||
}
|
||||
|
||||
if (r.empty ()) {
|
||||
r.push_back (route ());
|
||||
}
|
||||
|
||||
if (!_route->is_singleton ()) {
|
||||
route_group_menu->detach ();
|
||||
route_group_menu->build (r);
|
||||
items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
|
||||
}
|
||||
|
||||
build_automation_action_menu (true);
|
||||
items.push_back (MenuElem (_("Automation"), *automation_action_menu));
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
if (r.empty ()) {
|
||||
r.push_back (route ());
|
||||
}
|
||||
|
||||
if (!_route->is_singleton ()) {
|
||||
route_group_menu->build (r);
|
||||
items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
|
||||
}
|
||||
|
||||
build_automation_action_menu (true);
|
||||
items.push_back (MenuElem (_("Automation"), *automation_action_menu));
|
||||
|
||||
items.push_back (SeparatorElem());
|
||||
|
||||
int active = 0;
|
||||
int inactive = 0;
|
||||
int n_active = 0;
|
||||
int n_inactive = 0;
|
||||
bool always_active = false;
|
||||
TrackSelection const & s = _editor.get_selection().tracks;
|
||||
for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
|
||||
@ -879,9 +886,9 @@ RouteTimeAxisView::build_display_menu ()
|
||||
always_active |= r->route()->mixbus() != 0;
|
||||
#endif
|
||||
if (r->route()->active()) {
|
||||
++active;
|
||||
++n_active;
|
||||
} else {
|
||||
++inactive;
|
||||
++n_inactive;
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,19 +929,19 @@ RouteTimeAxisView::build_display_menu ()
|
||||
items.push_back (CheckMenuElem (_("Active")));
|
||||
i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
bool click_sets_active = true;
|
||||
if (active > 0 && inactive == 0) {
|
||||
if (n_active > 0 && n_inactive == 0) {
|
||||
i->set_active (true);
|
||||
click_sets_active = false;
|
||||
} else if (active > 0 && inactive > 0) {
|
||||
} else if (n_active > 0 && n_inactive > 0) {
|
||||
i->set_inconsistent (true);
|
||||
}
|
||||
i->set_sensitive(! _session->transport_rolling() && ! always_active);
|
||||
i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
|
||||
i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, !_editor.get_selection().tracks.empty ()));
|
||||
|
||||
items.push_back (SeparatorElem());
|
||||
items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
|
||||
items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, !_editor.get_selection().tracks.empty ())));
|
||||
|
||||
if (_route && !_route->is_singleton ()) {
|
||||
if (active && _route && !_route->is_singleton ()) {
|
||||
items.push_back (SeparatorElem());
|
||||
items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
|
||||
|
||||
|
@ -29,10 +29,12 @@
|
||||
#include "pbd/unwind.h"
|
||||
|
||||
#include "ardour/latent.h"
|
||||
#include "ardour/rc_configuration.h"
|
||||
#include "ardour/tailtime.h"
|
||||
|
||||
#include "gtkmm2ext/utils.h"
|
||||
|
||||
#include "latency_gui.h"
|
||||
#include "timectl_gui.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
@ -50,46 +52,66 @@ static const gchar *_unit_strings[] = {
|
||||
0
|
||||
};
|
||||
|
||||
std::vector<std::string> LatencyGUI::unit_strings;
|
||||
std::vector<std::string> TimeCtlGUI::unit_strings;
|
||||
|
||||
std::string
|
||||
LatencyBarController::get_label (double&)
|
||||
TimeCtlBarController::get_label (double&)
|
||||
{
|
||||
return ARDOUR_UI_UTILS::samples_as_time_string (
|
||||
_latency_gui->adjustment.get_value(), _latency_gui->sample_rate, true);
|
||||
_timectl_gui->adjustment.get_value(), _timectl_gui->sample_rate, true);
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUIControllable::set_value (double v, PBD::Controllable::GroupControlDisposition group_override)
|
||||
TimeCtlGUIControllable::set_value (double v, PBD::Controllable::GroupControlDisposition group_override)
|
||||
{
|
||||
_latency_gui->adjustment.set_value (v);
|
||||
_timectl_gui->adjustment.set_value (v);
|
||||
}
|
||||
|
||||
double
|
||||
LatencyGUIControllable::get_value () const
|
||||
TimeCtlGUIControllable::get_value () const
|
||||
{
|
||||
return _latency_gui->adjustment.get_value ();
|
||||
return _timectl_gui->adjustment.get_value ();
|
||||
}
|
||||
double
|
||||
LatencyGUIControllable::lower() const
|
||||
TimeCtlGUIControllable::lower() const
|
||||
{
|
||||
return _latency_gui->adjustment.get_lower ();
|
||||
return _timectl_gui->adjustment.get_lower ();
|
||||
}
|
||||
|
||||
double
|
||||
LatencyGUIControllable::upper() const
|
||||
TimeCtlGUIControllable::upper() const
|
||||
{
|
||||
return _latency_gui->adjustment.get_upper ();
|
||||
return _timectl_gui->adjustment.get_upper ();
|
||||
}
|
||||
|
||||
LatencyGUI::LatencyGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
||||
: _latent (l)
|
||||
TimeCtlGUI::TimeCtlGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
||||
: _latent (&l)
|
||||
, _tailtime (0)
|
||||
, sample_rate (sr)
|
||||
, period_size (psz)
|
||||
, _ignore_change (false)
|
||||
, adjustment (0, 0.0, sample_rate, 1.0, sample_rate / 1000.0f) /* max 1 second, step by samples, page by msecs */
|
||||
, bc (adjustment, this)
|
||||
, reset_button (_("Reset"))
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
TimeCtlGUI::TimeCtlGUI (TailTime& t, samplepos_t sr, samplepos_t psz)
|
||||
: _latent (0)
|
||||
, _tailtime (&t)
|
||||
, sample_rate (sr)
|
||||
, period_size (psz)
|
||||
, _ignore_change (false)
|
||||
, adjustment (0, 0.0, 20 * sample_rate, sample_rate / 1000.f, 1.0, sample_rate / 2.0f) /* max 20 second, step by msec, page by 0.5 sec */
|
||||
, bc (adjustment, this)
|
||||
, reset_button (_("Reset"))
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
void
|
||||
TimeCtlGUI::init ()
|
||||
{
|
||||
Widget* w;
|
||||
|
||||
@ -116,17 +138,21 @@ LatencyGUI::LatencyGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
||||
hbox2.pack_start (plus_button);
|
||||
hbox2.pack_start (units_combo, true, true);
|
||||
|
||||
minus_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &LatencyGUI::change_latency_from_button), -1));
|
||||
plus_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &LatencyGUI::change_latency_from_button), 1));
|
||||
reset_button.signal_clicked().connect (sigc::mem_fun (*this, &LatencyGUI::reset));
|
||||
minus_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &TimeCtlGUI::change_from_button), -1));
|
||||
plus_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &TimeCtlGUI::change_from_button), 1));
|
||||
reset_button.signal_clicked().connect (sigc::mem_fun (*this, &TimeCtlGUI::reset));
|
||||
|
||||
/* Limit value to adjustment range (max = sample_rate).
|
||||
* Otherwise if the signal_latency() is larger than the adjustment's max,
|
||||
* LatencyGUI::finish() would set the adjustment's max value as custom-latency.
|
||||
* TimeCtlGUI::finish() would set the adjustment's max value as custom-latency.
|
||||
*/
|
||||
adjustment.set_value (std::min (sample_rate, _latent.signal_latency ()));
|
||||
if (_latent) {
|
||||
adjustment.set_value (std::min (sample_rate, _latent->signal_latency ()));
|
||||
} else if (_tailtime) {
|
||||
adjustment.set_value (std::min (sample_rate, _tailtime->signal_tailtime ()));
|
||||
}
|
||||
|
||||
adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &LatencyGUI::finish));
|
||||
adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &TimeCtlGUI::finish));
|
||||
|
||||
bc.set_size_request (-1, 25);
|
||||
bc.set_name (X_("ProcessorControlSlider"));
|
||||
@ -137,32 +163,46 @@ LatencyGUI::LatencyGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUI::finish ()
|
||||
TimeCtlGUI::finish ()
|
||||
{
|
||||
if (_ignore_change) {
|
||||
return;
|
||||
}
|
||||
samplepos_t new_value = (samplepos_t) adjustment.get_value();
|
||||
_latent.set_user_latency (new_value);
|
||||
if (_latent) {
|
||||
_latent->set_user_latency (new_value);
|
||||
} else if (_tailtime) {
|
||||
_tailtime->set_user_tailtime (new_value);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUI::reset ()
|
||||
TimeCtlGUI::reset ()
|
||||
{
|
||||
_latent.unset_user_latency ();
|
||||
PBD::Unwinder<bool> uw (_ignore_change, true);
|
||||
adjustment.set_value (std::min (sample_rate, _latent.signal_latency ()));
|
||||
if (_latent) {
|
||||
_latent->unset_user_latency ();
|
||||
PBD::Unwinder<bool> uw (_ignore_change, true);
|
||||
adjustment.set_value (std::min (sample_rate, _latent->signal_latency ()));
|
||||
} else if (_tailtime) {
|
||||
_tailtime->unset_user_tailtime ();
|
||||
PBD::Unwinder<bool> uw (_ignore_change, true);
|
||||
adjustment.set_value (std::min<samplecnt_t> (Config->get_max_tail_samples (), _tailtime->signal_tailtime ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUI::refresh ()
|
||||
TimeCtlGUI::refresh ()
|
||||
{
|
||||
PBD::Unwinder<bool> uw (_ignore_change, true);
|
||||
adjustment.set_value (std::min (sample_rate, _latent.effective_latency ()));
|
||||
if (_latent) {
|
||||
adjustment.set_value (std::min (sample_rate, _latent->effective_latency ()));
|
||||
} else if (_tailtime) {
|
||||
adjustment.set_value (std::min<samplecnt_t> (Config->get_max_tail_samples (),_tailtime->effective_tailtime ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUI::change_latency_from_button (int dir)
|
||||
TimeCtlGUI::change_from_button (int dir)
|
||||
{
|
||||
std::string unitstr = units_combo.get_active_text();
|
||||
double shift = 0.0;
|
@ -2,7 +2,7 @@
|
||||
* Copyright (C) 2007-2017 Paul Davis <paul@linuxaudiosystems.com>
|
||||
* Copyright (C) 2009 Carl Hetherington <carl@carlh.net>
|
||||
* Copyright (C) 2009 David Robillard <d@drobilla.net>
|
||||
* Copyright (C) 2017-2019 Robin Gareus <robin@gareus.org>
|
||||
* Copyright (C) 2017-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
|
||||
@ -19,8 +19,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __gtk2_ardour_latency_gui_h__
|
||||
#define __gtk2_ardour_latency_gui_h__
|
||||
#ifndef __gtk2_ardour_timectl_gui_h__
|
||||
#define __gtk2_ardour_timectl_gui_h__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@ -38,16 +38,17 @@
|
||||
|
||||
namespace ARDOUR {
|
||||
class Latent;
|
||||
class TailTime;
|
||||
}
|
||||
|
||||
class LatencyGUI;
|
||||
class TimeCtlGUI;
|
||||
|
||||
class LatencyGUIControllable : public PBD::Controllable
|
||||
class TimeCtlGUIControllable : public PBD::Controllable
|
||||
{
|
||||
public:
|
||||
LatencyGUIControllable (LatencyGUI* g)
|
||||
TimeCtlGUIControllable (TimeCtlGUI* g)
|
||||
: PBD::Controllable ("ignoreMe")
|
||||
, _latency_gui (g)
|
||||
, _timectl_gui (g)
|
||||
{}
|
||||
|
||||
void set_value (double v, PBD::Controllable::GroupControlDisposition group_override);
|
||||
@ -62,44 +63,48 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
LatencyGUI* _latency_gui;
|
||||
TimeCtlGUI* _timectl_gui;
|
||||
};
|
||||
|
||||
class LatencyBarController : public ArdourWidgets::BarController
|
||||
class TimeCtlBarController : public ArdourWidgets::BarController
|
||||
{
|
||||
public:
|
||||
LatencyBarController (Gtk::Adjustment& adj, LatencyGUI* g)
|
||||
: BarController (adj, std::shared_ptr<PBD::Controllable> (new LatencyGUIControllable (g)))
|
||||
, _latency_gui (g)
|
||||
TimeCtlBarController (Gtk::Adjustment& adj, TimeCtlGUI* g)
|
||||
: BarController (adj, std::shared_ptr<PBD::Controllable> (new TimeCtlGUIControllable (g)))
|
||||
, _timectl_gui (g)
|
||||
{
|
||||
set_digits (0);
|
||||
}
|
||||
|
||||
private:
|
||||
LatencyGUI* _latency_gui;
|
||||
TimeCtlGUI* _timectl_gui;
|
||||
|
||||
std::string get_label (double&);
|
||||
};
|
||||
|
||||
class LatencyGUI : public Gtk::VBox
|
||||
class TimeCtlGUI : public Gtk::VBox
|
||||
{
|
||||
public:
|
||||
LatencyGUI (ARDOUR::Latent&, samplepos_t sample_rate, samplepos_t period_size);
|
||||
~LatencyGUI() { }
|
||||
TimeCtlGUI (ARDOUR::Latent&, samplepos_t sample_rate, samplepos_t period_size);
|
||||
TimeCtlGUI (ARDOUR::TailTime&, samplepos_t sample_rate, samplepos_t period_size);
|
||||
~TimeCtlGUI() { }
|
||||
|
||||
void refresh ();
|
||||
|
||||
private:
|
||||
void init ();
|
||||
void reset ();
|
||||
void finish ();
|
||||
|
||||
ARDOUR::Latent& _latent;
|
||||
ARDOUR::Latent* _latent;
|
||||
ARDOUR::TailTime* _tailtime;
|
||||
|
||||
samplepos_t sample_rate;
|
||||
samplepos_t period_size;
|
||||
|
||||
bool _ignore_change;
|
||||
Gtk::Adjustment adjustment;
|
||||
LatencyBarController bc;
|
||||
TimeCtlBarController bc;
|
||||
Gtk::HBox hbox1;
|
||||
Gtk::HBox hbox2;
|
||||
Gtk::HButtonBox hbbox;
|
||||
@ -108,12 +113,12 @@ private:
|
||||
Gtk::Button reset_button;
|
||||
Gtk::ComboBoxText units_combo;
|
||||
|
||||
void change_latency_from_button (int dir);
|
||||
void change_from_button (int dir);
|
||||
|
||||
friend class LatencyBarController;
|
||||
friend class LatencyGUIControllable;
|
||||
friend class TimeCtlBarController;
|
||||
friend class TimeCtlGUIControllable;
|
||||
|
||||
static std::vector<std::string> unit_strings;
|
||||
};
|
||||
|
||||
#endif /* __gtk2_ardour_latency_gui_h__ */
|
||||
#endif /* __gtk2_ardour_timectl_gui_h__ */
|
@ -258,8 +258,6 @@ TriggerStrip::build_route_ops_menu ()
|
||||
/* do not allow rename if the track is record-enabled */
|
||||
items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
|
||||
}
|
||||
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
if ((!_route->is_singleton () || !active)
|
||||
@ -268,11 +266,18 @@ TriggerStrip::build_route_ops_menu ()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (active) {
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
items.push_back (CheckMenuElem (_("Active")));
|
||||
Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
i->set_active (active);
|
||||
i->set_sensitive (!_session->transport_rolling());
|
||||
i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
|
||||
}
|
||||
|
||||
/* Plugin / Processor related */
|
||||
if (active) {
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
@ -286,12 +291,20 @@ TriggerStrip::build_route_ops_menu ()
|
||||
|
||||
uint32_t plugin_insert_cnt = 0;
|
||||
_route->foreach_processor (boost::bind (RouteUI::help_count_plugins, _1, & plugin_insert_cnt));
|
||||
|
||||
if (active && plugin_insert_cnt > 0) {
|
||||
items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
|
||||
}
|
||||
|
||||
if (active) {
|
||||
items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
|
||||
denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
denormal_menu_item->set_active (_route->denormal_protection());
|
||||
}
|
||||
|
||||
/* MIDI */
|
||||
|
||||
if (active && (std::dynamic_pointer_cast<MidiTrack>(_route) || _route->the_instrument ())) {
|
||||
items.push_back (SeparatorElem());
|
||||
items.push_back (MenuElem (_("Patch Selector..."),
|
||||
sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
|
||||
}
|
||||
@ -300,13 +313,8 @@ TriggerStrip::build_route_ops_menu ()
|
||||
// TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
|
||||
items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
|
||||
items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
|
||||
denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
|
||||
denormal_menu_item->set_active (_route->denormal_protection());
|
||||
|
||||
/* note that this relies on selection being shared across editor and
|
||||
* mixer (or global to the backend, in the future), which is the only
|
||||
* sane thing for users anyway.
|
||||
|
@ -139,7 +139,6 @@ gtk2_ardour_sources = [
|
||||
'hit.cc',
|
||||
'keyboard.cc',
|
||||
'keyeditor.cc',
|
||||
'latency_gui.cc',
|
||||
'led.cc',
|
||||
'level_meter.cc',
|
||||
'library_download_dialog.cc',
|
||||
@ -304,6 +303,7 @@ gtk2_ardour_sources = [
|
||||
'time_fx_dialog.cc',
|
||||
'time_info_box.cc',
|
||||
'time_selection.cc',
|
||||
'timectl_gui.cc',
|
||||
'timers.cc',
|
||||
'track_record_axis.cc',
|
||||
'track_selection.cc',
|
||||
|
@ -81,6 +81,7 @@ class LIBARDOUR_API AUPlugin : public ARDOUR::Plugin
|
||||
void deactivate ();
|
||||
void flush ();
|
||||
int set_block_size (pframes_t nframes);
|
||||
void set_non_realtime (bool);
|
||||
|
||||
int connect_and_run (BufferSet& bufs,
|
||||
samplepos_t start, samplepos_t end, double speed,
|
||||
@ -166,6 +167,7 @@ class LIBARDOUR_API AUPlugin : public ARDOUR::Plugin
|
||||
std::shared_ptr<CAAudioUnit> unit;
|
||||
|
||||
bool initialized;
|
||||
bool process_offline;
|
||||
int32_t input_channels;
|
||||
int32_t output_channels;
|
||||
std::vector<std::pair<int,int> > io_configs;
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <vamp-hostsdk/Plugin.h>
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/types.h"
|
||||
|
@ -50,6 +50,7 @@ namespace Properties {
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> default_fade_out;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> fade_in_active;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> fade_out_active;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> fade_before_fx;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<float> scale_amplitude;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > fade_in;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > inverse_fade_in;
|
||||
@ -100,6 +101,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
bool envelope_active () const { return _envelope_active; }
|
||||
bool fade_in_active () const { return _fade_in_active; }
|
||||
bool fade_out_active () const { return _fade_out_active; }
|
||||
bool fade_before_fx () const { return _fade_before_fx; }
|
||||
|
||||
std::shared_ptr<AutomationList> fade_in() { return _fade_in.val (); }
|
||||
std::shared_ptr<AutomationList> inverse_fade_in() { return _inverse_fade_in.val (); }
|
||||
@ -162,11 +164,15 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
void set_envelope_active (bool yn);
|
||||
void set_default_envelope ();
|
||||
|
||||
void set_fade_before_fx (bool yn);
|
||||
|
||||
int separate_by_channel (std::vector<std::shared_ptr<Region> >&) const;
|
||||
|
||||
bool remove_plugin (std::shared_ptr<RegionFxPlugin>);
|
||||
void reorder_plugins (RegionFxList const&);
|
||||
|
||||
timecnt_t tail () const;
|
||||
|
||||
/* automation */
|
||||
|
||||
std::shared_ptr<Evoral::Control>
|
||||
@ -220,6 +226,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
PBD::Property<bool> _default_fade_out;
|
||||
PBD::Property<bool> _fade_in_active;
|
||||
PBD::Property<bool> _fade_out_active;
|
||||
PBD::Property<bool> _fade_before_fx;
|
||||
/** linear gain to apply to the whole region */
|
||||
PBD::Property<gain_t> _scale_amplitude;
|
||||
|
||||
@ -259,6 +266,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
|
||||
void apply_region_fx (BufferSet&, samplepos_t, samplepos_t, samplecnt_t);
|
||||
void fx_latency_changed (bool no_emit);
|
||||
void fx_tail_changed (bool no_emit);
|
||||
void copy_plugin_state (std::shared_ptr<const AudioRegion>);
|
||||
|
||||
mutable samplepos_t _fx_pos;
|
||||
@ -269,6 +277,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
mutable BufferSet _readcache;
|
||||
mutable samplepos_t _cache_start;
|
||||
mutable samplepos_t _cache_end;
|
||||
mutable samplecnt_t _cache_tail;
|
||||
mutable std::atomic<bool> _invalidated;
|
||||
|
||||
protected:
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/types.h"
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
@ -34,6 +34,7 @@ namespace PBD {
|
||||
namespace DEBUG {
|
||||
LIBARDOUR_API extern DebugBits AudioEngine;
|
||||
LIBARDOUR_API extern DebugBits AudioPlayback;
|
||||
LIBARDOUR_API extern DebugBits AudioCacheRefill;
|
||||
LIBARDOUR_API extern DebugBits AudioUnitConfig;
|
||||
LIBARDOUR_API extern DebugBits AudioUnitGUI;
|
||||
LIBARDOUR_API extern DebugBits AudioUnitProcess;
|
||||
@ -68,6 +69,7 @@ namespace PBD {
|
||||
LIBARDOUR_API extern DebugBits LatencyRoute;
|
||||
LIBARDOUR_API extern DebugBits LaunchControlXL;
|
||||
LIBARDOUR_API extern DebugBits Launchpad;
|
||||
LIBARDOUR_API extern DebugBits Launchkey;
|
||||
LIBARDOUR_API extern DebugBits Layering;
|
||||
LIBARDOUR_API extern DebugBits MIDISurface;
|
||||
LIBARDOUR_API extern DebugBits MTC;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "ardour/disk_io.h"
|
||||
#include "ardour/event_ring_buffer.h"
|
||||
#include "ardour/midi_buffer.h"
|
||||
|
||||
namespace ARDOUR
|
||||
@ -201,8 +202,8 @@ private:
|
||||
/** A buffer that we use to put newly-arrived MIDI data in for
|
||||
* the GUI to read (so that it can update itself).
|
||||
*/
|
||||
MidiBuffer _gui_feed_buffer;
|
||||
mutable Glib::Threads::Mutex _gui_feed_buffer_mutex;
|
||||
mutable EventRingBuffer<samplepos_t> _gui_feed_fifo;
|
||||
mutable Glib::Threads::Mutex _gui_feed_reset_mutex;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include "ardour/export_channel.h"
|
||||
#include "ardour/export_pointers.h"
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
#ifndef _libardour_export_smf_writer_h_
|
||||
#define _libardour_export_smf_writer_h_
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "evoral/SMF.h"
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
@ -22,8 +22,6 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
|
@ -95,6 +95,9 @@ public:
|
||||
virtual bool get_stats (PBD::microseconds_t&, PBD::microseconds_t&, double&, double&) const;
|
||||
virtual void clear_stats ();
|
||||
|
||||
ChanMapping input_map (uint32_t num) const;
|
||||
ChanMapping output_map (uint32_t num) const;
|
||||
|
||||
/* ControlSet */
|
||||
std::shared_ptr<Evoral::Control> control_factory (const Evoral::Parameter& id);
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <lo/lo.h>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <rubberband/RubberBandStretcher.h>
|
||||
#include <vamp-hostsdk/Plugin.h>
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include "temporal/tempo.h"
|
||||
|
||||
|
@ -21,8 +21,6 @@
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include "pbd/command.h"
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "evoral/Parameter.h"
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
|
@ -19,8 +19,6 @@
|
||||
#ifndef _libardour_mixer_scene_h_
|
||||
#define _libardour_mixer_scene_h_
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "pbd/stateful.h"
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
@ -22,8 +22,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
#include "ardour/slavable_automation_control.h"
|
||||
#include "ardour/monitorable.h"
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@ -402,7 +401,7 @@ protected:
|
||||
|
||||
void _set_sort_id ();
|
||||
|
||||
std::shared_ptr<RegionList> regions_touched_locked (timepos_t const & start, timepos_t const & end);
|
||||
std::shared_ptr<RegionList> regions_touched_locked (timepos_t const & start, timepos_t const & end, bool with_tail);
|
||||
|
||||
void notify_region_removed (std::shared_ptr<Region>);
|
||||
void notify_region_added (std::shared_ptr<Region>);
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
#include "ardour/automation_control.h"
|
||||
#include "ardour/chan_mapping.h"
|
||||
#include "ardour/plugin.h"
|
||||
#include "ardour/plugin_types.h"
|
||||
|
||||
@ -68,18 +69,22 @@ public:
|
||||
virtual bool get_stats (PBD::microseconds_t&, PBD::microseconds_t&, double&, double&) const = 0;
|
||||
virtual void clear_stats () = 0;
|
||||
|
||||
virtual ChanMapping input_map (uint32_t num) const = 0;
|
||||
virtual ChanMapping output_map (uint32_t num) const = 0;
|
||||
|
||||
/** A control that manipulates a plugin parameter (control port). */
|
||||
struct PluginControl : public AutomationControl {
|
||||
class PluginControl : public AutomationControl {
|
||||
public:
|
||||
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;
|
||||
virtual 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);
|
||||
@ -94,8 +99,8 @@ public:
|
||||
const ParameterDescriptor& desc,
|
||||
std::shared_ptr<AutomationList> list = std::shared_ptr<AutomationList> ());
|
||||
|
||||
double get_value (void) const;
|
||||
XMLNode& get_state () const;
|
||||
virtual double get_value (void) const;
|
||||
XMLNode& get_state () const;
|
||||
|
||||
protected:
|
||||
virtual void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "ardour/parameter_descriptor.h"
|
||||
#include "ardour/tailtime.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/variant.h"
|
||||
|
||||
@ -74,7 +75,7 @@ typedef std::set<uint32_t> PluginOutputConfiguration;
|
||||
*
|
||||
* Plugins are not used directly in Ardour but always wrapped by a PluginInsert.
|
||||
*/
|
||||
class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public HasLatency
|
||||
class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public HasLatency, public HasTailTime
|
||||
{
|
||||
public:
|
||||
Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&);
|
||||
@ -87,12 +88,12 @@ public:
|
||||
virtual void set_insert_id (PBD::ID id) {}
|
||||
virtual void set_state_dir (const std::string& d = "") {}
|
||||
|
||||
void set_insert (PluginInsert* pi, uint32_t num) {
|
||||
_pi = pi;
|
||||
void set_insert (PlugInsertBase* pib, uint32_t num) {
|
||||
_pib = pib;
|
||||
_num = num;
|
||||
}
|
||||
|
||||
PluginInsert* plugin_insert () const { return _pi; }
|
||||
PlugInsertBase* plugin_insert () const { return _pib; }
|
||||
uint32_t plugin_number () const { return _num; }
|
||||
|
||||
virtual std::string unique_id () const = 0;
|
||||
@ -172,6 +173,11 @@ public:
|
||||
return plugin_latency ();
|
||||
}
|
||||
|
||||
samplecnt_t signal_tailtime () const
|
||||
{
|
||||
return plugin_tailtime ();
|
||||
}
|
||||
|
||||
/** the max possible latency a plugin will have */
|
||||
virtual samplecnt_t max_latency () const { return 0; }
|
||||
|
||||
@ -423,6 +429,11 @@ protected:
|
||||
|
||||
private:
|
||||
virtual samplecnt_t plugin_latency () const = 0;
|
||||
/** tail duration in samples. e.g. for reverb or delay plugins.
|
||||
*
|
||||
* The default when unknown is 2 sec */
|
||||
virtual samplecnt_t plugin_tailtime () const;
|
||||
|
||||
|
||||
/** Fill _presets with our presets */
|
||||
virtual void find_presets () = 0;
|
||||
@ -443,8 +454,8 @@ private:
|
||||
void invalidate_preset_cache (std::string const&, Plugin*, bool);
|
||||
void resolve_midi ();
|
||||
|
||||
PluginInsert* _pi;
|
||||
uint32_t _num;
|
||||
PlugInsertBase* _pib;
|
||||
uint32_t _num;
|
||||
|
||||
PBD::ScopedConnection _preset_connection;
|
||||
};
|
||||
|
@ -34,7 +34,6 @@
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/chan_mapping.h"
|
||||
#include "ardour/fixed_delay.h"
|
||||
#include "ardour/io.h"
|
||||
#include "ardour/types.h"
|
||||
@ -323,9 +322,7 @@ private:
|
||||
/** details of the match currently being used */
|
||||
Match _match;
|
||||
|
||||
/* ordered map [plugin instance ID] => ARDOUR::ChanMapping
|
||||
* TODO: consider replacing with boost::flat_map<> or std::vector<>.
|
||||
*/
|
||||
/* ordered map [plugin instance ID] => ARDOUR::ChanMapping */
|
||||
#if defined(_MSC_VER) /* && (_MSC_VER < 1900)
|
||||
* Regarding the note (below) it was initially
|
||||
* thought that this got fixed in VS2015 - but
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/container/set.hpp>
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "ardour/data_type.h"
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#include <vector>
|
||||
#include "ardour/chan_count.h"
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Port;
|
||||
|
@ -260,6 +260,9 @@ CONFIG_VARIABLE (uint32_t, plugin_scan_timeout, "plugin-scan-timeout", 150) /* d
|
||||
CONFIG_VARIABLE (uint32_t, limit_n_automatables, "limit-n-automatables", 512)
|
||||
CONFIG_VARIABLE (uint32_t, plugin_cache_version, "plugin-cache-version", 0)
|
||||
|
||||
CONFIG_VARIABLE (float, tail_duration_sec, "tail-duration-sec", 2.0)
|
||||
CONFIG_VARIABLE (uint32_t, max_tail_samples, "max-tail-samples", 0xffffffff) // aka kInfiniteTail
|
||||
|
||||
/* custom user plugin paths */
|
||||
CONFIG_VARIABLE (std::string, plugin_path_vst, "plugin-path-vst", "@default@")
|
||||
CONFIG_VARIABLE (std::string, plugin_path_lxvst, "plugin-path-lxvst", "@default@")
|
||||
|
@ -35,11 +35,11 @@ class LIBARDOUR_API ReadOnlyControl : public PBD::Destructible
|
||||
public:
|
||||
ReadOnlyControl (std::shared_ptr<Plugin>, const ParameterDescriptor&, uint32_t pnum);
|
||||
|
||||
double get_parameter () const;
|
||||
virtual double get_parameter () const;
|
||||
std::string describe_parameter ();
|
||||
const ParameterDescriptor& desc() const { return _desc; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::weak_ptr<Plugin> _plugin;
|
||||
const ParameterDescriptor _desc;
|
||||
uint32_t _parameter_num;
|
||||
|
@ -22,8 +22,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
#include "ardour/slavable_automation_control.h"
|
||||
#include "ardour/recordable.h"
|
||||
|
||||
|
@ -27,8 +27,6 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "temporal/domain_swap.h"
|
||||
#include "temporal/timeline.h"
|
||||
#include "temporal/range.h"
|
||||
@ -145,6 +143,8 @@ public:
|
||||
timepos_t end() const;
|
||||
timepos_t nt_last() const { return end().decrement(); }
|
||||
|
||||
virtual timecnt_t tail () const { return timecnt_t (0); }
|
||||
|
||||
timepos_t source_position () const;
|
||||
timecnt_t source_relative_position (Temporal::timepos_t const &) const;
|
||||
timecnt_t region_relative_position (Temporal::timepos_t const &) const;
|
||||
@ -301,8 +301,8 @@ public:
|
||||
* OverlapEnd: the range overlaps the end of this region.
|
||||
* OverlapExternal: the range overlaps all of this region.
|
||||
*/
|
||||
Temporal::OverlapType coverage (timepos_t const & start, timepos_t const & end) const {
|
||||
return Temporal::coverage_exclusive_ends (position(), nt_last(), start, end);
|
||||
Temporal::OverlapType coverage (timepos_t const & start, timepos_t const & end, bool with_tail = false) const {
|
||||
return Temporal::coverage_exclusive_ends (position(), with_tail ? nt_last() + tail() : nt_last(), start, end);
|
||||
}
|
||||
|
||||
bool exact_equivalent (std::shared_ptr<const Region>) const;
|
||||
@ -564,6 +564,7 @@ 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);
|
||||
virtual void fx_tail_changed (bool no_emit);
|
||||
|
||||
virtual void send_change (const PBD::PropertyChange&);
|
||||
virtual int _set_state (const XMLNode&, int version, PBD::PropertyChange& what_changed, bool send_signal);
|
||||
@ -584,6 +585,7 @@ protected:
|
||||
|
||||
mutable Glib::Threads::RWLock _fx_lock;
|
||||
uint32_t _fx_latency;
|
||||
uint32_t _fx_tail;
|
||||
RegionFxList _plugins;
|
||||
|
||||
PBD::Property<bool> _sync_marked;
|
||||
|
@ -43,7 +43,7 @@ namespace ARDOUR
|
||||
{
|
||||
class ReadOnlyControl;
|
||||
|
||||
class LIBARDOUR_API RegionFxPlugin : public SessionObject, public PlugInsertBase, public Latent, public Temporal::TimeDomainProvider
|
||||
class LIBARDOUR_API RegionFxPlugin : public SessionObject, public PlugInsertBase, public Latent, public TailTime, public Temporal::TimeDomainProvider
|
||||
{
|
||||
public:
|
||||
RegionFxPlugin (Session&, Temporal::TimeDomain const, std::shared_ptr<Plugin> = std::shared_ptr<Plugin> ());
|
||||
@ -61,22 +61,22 @@ public:
|
||||
|
||||
/* Latent */
|
||||
samplecnt_t signal_latency () const;
|
||||
/* TailTime */
|
||||
samplecnt_t signal_tailtime () const;
|
||||
|
||||
/* PlugInsertBase */
|
||||
uint32_t get_count () const
|
||||
{
|
||||
return _plugins.size ();
|
||||
}
|
||||
PluginType type () const
|
||||
{
|
||||
return plugin ()->get_info ()->type;
|
||||
}
|
||||
PluginType type () const;
|
||||
|
||||
std::shared_ptr<Plugin> plugin (uint32_t num = 0) const
|
||||
{
|
||||
if (num < _plugins.size ()) {
|
||||
return _plugins[num];
|
||||
} else {
|
||||
return _plugins[0];
|
||||
return std::shared_ptr<Plugin>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +91,8 @@ public:
|
||||
bool reset_parameters_to_default ();
|
||||
bool can_reset_all_parameters ();
|
||||
|
||||
void maybe_emit_changed_signals () const;
|
||||
|
||||
std::string describe_parameter (Evoral::Parameter param);
|
||||
|
||||
bool provides_stats () const
|
||||
@ -103,6 +105,22 @@ public:
|
||||
}
|
||||
void clear_stats () {}
|
||||
|
||||
ChanMapping input_map (uint32_t num) const {
|
||||
if (num < _in_map.size()) {
|
||||
return _in_map.find (num)->second;
|
||||
} else {
|
||||
return ChanMapping ();
|
||||
}
|
||||
}
|
||||
|
||||
ChanMapping output_map (uint32_t num) const {
|
||||
if (num < _out_map.size()) {
|
||||
return _out_map.find (num)->second;
|
||||
} else {
|
||||
return ChanMapping ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Stateful */
|
||||
XMLNode& get_state (void) const;
|
||||
int set_state (const XMLNode&, int version);
|
||||
@ -158,7 +176,8 @@ private:
|
||||
/** details of the match currently being used */
|
||||
Match _match;
|
||||
|
||||
uint32_t _plugin_signal_latency;
|
||||
samplecnt_t _plugin_signal_latency;
|
||||
samplecnt_t _plugin_signal_tailtime;
|
||||
|
||||
typedef std::vector<std::shared_ptr<Plugin>> Plugins;
|
||||
Plugins _plugins;
|
||||
@ -173,11 +192,17 @@ private:
|
||||
bool _configured;
|
||||
bool _no_inplace;
|
||||
|
||||
mutable samplepos_t _last_emit;
|
||||
|
||||
typedef std::map<uint32_t, std::shared_ptr<ReadOnlyControl>> CtrlOutMap;
|
||||
CtrlOutMap _control_outputs;
|
||||
|
||||
Gtkmm2ext::WindowProxy* _window_proxy;
|
||||
std::atomic<int> _flush;
|
||||
|
||||
XMLNode* _state;
|
||||
|
||||
mutable Glib::Threads::Mutex _process_lock;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
@ -49,8 +49,6 @@
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include <ltc.h>
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "pbd/statefuldestructible.h"
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/rational.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "ardour/automatable.h"
|
||||
|
69
libs/ardour/ardour/tailtime.h
Normal file
69
libs/ardour/ardour/tailtime.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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_tailtime_h__
|
||||
#define __ardour_tailtime_h__
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class LIBARDOUR_API HasTailTime {
|
||||
public:
|
||||
virtual ~HasTailTime () {}
|
||||
virtual samplecnt_t signal_tailtime () const = 0;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API TailTime : public HasTailTime {
|
||||
public:
|
||||
TailTime ();
|
||||
TailTime (TailTime const&);
|
||||
virtual ~TailTime() {}
|
||||
|
||||
samplecnt_t effective_tailtime () const;
|
||||
|
||||
samplecnt_t user_latency () const {
|
||||
if (_use_user_tailtime) {
|
||||
return _user_tailtime;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void unset_user_tailtime ();
|
||||
void set_user_tailtime (samplecnt_t val);
|
||||
|
||||
PBD::Signal0<void> TailTimeChanged;
|
||||
|
||||
protected:
|
||||
int set_state (const XMLNode& node, int version);
|
||||
void add_state (XMLNode*) const;
|
||||
|
||||
private:
|
||||
samplecnt_t _use_user_tailtime;
|
||||
samplecnt_t _user_tailtime;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
|
||||
#endif /* __ardour_tailtime_h__*/
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#ifdef HAVE_LV2_1_18_6
|
||||
|
@ -32,8 +32,6 @@
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
|
||||
#if __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif /* __APPLE__ */
|
||||
|
@ -167,6 +167,8 @@ public:
|
||||
Vst::ParamID index_to_id (uint32_t) const;
|
||||
|
||||
Glib::Threads::Mutex& process_lock () { return _process_lock; }
|
||||
bool& component_is_synced () { return _restart_component_is_synced; }
|
||||
|
||||
|
||||
enum ParameterChange { BeginGesture,
|
||||
EndGesture,
|
||||
@ -180,6 +182,7 @@ public:
|
||||
|
||||
/* API for Ardour -- Setup/Processing */
|
||||
uint32_t plugin_latency ();
|
||||
uint32_t plugin_tailtime ();
|
||||
bool set_block_size (int32_t);
|
||||
bool activate ();
|
||||
bool deactivate ();
|
||||
@ -326,6 +329,7 @@ private:
|
||||
bool _add_to_selection;
|
||||
|
||||
boost::optional<uint32_t> _plugin_latency;
|
||||
boost::optional<uint32_t> _plugin_tail;
|
||||
|
||||
int _n_bus_in;
|
||||
int _n_bus_out;
|
||||
@ -440,6 +444,7 @@ public:
|
||||
|
||||
private:
|
||||
samplecnt_t plugin_latency () const;
|
||||
samplecnt_t plugin_tailtime () const;
|
||||
void init ();
|
||||
void find_presets ();
|
||||
void forward_resize_view (int w, int h);
|
||||
|
@ -176,7 +176,14 @@ ARDOUR::timecnt_t
|
||||
AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, timepos_t const & start, timecnt_t const & cnt, uint32_t chan_n)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 mixdown @ %6 gain @ %7\n",
|
||||
name(), start, cnt, chan_n, regions.size(), mixdown_buffer, gain_buffer));
|
||||
name(), start.samples(), cnt.samples(), chan_n, regions.size(), mixdown_buffer, gain_buffer));
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioCacheRefill, string_compose ("Playlist '%1' chn: %2 from %3 to %4 [s] PH@ %5\n",
|
||||
name (), chan_n,
|
||||
std::setprecision (3), std::fixed,
|
||||
start.samples() / (float)_session.sample_rate (),
|
||||
(start.samples() + cnt.samples()) / (float)_session.sample_rate (),
|
||||
_session.transport_sample () / (float)_session.sample_rate ()));
|
||||
|
||||
samplecnt_t const scnt (cnt.samples ());
|
||||
|
||||
@ -204,7 +211,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
/* Find all the regions that are involved in the bit we are reading,
|
||||
and sort them by descending layer and ascending position.
|
||||
*/
|
||||
std::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt);
|
||||
std::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt, true);
|
||||
all->sort (ReadSorter ());
|
||||
|
||||
/* This will be a list of the bits of our read range that we have
|
||||
@ -236,7 +243,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
*/
|
||||
Temporal::Range rrange = ar->range_samples ();
|
||||
Temporal::Range region_range (max (rrange.start(), start),
|
||||
min (rrange.end(), start + cnt));
|
||||
min (rrange.end() + ar->tail (), start + cnt));
|
||||
|
||||
/* ... and then remove the bits that are already done */
|
||||
|
||||
@ -256,9 +263,9 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
/* Cut this range down to just the body and mark it done */
|
||||
Temporal::Range body = ar->body_range ();
|
||||
|
||||
if (body.start() < d.end() && body.end() > d.start()) {
|
||||
if (body.start() < d.end().earlier (ar->tail ()) && body.end() > d.start()) {
|
||||
d.set_start (max (d.start(), body.start()));
|
||||
d.set_end (min (d.end(), body.end()));
|
||||
d.set_end (min (d.end().earlier (ar->tail ()), body.end()));
|
||||
done.add (d);
|
||||
}
|
||||
}
|
||||
@ -285,8 +292,13 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
assert (soffset + read_cnt <= scnt);
|
||||
samplecnt_t nread = i->region->read_at (buf + soffset, mixdown_buffer, gain_buffer, read_pos, read_cnt, chan_n);
|
||||
if (nread != read_cnt) {
|
||||
std::cerr << name() << " tried to read " << read_cnt << " from " << nread << " in " << i->region->name() << " using range "
|
||||
<< i->range.start() << " .. " << i->range.end() << " len " << i->range.length() << std::endl;
|
||||
std::cerr << name() << " tried to read " << read_cnt
|
||||
<< " got " << nread
|
||||
<< " in " << i->region->name()
|
||||
<< " for chn " << chan_n
|
||||
<< " to offset " << soffset
|
||||
<< " using range " << i->range.start().samples() << " .. " << i->range.end().samples()
|
||||
<< " len " << i->range.length().samples() << std::endl;
|
||||
#ifndef NDEBUG
|
||||
/* forward error to DiskReader::audio_read. This does 2 things:
|
||||
* - error "DiskReader %1: when refilling, cannot read ..."
|
||||
|
@ -360,6 +360,7 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, std::shared_ptr<CACom
|
||||
, comp (_comp)
|
||||
, unit (new CAAudioUnit)
|
||||
, initialized (false)
|
||||
, process_offline (false)
|
||||
, _last_nframes (0)
|
||||
, _requires_fixed_size_buffers (false)
|
||||
, buffers (0)
|
||||
@ -401,6 +402,7 @@ AUPlugin::AUPlugin (const AUPlugin& other)
|
||||
, comp (other.get_comp())
|
||||
, unit (new CAAudioUnit)
|
||||
, initialized (false)
|
||||
, process_offline (false)
|
||||
, _last_nframes (0)
|
||||
, _requires_fixed_size_buffers (false)
|
||||
, buffers (0)
|
||||
@ -869,6 +871,33 @@ AUPlugin::requires_fixed_size_buffers() const
|
||||
return _requires_fixed_size_buffers;
|
||||
}
|
||||
|
||||
void
|
||||
AUPlugin::set_non_realtime (bool yn)
|
||||
{
|
||||
if (process_offline == yn) {
|
||||
return;
|
||||
}
|
||||
process_offline = yn;
|
||||
|
||||
bool was_initialized = initialized;
|
||||
if (initialized) {
|
||||
deactivate ();
|
||||
}
|
||||
|
||||
OSErr err;
|
||||
UInt32 isOffline = yn ? 1 : 0;
|
||||
if ((err = unit->SetProperty (/*kAudioUnitProperty_OfflineRender*/ 37, kAudioUnitScope_Global, 0, &isOffline, sizeof (isOffline))) != noErr) {
|
||||
info << string_compose (_("AU: cannot set offline rendering(err = %1)"), err) << endmsg;
|
||||
}
|
||||
if (yn) {
|
||||
UInt32 numSamples = _session.get_block_size();
|
||||
unit->SetProperty (/*kAudioUnitOfflineProperty_InputSize*/ 3020, kAudioUnitScope_Global, 0, &numSamples, sizeof(numSamples));
|
||||
}
|
||||
|
||||
if (was_initialized) {
|
||||
activate ();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
AUPlugin::set_block_size (pframes_t nframes)
|
||||
@ -888,6 +917,10 @@ AUPlugin::set_block_size (pframes_t nframes)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (process_offline) {
|
||||
unit->SetProperty (/*kAudioUnitOfflineProperty_InputSize*/ 3020, kAudioUnitScope_Global, 0, &numSamples, sizeof(numSamples));
|
||||
}
|
||||
|
||||
if (was_initialized) {
|
||||
activate ();
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "ardour/analysis_graph.h"
|
||||
#include "ardour/audioregion.h"
|
||||
#include "ardour/buffer_manager.h"
|
||||
#include "ardour/butler.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/dB.h"
|
||||
#include "ardour/debug.h"
|
||||
@ -81,6 +82,7 @@ namespace ARDOUR {
|
||||
PBD::PropertyDescriptor<bool> default_fade_out;
|
||||
PBD::PropertyDescriptor<bool> fade_in_active;
|
||||
PBD::PropertyDescriptor<bool> fade_out_active;
|
||||
PBD::PropertyDescriptor<bool> fade_before_fx;
|
||||
PBD::PropertyDescriptor<float> scale_amplitude;
|
||||
PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > fade_in;
|
||||
PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > inverse_fade_in;
|
||||
@ -175,6 +177,8 @@ AudioRegion::make_property_quarks ()
|
||||
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-active = %1\n", Properties::fade_in_active.property_id));
|
||||
Properties::fade_out_active.property_id = g_quark_from_static_string (X_("fade-out-active"));
|
||||
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-active = %1\n", Properties::fade_out_active.property_id));
|
||||
Properties::fade_before_fx.property_id = g_quark_from_static_string (X_("fade-before-fx"));
|
||||
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-before-fx = %1\n", Properties::fade_before_fx.property_id));
|
||||
Properties::scale_amplitude.property_id = g_quark_from_static_string (X_("scale-amplitude"));
|
||||
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for scale-amplitude = %1\n", Properties::scale_amplitude.property_id));
|
||||
Properties::fade_in.property_id = g_quark_from_static_string (X_("FadeIn"));
|
||||
@ -199,6 +203,7 @@ AudioRegion::register_properties ()
|
||||
add_property (_default_fade_out);
|
||||
add_property (_fade_in_active);
|
||||
add_property (_fade_out_active);
|
||||
add_property (_fade_before_fx);
|
||||
add_property (_scale_amplitude);
|
||||
add_property (_fade_in);
|
||||
add_property (_inverse_fade_in);
|
||||
@ -213,6 +218,7 @@ AudioRegion::register_properties ()
|
||||
, _default_fade_out (Properties::default_fade_out, true) \
|
||||
, _fade_in_active (Properties::fade_in_active, true) \
|
||||
, _fade_out_active (Properties::fade_out_active, true) \
|
||||
, _fade_before_fx (Properties::fade_before_fx, false) \
|
||||
, _scale_amplitude (Properties::scale_amplitude, 1.0) \
|
||||
, _fade_in (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation), tdp))) \
|
||||
, _inverse_fade_in (Properties::inverse_fade_in, std::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation), tdp))) \
|
||||
@ -225,6 +231,7 @@ AudioRegion::register_properties ()
|
||||
, _default_fade_out (Properties::default_fade_out, other->_default_fade_out) \
|
||||
, _fade_in_active (Properties::fade_in_active, other->_fade_in_active) \
|
||||
, _fade_out_active (Properties::fade_out_active, other->_fade_out_active) \
|
||||
, _fade_before_fx (Properties::fade_before_fx, other->_fade_before_fx) \
|
||||
, _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) \
|
||||
, _fade_in (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_fade_in.val()))) \
|
||||
, _inverse_fade_in (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_in.val()))) \
|
||||
@ -247,6 +254,7 @@ AudioRegion::init ()
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
}
|
||||
@ -340,6 +348,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other)
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
|
||||
@ -368,6 +377,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, timecnt_t co
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
|
||||
@ -394,6 +404,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, const Source
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
|
||||
@ -498,6 +509,32 @@ AudioRegion::set_envelope_active (bool yn)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegion::set_fade_before_fx (bool yn)
|
||||
{
|
||||
if (fade_before_fx() != yn) {
|
||||
_fade_before_fx = yn;
|
||||
send_change (PropertyChange (Properties::fade_before_fx));
|
||||
if (!has_region_fx ()) {
|
||||
return;
|
||||
}
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
}
|
||||
RegionFxChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
timecnt_t
|
||||
AudioRegion::tail () const
|
||||
{
|
||||
if (_fade_before_fx && has_region_fx ()) {
|
||||
return timecnt_t ((samplecnt_t)_fx_tail);
|
||||
} else {
|
||||
return timecnt_t (0);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param buf Buffer to put peak data in.
|
||||
* @param npeaks Number of peaks to read (ie the number of PeakDatas in buf)
|
||||
* @param offset Start position, as an offset from the start of this region's source.
|
||||
@ -573,6 +610,11 @@ AudioRegion::read_at (Sample* buf,
|
||||
The caller has verified that we cover the desired section.
|
||||
*/
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioCacheRefill, string_compose ("- Region '%1' chn: %2 from %3 to %4 [s]\n",
|
||||
name(), chan_n,
|
||||
std::setprecision (3), std::fixed,
|
||||
pos / (float)_session.sample_rate (), (pos + cnt) / (float)_session.sample_rate ()));
|
||||
|
||||
/* See doc/region_read.svg for a drawing which might help to explain
|
||||
what is going on.
|
||||
*/
|
||||
@ -584,49 +626,62 @@ AudioRegion::read_at (Sample* buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* WORK OUT WHERE TO GET DATA FROM */
|
||||
|
||||
samplecnt_t to_read;
|
||||
const samplepos_t psamples = position().samples();
|
||||
const samplecnt_t lsamples = _length.val().samples();
|
||||
|
||||
assert (pos >= psamples);
|
||||
sampleoffset_t const internal_offset = pos - psamples;
|
||||
|
||||
if (internal_offset >= lsamples) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
const samplecnt_t esamples = lsamples - internal_offset;
|
||||
assert (esamples >= 0);
|
||||
|
||||
if ((to_read = min (cnt, esamples)) == 0) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
std::shared_ptr<Playlist> pl (playlist());
|
||||
if (!pl){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */
|
||||
/* WORK OUT WHERE TO GET DATA FROM */
|
||||
|
||||
const samplepos_t psamples = position().samples();
|
||||
const samplecnt_t lsamples = _length.val().samples();
|
||||
const samplecnt_t tsamples = tail ().samples ();
|
||||
|
||||
assert (pos >= psamples);
|
||||
sampleoffset_t internal_offset = pos - psamples;
|
||||
sampleoffset_t suffix = 0;
|
||||
|
||||
if (internal_offset >= lsamples + tsamples) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
if (internal_offset > lsamples) {
|
||||
suffix = internal_offset - lsamples;
|
||||
internal_offset = lsamples;
|
||||
}
|
||||
|
||||
const samplecnt_t esamples = lsamples - internal_offset;
|
||||
assert (esamples >= 0);
|
||||
|
||||
if (min (cnt, esamples + tsamples) <= 0) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
/* does not include tail */
|
||||
samplecnt_t const to_read = max<samplecnt_t> (0, min (cnt, esamples));
|
||||
samplecnt_t const can_read = max<samplecnt_t> (0, min (cnt, esamples + tsamples));
|
||||
|
||||
/* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ
|
||||
*
|
||||
* This information is also used for inverse fades to fade out
|
||||
* layered regions below this one.
|
||||
*/
|
||||
bool const use_region_fades = _session.config.get_use_region_fades();
|
||||
|
||||
/* Amount (length) of fade in that we are dealing with in this read */
|
||||
samplecnt_t fade_in_limit = 0;
|
||||
|
||||
/* Offset from buf / mixdown_buffer of the start
|
||||
of any fade out that we are dealing with
|
||||
*/
|
||||
/* Offset from buf / mixdown_buffer of the start of any fade out that we are dealing with */
|
||||
sampleoffset_t fade_out_offset = 0;
|
||||
|
||||
/* Amount (length) of fade out that we are dealing with in this read */
|
||||
samplecnt_t fade_out_limit = 0;
|
||||
|
||||
/* offset for fade-out curve data */
|
||||
samplecnt_t fade_interval_start = 0;
|
||||
|
||||
/* Fade in */
|
||||
|
||||
if (_fade_in_active && _session.config.get_use_region_fades()) {
|
||||
if (_fade_in_active && use_region_fades) {
|
||||
|
||||
samplecnt_t fade_in_length = _fade_in->when(false).samples();
|
||||
|
||||
@ -638,15 +693,13 @@ AudioRegion::read_at (Sample* buf,
|
||||
}
|
||||
|
||||
/* Fade out */
|
||||
|
||||
if (_fade_out_active && _session.config.get_use_region_fades()) {
|
||||
if (_fade_out_active && use_region_fades) {
|
||||
|
||||
/* see if some part of this read is within the fade out */
|
||||
|
||||
/* ................. >| REGION
|
||||
/* ................. >| REGION
|
||||
* _length
|
||||
*
|
||||
* { } FADE
|
||||
* { } FADE
|
||||
* fade_out_length
|
||||
* ^
|
||||
* _length - fade_out_length
|
||||
@ -654,18 +707,19 @@ AudioRegion::read_at (Sample* buf,
|
||||
* |--------------|
|
||||
* ^internal_offset
|
||||
* ^internal_offset + to_read
|
||||
*
|
||||
* we need the intersection of [internal_offset,internal_offset+to_read] with
|
||||
* [_length - fade_out_length, _length]
|
||||
*
|
||||
*/
|
||||
/* we need the intersection of
|
||||
* [internal_offset, internal_offset + to_read]
|
||||
* with
|
||||
* [_length - fade_out_length, _length]
|
||||
*/
|
||||
|
||||
fade_interval_start = max (internal_offset, lsamples - _fade_out->when(false).samples());
|
||||
samplecnt_t fade_interval_end = min(internal_offset + to_read, lsamples);
|
||||
fade_interval_start = max (internal_offset, lsamples - _fade_out->when(false).samples());
|
||||
samplecnt_t fade_interval_end = min (internal_offset + to_read, lsamples);
|
||||
|
||||
if (fade_interval_end > fade_interval_start) {
|
||||
/* (part of the) the fade out is in this buffer */
|
||||
fade_out_limit = fade_interval_end - fade_interval_start;
|
||||
fade_out_limit = fade_interval_end - fade_interval_start;
|
||||
fade_out_offset = fade_interval_start - internal_offset;
|
||||
}
|
||||
}
|
||||
@ -673,16 +727,17 @@ AudioRegion::read_at (Sample* buf,
|
||||
Glib::Threads::Mutex::Lock cl (_cache_lock);
|
||||
if (chan_n == 0 && _invalidated.exchange (false)) {
|
||||
_cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
}
|
||||
|
||||
boost::scoped_array<gain_t> gain_array;
|
||||
boost::scoped_array<Sample> mixdown_array;
|
||||
|
||||
// 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);
|
||||
if (n_chn > 1 && _cache_start < _cache_end && internal_offset + suffix >= _cache_start && internal_offset + suffix + can_read <= _cache_end) {
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 copy from cache %3 - %4 to_read: %5 can_read: %6\n",
|
||||
name(), chan_n, internal_offset + suffix, internal_offset + suffix + can_read, to_read, can_read));
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (internal_offset + suffix - _cache_start), can_read);
|
||||
cl.release ();
|
||||
} else {
|
||||
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
|
||||
@ -690,24 +745,21 @@ AudioRegion::read_at (Sample* buf,
|
||||
uint32_t fx_latency = _fx_latency;
|
||||
lm.release ();
|
||||
|
||||
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 n_tail = 0; // further silence pad, read tail from FX
|
||||
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) {
|
||||
if (tsamples > 0 && cnt >= esamples) {
|
||||
n_tail = can_read - n_read;
|
||||
n_proc += n_tail;
|
||||
}
|
||||
|
||||
if (_cache_end != internal_offset + suffix && 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) {
|
||||
@ -716,13 +768,20 @@ AudioRegion::read_at (Sample* buf,
|
||||
n_read = max<samplecnt_t> (0, min (to_read, lsamples - offset));
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read: %3 - %4 (%5) to_read: %6 offset: %7 with fx: %8 fx_latency: %9\n",
|
||||
name(), chan_n, readat, readat + n_read, n_read, to_read, internal_offset, have_fx, fx_latency));
|
||||
if (n_proc > to_read) {
|
||||
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 ();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read: %3 - %4 (%5) to_read: %6 offset: %7 with fx: %8 fx_latency: %9 fx_tail %10\n",
|
||||
name(), chan_n, readat, readat + n_read, n_read, to_read, internal_offset, have_fx, fx_latency, n_tail));
|
||||
|
||||
ChanCount cc (DataType::AUDIO, n_channels ());
|
||||
_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);
|
||||
@ -730,6 +789,7 @@ AudioRegion::read_at (Sample* buf,
|
||||
|
||||
/* reset in case read fails we return early */
|
||||
_cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
|
||||
for (uint32_t chn = 0; chn < n_chn; ++chn) {
|
||||
/* READ DATA FROM THE SOURCE INTO mixdown_buffer.
|
||||
@ -759,6 +819,49 @@ AudioRegion::read_at (Sample* buf,
|
||||
apply_gain_to_buffer (mixdown_buffer, n_read, _scale_amplitude);
|
||||
}
|
||||
|
||||
/* Apply Region Fades before processing. */
|
||||
|
||||
/* Fade in. Precomputed data from above may not apply here.
|
||||
* latent FX may have increasded to_read -> n_read,
|
||||
* or internal_offset.
|
||||
*/
|
||||
if (_fade_before_fx && use_region_fades && _fade_in_active) {
|
||||
samplecnt_t fade_in_length = _fade_in->when(false).samples();
|
||||
if (offset < fade_in_length) {
|
||||
samplecnt_t fade_in_limit = min (n_read, fade_in_length - offset);
|
||||
|
||||
//fade_in_limit = min (fade_in_limit, n_read);
|
||||
assert (fade_in_limit <= n_read);
|
||||
_fade_in->curve().get_vector (timepos_t (offset), timepos_t (offset + fade_in_limit), gain_buffer, fade_in_limit);
|
||||
for (samplecnt_t n = 0; n < fade_in_limit; ++n) {
|
||||
mixdown_buffer[n] *= gain_buffer[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fade out. Precomputed data from above may not apply here.
|
||||
* If there are latent FX: internal_offset != offset
|
||||
*/
|
||||
if (_fade_before_fx && use_region_fades && _fade_out_active) {
|
||||
samplecnt_t fade_interval_start = max (offset, lsamples - _fade_out->when(false).samples());
|
||||
samplecnt_t fade_interval_end = min (offset + n_read, lsamples);
|
||||
|
||||
if (fade_interval_end > fade_interval_start) {
|
||||
/* (part of the) the fade out is in this buffer */
|
||||
samplecnt_t fade_out_limit = fade_interval_end - fade_interval_start;
|
||||
sampleoffset_t fade_out_offset = fade_interval_start - offset;
|
||||
|
||||
assert (fade_out_offset + fade_out_limit <= n_read);
|
||||
|
||||
/* apply fade out */
|
||||
samplecnt_t const curve_offset = fade_interval_start - _fade_out->when(false).distance (len_as_tpos ()).samples();
|
||||
_fade_out->curve().get_vector (timepos_t (curve_offset), timepos_t (curve_offset + fade_out_limit), gain_buffer, fade_out_limit);
|
||||
for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
|
||||
mixdown_buffer[m] *= gain_buffer[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* for mono regions no cache is required, unless there are
|
||||
* regionFX, which use the _readcache BufferSet.
|
||||
*/
|
||||
@ -769,17 +872,39 @@ AudioRegion::read_at (Sample* buf,
|
||||
|
||||
/* apply region FX to all channels */
|
||||
if (have_fx) {
|
||||
const_cast<AudioRegion*>(this)->apply_region_fx (_readcache, offset, offset + n_proc, n_proc);
|
||||
#ifndef NDEBUG
|
||||
microseconds_t t_start = get_microseconds ();
|
||||
#endif
|
||||
const_cast<AudioRegion*>(this)->apply_region_fx (_readcache, offset + suffix, offset + suffix + n_proc, n_proc);
|
||||
#ifndef NDEBUG
|
||||
if (DEBUG_ENABLED (DEBUG::AudioCacheRefill)) {
|
||||
microseconds_t t_end = get_microseconds ();
|
||||
int nsecs_per_sample = lrintf ((t_end - t_start) * 1000 / std::max<double> (1.0, n_proc * n_chn));
|
||||
float load = (t_end - t_start) / (10000.f * n_proc / (float) _session.sample_rate ());
|
||||
DEBUG_TRACE (DEBUG::AudioCacheRefill, string_compose ("- RegionFx '%1' took %2 us, frames: %3, nchn: %4, ns/spl: %5 load: %6%%\n",
|
||||
name (), (t_end - t_start), n_proc, n_chn, nsecs_per_sample, load));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (chan_n < n_channels()) {
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read + n_tail);
|
||||
} else {
|
||||
if (Config->get_replicate_missing_region_channels()) {
|
||||
chan_n = chan_n % n_channels ();
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read + n_tail);
|
||||
} else {
|
||||
memset (mixdown_buffer, 0, sizeof (Sample) * (to_read + n_tail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_cache_start = internal_offset;
|
||||
_cache_end = internal_offset + to_read;
|
||||
_cache_start = internal_offset + suffix;
|
||||
_cache_end = internal_offset + suffix + to_read + n_tail;
|
||||
_cache_tail = n_tail;
|
||||
cl.release ();
|
||||
}
|
||||
|
||||
@ -828,9 +953,13 @@ AudioRegion::read_at (Sample* buf,
|
||||
_fade_in->curve().get_vector (timepos_t (internal_offset), timepos_t (internal_offset + fade_in_limit), gain_buffer, fade_in_limit);
|
||||
}
|
||||
|
||||
/* Mix our newly-read data in, with the fade */
|
||||
for (samplecnt_t n = 0; n < fade_in_limit; ++n) {
|
||||
buf[n] += mixdown_buffer[n] * gain_buffer[n];
|
||||
if (!_fade_before_fx) {
|
||||
/* Mix our newly-read data in, with the fade */
|
||||
for (samplecnt_t n = 0; n < fade_in_limit; ++n) {
|
||||
buf[n] += mixdown_buffer[n] * gain_buffer[n];
|
||||
}
|
||||
} else {
|
||||
fade_in_limit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -869,11 +998,13 @@ AudioRegion::read_at (Sample* buf,
|
||||
_fade_out->curve().get_vector (timepos_t (curve_offset), timepos_t (curve_offset + fade_out_limit), gain_buffer, fade_out_limit);
|
||||
}
|
||||
|
||||
/* Mix our newly-read data with whatever was already there,
|
||||
with the fade out applied to our data.
|
||||
*/
|
||||
for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
|
||||
buf[m] += mixdown_buffer[m] * gain_buffer[n];
|
||||
if (!_fade_before_fx) {
|
||||
/* Mix our newly-read data with whatever was already there, with the fade out applied to our data. */
|
||||
for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
|
||||
buf[m] += mixdown_buffer[m] * gain_buffer[n];
|
||||
}
|
||||
} else {
|
||||
fade_out_limit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -890,8 +1021,16 @@ AudioRegion::read_at (Sample* buf,
|
||||
mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N);
|
||||
}
|
||||
}
|
||||
samplecnt_t T = _cache_tail;
|
||||
if (T > 0) {
|
||||
T = min (T, can_read);
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region %1 adding FX tail of %2 cut to_read %3 at %4 total len = %5 cnt was %6\n",
|
||||
name (), _cache_tail, T, to_read, to_read + T, cnt));
|
||||
/* AudioPlaylist::read reads regions in reverse order, so we can add the tail here */
|
||||
mix_buffers_no_gain (buf + to_read, mixdown_buffer + to_read, T);
|
||||
}
|
||||
|
||||
return to_read;
|
||||
return to_read + T;
|
||||
}
|
||||
|
||||
/** Read data directly from one of our sources, accounting for the situation when the track has a different channel
|
||||
@ -2341,7 +2480,12 @@ AudioRegion::_add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<R
|
||||
return;
|
||||
}
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
/* catch changes from some custom plugin GUI threads (VST2, and JUCE) */
|
||||
if (SessionEvent::has_per_thread_pool ()) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
} else {
|
||||
_session.butler ()->delegate (boost::bind (&AudioRegion::send_change, this, PropertyChange (Properties::region_fx)));
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!ac->alist ()) {
|
||||
@ -2356,6 +2500,7 @@ AudioRegion::_add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<R
|
||||
}
|
||||
|
||||
rfx->LatencyChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fx_latency_changed, this, false));
|
||||
rfx->TailTimeChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fx_tail_changed, this, false));
|
||||
rfx->set_block_size (_session.get_block_size ());
|
||||
|
||||
if (from_set_state) {
|
||||
@ -2374,6 +2519,8 @@ AudioRegion::_add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<R
|
||||
rfx->set_default_automation (len_as_tpos ());
|
||||
|
||||
fx_latency_changed (true);
|
||||
fx_tail_changed (true);
|
||||
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
}
|
||||
@ -2395,11 +2542,13 @@ AudioRegion::remove_plugin (std::shared_ptr<RegionFxPlugin> fx)
|
||||
|
||||
fx->drop_references ();
|
||||
fx_latency_changed (true);
|
||||
fx_tail_changed (true);
|
||||
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
}
|
||||
RegionFxChanged (); /* EMIT SIGNAL */
|
||||
_session.set_dirty ();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2434,6 +2583,27 @@ AudioRegion::fx_latency_changed (bool no_emit)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegion::fx_tail_changed (bool no_emit)
|
||||
{
|
||||
uint32_t t = 0;
|
||||
for (auto const& rfx : _plugins) {
|
||||
t = max<uint32_t> (t, rfx->effective_tailtime ());
|
||||
}
|
||||
if (t == _fx_tail) {
|
||||
return;
|
||||
}
|
||||
_fx_tail = t;
|
||||
|
||||
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)
|
||||
{
|
||||
@ -2463,7 +2633,8 @@ AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplep
|
||||
|
||||
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)) {
|
||||
samplepos_t cycle_start = start_sample + offset - latency_offset;
|
||||
if (!rfx->run (bufs, cycle_start, cycle_start + run, position().samples(), run, offset)) {
|
||||
lm.release ();
|
||||
/* this triggers a re-read */
|
||||
const_cast<AudioRegion*>(this)->remove_plugin (rfx);
|
||||
|
@ -29,6 +29,7 @@ using namespace std;
|
||||
|
||||
PBD::DebugBits PBD::DEBUG::AudioEngine = PBD::new_debug_bit ("AudioEngine");
|
||||
PBD::DebugBits PBD::DEBUG::AudioPlayback = PBD::new_debug_bit ("audioplayback");
|
||||
PBD::DebugBits PBD::DEBUG::AudioCacheRefill = PBD::new_debug_bit ("audiocacherefill");
|
||||
PBD::DebugBits PBD::DEBUG::AudioUnitConfig = PBD::new_debug_bit ("AudioUnitConfig");
|
||||
PBD::DebugBits PBD::DEBUG::AudioUnitGUI = PBD::new_debug_bit ("AudioUnitGUI");
|
||||
PBD::DebugBits PBD::DEBUG::AudioUnitProcess = PBD::new_debug_bit ("AudioUnitProcess");
|
||||
@ -63,6 +64,7 @@ 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::Launchkey = PBD::new_debug_bit ("launchkey");
|
||||
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");
|
||||
|
@ -423,6 +423,10 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||
cerr << "underrun for " << _name << " Available samples: " << available << " required: " << disk_samples_to_consume << endl;
|
||||
#endif
|
||||
DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3 vs %4\n", DEBUG_THREAD_SELF, name (), available, disk_samples_to_consume));
|
||||
DEBUG_TRACE (DEBUG::AudioCacheRefill, string_compose ("DR '%1' underrun have %2 need %3 samples at pos %4\n",
|
||||
name (), available, disk_samples_to_consume,
|
||||
std::setprecision (3), std::fixed,
|
||||
start_sample / (float)_session.sample_rate ()));
|
||||
Underrun ();
|
||||
return;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ DiskWriter::DiskWriter (Session& s, Track& t, string const & str, DiskIOProcesso
|
||||
, _accumulated_capture_offset (0)
|
||||
, _transport_looped (false)
|
||||
, _transport_loop_sample (0)
|
||||
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
|
||||
, _gui_feed_fifo (min<size_t> (64000, max<size_t> (s.sample_rate() / 10, 2 * AudioEngine::instance()->raw_buffer_size (DataType::MIDI))))
|
||||
{
|
||||
DiskIOProcessor::init ();
|
||||
_xruns.reserve (128);
|
||||
@ -684,25 +684,18 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||
_samples_pending_write.fetch_add ((int) nframes);
|
||||
|
||||
if (buf.size() != 0) {
|
||||
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
|
||||
|
||||
if (lm.locked ()) {
|
||||
/* Copy this data into our GUI feed buffer and tell the GUI
|
||||
that it can read it if it likes.
|
||||
*/
|
||||
_gui_feed_buffer.clear ();
|
||||
|
||||
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
|
||||
/* This may fail if buf is larger than _gui_feed_buffer, but it's not really
|
||||
* the end of the world if it does.
|
||||
*/
|
||||
samplepos_t mpos = (*i).time() + start_sample - _accumulated_capture_offset;
|
||||
if (mpos >= _first_recordable_sample) {
|
||||
_gui_feed_buffer.push_back (mpos, Evoral::MIDI_EVENT, (*i).size(), (*i).buffer());
|
||||
}
|
||||
/* Copy this data into our GUI feed buffer and tell the GUI
|
||||
* that it can read it if it likes.
|
||||
*/
|
||||
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
|
||||
/* This may fail if buf is larger than _gui_feed_fifo, but it's not really
|
||||
* the end of the world if it does.
|
||||
*/
|
||||
samplepos_t mpos = (*i).time() + start_sample - _accumulated_capture_offset;
|
||||
if (mpos >= _first_recordable_sample) {
|
||||
_gui_feed_fifo.write (mpos, Evoral::MIDI_EVENT, (*i).size(), (*i).buffer());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (cnt) {
|
||||
@ -807,10 +800,18 @@ DiskWriter::finish_capture (std::shared_ptr<ChannelList const> c)
|
||||
std::shared_ptr<MidiBuffer>
|
||||
DiskWriter::get_gui_feed_buffer () const
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (_gui_feed_reset_mutex);
|
||||
std::shared_ptr<MidiBuffer> b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)));
|
||||
|
||||
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex);
|
||||
b->copy (_gui_feed_buffer);
|
||||
vector<MIDI::byte> buffer (_gui_feed_fifo.capacity());
|
||||
samplepos_t time;
|
||||
Evoral::EventType type;
|
||||
uint32_t size;
|
||||
|
||||
while (_gui_feed_fifo.read (&time, &type, &size, &buffer[0])) {
|
||||
b->push_back (time, type, size, &buffer[0]);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
@ -1191,6 +1192,11 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
|
||||
|
||||
finish_capture (c);
|
||||
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (_gui_feed_reset_mutex);
|
||||
_gui_feed_fifo.reset ();
|
||||
}
|
||||
|
||||
/* butler is already stopped, but there may be work to do
|
||||
to flush remaining data to disk.
|
||||
*/
|
||||
|
@ -34,8 +34,8 @@ using namespace std;
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
|
||||
Signal2<std::pair<bool, string>,string, string> ElementImporter::Rename;
|
||||
Signal1 <bool,string> ElementImporter::Prompt;
|
||||
PBD::Signal2<std::pair<bool, string>,string, string> ElementImporter::Rename;
|
||||
PBD::Signal1 <bool,string> ElementImporter::Prompt;
|
||||
|
||||
ElementImporter::ElementImporter (XMLTree const & source, ARDOUR::Session & session) :
|
||||
source (source),
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "ardour/audiofile_tagger.h"
|
||||
#include "ardour/audio_port.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/disk_reader.h"
|
||||
#include "ardour/export_graph_builder.h"
|
||||
#include "ardour/export_handler.h"
|
||||
#include "ardour/export_timespan.h"
|
||||
@ -380,7 +381,10 @@ ExportHandler::start_timespan_bg (void* eh)
|
||||
ExportHandler* self = static_cast<ExportHandler*> (eh);
|
||||
self->process_connection.disconnect ();
|
||||
Glib::Threads::Mutex::Lock l (self->export_status->lock());
|
||||
SessionEvent::create_per_thread_pool (name, 512);
|
||||
DiskReader::allocate_working_buffers ();
|
||||
self->start_timespan ();
|
||||
DiskReader::free_working_buffers ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -447,17 +451,8 @@ ExportHandler::finish_timespan ()
|
||||
if (!fmt->command().empty()) {
|
||||
SessionMetadata const & metadata (*SessionMetadata::Metadata());
|
||||
|
||||
#if 0 // would be nicer with C++11 initialiser...
|
||||
std::map<char, std::string> subs {
|
||||
{ 'f', filename },
|
||||
{ 'd', Glib::path_get_dirname(filename) + G_DIR_SEPARATOR },
|
||||
{ 'b', PBD::basename_nosuffix(filename) },
|
||||
...
|
||||
};
|
||||
#endif
|
||||
export_status->active_job = ExportStatus::Command;
|
||||
PBD::ScopedConnection command_connection;
|
||||
std::map<char, std::string> subs;
|
||||
|
||||
std::stringstream track_number;
|
||||
track_number << metadata.track_number ();
|
||||
@ -466,30 +461,32 @@ ExportHandler::finish_timespan ()
|
||||
std::stringstream year;
|
||||
year << metadata.year ();
|
||||
|
||||
subs.insert (std::pair<char, std::string> ('a', metadata.artist ()));
|
||||
subs.insert (std::pair<char, std::string> ('b', PBD::basename_nosuffix (filename)));
|
||||
subs.insert (std::pair<char, std::string> ('c', metadata.copyright ()));
|
||||
subs.insert (std::pair<char, std::string> ('d', Glib::path_get_dirname (filename) + G_DIR_SEPARATOR));
|
||||
subs.insert (std::pair<char, std::string> ('f', filename));
|
||||
subs.insert (std::pair<char, std::string> ('l', metadata.lyricist ()));
|
||||
subs.insert (std::pair<char, std::string> ('n', session.name ()));
|
||||
subs.insert (std::pair<char, std::string> ('s', session.path ()));
|
||||
subs.insert (std::pair<char, std::string> ('o', metadata.conductor ()));
|
||||
subs.insert (std::pair<char, std::string> ('t', metadata.title ()));
|
||||
subs.insert (std::pair<char, std::string> ('z', metadata.organization ()));
|
||||
subs.insert (std::pair<char, std::string> ('A', metadata.album ()));
|
||||
subs.insert (std::pair<char, std::string> ('C', metadata.comment ()));
|
||||
subs.insert (std::pair<char, std::string> ('E', metadata.engineer ()));
|
||||
subs.insert (std::pair<char, std::string> ('G', metadata.genre ()));
|
||||
subs.insert (std::pair<char, std::string> ('L', total_tracks.str ()));
|
||||
subs.insert (std::pair<char, std::string> ('M', metadata.mixer ()));
|
||||
subs.insert (std::pair<char, std::string> ('N', current_timespan->name())); // =?= config_map.begin()->first->name ()
|
||||
subs.insert (std::pair<char, std::string> ('O', metadata.composer ()));
|
||||
subs.insert (std::pair<char, std::string> ('P', metadata.producer ()));
|
||||
subs.insert (std::pair<char, std::string> ('S', metadata.disc_subtitle ()));
|
||||
subs.insert (std::pair<char, std::string> ('T', track_number.str ()));
|
||||
subs.insert (std::pair<char, std::string> ('Y', year.str ()));
|
||||
subs.insert (std::pair<char, std::string> ('Z', metadata.country ()));
|
||||
std::map<char, std::string> subs {
|
||||
{'a', metadata.artist ()},
|
||||
{'b', PBD::basename_nosuffix (filename)},
|
||||
{'c', metadata.copyright ()},
|
||||
{'d', Glib::path_get_dirname (filename) + G_DIR_SEPARATOR},
|
||||
{'f', filename},
|
||||
{'l', metadata.lyricist ()},
|
||||
{'n', session.name ()},
|
||||
{'s', session.path ()},
|
||||
{'o', metadata.conductor ()},
|
||||
{'t', metadata.title ()},
|
||||
{'z', metadata.organization ()},
|
||||
{'A', metadata.album ()},
|
||||
{'C', metadata.comment ()},
|
||||
{'E', metadata.engineer ()},
|
||||
{'G', metadata.genre ()},
|
||||
{'L', total_tracks.str ()},
|
||||
{'M', metadata.mixer ()},
|
||||
{'N', current_timespan->name()}, // =?= config_map.begin()->first->name ()
|
||||
{'O', metadata.composer ()},
|
||||
{'P', metadata.producer ()},
|
||||
{'S', metadata.disc_subtitle ()},
|
||||
{'T', track_number.str ()},
|
||||
{'Y', year.str ()},
|
||||
{'Z', metadata.country ()}
|
||||
};
|
||||
|
||||
ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs, true);
|
||||
info << "Post-export command line : {" << se->to_s () << "}" << endmsg;
|
||||
|
@ -257,6 +257,7 @@ IOPlug::setup ()
|
||||
_plugin->reconfigure_io (_n_in, aux_in, _n_out);
|
||||
_plugin->ParameterChangedExternally.connect_same_thread (*this, boost::bind (&IOPlug::parameter_changed_externally, this, _1, _2));
|
||||
_plugin->activate ();
|
||||
_plugin->set_insert (this, 0);
|
||||
}
|
||||
|
||||
samplecnt_t
|
||||
@ -436,6 +437,26 @@ IOPlug::ensure_io ()
|
||||
return true;
|
||||
}
|
||||
|
||||
ChanMapping
|
||||
IOPlug::input_map (uint32_t num) const
|
||||
{
|
||||
if (num == 1) {
|
||||
return ChanMapping (_n_in);
|
||||
} else {
|
||||
return ChanMapping ();
|
||||
}
|
||||
}
|
||||
|
||||
ChanMapping
|
||||
IOPlug::output_map (uint32_t num) const
|
||||
{
|
||||
if (num == 1) {
|
||||
return ChanMapping (_n_out);
|
||||
} else {
|
||||
return ChanMapping ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IOPlug::process ()
|
||||
{
|
||||
|
@ -1638,7 +1638,7 @@ LuaBindings::common (lua_State* L)
|
||||
.addFunction ("has_transients", &Region::has_transients)
|
||||
.addFunction ("transients", (AnalysisFeatureList (Region::*)())&Region::transients)
|
||||
|
||||
#if 0
|
||||
#ifndef LIVETRAX // disable region FX
|
||||
.addFunction ("load_plugin", &Region::load_plugin)
|
||||
.addFunction ("add_plugin", &Region::add_plugin)
|
||||
.addFunction ("remove_plugin", &Region::add_plugin)
|
||||
@ -1694,7 +1694,9 @@ LuaBindings::common (lua_State* L)
|
||||
.addFunction ("envelope_active", &AudioRegion::envelope_active)
|
||||
.addFunction ("fade_in_active", &AudioRegion::fade_in_active)
|
||||
.addFunction ("fade_out_active", &AudioRegion::fade_out_active)
|
||||
.addFunction ("fade_before_fx", &AudioRegion::fade_before_fx)
|
||||
.addFunction ("set_envelope_active", &AudioRegion::set_envelope_active)
|
||||
.addFunction ("set_fade_before_fx", &AudioRegion::set_fade_before_fx)
|
||||
.addFunction ("set_fade_in_active", &AudioRegion::set_fade_in_active)
|
||||
.addFunction ("set_fade_in_shape", &AudioRegion::set_fade_in_shape)
|
||||
.addFunction ("set_fade_in_length", &AudioRegion::set_fade_in_length)
|
||||
|
@ -38,8 +38,6 @@
|
||||
#include <glib/gprintf.h>
|
||||
#include <glibmm.h>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "pbd/file_utils.h"
|
||||
#include "pbd/stl_delete.h"
|
||||
#include "pbd/compose.h"
|
||||
|
@ -338,6 +338,11 @@ MonitorPort::clear_ports (bool instantly)
|
||||
MonitorInputChanged (i->first, false); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
if (instantly) {
|
||||
/* release shared_ptr references */
|
||||
_monitor_ports.flush ();
|
||||
}
|
||||
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
@ -1989,16 +1989,16 @@ std::shared_ptr<RegionList>
|
||||
Playlist::regions_touched (timepos_t const & start, timepos_t const & end)
|
||||
{
|
||||
RegionReadLock rlock (this);
|
||||
return regions_touched_locked (start, end);
|
||||
return regions_touched_locked (start, end, false);
|
||||
}
|
||||
|
||||
std::shared_ptr<RegionList>
|
||||
Playlist::regions_touched_locked (timepos_t const & start, timepos_t const & end)
|
||||
Playlist::regions_touched_locked (timepos_t const & start, timepos_t const & end, bool with_tail)
|
||||
{
|
||||
std::shared_ptr<RegionList> rlist (new RegionList);
|
||||
|
||||
for (auto & r : regions) {
|
||||
if (r->coverage (start, end) != Temporal::OverlapNone) {
|
||||
if (r->coverage (start, end, with_tail) != Temporal::OverlapNone) {
|
||||
rlist->push_back (r);
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ Plugin::Plugin (AudioEngine& e, Session& s)
|
||||
, _parameter_changed_since_last_preset (false)
|
||||
, _immediate_events(6096) // FIXME: size?
|
||||
, _resolve_midi (false)
|
||||
, _pi (0)
|
||||
, _pib (0)
|
||||
, _num (0)
|
||||
{
|
||||
_pending_stop_events.ensure_buffers (DataType::MIDI, 1, 4096);
|
||||
@ -118,7 +118,7 @@ Plugin::Plugin (const Plugin& other)
|
||||
, _parameter_changed_since_last_preset (false)
|
||||
, _immediate_events(6096) // FIXME: size?
|
||||
, _resolve_midi (false)
|
||||
, _pi (other._pi)
|
||||
, _pib (other._pib)
|
||||
, _num (other._num)
|
||||
{
|
||||
_pending_stop_events.ensure_buffers (DataType::MIDI, 1, 4096);
|
||||
@ -317,6 +317,12 @@ Plugin::input_streams () const
|
||||
return ChanCount::ZERO;
|
||||
}
|
||||
|
||||
samplecnt_t
|
||||
Plugin::plugin_tailtime () const
|
||||
{
|
||||
return _session.sample_rate () * Config->get_tail_duration_sec ();
|
||||
}
|
||||
|
||||
Plugin::IOPortDescription
|
||||
Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id) const
|
||||
{
|
||||
|
@ -1160,6 +1160,9 @@ PortManager::update_input_ports (bool clear)
|
||||
* do this when called from ::reestablish_ports()
|
||||
* "JACK: Cannot connect ports owned by inactive clients"
|
||||
*/
|
||||
/* .. but take the opportunity to clear out dead wood */
|
||||
_audio_input_ports.flush ();
|
||||
_midi_input_ports.flush ();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -296,6 +296,7 @@ Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, c
|
||||
: SessionObject(s, name)
|
||||
, _type (type)
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_DEFAULT_STATE (start,length)
|
||||
, _last_length (length)
|
||||
, _first_edit (EditChangesNothing)
|
||||
@ -312,6 +313,7 @@ Region::Region (const SourceList& srcs)
|
||||
: SessionObject(srcs.front()->session(), "toBeRenamed")
|
||||
, _type (srcs.front()->type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (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))
|
||||
@ -332,6 +334,7 @@ Region::Region (std::shared_ptr<const Region> other)
|
||||
: SessionObject(other->session(), other->name())
|
||||
, _type (other->data_type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_COPY_STATE (other)
|
||||
, _last_length (other->_last_length)
|
||||
, _first_edit (EditChangesNothing)
|
||||
@ -391,6 +394,7 @@ Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
|
||||
: SessionObject(other->session(), other->name())
|
||||
, _type (other->data_type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_COPY_STATE (other)
|
||||
, _last_length (other->_last_length)
|
||||
, _first_edit (EditChangesNothing)
|
||||
@ -437,6 +441,7 @@ Region::Region (std::shared_ptr<const Region> other, const SourceList& srcs)
|
||||
: SessionObject (other->session(), other->name())
|
||||
, _type (srcs.front()->type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_COPY_STATE (other)
|
||||
, _last_length (other->_last_length)
|
||||
, _first_edit (EditChangesID)
|
||||
@ -1574,12 +1579,20 @@ Region::_set_state (const XMLNode& node, int version, PropertyChange& what_chang
|
||||
Glib::Threads::RWLock::WriterLock lm (_fx_lock);
|
||||
bool changed = !_plugins.empty ();
|
||||
|
||||
for (auto const& rfx : _plugins) {
|
||||
rfx->drop_references ();
|
||||
}
|
||||
|
||||
_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 (rfx->set_state (*child, version)) {
|
||||
PBD::warning << string_compose (_("Failed to load RegionFx Plugin for region `%1'"), name()) << endmsg;
|
||||
// TODO replace w/stub, retain config
|
||||
continue;
|
||||
}
|
||||
if (!_add_plugin (rfx, std::shared_ptr<RegionFxPlugin>(), true)) {
|
||||
continue;
|
||||
}
|
||||
@ -1587,8 +1600,10 @@ Region::_set_state (const XMLNode& node, int version, PropertyChange& what_chang
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
lm.release ();
|
||||
if (changed) {
|
||||
fx_latency_changed (true);
|
||||
fx_tail_changed (true);
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
RegionFxChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
@ -2408,7 +2423,11 @@ Region::load_plugin (ARDOUR::PluginType type, std::string const& name)
|
||||
bool
|
||||
Region::add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<RegionFxPlugin> pos)
|
||||
{
|
||||
return _add_plugin (rfx, pos, false);
|
||||
bool rv = _add_plugin (rfx, pos, false);
|
||||
if (rv) {
|
||||
_session.set_dirty ();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2435,6 +2454,7 @@ Region::reorder_plugins (RegionFxList const& new_order)
|
||||
oiter = _plugins.erase (oiter);
|
||||
}
|
||||
_plugins.insert (oiter, as_it_will_be.begin (), as_it_will_be.end ());
|
||||
_session.set_dirty ();
|
||||
}
|
||||
|
||||
void
|
||||
@ -2449,3 +2469,16 @@ Region::fx_latency_changed (bool)
|
||||
}
|
||||
_fx_latency = l;
|
||||
}
|
||||
|
||||
void
|
||||
Region::fx_tail_changed (bool)
|
||||
{
|
||||
uint32_t t = 0;
|
||||
for (auto const& rfx : _plugins) {
|
||||
t = max<uint32_t> (t, rfx->effective_tailtime ());
|
||||
}
|
||||
if (t == _fx_tail) {
|
||||
return;
|
||||
}
|
||||
_fx_tail = t;
|
||||
}
|
||||
|
@ -37,18 +37,189 @@ using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
class TimedReadOnlyControl : public ReadOnlyControl {
|
||||
public:
|
||||
TimedReadOnlyControl (std::shared_ptr<Plugin> p, const ParameterDescriptor& desc, uint32_t pnum)
|
||||
: ReadOnlyControl (p, desc, pnum)
|
||||
, _flush (false)
|
||||
{}
|
||||
|
||||
double get_parameter () const {
|
||||
std::shared_ptr<Plugin> p = _plugin.lock();
|
||||
|
||||
if (!p) {
|
||||
return 0;
|
||||
}
|
||||
samplepos_t when = p->session().audible_sample ();
|
||||
|
||||
Glib::Threads::Mutex::Lock lm (_history_mutex);
|
||||
auto it = _history.lower_bound (when);
|
||||
if (it != _history.begin ()) {
|
||||
--it;
|
||||
}
|
||||
if (it == _history.end ()) {
|
||||
return p->get_parameter (_parameter_num);
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void flush () {
|
||||
_flush = true;
|
||||
}
|
||||
|
||||
void store_value (samplepos_t start, samplepos_t end) {
|
||||
std::shared_ptr<Plugin> p = _plugin.lock();
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
double value = p->get_parameter (_parameter_num);
|
||||
Glib::Threads::Mutex::Lock lm (_history_mutex);
|
||||
if (_flush) {
|
||||
_flush = false;
|
||||
_history.clear ();
|
||||
}
|
||||
auto it = _history.lower_bound (start);
|
||||
if (it != _history.begin ()) {
|
||||
--it;
|
||||
if (it->second == value) {
|
||||
return;
|
||||
}
|
||||
assert (start > it->first);
|
||||
if (start - it->first < 512) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_history[start] = value;
|
||||
|
||||
if (_history.size () > 2000 && std::distance (_history.begin(), it) > 1500) {
|
||||
samplepos_t when = min (start, p->session().audible_sample ());
|
||||
auto io = _history.lower_bound (when - p->session().sample_rate ());
|
||||
if (std::distance (io, it) > 1) {
|
||||
_history.erase (_history.begin(), io);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<samplepos_t, double> _history;
|
||||
mutable Glib::Threads::Mutex _history_mutex;
|
||||
bool _flush;
|
||||
};
|
||||
|
||||
class TimedPluginControl : public PlugInsertBase::PluginControl
|
||||
{
|
||||
public:
|
||||
TimedPluginControl (Session& s,
|
||||
PlugInsertBase* p,
|
||||
const Evoral::Parameter& param,
|
||||
const ParameterDescriptor& desc,
|
||||
std::shared_ptr<AutomationList> list,
|
||||
bool replay_param)
|
||||
: PlugInsertBase::PluginControl (s, p, param, desc, list)
|
||||
, _last_value (desc.lower - 1)
|
||||
, _replay_param (replay_param)
|
||||
, _flush (false)
|
||||
{
|
||||
}
|
||||
|
||||
double get_value (void) const
|
||||
{
|
||||
samplepos_t when = _session.audible_sample ();
|
||||
Glib::Threads::Mutex::Lock lm (_history_mutex);
|
||||
auto it = _history.lower_bound (when);
|
||||
if (it != _history.begin ()) {
|
||||
--it;
|
||||
}
|
||||
if (it == _history.end ()) {
|
||||
return PlugInsertBase::PluginControl::get_value ();
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool maybe_emit_changed ()
|
||||
{
|
||||
double current = get_value ();
|
||||
if (current == _last_value) {
|
||||
return false;
|
||||
}
|
||||
_last_value = current;
|
||||
if (_replay_param) { // AU, VST2
|
||||
/* this is only called for automated parameters.
|
||||
* Next call to ::run() will set the actual value before
|
||||
* running the plugin (via automation_run).
|
||||
*/
|
||||
actually_set_value (current, PBD::Controllable::NoGroup);
|
||||
} else { // generic UI, LV2
|
||||
Changed (true, Controllable::NoGroup);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void flush () {
|
||||
if (automation_playback ()) {
|
||||
_flush = true;
|
||||
} else {
|
||||
Glib::Threads::Mutex::Lock lm (_history_mutex);
|
||||
_history.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void store_value (samplepos_t start, samplepos_t end)
|
||||
{
|
||||
double value = PlugInsertBase::PluginControl::get_value ();
|
||||
Glib::Threads::Mutex::Lock lm (_history_mutex);
|
||||
if (_flush) {
|
||||
_flush = false;
|
||||
_history.clear ();
|
||||
}
|
||||
auto it = _history.lower_bound (start);
|
||||
if (it != _history.begin ()) {
|
||||
--it;
|
||||
if (it->second == value) {
|
||||
return;
|
||||
}
|
||||
assert (start > it->first);
|
||||
if (start - it->first < 512) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_history[start] = value;
|
||||
|
||||
/* do not accumulate */
|
||||
if (_history.size () > 2000 && std::distance (_history.begin(), it) > 1500) {
|
||||
samplepos_t when = min (start, _session.audible_sample ());
|
||||
auto io = _history.lower_bound (when - _session.sample_rate ());
|
||||
if (std::distance (io, it) > 1) {
|
||||
_history.erase (_history.begin(), io);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<samplepos_t, double> _history;
|
||||
mutable Glib::Threads::Mutex _history_mutex;
|
||||
double _last_value;
|
||||
bool _replay_param;
|
||||
bool _flush;
|
||||
};
|
||||
|
||||
RegionFxPlugin::RegionFxPlugin (Session& s, Temporal::TimeDomain const td, std::shared_ptr<Plugin> plug)
|
||||
: SessionObject (s, (plug ? plug->name () : string ("toBeRenamed")))
|
||||
, TimeDomainProvider (td)
|
||||
, _plugin_signal_latency (0)
|
||||
, _configured (false)
|
||||
, _no_inplace (false)
|
||||
, _last_emit (0)
|
||||
, _window_proxy (0)
|
||||
, _state (0)
|
||||
{
|
||||
_flush.store (0);
|
||||
|
||||
if (plug) {
|
||||
add_plugin (plug);
|
||||
plug->activate ();
|
||||
create_parameters ();
|
||||
}
|
||||
}
|
||||
@ -64,14 +235,22 @@ RegionFxPlugin::~RegionFxPlugin ()
|
||||
std::dynamic_pointer_cast<AutomationControl>(i.second)->drop_references ();
|
||||
}
|
||||
_controls.clear ();
|
||||
|
||||
delete _state;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
RegionFxPlugin::get_state () const
|
||||
{
|
||||
if (_plugins.empty ()) {
|
||||
assert (_state);
|
||||
return *(new XMLNode (*_state));
|
||||
}
|
||||
|
||||
XMLNode* node = new XMLNode (/*state_node_name*/ "RegionFXPlugin");
|
||||
|
||||
Latent::add_state (node);
|
||||
TailTime::add_state (node);
|
||||
|
||||
node->set_property ("type", _plugins[0]->state_node_name ());
|
||||
node->set_property ("unique-id", _plugins[0]->unique_id ());
|
||||
@ -124,7 +303,15 @@ RegionFxPlugin::set_state (const XMLNode& node, int version)
|
||||
std::shared_ptr<Plugin> plugin = find_and_load_plugin (_session, node, type, unique_id, any_vst);
|
||||
|
||||
if (!plugin) {
|
||||
return -1;
|
||||
delete _state;
|
||||
_state = new XMLNode (node);
|
||||
string name;
|
||||
if (node.get_property ("name", name)) {
|
||||
set_name (name);
|
||||
} else {
|
||||
set_name ("Unknown Plugin");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
add_plugin (plugin);
|
||||
@ -197,9 +384,29 @@ RegionFxPlugin::set_state (const XMLNode& node, int version)
|
||||
ac->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
Latent::set_state (node, version);
|
||||
TailTime::set_state (node, version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PluginType
|
||||
RegionFxPlugin::type () const
|
||||
{
|
||||
if (!_plugins.empty ()) {
|
||||
return plugin ()->get_info ()->type;
|
||||
}
|
||||
if (_state) {
|
||||
ARDOUR::PluginType type;
|
||||
std::string unique_id;
|
||||
if (parse_plugin_type (*_state, type, unique_id)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return LXVST; /* whatever */
|
||||
}
|
||||
|
||||
void
|
||||
RegionFxPlugin::update_id (PBD::ID id)
|
||||
{
|
||||
@ -222,6 +429,8 @@ RegionFxPlugin::add_plugin (std::shared_ptr<Plugin> plugin)
|
||||
plugin->EndTouch.connect_same_thread (*this, boost::bind (&RegionFxPlugin::end_touch, this, _1));
|
||||
}
|
||||
|
||||
plugin->set_insert (this, _plugins.size ());
|
||||
|
||||
_plugins.push_back (plugin);
|
||||
|
||||
if (_plugins.size () > 1) {
|
||||
@ -291,9 +500,21 @@ RegionFxPlugin::drop_references ()
|
||||
ARDOUR::samplecnt_t
|
||||
RegionFxPlugin::signal_latency () const
|
||||
{
|
||||
if (_plugins.empty ()) {
|
||||
return 0;
|
||||
}
|
||||
return _plugins.front ()->signal_latency ();
|
||||
}
|
||||
|
||||
ARDOUR::samplecnt_t
|
||||
RegionFxPlugin::signal_tailtime () const
|
||||
{
|
||||
if (_plugins.empty ()) {
|
||||
return 0;
|
||||
}
|
||||
return _plugins.front ()->signal_tailtime ();
|
||||
}
|
||||
|
||||
PlugInsertBase::UIElements
|
||||
RegionFxPlugin::ui_elements () const
|
||||
{
|
||||
@ -308,6 +529,18 @@ RegionFxPlugin::create_parameters ()
|
||||
std::shared_ptr<Plugin> plugin = _plugins.front ();
|
||||
set<Evoral::Parameter> a = _plugins.front ()->automatable ();
|
||||
|
||||
bool replay_param = false;
|
||||
switch (_plugins.front ()->get_info ()->type) {
|
||||
case AudioUnit:
|
||||
case LXVST:
|
||||
case MacVST:
|
||||
case Windows_VST:
|
||||
replay_param = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < plugin->parameter_count (); ++i) {
|
||||
if (!plugin->parameter_is_control (i)) {
|
||||
continue;
|
||||
@ -317,7 +550,7 @@ RegionFxPlugin::create_parameters ()
|
||||
plugin->get_parameter_descriptor (i, desc);
|
||||
|
||||
if (!plugin->parameter_is_input (i)) {
|
||||
_control_outputs[i] = std::shared_ptr<ReadOnlyControl> (new ReadOnlyControl (plugin, desc, i));
|
||||
_control_outputs[i] = std::shared_ptr<TimedReadOnlyControl> (new TimedReadOnlyControl (plugin, desc, i));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -325,7 +558,7 @@ RegionFxPlugin::create_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 (_session, this, param, desc, list));
|
||||
std::shared_ptr<AutomationControl> c (new TimedPluginControl (_session, this, param, desc, list, replay_param));
|
||||
if (!automatable) {
|
||||
c->set_flag (Controllable::NotAutomatable);
|
||||
}
|
||||
@ -448,6 +681,7 @@ RegionFxPlugin::parameter_changed_externally (uint32_t which, float val)
|
||||
std::string
|
||||
RegionFxPlugin::describe_parameter (Evoral::Parameter param)
|
||||
{
|
||||
assert (!_plugins.empty ());
|
||||
if (param.type () == PluginAutomation) {
|
||||
return _plugins[0]->describe_parameter (param);
|
||||
} else if (param.type () == PluginPropertyAutomation) {
|
||||
@ -462,7 +696,6 @@ RegionFxPlugin::describe_parameter (Evoral::Parameter param)
|
||||
void
|
||||
RegionFxPlugin::start_touch (uint32_t param_id)
|
||||
{
|
||||
assert (0); // touch is N/A
|
||||
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl> (control (Evoral::Parameter (PluginAutomation, 0, param_id)));
|
||||
if (ac) {
|
||||
ac->start_touch (timepos_t (_session.audible_sample ())); // XXX subtract region position
|
||||
@ -472,7 +705,6 @@ RegionFxPlugin::start_touch (uint32_t param_id)
|
||||
void
|
||||
RegionFxPlugin::end_touch (uint32_t param_id)
|
||||
{
|
||||
assert (0); // touch is N/A
|
||||
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl> (control (Evoral::Parameter (PluginAutomation, 0, param_id)));
|
||||
if (ac) {
|
||||
ac->stop_touch (timepos_t (_session.audible_sample ())); // XXX subtract region position
|
||||
@ -482,6 +714,10 @@ RegionFxPlugin::end_touch (uint32_t param_id)
|
||||
bool
|
||||
RegionFxPlugin::can_reset_all_parameters ()
|
||||
{
|
||||
if (_plugins.empty ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool all = true;
|
||||
uint32_t params = 0;
|
||||
std::shared_ptr<Plugin> plugin = _plugins.front ();
|
||||
@ -511,6 +747,8 @@ RegionFxPlugin::can_reset_all_parameters ()
|
||||
bool
|
||||
RegionFxPlugin::reset_parameters_to_default ()
|
||||
{
|
||||
assert (!_plugins.empty ());
|
||||
|
||||
bool all = true;
|
||||
std::shared_ptr<Plugin> plugin = _plugins.front ();
|
||||
|
||||
@ -548,11 +786,24 @@ void
|
||||
RegionFxPlugin::flush ()
|
||||
{
|
||||
_flush.store (1);
|
||||
|
||||
for (auto const& i : _control_outputs) {
|
||||
shared_ptr<TimedReadOnlyControl> toc = std::dynamic_pointer_cast<TimedReadOnlyControl>(i.second);
|
||||
toc->flush ();
|
||||
}
|
||||
for (auto const& i : _controls) {
|
||||
shared_ptr<TimedPluginControl> tpc = std::dynamic_pointer_cast<TimedPluginControl>(i.second);
|
||||
tpc->flush ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RegionFxPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
|
||||
{
|
||||
if (_plugins.empty ()) {
|
||||
out = ChanCount::min (in, out);
|
||||
return true;
|
||||
}
|
||||
return private_can_support_io_configuration (in, out).method != Impossible;
|
||||
}
|
||||
|
||||
@ -706,6 +957,10 @@ RegionFxPlugin::configure_io (ChanCount in, ChanCount out)
|
||||
_configured_in = in;
|
||||
_configured_out = out;
|
||||
|
||||
if (_plugins.empty ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ChanCount natural_input_streams = _plugins[0]->get_info ()->n_inputs;
|
||||
ChanCount natural_output_streams = _plugins[0]->get_info ()->n_outputs;
|
||||
|
||||
@ -733,10 +988,13 @@ RegionFxPlugin::configure_io (ChanCount in, ChanCount out)
|
||||
if (_plugins.front ()->reconfigure_io (din, daux, dout) == false) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::RegionFx, string_compose ("Delegate configured in: %1, out: %2 for in: %3 out: %4", din, dout, in, _configured_out));
|
||||
DEBUG_TRACE (DEBUG::RegionFx, string_compose ("Delegate configured in: %1, out: %2 for in: %3 out: %4\n", din, dout, in, _configured_out));
|
||||
if (din < in || dout < _configured_out) {
|
||||
return false;
|
||||
}
|
||||
/* update after match_variable_io sets info */
|
||||
natural_input_streams = _plugins[0]->get_info ()->n_inputs;
|
||||
natural_output_streams = _plugins[0]->get_info ()->n_outputs;
|
||||
}
|
||||
break;
|
||||
case Replicate:
|
||||
@ -830,6 +1088,7 @@ RegionFxPlugin::configure_io (ChanCount in, ChanCount out)
|
||||
DEBUG_STR_APPEND(a, _out_map[pc]);
|
||||
}
|
||||
DEBUG_STR_APPEND(a, "-------->>--------\n");
|
||||
DEBUG_TRACE (DEBUG::RegionFx, DEBUG_STR(a).str());
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -989,6 +1248,12 @@ RegionFxPlugin::find_next_event (timepos_t const& start, timepos_t const& end, E
|
||||
bool
|
||||
RegionFxPlugin::run (BufferSet& bufs, samplepos_t start, samplepos_t end, samplepos_t pos, pframes_t nframes, sampleoffset_t off)
|
||||
{
|
||||
if (_plugins.empty ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Glib::Threads::Mutex::Lock lp (_process_lock);
|
||||
|
||||
int canderef (1);
|
||||
if (_flush.compare_exchange_strong (canderef, 0)) {
|
||||
for (auto const& i : _plugins) {
|
||||
@ -1162,10 +1427,47 @@ RegionFxPlugin::connect_and_run (BufferSet& bufs, samplepos_t start, samplepos_t
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& i : _control_outputs) {
|
||||
shared_ptr<TimedReadOnlyControl> toc = std::dynamic_pointer_cast<TimedReadOnlyControl>(i.second);
|
||||
toc->store_value (start + pos, end + pos);
|
||||
}
|
||||
|
||||
for (auto const& i : _controls) {
|
||||
shared_ptr<TimedPluginControl> tpc = std::dynamic_pointer_cast<TimedPluginControl>(i.second);
|
||||
if (tpc->automation_playback ()) {
|
||||
tpc->store_value (start + pos, end + pos);
|
||||
}
|
||||
}
|
||||
|
||||
const samplecnt_t l = effective_latency ();
|
||||
if (_plugin_signal_latency != l) {
|
||||
_plugin_signal_latency= l;
|
||||
LatencyChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
const samplecnt_t t = effective_latency ();
|
||||
if (_plugin_signal_tailtime != l) {
|
||||
_plugin_signal_tailtime = t;
|
||||
TailTimeChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RegionFxPlugin::maybe_emit_changed_signals () const
|
||||
{
|
||||
if (!_session.transport_rolling ()) {
|
||||
samplepos_t when = _session.audible_sample ();
|
||||
if (_last_emit == when) {
|
||||
return;
|
||||
}
|
||||
_last_emit = when;
|
||||
}
|
||||
|
||||
Glib::Threads::Mutex::Lock lp (_process_lock);
|
||||
for (auto const& i : _controls) {
|
||||
shared_ptr<TimedPluginControl> tpc = std::dynamic_pointer_cast<TimedPluginControl>(i.second);
|
||||
if (tpc->automation_playback ()) {
|
||||
tpc->maybe_emit_changed ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,31 +400,48 @@ Route::process_output_buffers (BufferSet& bufs,
|
||||
return;
|
||||
}
|
||||
|
||||
/* We should offset the route-owned ctrls by the given latency, however
|
||||
* this only affects Mute. Other route-owned controls (solo, polarity..)
|
||||
* are not automatable.
|
||||
*
|
||||
* Mute has its own issues since there's not a single mute-point,
|
||||
* but in general
|
||||
*/
|
||||
automation_run (start_sample, nframes);
|
||||
|
||||
if (_pannable) {
|
||||
_pannable->automation_run (start_sample + _signal_latency, nframes);
|
||||
/* this is only for the benfit of updating the UI.
|
||||
*
|
||||
* Panner's `::distribute_one_automated()` evalualte
|
||||
* a sample-accurate curve using start/end of the
|
||||
* delivery processor.
|
||||
*/
|
||||
_pannable->automation_run (start_sample, nframes);
|
||||
}
|
||||
|
||||
const int speed = (is_auditioner() ? 1 : _session.transport_speed ());
|
||||
assert (speed == -1 || speed == 0 || speed == 1);
|
||||
|
||||
const samplecnt_t output_latency = speed * _output_latency;
|
||||
const samplecnt_t latency_offset = speed * (_signal_latency + _output_latency);
|
||||
|
||||
/* Mute is the only actual route-owned control (solo, solo safe, polarity
|
||||
* are not automatable).
|
||||
*
|
||||
* Here we offset mute automation to align to output/master bus
|
||||
* to be consistent with the fader. This applied to the
|
||||
* "Main outs" mute point.
|
||||
*
|
||||
* Other mute points in the middle of signal flow flow
|
||||
* will not be handled correctly. That would mean to add
|
||||
* _signal_latency - accumulated processor effective_latency() at mute mute
|
||||
*/
|
||||
|
||||
automation_run (start_sample + output_latency, nframes);
|
||||
|
||||
/* figure out if we're going to use gain automation */
|
||||
if (gain_automation_ok) {
|
||||
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
|
||||
_amp->setup_gain_automation (
|
||||
start_sample + _amp->output_latency (),
|
||||
end_sample + _amp->output_latency (),
|
||||
start_sample + _amp->output_latency () + output_latency,
|
||||
end_sample + _amp->output_latency () + output_latency,
|
||||
nframes);
|
||||
|
||||
_trim->set_gain_automation_buffer (_session.trim_automation_buffer ());
|
||||
_trim->setup_gain_automation (
|
||||
start_sample + _trim->output_latency (),
|
||||
end_sample + _trim->output_latency (),
|
||||
start_sample + _trim->output_latency () + output_latency,
|
||||
end_sample + _trim->output_latency () + output_latency,
|
||||
nframes);
|
||||
}
|
||||
|
||||
@ -438,18 +455,8 @@ Route::process_output_buffers (BufferSet& bufs,
|
||||
* -> at Time T= -15, the disk-reader reads sample T=0.
|
||||
* By the Time T=0 is reached (dt=15 later) that sample is audible.
|
||||
*/
|
||||
|
||||
const double speed = (is_auditioner() ? 1.0 : _session.transport_speed ());
|
||||
|
||||
const sampleoffset_t latency_offset = _signal_latency + _output_latency;
|
||||
if (speed < 0) {
|
||||
/* when rolling backwards this can become negative */
|
||||
start_sample -= latency_offset;
|
||||
end_sample -= latency_offset;
|
||||
} else {
|
||||
start_sample += latency_offset;
|
||||
end_sample += latency_offset;
|
||||
}
|
||||
start_sample += latency_offset;
|
||||
end_sample += latency_offset;
|
||||
|
||||
/* Note: during initial pre-roll 'start_sample' as passed as argument can be negative.
|
||||
* Functions calling process_output_buffers() will set "run_disk_reader"
|
||||
|
@ -44,8 +44,6 @@
|
||||
#include <glibmm/miscutils.h>
|
||||
#include <glibmm/fileutils.h>
|
||||
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
||||
#include "pbd/atomic.h"
|
||||
#include "pbd/basename.h"
|
||||
#include "pbd/convert.h"
|
||||
@ -5574,6 +5572,7 @@ Session::unload_io_plugin (std::shared_ptr<IOPlug> ioplugin)
|
||||
}
|
||||
IOPluginsChanged (); /* EMIT SIGNAL */
|
||||
set_dirty();
|
||||
_io_plugins.flush ();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,7 @@ Session::remove_bundle (std::shared_ptr<Bundle> bundle)
|
||||
|
||||
if (removed) {
|
||||
BundleAddedOrRemoved (); /* EMIT SIGNAL */
|
||||
_bundles.flush ();
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
|
@ -173,6 +173,8 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex
|
||||
/* get everyone to the right position */
|
||||
|
||||
std::shared_ptr<RouteList const> rl = routes.reader();
|
||||
ARDOUR::ProcessThread* pt = new ProcessThread ();
|
||||
pt->get_buffers ();
|
||||
|
||||
for (auto const& i : *rl) {
|
||||
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (i);
|
||||
@ -182,6 +184,8 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
pt->drop_buffers ();
|
||||
delete pt;
|
||||
}
|
||||
|
||||
/* we just did the core part of a locate call above, but
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/enumwriter.h"
|
||||
@ -1258,6 +1256,12 @@ Session::plan_master_strategy_engine (pframes_t nframes, double master_speed, sa
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, "JACK transport: not moving\n");
|
||||
|
||||
if (!transport_stopped_or_stopping()) {
|
||||
DEBUG_TRACE (DEBUG::Slave, "JACK Transport: jack is stopped, we are not, so stop ...\n");
|
||||
TFSM_STOP (false, false);
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
const samplecnt_t wlp = worst_latency_preroll_buffer_size_ceil ();
|
||||
|
||||
if (delta != wlp) {
|
||||
|
@ -425,10 +425,10 @@ Session::post_engine_init ()
|
||||
void
|
||||
Session::session_loaded ()
|
||||
{
|
||||
SessionLoaded();
|
||||
|
||||
set_clean ();
|
||||
|
||||
SessionLoaded();
|
||||
|
||||
if (_is_new) {
|
||||
save_state ("");
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user