#include "pbd/basename.h"
#include "pbd/convert.h"
-#include "pbd/convert.h"
#include "pbd/error.h"
#include "pbd/file_utils.h"
#include "pbd/md5.h"
#include "ardour/recent_sessions.h"
#include "ardour/region.h"
#include "ardour/region_factory.h"
+#include "ardour/revision.h"
#include "ardour/route_graph.h"
#include "ardour/route_group.h"
-#include "ardour/route_sorters.h"
#include "ardour/send.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/session_playlists.h"
#include "ardour/smf_source.h"
+#include "ardour/solo_isolate_control.h"
#include "ardour/source_factory.h"
#include "ardour/speakers.h"
#include "ardour/tempo.h"
#include "ardour/user_bundle.h"
#include "ardour/utils.h"
#include "ardour/vca_manager.h"
+#include "ardour/vca.h"
#include "midi++/port.h"
#include "midi++/mmc.h"
#include "LuaBridge/LuaBridge.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
#include <glibmm/checksum.h>
, _record_status (Disabled)
, _transport_frame (0)
, _session_range_location (0)
+ , _session_range_end_is_free (true)
, _slave (0)
, _silent (false)
, _transport_speed (0)
, post_export_position (0)
, _exporting (false)
, _export_rolling (false)
+ , _realtime_export (false)
, _export_preroll (0)
+ , _export_latency (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)
+ , _mempool ("Session", 2097152)
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
, _n_lua_scripts (0)
, _butler (new Butler (*this))
, _rt_thread_active (false)
, _rt_emit_pending (false)
, _ac_thread_active (false)
+ , _latency_recompute_pending (0)
, step_speed (0)
, outbound_mtc_timecode_frame (0)
, next_quarter_frame_to_send (-1)
, _step_editors (0)
, _suspend_timecode_transmission (0)
, _speakers (new Speakers)
- , _order_hint (-1)
, ignore_route_processor_changes (false)
, midi_clock (0)
, _scene_changer (0)
{
uint32_t sr = 0;
+ created_with = string_compose ("%1 %2", PROGRAM_NAME, revision);
+
pthread_mutex_init (&_rt_emit_mutex, 0);
pthread_cond_init (&_rt_emit_cond, 0);
pthread_cond_init (&_auto_connect_cond, 0);
init_name_id_counter (1); // reset for new sessions, start at 1
+ VCA::set_next_vca_number (1); // reset for new sessions, start at 1
pre_engine_init (fullpath);
}
}
+void
+Session::get_physical_ports (vector<string>& inputs, vector<string>& outputs, DataType type, bool excluding)
+{
+ _engine.get_physical_inputs (type, inputs);
+
+ if (excluding) {
+ /* rip out ControlOnly ports, and ALSA MIDI Through ports */
+
+ for (vector<string>::iterator si = inputs.begin(); si != inputs.end(); ) {
+ if (PortManager::port_is_control_only (*si)) {
+ si = inputs.erase (si);
+ } else if ((*si).find (X_("Midi Through")) != string::npos || (*si).find (X_("Midi-Through")) != string::npos) {
+ si = inputs.erase (si);
+ } else {
+ ++si;
+ }
+ }
+ }
+ _engine.get_physical_outputs (type, outputs);
+
+ if (excluding) {
+ /* rip out ControlOnly ports, and ALSA MIDI Through ports */
+
+ for (vector<string>::iterator si = outputs.begin(); si != outputs.end(); ) {
+ if (PortManager::port_is_control_only (*si)) {
+ si = outputs.erase (si);
+ } else if ((*si).find (X_("Midi Through")) != string::npos || (*si).find (X_("Midi-Through")) != string::npos) {
+ si = outputs.erase (si);
+ } else {
+ ++si;
+ }
+ }
+ }
+}
+
void
Session::setup_bundles ()
{
vector<string> inputs[DataType::num_types];
vector<string> outputs[DataType::num_types];
+
for (uint32_t i = 0; i < DataType::num_types; ++i) {
- _engine.get_physical_inputs (DataType (DataType::Symbol (i)), inputs[i]);
- _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]);
+ get_physical_ports (inputs[i], outputs[i], DataType (DataType::Symbol (i)), true);
}
/* Create a set of Bundle objects that map
return;
}
- boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), Route::MonitorOut, DataType::AUDIO));
+ boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), PresentationInfo::MonitorOut, DataType::AUDIO));
if (r->init ()) {
return;
}
rl.push_back (r);
- add_routes (rl, false, false, false);
+ add_routes (rl, false, false, false, 0);
assert (_monitor_out);
_master_out->output()->disconnect (this);
_monitor_out->output()->disconnect (this);
- _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this);
- _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this);
+ // monitor section follow master bus - except midi
+ ChanCount mon_chn (_master_out->output()->n_ports());
+ mon_chn.set_midi (0);
+
+ _monitor_out->input()->ensure_io (mon_chn, false, this);
+ _monitor_out->output()->ensure_io (mon_chn, false, this);
for (uint32_t n = 0; n < limit; ++n) {
boost::shared_ptr<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
boost::shared_ptr<RouteList> rl = routes.reader ();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
- if (tr && tr->record_enabled ()) {
+ if (tr && tr->rec_enable_control()->get_value()) {
//cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
tr->request_input_monitoring (yn);
}
Session::set_all_tracks_record_enabled (bool enable )
{
boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr) {
- tr->set_record_enabled (enable, Controllable::NoGroup);
- }
- }
+ set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), enable, Controllable::NoGroup);
}
-
void
Session::disable_record (bool rt_context, bool force)
{
Session::audible_frame () const
{
framepos_t ret;
- framepos_t tf;
- framecnt_t offset;
- offset = worst_playback_latency ();
+ frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start ();
+ offset *= transport_speed ();
if (synced_to_engine()) {
/* Note: this is basically just sync-to-JACK */
- tf = _engine.transport_frame();
+ ret = _engine.transport_frame();
} else {
- tf = _transport_frame;
+ ret = _transport_frame;
}
- ret = tf;
-
- if (!non_realtime_work_pending()) {
-
- /* MOVING */
+ if (transport_rolling()) {
+ ret -= offset;
/* Check to see if we have passed the first guaranteed
- audible frame past our last start position. if not,
- return that last start point because in terms
- of audible frames, we have not moved yet.
-
- `Start position' in this context means the time we last
- either started, located, or changed transport direction.
- */
+ * audible frame past our last start position. if not,
+ * return that last start point because in terms
+ * of audible frames, we have not moved yet.
+ *
+ * `Start position' in this context means the time we last
+ * either started, located, or changed transport direction.
+ */
if (_transport_speed > 0.0f) {
if (!play_loop || !have_looped) {
- if (tf < _last_roll_or_reversal_location + offset) {
+ if (ret < _last_roll_or_reversal_location) {
return _last_roll_or_reversal_location;
}
+ } else {
+ // latent loops
+ Location *location = _locations->auto_loop_location();
+ frameoffset_t lo = location->start() - ret;
+ if (lo > 0) {
+ ret = location->end () - lo;
+ }
}
-
- /* forwards */
- ret -= offset;
-
} else if (_transport_speed < 0.0f) {
/* XXX wot? no backward looping? */
- if (tf > _last_roll_or_reversal_location - offset) {
+ if (ret > _last_roll_or_reversal_location) {
return _last_roll_or_reversal_location;
- } else {
- /* backwards */
- ret += offset;
}
}
}
- return ret;
+ return std::max ((framepos_t)0, ret);
}
void
#ifndef NDEBUG
DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n",
- (*i)->name(), (*i)->order_key ()));
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 presentation order %2\n", (*i)->name(), (*i)->presentation_info().order()));
}
#endif
* @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, boost::shared_ptr<PluginInfo> instrument,
- TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template, Plugin::PresetRecord* pset)
+Session::new_midi_track (const ChanCount& input, const ChanCount& output,
+ boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+ RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order,
+ TrackMode mode)
{
string track_name;
uint32_t track_id = 0;
boost::shared_ptr<MidiTrack> track;
try {
- track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode));
+ track.reset (new MidiTrack (*this, track_name, mode));
if (track->init ()) {
goto failed;
track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
- if (Config->get_remote_model() == UserOrdered) {
- track->set_remote_control_id (next_control_id());
- }
-
new_routes.push_back (track);
ret.push_back (track);
-
- RouteAddedOrRemoved (true); /* EMIT SIGNAL */
}
catch (failed_constructor &err) {
if (!new_routes.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
- add_routes (new_routes, false, false, false);
+ add_routes (new_routes, false, false, false, order);
} else {
- add_routes (new_routes, true, true, false);
+ add_routes (new_routes, true, true, false, order);
}
if (instrument) {
}
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, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+ PresentationInfo::Flag flag, PresentationInfo::order_t order)
{
string bus_name;
uint32_t bus_id = 0;
}
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
+ boost::shared_ptr<Route> bus (new Route (*this, bus_name, flag, DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI
if (bus->init ()) {
goto failure;
if (route_group) {
route_group->add (bus);
}
- if (Config->get_remote_model() == UserOrdered) {
- bus->set_remote_control_id (next_control_id());
- }
+ bus->add_internal_return ();
ret.push_back (bus);
- RouteAddedOrRemoved (true); /* EMIT SIGNAL */
- ARDOUR::GUIIdle ();
}
catch (failed_constructor &err) {
failure:
if (!ret.empty()) {
StateProtector sp (this);
- add_routes (ret, false, false, false);
+ add_routes (ret, false, false, false, order);
if (instrument) {
for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
#endif
+void
+Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many)
+{
+ if (first_new_order == PresentationInfo::max_order) {
+ /* adding at end, no worries */
+ return;
+ }
+
+ /* create a gap in the presentation info to accomodate @param how_many
+ * new objects.
+ */
+ StripableList sl;
+ get_stripables (sl);
+
+ for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
+ boost::shared_ptr<Stripable> s (*si);
+
+ if (s->is_monitor() || s->is_auditioner()) {
+ continue;
+ }
+
+ if (s->presentation_info().order () >= first_new_order) {
+ s->set_presentation_order (s->presentation_info().order () + how_many);
+ }
+ }
+}
+
/** Caller must not hold process lock
* @param name_template string to use for the start of the name, or "" to use "Audio".
*/
list< boost::shared_ptr<AudioTrack> >
-Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,
- uint32_t how_many, string name_template)
+Session::new_audio_track (int input_channels, int output_channels, RouteGroup* route_group,
+ uint32_t how_many, string name_template, PresentationInfo::order_t order,
+ TrackMode mode)
{
string track_name;
uint32_t track_id = 0;
boost::shared_ptr<AudioTrack> track;
try {
- track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode));
+ track.reset (new AudioTrack (*this, track_name, mode));
if (track->init ()) {
goto failed;
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), Controllable::NoGroup);
+ track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup);
}
}
track->non_realtime_input_change();
track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
- if (Config->get_remote_model() == UserOrdered) {
- track->set_remote_control_id (next_control_id());
- }
new_routes.push_back (track);
ret.push_back (track);
-
- RouteAddedOrRemoved (true); /* EMIT SIGNAL */
}
catch (failed_constructor &err) {
if (!new_routes.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
- add_routes (new_routes, false, false, false);
+ add_routes (new_routes, false, false, false, order);
} else {
- add_routes (new_routes, true, true, false);
+ add_routes (new_routes, true, true, false, order);
}
}
* @param name_template string to use for the start of the name, or "" to use "Bus".
*/
RouteList
-Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template)
+Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template,
+ PresentationInfo::Flag flags, PresentationInfo::order_t order)
{
string bus_name;
uint32_t bus_id = 0;
}
try {
- boost::shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO));
+ boost::shared_ptr<Route> bus (new Route (*this, bus_name, flags, DataType::AUDIO));
if (bus->init ()) {
goto failure;
if (route_group) {
route_group->add (bus);
}
- if (Config->get_remote_model() == UserOrdered) {
- bus->set_remote_control_id (next_control_id());
- }
bus->add_internal_return ();
-
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;
if (!ret.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
- add_routes (ret, false, false, false);
+ add_routes (ret, false, false, false, order);
} else {
- add_routes (ret, false, true, true); // autoconnect // outputs only
+ add_routes (ret, false, true, true, order); // autoconnect // outputs only
}
}
}
RouteList
-Session::new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name_base, PlaylistDisposition pd)
+Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t insert_at, const std::string& template_path, const std::string& name_base,
+ PlaylistDisposition pd)
{
XMLTree tree;
return RouteList();
}
- return new_route_from_template (how_many, *tree.root(), name_base, pd);
+ return new_route_from_template (how_many, insert_at, *tree.root(), name_base, pd);
}
RouteList
-Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd)
+Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t insert_at, 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
Stateful::ForceIDRegeneration force_ids;
IO::disable_connecting ();
- control_id = next_control_id ();
-
while (how_many) {
/* We're going to modify the node contents a bit so take a
(*i)->add_property ("bitslot", buf);
(*i)->add_property ("name", name);
}
+ else if (type && type->value() == X_("intreturn")) {
+ (*i)->remove_property (X_("bitslot"));
+ (*i)->add_property ("ignore-bitslot", "1");
+ }
else if (type && type->value() == X_("return")) {
// Return::set_state() generates a new one
(*i)->remove_property (X_("bitslot"));
route->output()->changed (change, this);
}
- route->set_remote_control_id (control_id);
- ++control_id;
-
boost::shared_ptr<Track> track;
if ((track = boost::dynamic_pointer_cast<Track> (route))) {
};
ret.push_back (route);
-
- RouteAddedOrRemoved (true); /* EMIT SIGNAL */
}
catch (failed_constructor &err) {
if (!ret.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
- add_routes (ret, false, false, false);
+ add_routes (ret, false, false, false, insert_at);
} else {
- add_routes (ret, true, true, false);
+ add_routes (ret, true, true, false, insert_at);
}
IO::enable_connecting ();
}
}
void
-Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save)
+Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save, PresentationInfo::order_t order)
{
try {
PBD::Unwinder<bool> aip (_adding_routes_in_progress, true);
- add_routes_inner (new_routes, input_auto_connect, output_auto_connect);
+ add_routes_inner (new_routes, input_auto_connect, output_auto_connect, order);
} catch (...) {
error << _("Adding new tracks/busses failed") << endmsg;
save_state (_current_snapshot_name);
}
- reassign_track_numbers();
-
update_route_record_state ();
RouteAdded (new_routes); /* EMIT SIGNAL */
}
void
-Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect)
+Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, PresentationInfo::order_t order)
{
ChanCount existing_inputs;
ChanCount existing_outputs;
- uint32_t order = next_control_id();
-
-
- if (_order_hint > -1) {
- order = _order_hint;
- _order_hint = -1;
- }
+ uint32_t n_routes;
+ uint32_t added = 0;
count_existing_track_channels (existing_inputs, existing_outputs);
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();
/* 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
}
}
- for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
+ 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());
+
+ for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
boost::weak_ptr<Route> wpr (*x);
boost::shared_ptr<Route> r (*x);
- r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr));
- r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
- r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
- r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+ r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2,wpr));
+ r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
+ r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+
r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
+ r->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, this));
if (r->is_master()) {
_master_out = 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->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
+ tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
if (mt) {
}
}
- 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 (!r->presentation_info().special()) {
- /* order keys are a GUI responsibility but we need to set up
- reasonable defaults because they also affect the remote control
- ID in most situations.
- */
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
+
+ /* presentation info order may already have been set from XML */
- if (!r->has_order_key ()) {
- if (r->is_auditioner()) {
- /* use an arbitrarily high value */
- r->set_order_key (UINT_MAX);
+ if (!r->presentation_info().order_set()) {
+
+ 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));
+ } 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 {
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("while adding, set %1 to order key %2\n", r->name(), order));
- r->set_order_key (order);
- order++;
+ 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()));
+#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();
+ }
+
ARDOUR::GUIIdle ();
}
}
}
}
+
+ reassign_track_numbers ();
}
void
continue;
}
- (*iter)->set_solo (false, Controllable::NoGroup);
+ (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
rs->remove (*iter);
} // end of RCU Writer scope
update_route_solo_state ();
- RouteAddedOrRemoved (false); /* EMIT SIGNAL */
update_latency_compensation ();
set_dirty();
return;
}
- Route::RemoteControlIDChange(); /* EMIT SIGNAL */
+ PresentationInfo::Change(); /* EMIT SIGNAL */
/* save the new state of the world */
save_history (_current_snapshot_name);
}
- reassign_track_numbers();
update_route_record_state ();
}
void
Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
{
- boost::shared_ptr<Route> route = wpr.lock();
+ boost::shared_ptr<Route> route (wpr.lock());
+
if (!route) {
- error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_listen_changed")) << endmsg;
return;
}
- if (route->listening_via_monitor ()) {
+ assert (Config->get_solo_control_is_listen_control());
+
+ if (route->solo_control()->soloed_by_self_or_masters()) {
if (Config->get_exclusive_solo()) {
RouteGroup* rg = route->route_group ();
- const bool group_already_accounted_for = route->use_group (group_override, &RouteGroup::is_solo);
+ const bool group_already_accounted_for = (group_override == Controllable::ForGroup);
boost::shared_ptr<RouteList> r = routes.reader ();
continue;
}
- if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
*/
continue;
}
- (*i)->set_listen (false, Controllable::NoGroup);
+ (*i)->solo_control()->set_value (0.0, Controllable::NoGroup);
}
}
update_route_solo_state ();
}
+
void
Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
{
- boost::shared_ptr<Route> route = wpr.lock ();
+ boost::shared_ptr<Route> route (wpr.lock());
if (!route) {
- /* should not happen */
- error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg;
return;
}
bool send_changed = false;
- if (route->solo_isolated()) {
+ if (route->solo_isolate_control()->solo_isolated()) {
if (_solo_isolated_cnt == 0) {
send_changed = true;
}
}
void
-Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_changed));
+
+ boost::shared_ptr<Route> route (wpr.lock());
- if (!self_solo_change) {
- // session doesn't care about changes to soloed-by-others
+ if (!route) {
return;
}
- boost::shared_ptr<Route> route = wpr.lock ();
- assert (route);
+ if (Config->get_solo_control_is_listen_control()) {
+ route_listen_changed (group_override, wpr);
+ return;
+ }
- boost::shared_ptr<RouteList> r = routes.reader ();
- int32_t delta;
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: self %2 masters %3 transition %4\n", route->name(), route->self_soloed(), route->solo_control()->get_masters_value(), route->solo_control()->transitioned_into_solo()));
- if (route->self_soloed()) {
- delta = 1;
- } else {
- delta = -1;
+ if (route->solo_control()->transitioned_into_solo() == 0) {
+ /* route solo changed by upstream/downstream; not interesting
+ to Session.
+ */
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 not self-soloed nor soloed by master (%2), ignoring\n", route->name(), route->solo_control()->get_masters_value()));
+ return;
}
+ if (route->solo_control()->transitioned_into_solo() == 0) {
+ /* reason for being soloed changed (e.g. master went away, we
+ * took over the master state), but actual status did
+ * not. nothing to do.
+ */
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: solo change was change in reason, not status\n", route->name()));
+ }
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ int32_t delta = route->solo_control()->transitioned_into_solo ();
+
/* 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
continue;
}
- if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
continue;
}
- (*i)->set_solo (false, group_override);
+ (*i)->solo_control()->set_value (0.0, group_override);
}
}
continue;
}
- if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 excluded from solo because iso = %2 can_solo = %3\n", (*i)->name(), (*i)->solo_isolate_control()->solo_isolated(),
+ (*i)->can_solo()));
continue;
}
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name()));
if (!via_sends_only) {
if (!route->soloed_by_others_upstream()) {
- (*i)->mod_solo_by_others_downstream (delta);
+ (*i)->solo_control()->mod_solo_by_others_downstream (delta);
} else {
DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n");
}
sends are involved.
*/
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 feeds %2 via sends only %3 sboD %4 sboU %5\n",
- route->name(),
- (*i)->name(),
- via_sends_only,
- route->soloed_by_others_downstream(),
- route->soloed_by_others_upstream()));
+ route->name(),
+ (*i)->name(),
+ via_sends_only,
+ route->soloed_by_others_downstream(),
+ route->soloed_by_others_upstream()));
if (!via_sends_only) {
//NB. Triggers Invert Push, which handles soloed by downstream
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta));
- (*i)->mod_solo_by_others_upstream (delta);
+ (*i)->solo_control()->mod_solo_by_others_upstream (delta);
} else {
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name()));
}
for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name()));
(*i)->act_on_mute ();
- (*i)->mute_changed ();
+ (*i)->mute_control()->Changed (false, Controllable::NoGroup);
}
SoloChanged (); /* EMIT SIGNAL */
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) {
- something_soloed = true;
- }
-
- if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) {
+ if ((*i)->can_solo()) {
if (Config->get_solo_control_is_listen_control()) {
- listeners++;
- something_listening = true;
+ if ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value()) {
+ listeners++;
+ something_listening = true;
+ }
} else {
- (*i)->set_listen (false, Controllable::NoGroup);
+ (*i)->set_listen (false);
+ if ((*i)->can_solo() && ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value())) {
+ something_soloed = true;
+ }
}
}
- if ((*i)->solo_isolated()) {
+ if ((*i)->solo_isolate_control()->solo_isolated()) {
isolated++;
}
}
something_soloed, listeners, isolated));
}
+void
+Session::get_stripables (StripableList& sl) const
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ sl.insert (sl.end(), r->begin(), r->end());
+
+ VCAList v = _vca_manager->vcas ();
+ sl.insert (sl.end(), v.begin(), v.end());
+}
+
boost::shared_ptr<RouteList>
Session::get_routes_with_internal_returns() const
{
}
bool
-Session::io_name_is_legal (const std::string& name)
+Session::io_name_is_legal (const std::string& name) const
{
boost::shared_ptr<RouteList> r = routes.reader ();
}
boost::shared_ptr<Route>
-Session::route_by_name (string name)
+Session::route_by_name (string name) const
{
boost::shared_ptr<RouteList> r = routes.reader ();
}
boost::shared_ptr<Route>
-Session::route_by_id (PBD::ID id)
+Session::route_by_id (PBD::ID id) const
{
boost::shared_ptr<RouteList> r = routes.reader ();
}
boost::shared_ptr<Track>
-Session::track_by_diskstream_id (PBD::ID id)
+Session::track_by_diskstream_id (PBD::ID id) const
{
boost::shared_ptr<RouteList> r = routes.reader ();
}
boost::shared_ptr<Route>
-Session::route_by_remote_id (uint32_t id)
+Session::get_remote_nth_route (PresentationInfo::order_t n) const
{
- boost::shared_ptr<RouteList> r = routes.reader ();
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if ((*i)->remote_control_id() == id) {
- return *i;
- }
- }
-
- return boost::shared_ptr<Route> ((Route*) 0);
+ return boost::dynamic_pointer_cast<Route> (get_remote_nth_stripable (n, PresentationInfo::Route));
}
-
boost::shared_ptr<Stripable>
-Session::stripable_by_remote_id (uint32_t id)
+Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo::Flag flags) const
{
- boost::shared_ptr<RouteList> r = routes.reader ();
+ StripableList sl;
+ PresentationInfo::order_t match_cnt = 0;
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if ((*i)->remote_control_id() == id) {
- return *i;
+ get_stripables (sl);
+ sl.sort (Stripable::PresentationOrderSorter());
+
+ for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
+
+ if ((*s)->presentation_info().hidden()) {
+ /* if the caller didn't explicitly ask for hidden
+ stripables, ignore hidden ones. This matches
+ the semantics of the pre-PresentationOrder
+ "get by RID" logic of Ardour 4.x and earlier.
+
+ XXX at some point we should likely reverse
+ the logic of the flags, because asking for "the
+ hidden stripables" is not going to be common,
+ whereas asking for visible ones is normal.
+ */
+
+ if (! (flags & PresentationInfo::Hidden)) {
+ continue;
+ }
+ }
+
+ if ((*s)->presentation_info().flag_match (flags)) {
+ if (match_cnt++ == n) {
+ return *s;
+ }
}
}
- return boost::shared_ptr<Route> ((Route*) 0);
+ /* there is no nth stripable that matches the given flags */
+ return boost::shared_ptr<Stripable>();
}
-
boost::shared_ptr<Route>
-Session::route_by_selected_count (uint32_t id)
+Session::route_by_selected_count (uint32_t id) const
{
boost::shared_ptr<RouteList> r = routes.reader ();
return boost::shared_ptr<Route> ((Route*) 0);
}
+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();
+ }
+ }
+};
void
Session::reassign_track_numbers ()
int64_t tn = 0;
int64_t bn = 0;
RouteList r (*(routes.reader ()));
- SignalOrderRouteSorter sorter;
+ PresentationOrderSorter sorter;
r.sort (sorter);
StateProtector sp (this);
// trigger GUI re-layout
config.ParameterChanged("track-name-number");
}
+
+#ifndef NDEBUG
+ if (DEBUG_ENABLED(DEBUG::OrderKeys)) {
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 numbered %2\n", (*i)->name(), (*i)->track_number()));
+ }
+ }
+#endif /* NDEBUG */
+
}
void
_session_range_location->set_start (a);
}
- if (b > _session_range_location->end()) {
+ if (_session_range_end_is_free && (b > _session_range_location->end())) {
_session_range_location->set_end (b);
}
}
}
+void
+Session::set_end_is_free (bool yn)
+{
+ _session_range_end_is_free = yn;
+}
+
void
Session::playlist_ranges_moved (list<Evoral::RangeMove<framepos_t> > const & ranges)
{
Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
if (tm.locked ()) {
try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { }
+ lua.collect_garbage_step ();
}
}
#ifndef NDEBUG
lua.Print.connect (&_lua_print);
#endif
+ lua.tweak_rt_gc ();
lua.do_command (
"function ArdourSession ()"
" local self = { scripts = {}, instances = {} }"
if (b->is_monitor()) {
return false;
}
- return a->order_key () < b->order_key ();
+ return a->presentation_info().order() < b->presentation_info().order();
}
bool
while (i != rl->end ()) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && tr->record_enabled ()) {
+ if (tr && tr->rec_enable_control()->get_value()) {
break;
}
for (i = rl->begin(); i != rl->end (); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && !tr->record_enabled ()) {
+ if (tr && !tr->rec_enable_control()->get_value()) {
break;
}
}
void
Session::solo_control_mode_changed ()
{
- /* cancel all solo or all listen when solo control mode changes */
-
- if (soloing()) {
- set_solo (get_routes(), false);
- } else if (listening()) {
- set_listen (get_routes(), false);
+ if (soloing() || listening()) {
+ /* We can't use ::clear_all_solo_state() here because during
+ session loading at program startup, that will queue a call
+ to rt_clear_all_solo_state() that will not execute until
+ AFTER solo states have been established (thus throwing away
+ the session's saved solo state). So just explicitly turn
+ them all off.
+ */
+ set_controls (route_list_to_control_list (get_routes(), &Stripable::solo_control), 0.0, Controllable::NoGroup);
}
}
}
void
-Session::goto_start ()
+Session::goto_start (bool and_roll)
{
if (_session_range_location) {
- request_locate (_session_range_location->start(), false);
+ request_locate (_session_range_location->start(), and_roll);
} else {
- request_locate (0, false);
+ request_locate (0, and_roll);
}
}
if (l && l->start() == old) {
l->set_start (s->start(), true);
}
+ set_dirty ();
}
void
if (l && l->end() == old) {
l->set_end (s->end(), true);
}
+ set_dirty ();
}
std::vector<std::string>
return 0;
}
-uint32_t
-Session::next_control_id () const
-{
- int subtract = 0;
-
- /* the monitor bus remote ID is in a different
- * "namespace" than regular routes. its existence doesn't
- * affect normal (low) numbered routes.
- */
-
- if (_monitor_out) {
- subtract++;
- }
-
- /* the same about masterbus in Waves Tracks */
-
- if (Profile->get_trx() && _master_out) {
- subtract++;
- }
-
- return nroutes() - subtract;
-}
-
void
-Session::notify_remote_id_change ()
+Session::notify_presentation_info_change ()
{
if (deletion_in_progress()) {
return;
}
- switch (Config->get_remote_model()) {
- case MixerOrdered:
- Route::RemoteControlIDChange (); /* EMIT SIGNAL */
- break;
- default:
- break;
- }
+ PresentationInfo::Change (); /* EMIT SIGNAL */
+ reassign_track_numbers();
#ifdef USE_TRACKS_CODE_FEATURES
- /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs
- * if track order has been changed by user
- */
- reconnect_existing_routes(true, true);
+ /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs
+ * if track order has been changed by user
+ */
+ reconnect_existing_routes(true, true);
#endif
}
-void
-Session::sync_order_keys ()
-{
- if (deletion_in_progress()) {
- return;
- }
-
- /* tell everyone that something has happened to the sort keys
- and let them sync up with the change(s)
- this will give objects that manage the sort order keys the
- opportunity to keep them in sync if they wish to.
- */
-
- DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n");
-
- reassign_track_numbers();
-
- Route::SyncOrderKeys (); /* EMIT SIGNAL */
-
- DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n");
-}
-
bool
Session::operation_in_progress (GQuark op) const
{
}
}
+void
+Session::queue_latency_recompute ()
+{
+ g_atomic_int_inc (&_latency_recompute_pending);
+ if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
+ pthread_cond_signal (&_auto_connect_cond);
+ pthread_mutex_unlock (&_auto_connect_mutex);
+ }
+}
+
void
Session::auto_connect (const AutoConnectRequest& ar)
{
vector<string> physinputs;
vector<string> physoutputs;
- _engine.get_physical_outputs (*t, physoutputs);
- _engine.get_physical_inputs (*t, physinputs);
+ get_physical_ports (physinputs, physoutputs, *t, true);
if (!physinputs.empty() && ar.connect_inputs) {
uint32_t nphysical_in = physinputs.size();
}
}
+ if (!actively_recording ()) { // might not be needed,
+ /* 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
+ * modifies the capture offset... which can be a proplem
+ * in "prepare_to_stop"
+ */
+ while (g_atomic_int_and (&_latency_recompute_pending, 0)) {
+ update_latency_compensation ();
+ }
+ }
+
pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);
}
pthread_mutex_unlock (&_auto_connect_mutex);
}
+
+void
+Session::cancel_all_solo ()
+{
+ StripableList sl;
+
+ get_stripables (sl);
+
+ set_controls (stripable_list_to_control_list (sl, &Stripable::solo_control), 0.0, Controllable::NoGroup);
+ clear_all_solo_state (routes.reader());
+}