13
0

Merge branch 'ardour'

This commit is contained in:
Robin Gareus 2024-09-05 23:49:53 +02:00
commit 544b53f65c
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
172 changed files with 11459 additions and 3515 deletions

View File

@ -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.

View File

@ -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

View File

@ -48,8 +48,6 @@
#include <list>
#include <cmath>
#include <boost/utility.hpp>
#include "pbd/xml++.h"
#include <gtkmm/box.h>
#include <gtkmm/frame.h>

View File

@ -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 ());

View File

@ -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;

View File

@ -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));

View File

@ -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"

View File

@ -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

View File

@ -20,8 +20,6 @@
#include <cstdlib>
#include <ctime>
#include <boost/integer/common_factor.hpp>
#include "pbd/compose.h"
#include "pbd/i18n.h"

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@ -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>

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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"

View File

@ -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.

View File

@ -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)
{

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 ();
}

View File

@ -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 ();

View File

@ -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();

View File

@ -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:

View File

@ -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())));

View File

@ -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;

View File

@ -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__ */

View File

@ -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.

View File

@ -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',

View File

@ -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;

View File

@ -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"

View File

@ -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:

View File

@ -24,7 +24,7 @@
#include <stddef.h>
#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"

View File

@ -25,8 +25,6 @@
#include <string>
#include "boost/shared_ptr.hpp"
#include "pbd/signals.h"
#include "ardour/libardour_visibility.h"

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -22,8 +22,6 @@
#include <map>
#include <set>
#include <boost/shared_ptr.hpp>
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"

View File

@ -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);

View File

@ -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>

View File

@ -28,7 +28,6 @@
#include <set>
#include <string>
#include <vector>
#include <boost/enable_shared_from_this.hpp>
#include "temporal/tempo.h"

View File

@ -21,8 +21,6 @@
#include <set>
#include <boost/utility.hpp>
#include "pbd/signals.h"

View File

@ -29,7 +29,6 @@
#include <queue>
#include <utility>
#include <boost/utility.hpp>
#include <glibmm/threads.h>
#include "pbd/command.h"

View File

@ -26,8 +26,6 @@
#include <vector>
#include <list>
#include <boost/utility.hpp>
#include "evoral/Parameter.h"
#include "ardour/ardour.h"

View File

@ -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"

View File

@ -22,8 +22,6 @@
#include <memory>
#include <string>
#include <boost/dynamic_bitset.hpp>
#include "ardour/slavable_automation_control.h"
#include "ardour/monitorable.h"

View File

@ -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>);

View File

@ -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);

View File

@ -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;
};

View File

@ -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

View File

@ -32,7 +32,6 @@
#include <map>
#include <string>
#include <set>
#include <boost/utility.hpp>
#include <boost/container/set.hpp>
#include "ardour/libardour_visibility.h"

View File

@ -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"

View File

@ -23,8 +23,6 @@
#include <vector>
#include "ardour/chan_count.h"
#include <boost/utility.hpp>
namespace ARDOUR {
class Port;

View File

@ -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@")

View File

@ -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;

View File

@ -22,8 +22,6 @@
#include <memory>
#include <string>
#include <boost/dynamic_bitset.hpp>
#include "ardour/slavable_automation_control.h"
#include "ardour/recordable.h"

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -30,8 +30,6 @@
#include <glibmm/threads.h>
#include <boost/utility.hpp>
#include "pbd/statefuldestructible.h"
#include "ardour/ardour.h"

View File

@ -23,7 +23,6 @@
#include <unistd.h>
#include <boost/atomic.hpp>
#include <boost/rational.hpp>
#include <boost/intrusive/list.hpp>
#include <glibmm/threads.h>

View File

@ -26,8 +26,6 @@
#include <memory>
#include <string>
#include <boost/utility.hpp>
#include "pbd/signals.h"
#include "ardour/automatable.h"

View 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__*/

View File

@ -23,8 +23,6 @@
#include <map>
#include <boost/utility.hpp>
#include <glibmm/threads.h>
#ifdef HAVE_LV2_1_18_6

View File

@ -32,8 +32,6 @@
#include <string>
#include <cmath>
#include "boost/shared_ptr.hpp"
#if __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#endif /* __APPLE__ */

View File

@ -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);

View File

@ -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 ..."

View File

@ -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 ();
}

View File

@ -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);

View File

@ -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");

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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),

View File

@ -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;

View File

@ -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 ()
{

View File

@ -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)

View File

@ -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"

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 ();
}
}
}

View File

@ -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"

View File

@ -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;
}

View File

@ -75,6 +75,7 @@ Session::remove_bundle (std::shared_ptr<Bundle> bundle)
if (removed) {
BundleAddedOrRemoved (); /* EMIT SIGNAL */
_bundles.flush ();
}
set_dirty();

View File

@ -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

View File

@ -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) {

View File

@ -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