#include "ardour/route_graph.h"
#include "ardour/route_group.h"
#include "ardour/send.h"
+#include "ardour/selection.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"
#include "ardour/tempo.h"
#include "ardour/ticker.h"
#include "ardour/track.h"
+#include "ardour/types_convert.h"
#include "ardour/user_bundle.h"
#include "ardour/utils.h"
#include "ardour/vca_manager.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)
, pending_locate_flush (false)
, pending_abort (false)
, pending_auto_loop (false)
- , _mempool ("Session", 2097152)
+ , _mempool ("Session", 3145728)
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
, _n_lua_scripts (0)
, _butler (new Butler (*this))
, _bundle_xml_node (0)
, _current_trans (0)
, _clicking (false)
+ , _click_rec_only (false)
, click_data (0)
, click_emphasis_data (0)
, click_length (0)
, _range_selection (-1,-1)
, _object_selection (-1,-1)
, _preroll_record_punch_pos (-1)
+ , _preroll_record_trim_len (0)
+ , _count_in_once (false)
, main_outs (0)
, first_file_data_format_reset (true)
, first_file_header_format_reset (true)
, _midi_ports (0)
, _mmc (0)
, _vca_manager (new VCAManager (*this))
+ , _selection (new CoreSelection (*this))
{
uint32_t sr = 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);
_state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+ PresentationInfo::Change.connect_same_thread (*this, boost::bind (&Session::notify_presentation_info_change, this));
+
Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false));
config.ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, true));
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"));
}
}
try {
- LocaleGuard lg;
BootMessage (_("Set up LTC"));
setup_ltc ();
BootMessage (_("Set up Click"));
_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 ();
- /* stop autoconnecting */
+ /* stop auto dis/connecting */
auto_connect_thread_terminate ();
MIDI::Name::MidiPatchManager::instance().remove_search_path(session_directory().midi_patch_path());
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;
}
}
+ {
+ /* unregister all dropped ports, process pending port deletion. */
+ // this may call ARDOUR::Port::drop ... jack_port_unregister ()
+ // jack1 cannot cope with removing ports while processing
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ 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 ()
{
location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ }
+
+ if (location->is_range_marker()) {
+ /* listen for per-location signals that require us to do any * global updates for marks */
+
+ location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
}
if (location->is_skip()) {
location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
+ location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
update_skips (location, true);
}
}
framepos_t
-Session::audible_frame () const
+Session::audible_frame (bool* latent_locate) const
{
framepos_t ret;
frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start ();
offset *= transport_speed ();
+ if (latent_locate) {
+ *latent_locate = false;
+ }
if (synced_to_engine()) {
/* Note: this is basically just sync-to-JACK */
if (!play_loop || !have_looped) {
if (ret < _last_roll_or_reversal_location) {
+ if (latent_locate) {
+ *latent_locate = true;
+ }
return _last_roll_or_reversal_location;
}
} else {
- // latent loops
+ /* the play-position wrapped at the loop-point
+ * ardour is already playing the beginning of the loop,
+ * but due to playback latency, the "audible frame"
+ * is still at the end of the loop.
+ */
Location *location = _locations->auto_loop_location();
frameoffset_t lo = location->start() - ret;
if (lo > 0) {
ret = location->end () - lo;
+ if (latent_locate) {
+ *latent_locate = 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();
}
}
* @param instrument plugin info for the instrument to insert pre-fader, if any
*/
list<boost::shared_ptr<MidiTrack> >
-Session::new_midi_track (const ChanCount& input, const ChanCount& output,
+Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool strict_io,
boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
- RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order,
+ RouteGroup* route_group, uint32_t how_many,
+ string name_template, PresentationInfo::order_t order,
TrackMode mode)
{
string track_name;
goto failed;
}
- if (Profile->get_mixbus ()) {
+ if (strict_io) {
track->set_strict_io (true);
}
if (pset) {
plugin->load_preset (*pset);
}
- boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
- (*r)->add_processor (p, PreFader);
+ boost::shared_ptr<PluginInsert> pi (new PluginInsert (*this, plugin));
+ if (strict_io) {
+ pi->set_strict_io (true);
+ }
+
+ (*r)->add_processor (pi, PreFader);
+
+ if (Profile->get_mixbus () && pi->configured () && pi->output_streams().n_audio() > 2) {
+ (*r)->move_instrument_down (false);
+ }
}
}
}
}
RouteList
-Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, bool strict_io,
+ boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
PresentationInfo::Flag flag, PresentationInfo::order_t order)
{
string bus_name;
goto failure;
}
- if (Profile->get_mixbus ()) {
+ if (strict_io) {
bus->set_strict_io (true);
}
if (pset) {
plugin->load_preset (*pset);
}
- boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
- (*r)->add_processor (p, PreFader);
+ boost::shared_ptr<PluginInsert> pi (new PluginInsert (*this, plugin));
+ if (strict_io) {
+ pi->set_strict_io (true);
+ }
+
+ (*r)->add_processor (pi, PreFader);
+
+ if (Profile->get_mixbus () && pi->configured () && pi->output_streams().n_audio() > 2) {
+ (*r)->move_instrument_down (false);
+ }
}
}
}
#endif
+bool
+Session::ensure_stripable_sort_order ()
+{
+ StripableList sl;
+ get_stripables (sl);
+ sl.sort (Stripable::Sorter ());
+
+ bool change = false;
+ PresentationInfo::order_t order = 0;
+
+ for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
+ boost::shared_ptr<Stripable> s (*si);
+ assert (!s->is_auditioner ()); // XXX remove me
+ if (s->is_monitor ()) {
+ continue;
+ }
+ if (order != s->presentation_info().order()) {
+ s->set_presentation_order (order);
+ change = true;
+ }
+ ++order;
+ }
+ return change;
+}
+
void
Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many)
{
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", first_new_order, how_many));
+
if (first_new_order == PresentationInfo::max_order) {
/* adding at end, no worries */
return;
for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
boost::shared_ptr<Stripable> s (*si);
- if (s->is_monitor() || s->is_auditioner()) {
+ if (s->presentation_info().special (false)) {
+ continue;
+ }
+
+ if (!s->presentation_info().order_set()) {
continue;
}
*/
XMLProperty const * target = (*i)->property (X_("target"));
if (!target) {
- (*i)->add_property ("type", "dangling-aux-send");
+ (*i)->set_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");
+ (*i)->set_property ("type", "dangling-aux-send");
continue;
}
}
(*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);
+ (*i)->set_property ("bitslot", bitslot);
+ (*i)->set_property ("name", name);
}
else if (type && type->value() == X_("intreturn")) {
(*i)->remove_property (X_("bitslot"));
- (*i)->add_property ("ignore-bitslot", "1");
+ (*i)->set_property ("ignore-bitslot", "1");
}
else if (type && type->value() == X_("return")) {
// Return::set_state() generates a new one
else if (type && type->value() == X_("port")) {
// PortInsert::set_state() handles the bitslot
(*i)->remove_property (X_("bitslot"));
- (*i)->add_property ("ignore-name", "1");
+ (*i)->set_property ("ignore-name", "1");
}
}
}
+ /* new routes start off unsoloed to avoid issues related to
+ upstream / downstream buses. */
+ node_copy.remove_node_and_delete (X_("Controllable"), X_("name"), X_("solo"));
+
boost::shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
if (route == 0) {
{
RCUWriter<RouteList> writer (routes);
boost::shared_ptr<RouteList> r = writer.get_copy ();
- r->insert (r->end(), new_routes.begin(), new_routes.end());
n_routes = r->size();
+ r->insert (r->end(), new_routes.begin(), new_routes.end());
/* if there is no control out and we're not in the middle of loading,
* resort the graph here. if there is a control out, we will resort
}
}
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", order, new_routes.size()));
- ensure_route_presentation_info_gap (order, new_routes.size());
+ /* monitor is not part of the order */
+ if (_monitor_out) {
+ assert (n_routes > 0);
+ --n_routes;
+ }
- for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
+ {
+ PresentationInfo::ChangeSuspender cs;
+ ensure_route_presentation_info_gap (order, new_routes.size());
- boost::weak_ptr<Route> wpr (*x);
- boost::shared_ptr<Route> r (*x);
+ for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
- 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));
+ boost::weak_ptr<Route> wpr (*x);
+ boost::shared_ptr<Route> r (*x);
- 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));
- r->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, 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));
- if (r->is_master()) {
- _master_out = r;
- }
+ 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));
+ r->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, this));
- if (r->is_monitor()) {
- _monitor_out = r;
- }
+ if (r->is_master()) {
+ _master_out = r;
+ }
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (r);
- 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->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) {
- mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1));
- mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr<Route>(mt)));
- mt->presentation_info().PropertyChanged.connect_same_thread (*this, boost::bind (&Session::midi_track_presentation_info_changed, this, _1, boost::weak_ptr<MidiTrack>(mt)));
+ if (r->is_monitor()) {
+ _monitor_out = r;
}
- }
- if (!r->presentation_info().special()) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (r);
+ 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->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) {
+ mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1));
+ mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr<Route>(mt)));
+ mt->presentation_info().PropertyChanged.connect_same_thread (*this, boost::bind (&Session::midi_track_presentation_info_changed, this, _1, boost::weak_ptr<MidiTrack>(mt)));
+ }
+ }
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
+ if (!r->presentation_info().special (false)) {
- /* presentation info order may already have been set from XML */
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
- if (!r->presentation_info().order_set()) {
+ /* presentation info order may already have been set from XML */
- if (order == PresentationInfo::max_order) {
- /* just add to the end */
- r->set_presentation_order (n_routes + added, false);
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to NR %1 + %2 = %3\n", n_routes, added, n_routes + added));
+ if (!r->presentation_info().order_set()) {
+ if (order == PresentationInfo::max_order) {
+ /* just add to the end */
+ r->set_presentation_order (n_routes + added);
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to NR %1 + %2 = %3\n", n_routes, added, n_routes + added));
+ } else {
+ r->set_presentation_order (order + added);
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added));
+ }
} else {
- r->set_presentation_order (order + added);
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added));
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order()));
}
- } else {
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order()));
}
- }
#if !defined(__APPLE__) && !defined(__FreeBSD__)
- /* clang complains: 'operator<<' should be declared prior to the call site or in an associated namespace of one of its
- * arguments std::ostream& operator<<(std::ostream& o, ARDOUR::PresentationInfo const& rid)"
- */
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n",
- r->name(),
- r->presentation_info().order(),
- enum_2_string (r->presentation_info().flags()),
- r->presentation_info()));
+ /* clang complains: 'operator<<' should be declared prior to the call site or in an associated namespace of one of its
+ * arguments std::ostream& operator<<(std::ostream& o, ARDOUR::PresentationInfo const& rid)"
+ */
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n",
+ r->name(),
+ r->presentation_info().order(),
+ enum_2_string (r->presentation_info().flags()),
+ r->presentation_info()));
#endif
- if (input_auto_connect || output_auto_connect) {
- auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs);
- existing_inputs += r->n_inputs();
- existing_outputs += r->n_outputs();
- }
+ if (input_auto_connect || output_auto_connect) {
+ auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs);
+ existing_inputs += r->n_inputs();
+ existing_outputs += r->n_outputs();
+ }
- ARDOUR::GUIIdle ();
+ ARDOUR::GUIIdle ();
+ }
+ ensure_stripable_sort_order ();
}
if (_monitor_out && IO::connecting_legal) {
void
Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
{
+ bool mute_changed = false;
+ bool send_selected = false;
+
{ // RCU Writer scope
PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
RCUWriter<RouteList> writer (routes);
boost::shared_ptr<RouteList> rs = writer.get_copy ();
-
for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
+ if (_selection->selected (*iter)) {
+ send_selected = true;
+ }
+
if (*iter == _master_out) {
continue;
}
(*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
}
+ if ((*iter)->mute_control()->muted ()) {
+ mute_changed = true;
+ }
+
rs->remove (*iter);
/* deleting the master out seems like a dumb
} // end of RCU Writer scope
+ if (mute_changed) {
+ MuteChanged (); /* EMIT SIGNAL */
+ }
+
update_route_solo_state ();
update_latency_compensation ();
set_dirty();
routes.flush ();
+ /* remove these routes from the selection if appropriate, and signal
+ * the change *before* we call DropReferences for them.
+ */
+
+ if (send_selected && !deletion_in_progress()) {
+ for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
+ _selection->remove_stripable_by_id ((*iter)->id());
+ }
+ PropertyChange pc;
+ pc.add (Properties::selected);
+ PresentationInfo::Change (pc);
+ }
+
/* try to cause everyone to drop their references
* and unregister ports from the backend
*/
for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
- cerr << "Drop references to " << (*iter)->name() << endl;
(*iter)->drop_references ();
}
- if (_state_of_the_state & Deletion) {
+ if (deletion_in_progress()) {
return;
}
- PresentationInfo::Change(); /* EMIT SIGNAL */
+ PropertyChange pc;
+ pc.add (Properties::order);
+ PresentationInfo::Change (pc);
/* save the new state of the world */
void
Session::route_mute_changed ()
{
+ MuteChanged (); /* EMIT SIGNAL */
set_dirty ();
}
set_dirty();
}
+bool
+Session::muted () const
+{
+ // TODO consider caching the value on every MuteChanged signal,
+ // Note that API users may also subscribe to MuteChanged and hence
+ // this method needs to be called first.
+ bool muted = false;
+ StripableList all;
+ get_stripables (all);
+ for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
+ assert (!(*i)->is_auditioner()); // XXX remove me
+ if ((*i)->is_monitor()) {
+ continue;
+ }
+ boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
+ if (r && !r->active()) {
+ continue;
+ }
+ boost::shared_ptr<MuteControl> mc = (*i)->mute_control();
+ if (mc && mc->muted ()) {
+ muted = true;
+ break;
+ }
+ }
+ return muted;
+}
+
+std::vector<boost::weak_ptr<AutomationControl> >
+Session::cancel_all_mute ()
+{
+ StripableList all;
+ get_stripables (all);
+ 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) {
+ assert (!(*i)->is_auditioner());
+ if ((*i)->is_monitor()) {
+ continue;
+ }
+ boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*i);
+ if (r && !r->active()) {
+ continue;
+ }
+ boost::shared_ptr<AutomationControl> ac = (*i)->mute_control();
+ if (ac && ac->get_value () > 0) {
+ cl->push_back (ac);
+ muted.push_back (boost::weak_ptr<AutomationControl>(ac));
+ }
+ }
+ if (!cl->empty ()) {
+ set_controls (cl, 0.0, PBD::Controllable::UseGroup);
+ }
+ return muted;
+}
+
void
Session::get_stripables (StripableList& sl) const
{
sl.insert (sl.end(), v.begin(), v.end());
}
+StripableList
+Session::get_stripables () const
+{
+ StripableList rv;
+ Session::get_stripables (rv);
+ rv.sort (Stripable::Sorter ());
+ return rv;
+}
+
boost::shared_ptr<RouteList>
Session::get_routes_with_internal_returns() const
{
for (map<string,bool>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
if (name == reserved->first) {
if (!route_by_name (reserved->first)) {
- /* first instance of a reserved name is allowed */
- return true;
+ /* first instance of a reserved name is allowed for some */
+ return reserved->second;
}
/* all other instances of a reserved name are not allowed */
return false;
return boost::shared_ptr<Route> ((Route*) 0);
}
+
+boost::shared_ptr<Stripable>
+Session::stripable_by_id (PBD::ID id) const
+{
+ StripableList sl;
+ get_stripables (sl);
+
+ for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
+ if ((*s)->id() == id) {
+ return *s;
+ }
+ }
+
+ return boost::shared_ptr<Stripable>();
+}
+
boost::shared_ptr<Processor>
Session::processor_by_id (PBD::ID id) const
{
PresentationInfo::order_t match_cnt = 0;
get_stripables (sl);
- sl.sort (Stripable::PresentationOrderSorter());
+ sl.sort (Stripable::Sorter());
for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
boost::shared_ptr<Route>
Session::route_by_selected_count (uint32_t id) const
{
- boost::shared_ptr<RouteList> r = routes.reader ();
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- /* NOT IMPLEMENTED */
- }
+ RouteList r (*(routes.reader ()));
+ r.sort (Stripable::Sorter());
- return boost::shared_ptr<Route> ((Route*) 0);
-}
+ RouteList::iterator i;
-struct PresentationOrderSorter {
- bool operator() (boost::shared_ptr<Stripable> a, boost::shared_ptr<Stripable> b) {
- if (a->presentation_info().special() && !b->presentation_info().special()) {
- /* a is not ordered, b is; b comes before a */
- return false;
- } else if (!b->presentation_info().order_set() && a->presentation_info().order_set()) {
- /* b is not ordered, a is; a comes before b */
- return true;
- } else {
- return a->presentation_info().order() < b->presentation_info().order();
+ for (i = r.begin(); i != r.end(); ++i) {
+ if ((*i)->is_selected()) {
+ if (id == 0) {
+ return *i;
+ }
+ --id;
}
}
-};
+
+ return boost::shared_ptr<Route> ();
+}
void
Session::reassign_track_numbers ()
int64_t tn = 0;
int64_t bn = 0;
RouteList r (*(routes.reader ()));
- PresentationOrderSorter sorter;
- r.sort (sorter);
+ r.sort (Stripable::Sorter());
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);
}
}
}
boost::shared_ptr<MidiSource>
-Session::midi_source_by_path (const std::string& path) const
+Session::midi_source_by_path (const std::string& path, bool need_source_lock) const
{
/* Restricted to MIDI files because audio sources require a channel
for unique identification, in addition to a path.
*/
- Glib::Threads::Mutex::Lock lm (source_lock);
+ Glib::Threads::Mutex::Lock lm (source_lock, Glib::Threads::NOT_LOCK);
+ if (need_source_lock) {
+ lm.acquire ();
+ }
for (SourceMap::const_iterator s = sources.begin(); s != sources.end(); ++s) {
boost::shared_ptr<MidiSource> ms
/** Return a unique name based on `base` for a new internal MIDI source */
string
-Session::new_midi_source_path (const string& base)
+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 (midi_source_by_path (possible_path)) {
+ if (midi_source_by_path (possible_path, need_lock)) {
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
tbl_arg[(*i)->name] = (*i)->value;
}
(*_lua_add)(name, bytecode, tbl_arg); // throws luabridge::LuaException
+ lm.release();
+
+ LuaScriptsChanged (); /* EMIT SIGNAL */
set_dirty();
}
Glib::Threads::Mutex::Lock lm (lua_lock);
(*_lua_del)(name); // throws luabridge::LuaException
lua.collect_garbage ();
+ lm.release();
+
+ LuaScriptsChanged (); /* EMIT SIGNAL */
set_dirty();
}
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;
}
}
-bool
-Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
-{
- if (a->is_monitor()) {
- return true;
- }
- if (b->is_monitor()) {
- return false;
- }
- return a->presentation_info().order() < b->presentation_info().order();
-}
-
bool
Session::is_auditioning () const
{
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)
{
return ProcessThread::send_gain_automation_buffer ();
}
+gain_t*
+Session::scratch_automation_buffer() const
+{
+ return ProcessThread::scratch_automation_buffer ();
+}
+
pan_t**
Session::pan_automation_buffer() const
{
return n;
}
+uint32_t
+Session::nstripables (bool with_monitor) const
+{
+ uint32_t rv = routes.reader()->size ();
+ rv += _vca_manager->vcas ().size ();
+
+ if (with_monitor) {
+ return rv;
+ }
+
+ if (_monitor_out) {
+ assert (rv > 0);
+ --rv;
+ }
+ return rv;
+}
+
void
Session::add_automation_list(AutomationList *al)
{
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 ());
}
}
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;
return;
}
- PresentationInfo::Change (); /* EMIT SIGNAL */
reassign_track_numbers();
#ifdef USE_TRACKS_CODE_FEATURES
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