#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/source_factory.h"
#include "ardour/speakers.h"
#include "ardour/tempo.h"
+#include "ardour/ticker.h"
#include "ardour/track.h"
#include "ardour/user_bundle.h"
#include "ardour/utils.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)
, _order_hint (-1)
, ignore_route_processor_changes (false)
+ , midi_clock (0)
, _scene_changer (0)
, _midi_ports (0)
, _mmc (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"));
/* disconnect from any and all signals that we are connected to */
+ Port::PortSignalDrop (); /* EMIT SIGNAL */
drop_connections ();
/* shutdown control surface protocols while we still have ports
* callbacks from the engine any more.
*/
- Port::PortSignalDrop (); /* EMIT SIGNAL */
Port::PortDrop (); /* EMIT SIGNAL */
ltc_tx_cleanup();
delete _midi_ports; _midi_ports = 0;
delete _locations; _locations = 0;
+ 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
}
remove_route (_monitor_out);
+ if (_state_of_the_state & Deletion) {
+ return;
+ }
+
auto_connect_master_bus ();
if (auditioner) {
auditioner->connect ();
}
+
Config->ParameterChanged ("use-monitor-bus");
}
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);
}
#ifndef NDEBUG
- boost::shared_ptr<RouteList> rl = routes.reader ();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
+ if (DEBUG_ENABLED(DEBUG::Graph)) {
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
- const Route::FedBy& fb ((*i)->fed_by());
+ const Route::FedBy& fb ((*i)->fed_by());
- for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
- boost::shared_ptr<Route> sf = f->r.lock();
- if (sf) {
- DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+ for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
+ boost::shared_ptr<Route> sf = f->r.lock();
+ if (sf) {
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+ }
}
}
}
*/
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);
}
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 ());
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");
+ }
}
}
graph_reordered ();
}
-
void
Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
{
resort_routes ();
#endif
- if (_process_graph) {
+ if (_process_graph && !(_state_of_the_state & Deletion)) {
_process_graph->clear_other_chain ();
}
(*iter)->drop_references ();
}
+ if (_state_of_the_state & Deletion) {
+ return;
+ }
+
Route::RemoteControlIDChange(); /* EMIT SIGNAL */
/* save the new state of the world */
" 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;
}
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;
}
return;
}
- //why would we need the process lock ??
- //Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
-
/* If both inputs and outputs are auto-connected to physical ports,
* use the max of input and output offsets to ensure auto-connected
* port numbers always match up (e.g. the first audio input and the
Session::auto_connect_thread_run ()
{
pthread_set_name (X_("autoconnect"));
- SessionEvent::create_per_thread_pool (X_("autoconnect"), 256);
- PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 256);
+ SessionEvent::create_per_thread_pool (X_("autoconnect"), 1024);
+ PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 1024);
pthread_mutex_lock (&_auto_connect_mutex);
while (_ac_thread_active) {
- while (!_auto_connect_queue.empty ()) {
+ if (!_auto_connect_queue.empty ()) {
+ // Why would we need the process lock ??
+ // A: if ports are added while we're connecting, the backend's iterator may be invalidated:
+ // graph_order_callback() -> resort_routes() -> direct_feeds_according_to_reality () -> backend::connected_to()
+ // All ardour-internal backends use a std::vector xxxAudioBackend::find_port()
+ // We have control over those, but what does jack do?
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
- if (_auto_connect_queue.empty ()) { break; } // re-check with lock
- const AutoConnectRequest ar (_auto_connect_queue.front());
- _auto_connect_queue.pop ();
- lx.release ();
- auto_connect (ar);
+ while (!_auto_connect_queue.empty ()) {
+ const AutoConnectRequest ar (_auto_connect_queue.front());
+ _auto_connect_queue.pop ();
+ lx.release ();
+ auto_connect (ar);
+ lx.acquire ();
+ }
}
pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);