#include "ardour/session_playlists.h"
#include "ardour/smf_source.h"
#include "ardour/source_factory.h"
+#include "ardour/speakers.h"
#include "ardour/utils.h"
#include "midi++/port.h"
bool Session::_disable_all_loaded_plugins = false;
-PBD::Signal0<int> Session::AudioEngineSetupRequired;
+PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
PBD::Signal1<void,std::string> Session::Dialog;
PBD::Signal0<int> Session::AskAboutPendingState;
PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
const string& snapshot_name,
BusProfile* bus_profile,
string mix_template)
- : _engine (eng)
+ : playlists (new SessionPlaylists)
+ , _engine (eng)
+ , process_function (&Session::process_with_events)
+ , waiting_for_sync_offset (false)
+ , _base_frame_rate (0)
+ , _current_frame_rate (0)
+ , _nominal_frame_rate (0)
+ , transport_sub_state (0)
+ , _record_status (Disabled)
+ , _transport_frame (0)
+ , _session_range_location (0)
+ , _slave (0)
+ , _silent (false)
+ , _transport_speed (0)
+ , _default_transport_speed (1.0)
+ , _last_transport_speed (0)
, _target_transport_speed (0.0)
+ , auto_play_legal (false)
+ , _last_slave_transport_frame (0)
+ , maximum_output_latency (0)
, _requested_return_frame (-1)
+ , current_block_size (0)
+ , _worst_output_latency (0)
+ , _worst_input_latency (0)
+ , _worst_track_latency (0)
+ , _have_captured (false)
+ , _meter_hold (0)
+ , _meter_falloff (0)
+ , _non_soloed_outs_muted (false)
+ , _listen_cnt (0)
+ , _solo_isolated_cnt (0)
+ , _writable (false)
+ , _was_seamless (Config->get_seamless_loop ())
, _under_nsm_control (false)
- , _session_dir (new SessionDirectory(fullpath))
+ , delta_accumulator_cnt (0)
+ , average_slave_delta (1800) // !!! why 1800 ???
+ , average_dir (0)
+ , have_first_delta_accumulator (false)
+ , _slave_state (Stopped)
+ , post_export_sync (false)
+ , post_export_position (0)
+ , _exporting (false)
+ , _export_started (false)
+ , _export_rolling (false)
+ , _pre_export_mmc_enabled (false)
+ , _name (snapshot_name)
+ , _is_new (true)
+ , _send_qf_mtc (false)
+ , _pframes_since_last_mtc (0)
+ , session_midi_feedback (0)
+ , play_loop (false)
+ , loop_changing (false)
+ , last_loopend (0)
+ , _session_dir (new SessionDirectory (fullpath))
+ , _current_snapshot_name (snapshot_name)
, state_tree (0)
- , _state_of_the_state (Clean)
+ , state_was_pending (false)
+ , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
+ , _last_roll_location (0)
+ , _last_roll_or_reversal_location (0)
+ , _last_record_location (0)
+ , pending_locate_roll (false)
+ , pending_locate_frame (0)
+ , pending_locate_flush (false)
+ , pending_abort (false)
+ , pending_auto_loop (false)
, _butler (new Butler (*this))
, _post_transport_work (0)
+ , cumulative_rf_motion (0)
+ , rf_scale (1.0)
+ , _locations (new Locations (*this))
+ , step_speed (0)
+ , outbound_mtc_timecode_frame (0)
+ , next_quarter_frame_to_send (-1)
+ , _frames_per_timecode_frame (0)
+ , _frames_per_hour (0)
+ , _timecode_frames_per_hour (0)
+ , last_timecode_valid (false)
+ , last_timecode_when (0)
, _send_timecode_update (false)
+ , ltc_encoder (0)
, ltc_enc_buf(0)
+ , ltc_buf_off (0)
+ , ltc_buf_len (0)
+ , ltc_speed (0)
+ , ltc_enc_byte (0)
+ , ltc_enc_pos (0)
+ , ltc_enc_cnt (0)
+ , ltc_enc_off (0)
+ , restarting (false)
+ , ltc_prev_cycle (0)
+ , ltc_timecode_offset (0)
+ , ltc_timecode_negative_offset (false)
+ , midi_control_ui (0)
+ , _tempo_map (0)
, _all_route_group (new RouteGroup (*this, "all"))
, routes (new RouteList)
+ , _adding_routes_in_progress (false)
+ , destructive_index (0)
+ , solo_update_disabled (false)
+ , default_fade_steepness (0)
+ , default_fade_msecs (0)
, _total_free_4k_blocks (0)
, _total_free_4k_blocks_uncertain (false)
+ , no_questions_about_missing_files (false)
+ , _playback_load (0)
+ , _capture_load (0)
, _bundles (new BundleList)
, _bundle_xml_node (0)
, _current_trans (0)
+ , _clicking (false)
, click_data (0)
, click_emphasis_data (0)
+ , click_length (0)
+ , click_emphasis_length (0)
+ , _clicks_cleared (0)
+ , _play_range (false)
, main_outs (0)
+ , first_file_data_format_reset (true)
+ , first_file_header_format_reset (true)
+ , have_looped (false)
, _have_rec_enabled_track (false)
+ , _step_editors (0)
, _suspend_timecode_transmission (0)
+ , _speakers (new Speakers)
+ , _order_hint (0)
+ , ignore_route_processor_changes (false)
+ , _midi_ports (0)
+ , _mmc (0)
{
- if (_engine.current_backend() == 0 || _engine.setup_required()) {
- boost::optional<int> r = AudioEngineSetupRequired ();
- if (r.get_value_or (-1) != 0) {
- throw failed_constructor();
- }
- }
-
- if (!_engine.connected()) {
- throw failed_constructor();
- }
+ uint32_t sr = 0;
- _locations = new Locations (*this);
- ltc_encoder = NULL;
-
- _midi_ports = new MidiPortManager;
- _mmc = new MIDI::MachineControl;
-
- _mmc->set_ports (_midi_ports->mmc_input_port(), _midi_ports->mmc_output_port());
-
- if (how_many_dsp_threads () > 1) {
- /* For now, only create the graph if we are using >1 DSP threads, as
- it is a bit slower than the old code with 1 thread.
- */
- _process_graph.reset (new Graph (*this));
- }
+ pre_engine_init (fullpath);
+
+ if (_is_new) {
+ if (ensure_engine (sr)) {
+ destroy ();
+ throw failed_constructor ();
+ }
- playlists.reset (new SessionPlaylists);
+ if (create (mix_template, bus_profile)) {
+ destroy ();
+ throw failed_constructor ();
+ }
- _all_route_group->set_active (true, this);
+ /* if a mix template was provided, then ::create() will
+ * have copied it into the session and we need to load it
+ * so that we have the state ready for ::set_state()
+ * after the engine is started.
+ *
+ * Note that we do NOT try to get the sample rate from
+ * the template at this time, though doing so would
+ * be easy if we decided this was an appropriate part
+ * of a template.
+ */
- interpolation.add_channel_to (0, 0);
+ if (!mix_template.empty() && load_state (_current_snapshot_name)) {
+ throw failed_constructor ();
+ }
- n_physical_outputs = _engine.n_physical_outputs ();
- n_physical_inputs = _engine.n_physical_inputs ();
+ } else {
- first_stage_init (fullpath, snapshot_name);
+ if (load_state (_current_snapshot_name)) {
+ throw failed_constructor ();
+ }
+
+ /* try to get sample rate from XML state so that we
+ * can influence the SR if we set up the audio
+ * engine.
+ */
- _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ if (state_tree) {
+ const XMLProperty* prop;
+ if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) {
+ sr = atoi (prop->value());
+ }
+ }
- if (_is_new) {
- if (create (mix_template, bus_profile)) {
+ if (ensure_engine (sr)) {
destroy ();
throw failed_constructor ();
}
}
- if (second_stage_init ()) {
+ if (post_engine_init ()) {
destroy ();
throw failed_constructor ();
}
- store_recent_sessions(_name, _path);
+ store_recent_sessions (_name, _path);
bool was_dirty = dirty();
EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
_is_new = false;
+
+ /* hook us up to the engine since we are now completely constructed */
+
+ BootMessage (_("Connect to engine"));
+
+ _engine.set_session (this);
+ _engine.reset_timebase ();
+
+ BootMessage (_("Session loading complete"));
+
}
Session::~Session ()
destroy ();
}
+int
+Session::ensure_engine (uint32_t desired_sample_rate)
+{
+ if (_engine.current_backend() == 0) {
+ /* backend is unknown ... */
+ boost::optional<int> r = AudioEngineSetupRequired (desired_sample_rate);
+ if (r.get_value_or (-1) != 0) {
+ return -1;
+ }
+ } else if (_engine.setup_required()) {
+ /* backend is known, but setup is needed */
+ boost::optional<int> r = AudioEngineSetupRequired (desired_sample_rate);
+ if (r.get_value_or (-1) != 0) {
+ return -1;
+ }
+ } else if (!_engine.running()) {
+ if (_engine.start()) {
+ return -1;
+ }
+ }
+
+ /* at this point the engine should be running
+ */
+
+ if (!_engine.running()) {
+ return -1;
+ }
+
+ return immediately_post_engine ();
+
+}
+
+int
+Session::immediately_post_engine ()
+{
+ /* Do various initializations that should take place directly after we
+ * know that the engine is running, but before we either create a
+ * session or set state for an existing one.
+ */
+
+ if (how_many_dsp_threads () > 1) {
+ /* For now, only create the graph if we are using >1 DSP threads, as
+ it is a bit slower than the old code with 1 thread.
+ */
+ _process_graph.reset (new Graph (*this));
+ }
+
+ /* every time we reconnect, recompute worst case output latencies */
+
+ _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
+
+ if (synced_to_engine()) {
+ _engine.transport_stop ();
+ }
+
+ if (config.get_jack_time_master()) {
+ _engine.transport_locate (_transport_frame);
+ }
+
+ try {
+ BootMessage (_("Set up LTC"));
+ setup_ltc ();
+ BootMessage (_("Set up Click"));
+ setup_click ();
+ BootMessage (_("Set up standard connections"));
+ setup_bundles ();
+ }
+
+ catch (failed_constructor& err) {
+ return -1;
+ }
+
+ return 0;
+}
+
void
Session::destroy ()
{
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
playlists.reset ();
- delete _mmc;
- delete _midi_ports;
- delete _locations;
+ delete _mmc; _mmc = 0;
+ delete _midi_ports; _midi_ports = 0;
+ delete _locations; _locations = 0;
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
}
void
-Session::when_engine_running ()
+Session::setup_ltc ()
{
- string first_physical_output;
-
- BootMessage (_("Set block size and sample rate"));
-
- set_block_size (_engine.samples_per_cycle());
- set_frame_rate (_engine.sample_rate());
-
- BootMessage (_("Using configuration"));
-
- boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
- boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
-
- Config->map_parameters (ff);
- config.map_parameters (ft);
-
- /* every time we reconnect, recompute worst case output latencies */
-
- _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
-
- if (synced_to_jack()) {
- _engine.transport_stop ();
+ XMLNode* child = 0;
+
+ _ltc_input.reset (new IO (*this, X_("LTC In"), IO::Input));
+ _ltc_output.reset (new IO (*this, X_("LTC Out"), IO::Output));
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC In"))) != 0) {
+ _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
+ } else {
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+ }
+ reconnect_ltc_input ();
}
-
- if (config.get_jack_time_master()) {
- _engine.transport_locate (_transport_frame);
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) {
+ _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
+ } else {
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+ }
+ reconnect_ltc_output ();
}
+
+ /* fix up names of LTC ports because we don't want the normal
+ * IO style of NAME/TYPE-{in,out}N
+ */
+
+ _ltc_input->nth (0)->set_name (X_("LTC-in"));
+ _ltc_output->nth (0)->set_name (X_("LTC-out"));
+}
+void
+Session::setup_click ()
+{
_clicking = false;
+ _click_io.reset (new ClickIO (*this, X_("Click")));
+ _click_gain.reset (new Amp (*this));
+ _click_gain->activate ();
+ if (state_tree) {
+ setup_click_state (state_tree->root());
+ } else {
+ setup_click_state (0);
+ }
+}
- try {
- XMLNode* child = 0;
+void
+Session::setup_click_state (const XMLNode* node)
+{
+ const XMLNode* child = 0;
+
+ if (node && (child = find_named_node (*node, "Click")) != 0) {
- _ltc_input.reset (new IO (*this, _("LTC In"), IO::Input));
- _ltc_output.reset (new IO (*this, _("LTC Out"), IO::Output));
-
- if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-In")) != 0) {
- _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
- } else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- }
- reconnect_ltc_input ();
- }
+ /* existing state for Click */
+ int c = 0;
- if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-Out")) != 0) {
- _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
+ if (Stateful::loading_state_version < 3000) {
+ c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false);
} else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- }
- reconnect_ltc_output ();
- }
-
- /* fix up names of LTC ports because we don't want the normal
- * IO style of NAME/TYPE-{in,out}N
- */
-
- _ltc_input->nth (0)->set_name (_("LTC-in"));
- _ltc_output->nth (0)->set_name (_("LTC-out"));
-
- _click_io.reset (new ClickIO (*this, "click"));
- _click_gain.reset (new Amp (*this));
- _click_gain->activate ();
-
- if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) {
-
- /* existing state for Click */
- int c = 0;
-
- if (Stateful::loading_state_version < 3000) {
- c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false);
- } else {
- const XMLNodeList& children (child->children());
- XMLNodeList::const_iterator i = children.begin();
- if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) {
- ++i;
- if (i != children.end()) {
- c = _click_gain->set_state (**i, Stateful::loading_state_version);
- }
+ const XMLNodeList& children (child->children());
+ XMLNodeList::const_iterator i = children.begin();
+ if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) {
+ ++i;
+ if (i != children.end()) {
+ c = _click_gain->set_state (**i, Stateful::loading_state_version);
}
}
+ }
- if (c == 0) {
- _clicking = Config->get_clicking ();
+ if (c == 0) {
+ _clicking = Config->get_clicking ();
- } else {
+ } else {
- error << _("could not setup Click I/O") << endmsg;
- _clicking = false;
- }
+ error << _("could not setup Click I/O") << endmsg;
+ _clicking = false;
+ }
- } else {
+ } else {
- /* default state for Click: dual-mono to first 2 physical outputs */
+ /* default state for Click: dual-mono to first 2 physical outputs */
- vector<string> outs;
- _engine.get_physical_outputs (DataType::AUDIO, outs);
+ vector<string> outs;
+ _engine.get_physical_outputs (DataType::AUDIO, outs);
- for (uint32_t physport = 0; physport < 2; ++physport) {
- if (outs.size() > physport) {
- if (_click_io->add_port (outs[physport], this)) {
- // relax, even though its an error
- }
+ for (uint32_t physport = 0; physport < 2; ++physport) {
+ if (outs.size() > physport) {
+ if (_click_io->add_port (outs[physport], this)) {
+ // relax, even though its an error
}
}
-
- if (_click_io->n_ports () > ChanCount::ZERO) {
- _clicking = Config->get_clicking ();
- }
}
- }
-
- catch (failed_constructor& err) {
- error << _("cannot setup Click I/O") << endmsg;
- }
- BootMessage (_("Compute I/O Latencies"));
-
- if (_clicking) {
- // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO?
+ if (_click_io->n_ports () > ChanCount::ZERO) {
+ _clicking = Config->get_clicking ();
+ }
}
+}
- BootMessage (_("Set up standard connections"));
-
+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) {
add_bundle (c);
}
- BootMessage (_("Setup signal flow and plugins"));
-
- /* Reset all panners */
-
- Delivery::reset_panners ();
-
- /* this will cause the CPM to instantiate any protocols that are in use
- * (or mandatory), which will pass it this Session, and then call
- * set_state() on each instantiated protocol to match stored state.
- */
-
- ControlProtocolManager::instance().set_session (this);
-
- /* This must be done after the ControlProtocolManager set_session above,
- as it will set states for ports which the ControlProtocolManager creates.
- */
-
- // XXX set state of MIDI::Port's
- // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
-
- /* And this must be done after the MIDI::Manager::set_port_states as
- * it will try to make connections whose details are loaded by set_port_states.
- */
-
- hookup_io ();
-
- /* Let control protocols know that we are now all connected, so they
- * could start talking to surfaces if they want to.
- */
-
- ControlProtocolManager::instance().midi_connectivity_established ();
-
- if (_is_new && !no_auto_connect()) {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock());
- auto_connect_master_bus ();
- }
-
- _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
-
- /* update latencies */
-
- initialize_latencies ();
-
- /* hook us up to the engine */
-
- BootMessage (_("Connect to engine"));
- _engine.set_session (this);
- _engine.reset_timebase ();
}
void
/* Monitor bus is audio only */
- uint32_t mod = n_physical_outputs.get (DataType::AUDIO);
- uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO);
vector<string> outputs[DataType::num_types];
for (uint32_t i = 0; i < DataType::num_types; ++i) {
_engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]);
}
-
+
+ uint32_t mod = outputs[DataType::AUDIO].size();
+ uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO);
if (mod != 0) {
offset = current_block_size;
}
- if (synced_to_jack()) {
+ if (synced_to_engine()) {
tf = _engine.transport_frame();
} else {
tf = _transport_frame;
*/
_base_frame_rate = frames_per_second;
+ _nominal_frame_rate = frames_per_second;
sync_time_vars();
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 (MixerSort)));
+ (*i)->name(), (*i)->order_key ()));
}
#endif
for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
string port;
- if ((*t) == DataType::MIDI || Config->get_output_auto_connect() & AutoConnectPhysical) {
+ if ((*t) == DataType::MIDI && Config->get_output_auto_connect() & AutoConnectPhysical) {
port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
} else if ((*t) == DataType::AUDIO && Config->get_output_auto_connect() & AutoConnectMaster) {
/* master bus is audio only */
ChanCount existing_outputs;
uint32_t order = next_control_id();
+ if (_order_hint != 0) {
+ order = _order_hint;
+ _order_hint = 0;
+ }
+
count_existing_track_channels (existing_inputs, existing_outputs);
{
ID in most situations.
*/
- if (!r->has_order_key (EditorSort)) {
+ if (!r->has_order_key ()) {
if (r->is_auditioner()) {
/* use an arbitrarily high value */
- r->set_order_key (EditorSort, UINT_MAX);
- r->set_order_key (MixerSort, UINT_MAX);
+ r->set_order_key (UINT_MAX);
} else {
DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("while adding, set %1 to order key %2\n", r->name(), order));
- r->set_order_key (EditorSort, order);
- r->set_order_key (MixerSort, order);
+ r->set_order_key (order);
order++;
}
}
if (b->is_monitor()) {
return false;
}
- return a->order_key (MixerSort) < b->order_key (MixerSort);
+ return a->order_key () < b->order_key ();
}
bool
}
switch (Config->get_remote_model()) {
- case MixerSort:
- case EditorSort:
+ case MixerOrdered:
Route::RemoteControlIDChange (); /* EMIT SIGNAL */
break;
default:
}
void
-Session::sync_order_keys (RouteSortOrderKey sort_key_changed)
+Session::sync_order_keys ()
{
if (deletion_in_progress()) {
return;
opportunity to keep them in sync if they wish to.
*/
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("Sync Order Keys, based on %1\n", enum_2_string (sort_key_changed)));
+ DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n");
- Route::SyncOrderKeys (sort_key_changed); /* EMIT SIGNAL */
+ Route::SyncOrderKeys (); /* EMIT SIGNAL */
DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n");
}