#include "ardour/engine_state_controller.h"
#endif
#include "ardour/filename_extensions.h"
+#include "ardour/gain_control.h"
#include "ardour/graph.h"
+#include "ardour/luabindings.h"
#include "ardour/midiport_manager.h"
#include "ardour/scene_changer.h"
#include "ardour/midi_patch_manager.h"
#include "midi++/port.h"
#include "midi++/mmc.h"
+#include "LuaBridge/LuaBridge.h"
+
#include "i18n.h"
#include <glibmm/checksum.h>
bool Session::_disable_all_loaded_plugins = false;
bool Session::_bypass_all_loaded_plugins = false;
+guint Session::_name_id_counter = 0;
PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
PBD::Signal1<void,std::string> Session::Dialog;
, current_block_size (0)
, _worst_output_latency (0)
, _worst_input_latency (0)
- , _worst_track_latency (0)
+ , _worst_track_latency (0)
, _have_captured (false)
, _non_soloed_outs_muted (false)
+ , _listening (false)
, _listen_cnt (0)
, _solo_isolated_cnt (0)
, _writable (false)
, post_export_sync (false)
, post_export_position (0)
, _exporting (false)
- , _export_started (false)
, _export_rolling (false)
+ , _export_preroll (0)
, _pre_export_mmc_enabled (false)
, _name (snapshot_name)
, _is_new (true)
, pending_locate_flush (false)
, pending_abort (false)
, pending_auto_loop (false)
+ , _mempool ("Session", 1048576)
+ , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
+ , _n_lua_scripts (0)
, _butler (new Butler (*this))
, _post_transport_work (0)
, cumulative_rf_motion (0)
pthread_mutex_init (&_rt_emit_mutex, 0);
pthread_cond_init (&_rt_emit_cond, 0);
+ init_name_id_counter (1); // reset for new sessions, start at 1
+
pre_engine_init (fullpath);
+ setup_lua ();
+
if (_is_new) {
Stateful::loading_state_version = CURRENT_SESSION_FILE_VERSION;
throw SessionException (_("Cannot connect to audio/midi engine"));
}
+ // set samplerate for plugins added early
+ // e.g from templates or MB channelstrip
+ set_block_size (_engine.samples_per_cycle());
+ set_frame_rate (_engine.sample_rate());
+
if (create (mix_template, bus_profile)) {
destroy ();
throw SessionException (_("Session initialization failed"));
destroy ();
}
+unsigned int
+Session::next_name_id ()
+{
+ return g_atomic_int_add (&_name_id_counter, 1);
+}
+
+unsigned int
+Session::name_id_counter ()
+{
+ return g_atomic_int_get (&_name_id_counter);
+}
+
+void
+Session::init_name_id_counter (guint n)
+{
+ g_atomic_int_set (&_name_id_counter, n);
+}
+
int
Session::ensure_engine (uint32_t desired_sample_rate)
{
remove_pending_capture_state ();
+ Analyser::flush ();
+
_state_of_the_state = StateOfTheState (CannotSave|Deletion);
/* disconnect from any and all signals that we are connected to */
delete state_tree;
state_tree = 0;
- /* reset dynamic state version back to default */
+ // 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 ();
+ /* reset dynamic state version back to default */
Stateful::loading_state_version = 0;
_butler->drop_references ();
Session::setup_click ()
{
_clicking = false;
+
+ boost::shared_ptr<AutomationList> gl (new AutomationList (Evoral::Parameter (GainAutomation)));
+ boost::shared_ptr<GainControl> gain_control = boost::shared_ptr<GainControl> (new GainControl (*this, Evoral::Parameter(GainAutomation), gl));
+
_click_io.reset (new ClickIO (*this, X_("Click")));
- _click_gain.reset (new Amp (*this));
+ _click_gain.reset (new Amp (*this, _("Fader"), gain_control, true));
_click_gain->activate ();
if (state_tree) {
setup_click_state (state_tree->root());
if (auditioner) {
auditioner->connect ();
}
+ Config->ParameterChanged ("use-monitor-bus");
}
void
if (auditioner) {
auditioner->connect ();
}
+ Config->ParameterChanged ("use-monitor-bus");
}
void
}
}
+void
+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);
+ }
+ }
+}
+
+
void
Session::disable_record (bool rt_context, bool force)
{
goto failed;
}
+ if (Profile->get_mixbus ()) {
+ track->set_strict_io (true);
+ }
+
track->use_new_diskstream();
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
return ret;
}
+RouteList
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument)
+{
+ string bus_name;
+ uint32_t bus_id = 0;
+ string port;
+ RouteList ret;
+
+ bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Midi Bus");
+
+ while (how_many) {
+ if (!find_route_name (name_template.empty () ? _("Midi Bus") : name_template, ++bus_id, bus_name, use_number)) {
+ error << "cannot find name for new midi bus" << endmsg;
+ goto failure;
+ }
+
+ try {
+ boost::shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI
+
+ if (bus->init ()) {
+ goto failure;
+ }
+
+ if (Profile->get_mixbus ()) {
+ bus->set_strict_io (true);
+ }
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+ // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
+#endif
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ if (bus->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+ error << _("cannot configure new midi bus input") << endmsg;
+ goto failure;
+ }
+
+
+ if (bus->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+ error << _("cannot configure new midi bus output") << endmsg;
+ goto failure;
+ }
+ }
+
+ if (route_group) {
+ route_group->add (bus);
+ }
+ if (Config->get_remote_model() == UserOrdered) {
+ bus->set_remote_control_id (next_control_id());
+ }
+
+ ret.push_back (bus);
+ RouteAddedOrRemoved (true); /* EMIT SIGNAL */
+ ARDOUR::GUIIdle ();
+ }
+
+ catch (failed_constructor &err) {
+ error << _("Session: could not create new audio route.") << endmsg;
+ goto failure;
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& pfe) {
+ error << pfe.what() << endmsg;
+ goto failure;
+ }
+
+
+ --how_many;
+ }
+
+ failure:
+ if (!ret.empty()) {
+ StateProtector sp (this);
+ add_routes (ret, false, false, false);
+
+ if (instrument) {
+ for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
+ PluginPtr plugin = instrument->load (*this);
+ boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
+ (*r)->add_processor (p, PreFader);
+ }
+ }
+ }
+
+ return ret;
+
+}
+
+
void
Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::weak_ptr<Route> wmt)
{
goto failed;
}
+ if (Profile->get_mixbus ()) {
+ track->set_strict_io (true);
+ }
+
+
if (ARDOUR::Profile->get_trx ()) {
// TRACKS considers it's not a USE CASE, it's
// a piece of behavior of the session model:
// 0 for Stereo Out mode
// 0 Multi Out mode
if (Config->get_output_auto_connect() & AutoConnectMaster) {
- track->set_gain (dB_to_coefficient (0), 0);
+ track->set_gain (dB_to_coefficient (0), Controllable::NoGroup);
}
}
goto failure;
}
+ if (Profile->get_mixbus ()) {
+ bus->set_strict_io (true);
+ }
+
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
// boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
#endif
}
RouteList
-Session::new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name_base)
+Session::new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name_base, PlaylistDisposition pd)
{
- RouteList ret;
- uint32_t control_id;
XMLTree tree;
- uint32_t number = 0;
- const uint32_t being_added = how_many;
if (!tree.read (template_path.c_str())) {
- return ret;
+ return RouteList();
}
- XMLNode* node = tree.root();
+ return new_route_from_template (how_many, *tree.root(), name_base, pd);
+}
+RouteList
+Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd)
+{
+ RouteList ret;
+ uint32_t control_id;
+ uint32_t number = 0;
+ const uint32_t being_added = how_many;
+ /* This will prevent the use of any existing XML-provided PBD::ID
+ values by Stateful.
+ */
+ Stateful::ForceIDRegeneration force_ids;
IO::disable_connecting ();
control_id = next_control_id ();
while (how_many) {
- XMLNode node_copy (*node);
+ /* We're going to modify the node contents a bit so take a
+ * copy. The node may be re-used when duplicating more than once.
+ */
- /* Remove IDs of everything so that new ones are used */
- node_copy.remove_property_recursively (X_("id"));
+ XMLNode node_copy (node);
try {
string name;
}
/* set this name in the XML description that we are about to use */
- Route::set_name_in_state (node_copy, name);
+
+ bool rename_playlist;
+ switch (pd) {
+ case NewPlaylist:
+ rename_playlist = true;
+ break;
+ case CopyPlaylist:
+ case SharePlaylist:
+ rename_playlist = false;
+ }
+
+ Route::set_name_in_state (node_copy, name, rename_playlist);
/* trim bitslots from listen sends so that new ones are used */
XMLNodeList children = node_copy.children ();
route->set_remote_control_id (control_id);
++control_id;
+ boost::shared_ptr<Track> track;
+
+ if ((track = boost::dynamic_pointer_cast<Track> (route))) {
+ switch (pd) {
+ case NewPlaylist:
+ track->use_new_playlist ();
+ break;
+ case CopyPlaylist:
+ track->use_copy_playlist ();
+ break;
+ case SharePlaylist:
+ break;
+ }
+ };
+
ret.push_back (route);
RouteAddedOrRemoved (true); /* EMIT SIGNAL */
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, _2, wpr));
- r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _3, wpr));
- r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, _1, wpr));
- r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1));
+ 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->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));
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((s = (*i)->internal_send_for (dest)) != 0) {
- s->amp()->gain_control()->set_value (GAIN_COEFF_ZERO);
+ s->amp()->gain_control()->set_value (GAIN_COEFF_ZERO, Controllable::NoGroup);
}
}
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((s = (*i)->internal_send_for (dest)) != 0) {
- s->amp()->gain_control()->set_value (GAIN_COEFF_UNITY);
+ s->amp()->gain_control()->set_value (GAIN_COEFF_UNITY, Controllable::NoGroup);
}
}
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((s = (*i)->internal_send_for (dest)) != 0) {
- s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value());
+ s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value(), Controllable::NoGroup);
}
}
}
continue;
}
- (*iter)->set_solo (false, this);
+ (*iter)->set_solo (false, Controllable::NoGroup);
rs->remove (*iter);
}
void
-Session::route_mute_changed (void* /*src*/)
+Session::route_mute_changed ()
{
set_dirty ();
}
void
-Session::route_listen_changed (bool group_override, boost::weak_ptr<Route> wpr)
+Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
{
boost::shared_ptr<Route> route = wpr.lock();
if (!route) {
if (route->listening_via_monitor ()) {
if (Config->get_exclusive_solo()) {
- /* new listen: disable all other listen, except solo-grouped channels */
+
RouteGroup* rg = route->route_group ();
- bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
- if (group_override && rg) {
- leave_group_alone = !leave_group_alone;
- }
+ const bool group_already_accounted_for = route->use_group (group_override, &RouteGroup::is_solo);
+
boost::shared_ptr<RouteList> r = routes.reader ();
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) {
+ if ((*i) == route) {
+ /* already changed */
+ continue;
+ }
+
+ if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ /* route does not get solo propagated to it */
+ continue;
+ }
+
+ if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+ /* this route is a part of the same solo group as the route
+ * that was changed. Changing that route did change or will
+ * change all group members appropriately, so we can ignore it
+ * here
+ */
continue;
}
- (*i)->set_listen (false, this, group_override);
+ (*i)->set_listen (false, Controllable::NoGroup);
}
}
update_route_solo_state ();
}
void
-Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
{
boost::shared_ptr<Route> route = wpr.lock ();
}
void
-Session::route_solo_changed (bool self_solo_change, bool group_override, boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
{
DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
delta = -1;
}
+ /* the route may be a member of a group that has shared-solo
+ * semantics. If so, then all members of that group should follow the
+ * solo of the changed route. But ... this is optional, controlled by a
+ * Controllable::GroupControlDisposition.
+ *
+ * The first argument to the signal that this method is connected to is the
+ * GroupControlDisposition value that was used to change solo.
+ *
+ * If the solo change was done with group semantics (either InverseGroup
+ * (force the entire group to change even if the group shared solo is
+ * disabled) or UseGroup (use the group, which may or may not have the
+ * shared solo property enabled)) then as we propagate the change to
+ * the entire session we should IGNORE THE GROUP that the changed route
+ * belongs to.
+ */
+
RouteGroup* rg = route->route_group ();
- bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
- if (group_override && rg) {
- leave_group_alone = !leave_group_alone;
- }
+ const bool group_already_accounted_for = (group_override == Controllable::ForGroup);
+
if (delta == 1 && Config->get_exclusive_solo()) {
/* new solo: disable all other solos, but not the group if its solo-enabled */
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ||
- (leave_group_alone && ((*i)->route_group() == rg))) {
+
+ if ((*i) == route) {
+ /* already changed */
continue;
}
- (*i)->set_solo (false, this, group_override);
+
+ if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ /* route does not get solo propagated to it */
+ continue;
+ }
+
+ if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+ /* this route is a part of the same solo group as the route
+ * that was changed. Changing that route did change or will
+ * change all group members appropriately, so we can ignore it
+ * here
+ */
+ continue;
+ }
+
+ (*i)->set_solo (false, group_override);
}
}
bool via_sends_only;
bool in_signal_flow;
- if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ||
- (leave_group_alone && ((*i)->route_group() == rg))) {
+ if ((*i) == route) {
+ /* already changed */
+ continue;
+ }
+
+ if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ /* route does not get solo propagated to it */
+ continue;
+ }
+
+ if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+ /* this route is a part of the same solo group as the route
+ * that was changed. Changing that route did change or will
+ * change all group members appropriately, so we can ignore it
+ * here
+ */
continue;
}
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 (this);
+ (*i)->mute_changed ();
}
SoloChanged (); /* EMIT SIGNAL */
/* now figure out if anything that matters is soloed (or is "listening")*/
bool something_soloed = false;
+ bool something_listening = false;
uint32_t listeners = 0;
uint32_t isolated = 0;
if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) {
if (Config->get_solo_control_is_listen_control()) {
listeners++;
+ something_listening = true;
} else {
- (*i)->set_listen (false, this);
+ (*i)->set_listen (false, Controllable::NoGroup);
}
}
SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
}
+ if (something_listening != _listening) {
+ _listening = something_listening;
+ SoloActive (_listening);
+ }
+
_listen_cnt = listeners;
if (isolated != _solo_isolated_cnt) {
for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
if (name == *reserved) {
- if (route_by_name (*reserved)) {
- return false;
- } else {
+ if (!route_by_name (*reserved)) {
+ /* first instance of a reserved name is allowed */
return true;
}
+ /* all other instances of a reserved name are not allowed */
+ return false;
}
}
return boost::shared_ptr<Route> ((Route*) 0);
}
+boost::shared_ptr<Processor>
+Session::processor_by_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<Processor> p = (*i)->Route::processor_by_id (id);
+ if (p) {
+ return p;
+ }
+ }
+
+ return boost::shared_ptr<Processor> ();
+}
+
boost::shared_ptr<Track>
Session::track_by_diskstream_id (PBD::ID id)
{
}
+boost::shared_ptr<Route>
+Session::route_by_selected_count (uint32_t id)
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ /* NOT IMPLEMENTED */
+ }
+
+ return boost::shared_ptr<Route> ((Route*) 0);
+}
+
+
void
Session::reassign_track_numbers ()
{
queue_event (ev);
}
+
+void
+Session::register_lua_function (
+ const std::string& name,
+ const std::string& script,
+ const LuaScriptParamList& args
+ )
+{
+ Glib::Threads::Mutex::Lock lm (lua_lock);
+
+ lua_State* L = lua.getState();
+
+ const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
+ luabridge::LuaRef tbl_arg (luabridge::newTable(L));
+ for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
+ if ((*i)->optional && !(*i)->is_set) { continue; }
+ tbl_arg[(*i)->name] = (*i)->value;
+ }
+ (*_lua_add)(name, bytecode, tbl_arg); // throws luabridge::LuaException
+ set_dirty();
+}
+
+void
+Session::unregister_lua_function (const std::string& name)
+{
+ Glib::Threads::Mutex::Lock lm (lua_lock);
+ (*_lua_del)(name); // throws luabridge::LuaException
+ lua.collect_garbage ();
+ set_dirty();
+}
+
+std::vector<std::string>
+Session::registered_lua_functions ()
+{
+ Glib::Threads::Mutex::Lock lm (lua_lock);
+ std::vector<std::string> rv;
+
+ try {
+ luabridge::LuaRef list ((*_lua_list)());
+ for (luabridge::Iterator i (list); !i.isNil (); ++i) {
+ if (!i.key ().isString ()) { assert(0); continue; }
+ rv.push_back (i.key ().cast<std::string> ());
+ }
+ } catch (luabridge::LuaException const& e) { }
+ return rv;
+}
+
+#ifndef NDEBUG
+static void _lua_print (std::string s) {
+ std::cout << "SessionLua: " << s << "\n";
+}
+#endif
+
+void
+Session::try_run_lua (pframes_t nframes)
+{
+ 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) { }
+ }
+}
+
+void
+Session::setup_lua ()
+{
+#ifndef NDEBUG
+ lua.Print.connect (&_lua_print);
+#endif
+ lua.do_command (
+ "function ArdourSession ()"
+ " local self = { scripts = {}, instances = {} }"
+ ""
+ " local remove = function (n)"
+ " self.scripts[n] = nil"
+ " self.instances[n] = nil"
+ " Session:scripts_changed()" // call back
+ " end"
+ ""
+ " local addinternal = function (n, f, a)"
+ " assert(type(n) == 'string', 'function-name must be string')"
+ " assert(type(f) == 'function', 'Given script is a not a function')"
+ " 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, 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 }"
+ " self.instances[n] = load (string.dump(f, true), nil, nil, env)(a)"
+ " Session:scripts_changed()" // call back
+ " end"
+ ""
+ " local add = function (n, b, a)"
+ " assert(type(b) == 'string', 'ByteCode must be string')"
+ " load (b)()" // assigns f
+ " assert(type(f) == 'string', 'Assigned ByteCode must be string')"
+ " addinternal (n, load(f), a)"
+ " end"
+ ""
+ " local run = function (...)"
+ " for n, s in pairs (self.instances) do"
+ " local status, err = pcall (s, ...)"
+ " if not status then"
+ " print ('fn \"'.. n .. '\": ', err)"
+ " remove (n)"
+ " end"
+ " end"
+ " collectgarbage()"
+ " end"
+ ""
+ " local cleanup = function ()"
+ " self.scripts = nil"
+ " self.instances = nil"
+ " end"
+ ""
+ " local list = function ()"
+ " local rv = {}"
+ " for n, _ in pairs (self.scripts) do"
+ " rv[n] = true"
+ " end"
+ " return rv"
+ " end"
+ ""
+ " local function basic_serialize (o)"
+ " if type(o) == \"number\" then"
+ " return tostring(o)"
+ " else"
+ " return string.format(\"%q\", o)"
+ " end"
+ " end"
+ ""
+ " local function serialize (name, value)"
+ " local rv = name .. ' = '"
+ " collectgarbage()"
+ " if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
+ " return rv .. basic_serialize(value) .. ' '"
+ " elseif type(value) == \"table\" then"
+ " rv = rv .. '{} '"
+ " for k,v in pairs(value) do"
+ " local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
+ " rv = rv .. serialize(fieldname, v) .. ' '"
+ " collectgarbage()" // string concatenation allocates a new string :(
+ " end"
+ " return rv;"
+ " elseif type(value) == \"function\" then"
+ " return rv .. string.format(\"%q\", string.dump(value, true))"
+ " else"
+ " error('cannot save a ' .. type(value))"
+ " end"
+ " end"
+ ""
+ ""
+ " local save = function ()"
+ " return (serialize('scripts', self.scripts))"
+ " end"
+ ""
+ " local restore = function (state)"
+ " self.scripts = {}"
+ " load (state)()"
+ " for n, s in pairs (scripts) do"
+ " addinternal (n, load(s['f']), s['a'])"
+ " end"
+ " end"
+ ""
+ " return { run = run, add = add, remove = remove,"
+ " list = list, restore = restore, save = save, cleanup = cleanup}"
+ " end"
+ " "
+ " sess = ArdourSession ()"
+ " ArdourSession = nil"
+ " "
+ "function ardour () end"
+ );
+
+ lua_State* L = lua.getState();
+
+ try {
+ luabridge::LuaRef lua_sess = luabridge::getGlobal (L, "sess");
+ lua.do_command ("sess = nil"); // hide it.
+ lua.do_command ("collectgarbage()");
+
+ _lua_run = new luabridge::LuaRef(lua_sess["run"]);
+ _lua_add = new luabridge::LuaRef(lua_sess["add"]);
+ _lua_del = new luabridge::LuaRef(lua_sess["remove"]);
+ _lua_list = new luabridge::LuaRef(lua_sess["list"]);
+ _lua_save = new luabridge::LuaRef(lua_sess["save"]);
+ _lua_load = new luabridge::LuaRef(lua_sess["restore"]);
+ _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"))
+ << endmsg;
+ abort(); /*NOTREACHED*/
+ }
+
+ LuaBindings::stddef (L);
+ LuaBindings::common (L);
+ LuaBindings::dsp (L);
+ luabridge::push <Session *> (L, this);
+ lua_setglobal (L, "Session");
+}
+
+void
+Session::scripts_changed ()
+{
+ assert (!lua_lock.trylock()); // must hold lua_lock
+
+ try {
+ luabridge::LuaRef list ((*_lua_list)());
+ int cnt = 0;
+ for (luabridge::Iterator i (list); !i.isNil (); ++i) {
+ if (!i.key ().isString ()) { assert(0); continue; }
+ ++cnt;
+ }
+ _n_lua_scripts = cnt;
+ } catch (luabridge::LuaException const& e) {
+ fatal << string_compose (_("programming error: %1"),
+ X_("Indexing Lua Session Scripts failed."))
+ << endmsg;
+ abort(); /*NOTREACHED*/
+ }
+}
+
void
Session::non_realtime_set_audition ()
{
void
Session::unmark_return_id (uint32_t id)
{
+ if (_state_of_the_state & Deletion) { return; }
if (id < return_bitset.size()) {
return_bitset[id] = false;
}
}
unblock_processing ();
+ itt.done = true;
return result;
}
return ProcessThread::get_scratch_buffers (count, silence);
}
+BufferSet&
+Session::get_noinplace_buffers (ChanCount count)
+{
+ return ProcessThread::get_noinplace_buffers (count);
+}
+
BufferSet&
Session::get_route_buffers (ChanCount count, bool silence)
{