#include "ardour/analyser.h"
#include "ardour/async_midi_port.h"
#include "ardour/audio_buffer.h"
-#include "ardour/audio_diskstream.h"
#include "ardour/audio_port.h"
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/data_type.h"
#include "ardour/debug.h"
+#include "ardour/disk_reader.h"
#include "ardour/directory_names.h"
#ifdef USE_TRACKS_CODE_FEATURES
#include "ardour/engine_state_controller.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/session_playlists.h"
+#include "ardour/slave.h"
#include "ardour/smf_source.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/source_factory.h"
, _transport_speed (0)
, _default_transport_speed (1.0)
, _last_transport_speed (0)
+ , _signalled_varispeed (0)
, _target_transport_speed (0.0)
, auto_play_legal (false)
, _last_slave_transport_frame (0)
*/
if (!mix_template.empty()) {
- if (load_state (_current_snapshot_name)) {
- throw SessionException (_("Failed to load template/snapshot state"));
+ try {
+ if (load_state (_current_snapshot_name)) {
+ throw SessionException (_("Failed to load template/snapshot state"));
+ }
+ } catch (PBD::unknown_enumeration& e) {
+ throw SessionException (_("Failed to parse template/snapshot state"));
}
store_recent_templates (mix_template);
}
}
}
- if (post_engine_init ()) {
+ int err = post_engine_init ();
+ if (err) {
destroy ();
- throw SessionException (_("Cannot configure audio/midi engine with session parameters"));
+ switch (err) {
+ case -1:
+ throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Failed to create background threads.")));
+ break;
+ case -2:
+ case -3:
+ throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid TempoMap in session-file.")));
+ break;
+ case -4:
+ throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid or corrupt session state.")));
+ break;
+ case -5:
+ throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Port registration failed.")));
+ break;
+ default:
+ throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Unexpected exception during session setup, possibly invalid audio/midi engine parameters. Please see stdout/stderr for details")));
+ break;
+ }
}
store_recent_sessions (_name, _path);
ensure_subdirs (); // archived or zipped sessions may lack peaks/ analysis/ etc
- _is_new = false;
+ if (!mix_template.empty ()) {
+ /* ::create() unsets _is_new after creating the session.
+ * But for templated sessions, the sample-rate is initially unset
+ * (not read from template), so we need to save it (again).
+ */
+ _is_new = true;
+ }
+
session_loaded ();
+ _is_new = false;
BootMessage (_("Session loading complete"));
}
_state_of_the_state = StateOfTheState (CannotSave|Deletion);
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ ltc_tx_cleanup();
+ delete _slave;
+ _slave = 0;
+ }
+
/* 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
- and the engine to move data to any devices.
- */
+ * and the engine to move data to any devices.
+ */
+
+ /* remove I/O objects before unsetting the engine session */
+ _click_io.reset ();
+ _ltc_input.reset ();
+ _ltc_output.reset ();
ControlProtocolManager::instance().drop_protocols ();
Port::PortDrop (); /* EMIT SIGNAL */
- ltc_tx_cleanup();
-
/* clear history so that no references to objects are held any more */
_history.clear ();
delete state_tree;
state_tree = 0;
- // unregister all lua functions, drop held references (if any)
- (*_lua_cleanup)();
- lua.do_command ("Session = nil");
- delete _lua_run;
- delete _lua_add;
- delete _lua_del;
- delete _lua_list;
- delete _lua_save;
- delete _lua_load;
- delete _lua_cleanup;
- lua.collect_garbage ();
+ {
+ /* unregister all lua functions, drop held references (if any) */
+ Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
+ (*_lua_cleanup)();
+ lua.do_command ("Session = nil");
+ delete _lua_run;
+ delete _lua_add;
+ delete _lua_del;
+ delete _lua_list;
+ delete _lua_save;
+ delete _lua_load;
+ delete _lua_cleanup;
+ lua.collect_garbage ();
+ }
/* reset dynamic state version back to default */
Stateful::loading_state_version = 0;
/* need to remove auditioner before monitoring section
* otherwise it is re-connected */
+ auditioner->drop_references ();
auditioner.reset ();
/* drop references to routes held by the monitoring section
routes.flush ();
_bundles.flush ();
- AudioDiskstream::free_working_buffers();
+ DiskReader::free_working_buffers();
/* tell everyone who is still standing that we're about to die */
drop_references ();
AudioEngine::instance()->clear_pending_port_deletions ();
}
+ DEBUG_TRACE (DEBUG::Destruction, "delete selection\n");
+ delete _selection;
+ _selection = 0;
+
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
BOOST_SHOW_POINTERS ();
}
}
+int
+Session::add_master_bus (ChanCount const& count)
+{
+ if (master_out ()) {
+ return -1;
+ }
+
+ RouteList rl;
+
+ boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
+ if (r->init ()) {
+ return -1;
+ }
+
+ BOOST_MARK_ROUTE(r);
+
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ r->input()->ensure_io (count, false, this);
+ r->output()->ensure_io (count, false, this);
+ }
+
+ rl.push_back (r);
+ add_routes (rl, false, false, false, PresentationInfo::max_order);
+ return 0;
+}
+
void
Session::hookup_io ()
{
if (a->init()) {
throw failed_constructor ();
}
- a->use_new_diskstream ();
auditioner = a;
}
}
else if (Config->get_seamless_loop() && !loop_changing) {
- // schedule a locate-roll to refill the diskstreams at the
+ // schedule a locate-roll to refill the disk readers at the
// previous loop end
loop_changing = true;
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && !tr->is_auditioner()) {
- in += tr->n_inputs();
- out += tr->n_outputs();
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (!tr) {
+ continue;
}
+ assert (!tr->is_auditioner()); // XXX remove me
+ in += tr->n_inputs();
+ out += tr->n_outputs();
}
}
track->set_strict_io (true);
}
- track->use_new_diskstream();
-
BOOST_MARK_TRACK (track);
{
}
}
- track->non_realtime_input_change();
-
if (route_group) {
route_group->add (track);
}
- track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-
new_routes.push_back (track);
ret.push_back (track);
}
for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
boost::shared_ptr<Stripable> s (*si);
- if (s->is_monitor () || s->is_auditioner ()) {
+ assert (!s->is_auditioner ()); // XXX remove me
+ if (s->is_monitor ()) {
continue;
}
if (order != s->presentation_info().order()) {
}
}
- track->use_new_diskstream();
-
BOOST_MARK_TRACK (track);
{
route_group->add (track);
}
- track->non_realtime_input_change();
-
- track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-
new_routes.push_back (track);
ret.push_back (track);
}
}
}
- /* auditioner and monitor routes are not part of the order */
- if (auditioner) {
- assert (n_routes > 0);
- --n_routes;
- }
+ /* monitor is not part of the order */
if (_monitor_out) {
assert (n_routes > 0);
--n_routes;
StripableList all;
get_stripables (all);
for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
- if ((*i)->is_auditioner() || (*i)->is_monitor()) {
+ assert (!(*i)->is_auditioner()); // XXX remove me
+ if ((*i)->is_monitor()) {
continue;
}
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
std::vector<boost::weak_ptr<AutomationControl> > muted;
boost::shared_ptr<ControlList> cl (new ControlList);
for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
- if ((*i)->is_auditioner() || (*i)->is_monitor()) {
+ assert (!(*i)->is_auditioner());
+ if ((*i)->is_monitor()) {
continue;
}
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*i);
return boost::shared_ptr<Processor> ();
}
-boost::shared_ptr<Track>
-Session::track_by_diskstream_id (PBD::ID id) const
-{
- boost::shared_ptr<RouteList> r = routes.reader ();
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
- if (t && t->using_diskstream_id (id)) {
- return t;
- }
- }
-
- return boost::shared_ptr<Track> ();
-}
-
boost::shared_ptr<Route>
Session::get_remote_nth_route (PresentationInfo::order_t n) const
{
StateProtector sp (this);
for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+ assert (!(*i)->is_auditioner());
if (boost::dynamic_pointer_cast<Track> (*i)) {
(*i)->set_track_number(++tn);
}
- else if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) {
+ else if (!(*i)->is_master() && !(*i)->is_monitor()) {
(*i)->set_track_number(--bn);
}
}
string
Session::new_midi_source_path (const string& base, bool need_lock)
{
- uint32_t cnt;
- char buf[PATH_MAX+1];
- const uint32_t limit = 10000;
- string legalized;
string possible_path;
string possible_name;
- buf[0] = '\0';
- legalized = legalize_for_path (base);
+ possible_name = legalize_for_path (base);
// Find a "version" of the file name that doesn't exist in any of the possible directories.
std::vector<string> sdirs = source_search_path(DataType::MIDI);
*/
std::reverse(sdirs.begin(), sdirs.end());
- for (cnt = 1; cnt <= limit; ++cnt) {
+ while (true) {
+ possible_name = bump_name_once (possible_name, '-');
vector<space_and_path>::iterator i;
uint32_t existing = 0;
for (vector<string>::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) {
- snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
- possible_name = buf;
-
- possible_path = Glib::build_filename (*i, possible_name);
+ possible_path = Glib::build_filename (*i, possible_name + ".mid");
if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
existing++;
}
}
- if (existing == 0) {
- break;
- }
-
- if (cnt > limit) {
+ if (possible_path.size () >= PATH_MAX) {
error << string_compose(
- _("There are already %1 recordings for %2, which I consider too many."),
- limit, base) << endmsg;
+ _("There are already many recordings for %1, resulting in a too long file-path %2."),
+ base, possible_path) << endmsg;
destroy ();
return 0;
}
+
+ if (existing == 0) {
+ break;
+ }
}
/* No need to "find best location" for software/app-based RAID, because
if (!i.key ().isString ()) { assert(0); continue; }
rv.push_back (i.key ().cast<std::string> ());
}
- } catch (luabridge::LuaException const& e) { }
+ } catch (...) { }
return rv;
}
if (_n_lua_scripts == 0) return;
Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
if (tm.locked ()) {
- try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { }
+ try { (*_lua_run)(nframes); } catch (...) { }
lua.collect_garbage_step ();
}
}
lua.Print.connect (&_lua_print);
#endif
lua.tweak_rt_gc ();
+ lua.sandbox (true);
lua.do_command (
"function ArdourSession ()"
" local self = { scripts = {}, instances = {} }"
" assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
" 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, 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, bit32=bit32, 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
_lua_cleanup = new luabridge::LuaRef(lua_sess["cleanup"]);
} catch (luabridge::LuaException const& e) {
fatal << string_compose (_("programming error: %1"),
- X_("Failed to setup Lua interpreter"))
+ std::string ("Failed to setup session Lua interpreter") + e.what ())
+ << endmsg;
+ abort(); /*NOTREACHED*/
+ } catch (...) {
+ fatal << string_compose (_("programming error: %1"),
+ X_("Failed to setup session Lua interpreter"))
<< endmsg;
abort(); /*NOTREACHED*/
}
}
_n_lua_scripts = cnt;
} catch (luabridge::LuaException const& e) {
+ fatal << string_compose (_("programming error: %1"),
+ std::string ("Indexing Lua Session Scripts failed.") + e.what ())
+ << endmsg;
+ abort(); /*NOTREACHED*/
+ } catch (...) {
fatal << string_compose (_("programming error: %1"),
X_("Indexing Lua Session Scripts failed."))
<< endmsg;
return;
}
- /* every track/bus asked for this to be handled but it was deferred because
- we were connecting. do it now.
- */
-
- request_input_change_handling ();
-
resort_routes ();
/* force all diskstreams to update their capture offset values to
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_capture_offset ();
+ tr->update_latency_information ();
}
}
}
}
uint32_t
-Session::nstripables (bool with_auditioner_and_monitor) const
+Session::nstripables (bool with_monitor) const
{
uint32_t rv = routes.reader()->size ();
rv += _vca_manager->vcas ().size ();
- if (with_auditioner_and_monitor) {
+ if (with_monitor) {
return rv;
}
- if (auditioner) {
- assert (rv > 0);
- --rv;
- }
if (_monitor_out) {
assert (rv > 0);
--rv;
for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
if (boost::dynamic_pointer_cast<Track> (*r)) {
- if (!(*r)->is_auditioner()) {
- tl->push_back (*r);
- }
+ assert (!(*r)->is_auditioner()); // XXX remove me
+ tl->push_back (*r);
}
}
return tl;
if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
return;
}
+ if (!_engine.running()) {
+ return;
+ }
boost::shared_ptr<RouteList> r = routes.reader ();
framecnt_t max_latency = 0;
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (!(*i)->is_auditioner() && ((*i)->active())) {
+ assert (!(*i)->is_auditioner()); // XXX remove me
+ if ((*i)->active()) {
_worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
}
}
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_capture_offset ();
+ tr->update_latency_information ();
}
}
}
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (!(*i)->is_auditioner() && ((*i)->active())) {
+ assert (!(*i)->is_auditioner()); // XXX remove me
+ if ((*i)->active()) {
framecnt_t tl;
if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) {
some_track_latency_changed = true;
if (!tr) {
continue;
}
- tr->set_capture_offset ();
+ tr->update_latency_information ();
}
}
boost::shared_ptr<Port>
Session::ltc_input_port () const
{
+ assert (_ltc_input);
return _ltc_input->nth (0);
}
boost::shared_ptr<Port>
Session::ltc_output_port () const
{
- return _ltc_output->nth (0);
+ return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr<Port> ();
}
void
/* this is only used for updating plugin latencies, the
* graph does not change. so it's safe in general.
* BUT..
- * .. update_latency_compensation () entails set_capture_offset()
- * which calls Diskstream::set_capture_offset () which
+ * .. update_latency_compensation () entails Track::update_latency_information()
+ * which calls DiskWriter::set_capture_offset () which
* modifies the capture offset... which can be a proplem
* in "prepare_to_stop"
*/