#include <boost/algorithm/string/erase.hpp>
#include "pbd/basename.h"
-#include "pbd/boost_debug.h"
#include "pbd/convert.h"
#include "pbd/convert.h"
#include "pbd/error.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
#include "ardour/auditioner.h"
+#include "ardour/boost_debug.h"
#include "ardour/buffer_manager.h"
#include "ardour/buffer_set.h"
#include "ardour/bundle.h"
#include "ardour/session_directory.h"
#include "ardour/session_playlists.h"
#include "ardour/smf_source.h"
+#include "ardour/solo_isolate_control.h"
#include "ardour/source_factory.h"
#include "ardour/speakers.h"
#include "ardour/tempo.h"
#include "ardour/track.h"
#include "ardour/user_bundle.h"
#include "ardour/utils.h"
+#include "ardour/vca_manager.h"
#include "midi++/port.h"
#include "midi++/mmc.h"
, first_file_header_format_reset (true)
, have_looped (false)
, _have_rec_enabled_track (false)
- , _have_rec_disabled_track (true)
+ , _have_rec_disabled_track (true)
, _step_editors (0)
, _suspend_timecode_transmission (0)
, _speakers (new Speakers)
, _scene_changer (0)
, _midi_ports (0)
, _mmc (0)
+ , _vca_manager (new VCAManager (*this))
{
uint32_t sr = 0;
#ifdef USE_TRACKS_CODE_FEATURES
sr = EngineStateController::instance()->get_current_sample_rate();
#endif
- if (ensure_engine (sr)) {
+ if (ensure_engine (sr, true)) {
destroy ();
throw SessionException (_("Cannot connect to audio/midi engine"));
}
*/
if (state_tree) {
- const XMLProperty* prop;
- if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) {
+ XMLProperty const * prop;
+ XMLNode const * root (state_tree->root());
+ if ((prop = root->property (X_("sample-rate"))) != 0) {
sr = atoi (prop->value());
}
}
- if (ensure_engine (sr)) {
+ if (ensure_engine (sr, false)) {
destroy ();
throw SessionException (_("Cannot connect to audio/midi engine"));
}
}
int
-Session::ensure_engine (uint32_t desired_sample_rate)
+Session::ensure_engine (uint32_t desired_sample_rate, bool isnew)
{
if (_engine.current_backend() == 0) {
/* backend is unknown ... */
if (r.get_value_or (-1) != 0) {
return -1;
}
+ } else if (!isnew && _engine.running() && _engine.sample_rate () == desired_sample_rate) {
+ /* keep engine */
} else if (_engine.setup_required()) {
/* backend is known, but setup is needed */
boost::optional<int> r = AudioEngineSetupRequired (desired_sample_rate);
}
}
- /* at this point the engine should be running
- */
+ /* at this point the engine should be running */
if (!_engine.running()) {
return -1;
}
try {
+ LocaleGuard lg;
BootMessage (_("Set up LTC"));
setup_ltc ();
BootMessage (_("Set up Click"));
DEBUG_TRACE (DEBUG::Destruction, "delete regions\n");
RegionFactory::delete_all_regions ();
+ /* Do this early so that VCAs no longer hold references to routes */
+
+ DEBUG_TRACE (DEBUG::Destruction, "delete vcas\n");
+ delete _vca_manager;
+
DEBUG_TRACE (DEBUG::Destruction, "delete routes\n");
/* reset these three references to special routes before we do the usual route delete thing */
delete midi_clock;
delete _tempo_map;
+ /* clear event queue, the session is gone, nobody is interested in
+ * those anymore, but they do leak memory if not removed
+ */
+ while (!immediate_events.empty ()) {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ SessionEvent *ev = immediate_events.front ();
+ DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("Drop event: %1\n", enum_2_string (ev->type)));
+ immediate_events.pop_front ();
+ bool remove = true;
+ bool del = true;
+ switch (ev->type) {
+ case SessionEvent::AutoLoop:
+ case SessionEvent::AutoLoopDeclick:
+ case SessionEvent::Skip:
+ case SessionEvent::PunchIn:
+ case SessionEvent::PunchOut:
+ case SessionEvent::StopOnce:
+ case SessionEvent::RangeStop:
+ case SessionEvent::RangeLocate:
+ remove = false;
+ del = false;
+ break;
+ case SessionEvent::RealTimeOperation:
+ process_rtop (ev);
+ del = false;
+ default:
+ break;
+ }
+ if (remove) {
+ del = del && !_remove_event (ev);
+ }
+ if (del) {
+ delete ev;
+ }
+ }
+
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_list_ptrs ();
-#endif
+ BOOST_SHOW_POINTERS ();
}
void
return;
}
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
+ BOOST_MARK_ROUTE(r);
+
try {
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
r->input()->ensure_io (_master_out->output()->n_ports(), false, this);
boost::shared_ptr<RouteList> rl = routes.reader ();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
- if (tr && tr->record_enabled ()) {
+ if (tr && tr->rec_enable_control()->get_value()) {
//cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
tr->request_input_monitoring (yn);
}
Session::set_all_tracks_record_enabled (bool enable )
{
boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr) {
- tr->set_record_enabled (enable, Controllable::NoGroup);
- }
- }
+ set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), enable, Controllable::NoGroup);
}
-
void
Session::disable_record (bool rt_context, bool force)
{
*/
list<boost::shared_ptr<MidiTrack> >
Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr<PluginInfo> instrument,
- TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template)
+ TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template, Plugin::PresetRecord* pset)
{
string track_name;
uint32_t track_id = 0;
track->use_new_diskstream();
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+ BOOST_MARK_TRACK (track);
+
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (track->input()->ensure_io (input, false, this)) {
if (instrument) {
for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) {
PluginPtr plugin = instrument->load (*this);
+ if (pset) {
+ plugin->load_preset (*pset);
+ }
boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
(*r)->add_processor (p, PreFader);
}
RouteList
-Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument)
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset)
{
string bus_name;
uint32_t bus_id = 0;
bus->set_strict_io (true);
}
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
-#endif
+ BOOST_MARK_ROUTE(bus);
+
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (instrument) {
for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
PluginPtr plugin = instrument->load (*this);
+ if (pset) {
+ plugin->load_preset (*pset);
+ }
boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
(*r)->add_processor (p, PreFader);
}
// 0 for Stereo Out mode
// 0 Multi Out mode
if (Config->get_output_auto_connect() & AutoConnectMaster) {
- track->set_gain (dB_to_coefficient (0), Controllable::NoGroup);
+ track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup);
}
}
track->use_new_diskstream();
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+ BOOST_MARK_TRACK (track);
+
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
bus->set_strict_io (true);
}
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
-#endif
+ BOOST_MARK_ROUTE(bus);
+
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
case NewPlaylist:
rename_playlist = true;
break;
+ default:
case CopyPlaylist:
case SharePlaylist:
rename_playlist = false;
XMLNodeList children = node_copy.children ();
for (XMLNodeList::iterator i = children.begin(); i != children.end(); ++i) {
if ((*i)->name() == X_("Processor")) {
- XMLProperty* role = (*i)->property (X_("role"));
+ /* ForceIDRegeneration does not catch the following */
+ XMLProperty const * role = (*i)->property (X_("role"));
+ XMLProperty const * type = (*i)->property (X_("type"));
+ if (role && role->value() == X_("Aux")) {
+ /* check if the target bus exists.
+ * we should not save aux-sends in templates.
+ */
+ XMLProperty const * target = (*i)->property (X_("target"));
+ if (!target) {
+ (*i)->add_property ("type", "dangling-aux-send");
+ continue;
+ }
+ boost::shared_ptr<Route> r = route_by_id (target->value());
+ if (!r || boost::dynamic_pointer_cast<Track>(r)) {
+ (*i)->add_property ("type", "dangling-aux-send");
+ continue;
+ }
+ }
if (role && role->value() == X_("Listen")) {
(*i)->remove_property (X_("bitslot"));
}
+ else if (role && (role->value() == X_("Send") || role->value() == X_("Aux"))) {
+ char buf[32];
+ Delivery::Role xrole;
+ uint32_t bitslot = 0;
+ xrole = Delivery::Role (string_2_enum (role->value(), xrole));
+ std::string name = Send::name_and_id_new_send(*this, xrole, bitslot, false);
+ snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
+ (*i)->remove_property (X_("bitslot"));
+ (*i)->remove_property (X_("name"));
+ (*i)->add_property ("bitslot", buf);
+ (*i)->add_property ("name", name);
+ }
+ else if (type && type->value() == X_("return")) {
+ // Return::set_state() generates a new one
+ (*i)->remove_property (X_("bitslot"));
+ }
+ else if (type && type->value() == X_("port")) {
+ // PortInsert::set_state() handles the bitslot
+ (*i)->remove_property (X_("bitslot"));
+ (*i)->add_property ("ignore-name", "1");
+ }
}
}
boost::weak_ptr<Route> wpr (*x);
boost::shared_ptr<Route> r (*x);
- r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr));
- r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
- r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
- r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+ r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
+ r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
+ r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+
r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
if (tr) {
tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr<Track> (tr)));
track_playlist_changed (boost::weak_ptr<Track> (tr));
- tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
+ tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
if (mt) {
graph_reordered ();
}
-
void
Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
{
continue;
}
- (*iter)->set_solo (false, Controllable::NoGroup);
+ (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
rs->remove (*iter);
void
Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
{
- boost::shared_ptr<Route> route = wpr.lock();
+ boost::shared_ptr<Route> route (wpr.lock());
+
if (!route) {
- error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_listen_changed")) << endmsg;
return;
}
continue;
}
- if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
*/
continue;
}
- (*i)->set_listen (false, Controllable::NoGroup);
+ (*i)->solo_control()->set_value (0.0, Controllable::NoGroup);
}
}
update_route_solo_state ();
}
+
void
Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
{
- boost::shared_ptr<Route> route = wpr.lock ();
+ boost::shared_ptr<Route> route (wpr.lock());
if (!route) {
- /* should not happen */
- error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg;
return;
}
bool send_changed = false;
- if (route->solo_isolated()) {
+ if (route->solo_isolate_control()->solo_isolated()) {
if (_solo_isolated_cnt == 0) {
send_changed = true;
}
void
Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
{
+ cerr << "route solo change (self ? " << self_solo_change << endl;
+
DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
+ boost::shared_ptr<Route> route (wpr.lock());
+
+ if (!route) {
+ return;
+ }
+
+ if (Config->get_solo_control_is_listen_control()) {
+ route_listen_changed (group_override, wpr);
+ return;
+ }
+
if (!self_solo_change) {
// session doesn't care about changes to soloed-by-others
return;
}
- boost::shared_ptr<Route> route = wpr.lock ();
- assert (route);
-
boost::shared_ptr<RouteList> r = routes.reader ();
int32_t delta;
continue;
}
- if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
continue;
}
- (*i)->set_solo (false, group_override);
+ (*i)->solo_control()->set_value (0.0, group_override);
}
}
continue;
}
- if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name()));
if (!via_sends_only) {
if (!route->soloed_by_others_upstream()) {
- (*i)->mod_solo_by_others_downstream (delta);
+ (*i)->solo_control()->mod_solo_by_others_downstream (delta);
} else {
DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n");
}
if (!via_sends_only) {
//NB. Triggers Invert Push, which handles soloed by downstream
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta));
- (*i)->mod_solo_by_others_upstream (delta);
+ (*i)->solo_control()->mod_solo_by_others_upstream (delta);
} else {
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name()));
}
for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name()));
(*i)->act_on_mute ();
- (*i)->mute_changed ();
+ (*i)->mute_control()->Changed (false, Controllable::NoGroup);
}
SoloChanged (); /* EMIT SIGNAL */
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) {
+ if ((*i)->can_solo() && (*i)->self_soloed()) {
something_soloed = true;
}
listeners++;
something_listening = true;
} else {
- (*i)->set_listen (false, Controllable::NoGroup);
+ (*i)->set_listen (false);
}
}
- if ((*i)->solo_isolated()) {
+ if ((*i)->solo_isolate_control()->solo_isolated()) {
isolated++;
}
}
}
+boost::shared_ptr<Stripable>
+Session::stripable_by_remote_id (uint32_t id)
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->remote_control_id() == id) {
+ return *i;
+ }
+ }
+
+ return boost::shared_ptr<Route> ((Route*) 0);
+}
+
+
boost::shared_ptr<Route>
Session::route_by_selected_count (uint32_t id)
{
" assert(self.scripts[n] == nil, 'Callback \"'.. n ..'\" already exists.')"
" self.scripts[n] = { ['f'] = f, ['a'] = a }"
" local env = _ENV; env.f = nil env.io = nil env.os = nil env.loadfile = nil env.require = nil env.dofile = nil env.package = nil env.debug = nil"
- " local env = { print = print, Session = Session, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall }"
+ " local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR }"
" self.instances[n] = load (string.dump(f, true), nil, nil, env)(a)"
" Session:scripts_changed()" // call back
" end"
set_dirty ();
}
+void
+Session::gui_tempo_map_changed ()
+{
+ clear_clicks ();
+
+ playlists->update_after_tempo_map_change ();
+
+ _locations->apply (*this, &Session::update_locations_after_tempo_map_change);
+}
+
void
Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc)
{
}
unblock_processing ();
- itt.done = true;
return result;
}
while (i != rl->end ()) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && tr->record_enabled ()) {
+ if (tr && tr->rec_enable_control()->get_value()) {
break;
}
for (i = rl->begin(); i != rl->end (); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && !tr->record_enabled ()) {
+ if (tr && !tr->rec_enable_control()->get_value()) {
break;
}
}
Session::solo_control_mode_changed ()
{
/* cancel all solo or all listen when solo control mode changes */
-
- if (soloing()) {
- set_solo (get_routes(), false);
- } else if (listening()) {
- set_listen (get_routes(), false);
- }
+ clear_all_solo_state (get_routes());
}
/** Called when a property of one of our route groups changes */
void
Session::update_latency (bool playback)
{
+
DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
- if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress) {
+ if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
return;
}