#include "i18n.h"
+#include <glibmm/checksum.h>
+
namespace ARDOUR {
class MidiSource;
class Processor;
using namespace PBD;
bool Session::_disable_all_loaded_plugins = false;
+bool Session::_bypass_all_loaded_plugins = false;
PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
PBD::Signal1<void,std::string> Session::Dialog;
PBD::Signal0<void> Session::SuccessfulGraphSort;
PBD::Signal2<void,std::string,std::string> Session::VersionMismatch;
-const framecnt_t Session::bounce_chunk_size = 65536;
+const framecnt_t Session::bounce_chunk_size = 8192;
static void clean_up_session_event (SessionEvent* ev) { delete ev; }
const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
+// seconds should be added after the region exceeds end marker
+#ifdef USE_TRACKS_CODE_FEATURES
+const uint32_t Session::session_end_shift = 5;
+#else
+const uint32_t Session::session_end_shift = 0;
+#endif
+
/** @param snapshot_name Snapshot name, without .ardour suffix */
Session::Session (AudioEngine &eng,
const string& fullpath,
, average_dir (0)
, have_first_delta_accumulator (false)
, _slave_state (Stopped)
- , _mtc_active (false)
+ , _mtc_active (false)
+ , _ltc_active (false)
, post_export_sync (false)
, post_export_position (0)
, _exporting (false)
, loop_changing (false)
, last_loopend (0)
, _session_dir (new SessionDirectory (fullpath))
- , _current_snapshot_name (snapshot_name)
+ , _current_snapshot_name (snapshot_name)
, state_tree (0)
, state_was_pending (false)
, _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
, _route_deletion_in_progress (false)
, destructive_index (0)
, _track_number_decimals(1)
- , solo_update_disabled (false)
, default_fade_steepness (0)
, default_fade_msecs (0)
, _total_free_4k_blocks (0)
if (_is_new) {
+ Stateful::loading_state_version = CURRENT_SESSION_FILE_VERSION;
+
#ifdef USE_TRACKS_CODE_FEATURES
sr = EngineStateController::instance()->get_current_sample_rate();
#endif
if (ensure_engine (sr)) {
destroy ();
- throw failed_constructor ();
+ throw SessionException (_("Cannot connect to audio/midi engine"));
}
if (create (mix_template, bus_profile)) {
destroy ();
- throw failed_constructor ();
+ throw SessionException (_("Session initialization failed"));
}
/* if a mix template was provided, then ::create() will
* of a template.
*/
- if (!mix_template.empty()) {
+ if (!mix_template.empty()) {
if (load_state (_current_snapshot_name)) {
- throw failed_constructor ();
+ throw SessionException (_("Failed to load template/snapshot state"));
}
store_recent_templates (mix_template);
}
} else {
if (load_state (_current_snapshot_name)) {
- throw failed_constructor ();
+ throw SessionException (_("Failed to load state"));
}
/* try to get sample rate from XML state so that we
if (ensure_engine (sr)) {
destroy ();
- throw failed_constructor ();
+ throw SessionException (_("Cannot connect to audio/midi engine"));
}
}
if (post_engine_init ()) {
destroy ();
- throw failed_constructor ();
+ throw SessionException (_("Cannot configure audio/midi engine with session parameters"));
}
store_recent_sessions (_name, _path);
StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1));
EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
- _is_new = false;
-
emit_thread_start ();
/* hook us up to the engine since we are now completely constructed */
list<boost::shared_ptr<AudioTrack> > tracks;
- // Track names after driver
+ // Track names after driver
if (Config->get_tracks_auto_naming() == NameAfterDriver) {
string track_name = "";
for (std::vector<string>::size_type i = 0; i < inputs.size(); ++i) {
list<boost::shared_ptr<AudioTrack> > single_track = new_audio_track (1, 1, Normal, 0, 1, track_name);
tracks.insert(tracks.begin(), single_track.front());
- }
+ }
} else { // Default track names
tracks = new_audio_track (1, 1, Normal, 0, how_many, string());
}
* 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.
drop_connections ();
+ /* shutdown control surface protocols while we still have ports
+ and the engine to move data to any devices.
+ */
+
+ ControlProtocolManager::instance().drop_protocols ();
+
_engine.remove_session ();
#ifdef USE_TRACKS_CODE_FEATURES
/* mono output bundles */
for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); ++np) {
- char buf[32];
+ char buf[64];
std::string pn = _engine.get_pretty_name_by_name (outputs[DataType::AUDIO][np]);
if (!pn.empty()) {
- snprintf (buf, sizeof (buf), _("out %s"), pn.substr(0,12).c_str());
+ snprintf (buf, sizeof (buf), _("out %s"), pn.c_str());
} else {
snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
}
/* mono input bundles */
for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); ++np) {
- char buf[32];
+ char buf[64];
std::string pn = _engine.get_pretty_name_by_name (inputs[DataType::AUDIO][np]);
if (!pn.empty()) {
- snprintf (buf, sizeof (buf), _("in %s"), pn.substr(0,12).c_str());
+ snprintf (buf, sizeof (buf), _("in %s"), pn.c_str());
} else {
snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
}
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
// boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
#endif
- {
+ try {
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
r->input()->ensure_io (_master_out->output()->n_ports(), false, this);
r->output()->ensure_io (_master_out->output()->n_ports(), false, this);
+ } catch (...) {
+ error << _("Cannot create monitor section. 'Monitor' Port name is not unique.") << endmsg;
+ return;
}
rl.push_back (r);
/* Hold process lock while doing this so that we don't hear bits and
* pieces of audio as we work on each route.
*/
-
+
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
/* Connect tracks to monitor section. Note that in an
}
}
}
-
+
/* take care of our stuff first */
auto_loop_changed (location);
}
sync_locations_to_skips ();
-
+
set_dirty ();
}
++l;
continue;
}
-
+
switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
case Evoral::OverlapInternal:
case Evoral::OverlapExternal:
if (location->is_auto_loop()) {
set_auto_loop_location (location);
}
-
+
if (location->is_session_range()) {
/* no need for any signal handling or event setting with the session range,
because we keep a direct reference to it and use its start/end directly.
update_skips (location, true);
}
-
+
set_dirty ();
}
set_auto_loop_location (0);
set_track_loop (false);
}
-
+
if (location->is_auto_punch()) {
set_auto_punch_location (0);
}
}
if (location->is_skip()) {
-
+
update_skips (location, false);
}
void
Session::_locations_changed (const Locations::LocationList& locations)
{
- /* There was some mass-change in the Locations object.
+ /* There was some mass-change in the Locations object.
We might be re-adding a location here but it doesn't actually matter
for all the locations that the Session takes an interest in.
if ((rs = (RecordState) g_atomic_int_get (&_record_status)) != Disabled) {
- if ((!Config->get_latched_record_enable () && !play_loop) || force) {
+ if (!Config->get_latched_record_enable () || force) {
g_atomic_int_set (&_record_status, Disabled);
send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
} else {
save_state ("", true);
- if (Config->get_loop_is_mode()) {
- /* makes no sense to use loop play as mode when recording */
- request_play_loop (false);
- }
-
if (_transport_speed) {
if (!config.get_punch_in()) {
enable_record ();
* routes directly or indirectly feed them. This information
* is used by the solo code.
*/
-
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
/* Clear out the route's list of direct or indirect feeds */
bool
Session::find_route_name (string const & base, uint32_t& id, string& name, bool definitely_add_number)
{
+ /* the base may conflict with ports that do not belong to existing
+ routes, but hidden objects like the click track. So check port names
+ before anything else.
+ */
+
+ for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
+ if (base == *reserved) {
+ definitely_add_number = true;
+ if (id < 1) {
+ id = 1;
+ }
+ break;
+ }
+ }
+
if (!definitely_add_number && route_by_name (base) == 0) {
/* juse use the base */
name = base;
}
}
+string
+Session::default_track_name_pattern (DataType t)
+{
+ switch (t) {
+ case DataType::AUDIO:
+ if (Profile->get_trx()) {
+ return _("Track ");
+ } else {
+ return _("Audio ");
+ }
+ break;
+
+ case DataType::MIDI:
+ return _("MIDI ");
+ }
+
+ return "";
+}
+
/** Caller must not hold process lock
* @param name_template string to use for the start of the name, or "" to use "MIDI".
* @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,
+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)
{
string track_name;
RouteList new_routes;
list<boost::shared_ptr<MidiTrack> > ret;
- bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("MIDI");
+ const string name_pattern = default_track_name_pattern (DataType::MIDI);
+ bool const use_number = (how_many != 1) || name_template.empty () || (name_template == name_pattern);
while (how_many) {
if (!find_route_name (name_template.empty() ? _("MIDI") : name_template, ++track_id, track_name, use_number)) {
}
}
-#ifdef USE_TRACKS_CODE_FEATURES
+#ifdef USE_TRACKS_CODE_FEATURES
static bool
compare_routes_by_remote_id (const boost::shared_ptr<Route>& route1, const boost::shared_ptr<Route>& route2)
if (!IO::connecting_legal) {
return;
}
-
+
// if we are deleting routes we will call this once at the end
if (_route_deletion_in_progress) {
return;
}
-
+
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
-
+
if (withLock) {
lm.acquire ();
}
-
+
// We need to disconnect the route's inputs and outputs first
// basing on autoconnect configuration
bool reconnectIputs = !(Config->get_input_auto_connect() & ManualConnect) && reconnect_inputs;
bool reconnectOutputs = !(Config->get_output_auto_connect() & ManualConnect) && reconnect_outputs;
-
+
ChanCount existing_inputs;
ChanCount existing_outputs;
count_existing_track_channels (existing_inputs, existing_outputs);
-
+
//ChanCount inputs = ChanCount::ZERO;
//ChanCount outputs = ChanCount::ZERO;
-
+
RouteList existing_routes = *routes.reader ();
existing_routes.sort (compare_routes_by_remote_id);
-
+
{
PBD::Unwinder<bool> protect_ignore_changes (_reconnecting_routes_in_progress, true);
vector<string> physinputs;
vector<string> physoutputs;
-
+
EngineStateController::instance()->get_physical_audio_outputs(physoutputs);
EngineStateController::instance()->get_physical_audio_inputs(physinputs);
-
+
uint32_t input_n = 0;
uint32_t output_n = 0;
RouteList::iterator rIter = existing_routes.begin();
} else if (current_output_auto_connection == AutoConnectMaster) {
(*rIter)->amp()->activate();
}
-
+
if (reconnectIputs) {
(*rIter)->input()->disconnect (this); //GZ: check this; could be heavy
-
+
for (uint32_t route_input_n = 0; route_input_n < (*rIter)->n_inputs().get(DataType::AUDIO); ++route_input_n) {
-
+
if (current_input_auto_connection & AutoConnectPhysical) {
-
+
if ( input_n == physinputs.size() ) {
break;
}
-
+
string port = physinputs[input_n];
-
+
if (port.empty() ) {
error << "Physical Input number "<< input_n << " is unavailable and cannot be connected" << endmsg;
}
-
+
//GZ: check this; could be heavy
(*rIter)->input()->connect ((*rIter)->input()->ports().port(DataType::AUDIO, route_input_n), port, this);
++input_n;
}
}
}
-
+
if (reconnectOutputs) {
-
+
//normalize route ouptuts: reduce the amount outputs to be equal to the amount of inputs
if (current_output_auto_connection & AutoConnectPhysical) {
-
+
//GZ: check this; could be heavy
(*rIter)->output()->disconnect (this);
size_t route_inputs_count = (*rIter)->n_inputs().get(DataType::AUDIO);
-
+
//GZ: check this; could be heavy
(*rIter)->output()->ensure_io(ChanCount(DataType::AUDIO, route_inputs_count), false, this );
-
+
} else if (current_output_auto_connection & AutoConnectMaster){
-
+
if (!reconnect_master) {
continue;
}
-
+
//GZ: check this; could be heavy
(*rIter)->output()->disconnect (this);
-
+
if (_master_out) {
uint32_t master_inputs_count = _master_out->n_inputs().get(DataType::AUDIO);
(*rIter)->output()->ensure_io(ChanCount(DataType::AUDIO, master_inputs_count), false, this );
break;
}
}
-
+
for (uint32_t route_output_n = 0; route_output_n < (*rIter)->n_outputs().get(DataType::AUDIO); ++route_output_n) {
if (current_output_auto_connection & AutoConnectPhysical) {
-
+
if ( output_n == physoutputs.size() ) {
break;
}
-
+
string port = physoutputs[output_n];
-
+
if (port.empty() ) {
error << "Physical Output number "<< output_n << " is unavailable and cannot be connected" << endmsg;
}
-
+
//GZ: check this; could be heavy
(*rIter)->output()->connect ((*rIter)->output()->ports().port(DataType::AUDIO, route_output_n), port, this);
++output_n;
-
+
} else if (current_output_auto_connection & AutoConnectMaster) {
-
+
if ( route_output_n == _master_out->n_inputs().get(DataType::AUDIO) ) {
break;
}
-
+
// connect to master bus
string port = _master_out->input()->ports().port(DataType::AUDIO, route_output_n)->name();
-
+
if (port.empty() ) {
error << "MasterBus Input number "<< route_output_n << " is unavailable and cannot be connected" << endmsg;
}
-
-
+
+
//GZ: check this; could be heavy
(*rIter)->output()->connect ((*rIter)->output()->ports().port(DataType::AUDIO, route_output_n), port, this);
-
+
}
}
}
-
+
//auto_connect_route (*rIter, inputs, outputs, false, reconnectIputs);
}
-
+
_master_out->output()->disconnect (this);
auto_connect_master_bus ();
}
-
+
graph_reordered ();
-
+
session_routes_reconnected (); /* EMIT SIGNAL */
}
Session::reconnect_midi_scene_ports(bool inputs)
{
if (inputs ) {
-
+
boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
if (scene_in_ptr) {
scene_in_ptr->disconnect_all ();
-
+
std::vector<EngineStateController::MidiPortState> midi_port_states;
EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
-
+
std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
-
+
for (; state_iter != midi_port_states.end(); ++state_iter) {
if (state_iter->active && state_iter->available && state_iter->scene_connected) {
scene_in_ptr->connect (state_iter->name);
}
} else {
-
+
boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
-
+
if (scene_out_ptr ) {
scene_out_ptr->disconnect_all ();
std::vector<EngineStateController::MidiPortState> midi_port_states;
EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
-
+
std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
-
+
for (; state_iter != midi_port_states.end(); ++state_iter) {
if (state_iter->active && state_iter->available && state_iter->scene_connected) {
scene_out_ptr->connect (state_iter->name);
Session::reconnect_mmc_ports(bool inputs)
{
if (inputs ) { // get all enabled midi input ports
-
+
boost::shared_ptr<MidiPort> mmc_in_ptr = _midi_ports->mmc_in();
if (mmc_in_ptr) {
mmc_in_ptr->disconnect_all ();
std::vector<std::string> enabled_midi_inputs;
EngineStateController::instance()->get_physical_midi_inputs (enabled_midi_inputs);
-
+
std::vector<std::string>::iterator port_iter = enabled_midi_inputs.begin();
-
+
for (; port_iter != enabled_midi_inputs.end(); ++port_iter) {
mmc_in_ptr->connect (*port_iter);
}
}
} else { // get all enabled midi output ports
-
+
boost::shared_ptr<MidiPort> mmc_out_ptr = _midi_ports->mmc_out();
if (mmc_out_ptr ) {
mmc_out_ptr->disconnect_all ();
std::vector<std::string> enabled_midi_outputs;
EngineStateController::instance()->get_physical_midi_outputs (enabled_midi_outputs);
-
+
std::vector<std::string>::iterator port_iter = enabled_midi_outputs.begin();
-
+
for (; port_iter != enabled_midi_outputs.end(); ++port_iter) {
mmc_out_ptr->connect (*port_iter);
}
* @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,
+Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,
uint32_t how_many, string name_template)
{
string track_name;
RouteList new_routes;
list<boost::shared_ptr<AudioTrack> > ret;
- string name_pattern;
-
- if (Profile->get_trx() ) {
- name_pattern = "Track ";
- } else {
- name_pattern = "Audio ";
- }
-
- bool const use_number = (how_many != 1) || name_template.empty () || name_template == _(name_pattern.c_str() );
+ const string name_pattern = default_track_name_pattern (DataType::AUDIO);
+ bool const use_number = (how_many != 1) || name_template.empty () || (name_template == name_pattern);
while (how_many) {
reassign_track_numbers();
update_route_record_state ();
-
+
RouteAdded (new_routes); /* EMIT SIGNAL */
}
void
Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
{
+ PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
+
{ // RCU Writer scope
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 (*iter == _master_out) {
continue;
}
-
+
(*iter)->set_solo (false, this);
-
+
rs->remove (*iter);
-
+
/* deleting the master out seems like a dumb
idea, but its more of a UI policy issue
than our concern.
*/
-
+
if (*iter == _master_out) {
_master_out = boost::shared_ptr<Route> ();
}
-
+
if (*iter == _monitor_out) {
_monitor_out.reset ();
}
- update_route_solo_state ();
-
// We need to disconnect the route's inputs and outputs
-
+
(*iter)->input()->disconnect (0);
(*iter)->output()->disconnect (0);
-
+
/* if the route had internal sends sending to it, remove them */
if ((*iter)->internal_return()) {
-
+
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
boost::shared_ptr<Send> s = (*i)->internal_send_for (*iter);
}
}
}
-
+
/* if the monitoring section had a pointer to this route, remove it */
if (_monitor_out && !(*iter)->is_master() && !(*iter)->is_monitor()) {
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
(*iter)->remove_aux_or_listen (_monitor_out);
}
-
+
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (*iter);
if (mt && mt->step_editing()) {
if (_step_editors > 0) {
_step_editors--;
}
}
-
- RouteAddedOrRemoved (false); /* EMIT SIGNAL */
}
-
+
/* writer goes out of scope, forces route list update */
} // end of RCU Writer scope
-
+
+ update_route_solo_state ();
+ RouteAddedOrRemoved (false); /* EMIT SIGNAL */
update_latency_compensation ();
set_dirty();
-
+
/* Re-sort routes to remove the graph's current references to the one that is
* going away, then flush old references out of the graph.
* Wave Tracks: reconnect routes
#ifdef USE_TRACKS_CODE_FEATURES
reconnect_existing_routes(true, false);
#else
+ routes.flush (); // maybe unsafe, see below.
resort_routes ();
#endif
-
+
if (_process_graph) {
_process_graph->clear_other_chain ();
}
-
+
/* get rid of it from the dead wood collection in the route list manager */
/* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */
-
+
routes.flush ();
-
+
/* try to cause everyone to drop their references
* and unregister ports from the backend
*/
- PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
(*iter)->drop_references ();
}
-
+
Route::RemoteControlIDChange(); /* EMIT SIGNAL */
-
+
/* save the new state of the world */
-
+
if (save_state (_current_snapshot_name)) {
save_history (_current_snapshot_name);
}
if (route->listening_via_monitor ()) {
if (Config->get_exclusive_solo()) {
- /* new listen: disable all other listen */
+ /* new listen: disable all other listen, except solo-grouped channels */
+ RouteGroup* rg = route->route_group ();
+ bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) {
continue;
}
(*i)->set_listen (false, this);
if (!route) {
/* should not happen */
- error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg;
+ error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg;
return;
}
return;
}
- if (solo_update_disabled) {
- // We know already
- DEBUG_TRACE (DEBUG::Solo, "solo update disabled - changed ignored\n");
- return;
- }
-
boost::shared_ptr<Route> route = wpr.lock ();
assert (route);
DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate solo change, delta = %1\n", delta));
- solo_update_disabled = true;
-
RouteList uninvolved;
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1\n", route->name()));
}
}
- solo_update_disabled = false;
DEBUG_TRACE (DEBUG::Solo, "propagation complete\n");
update_route_solo_state (r);
{
boost::shared_ptr<RouteList> r = routes.reader ();
+ for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
+ if (name == *reserved) {
+ return false;
+ }
+ }
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->name() == name) {
return false;
return;
}
+ framepos_t session_end_marker_shift_samples = session_end_shift * _nominal_frame_rate;
+
if (_session_range_location == 0) {
- add_session_range_location (a, b);
+ set_session_range_location (a, b + session_end_marker_shift_samples);
} else {
return cnt;
}
+static string
+peak_file_helper (const string& peak_path, const string& file_path, const string& file_base, bool hash) {
+ if (hash) {
+ std::string checksum = Glib::Checksum::compute_checksum(Glib::Checksum::CHECKSUM_SHA1, file_path + G_DIR_SEPARATOR + file_base);
+ return Glib::build_filename (peak_path, checksum + peakfile_suffix);
+ } else {
+ return Glib::build_filename (peak_path, file_base + peakfile_suffix);
+ }
+}
+
string
-Session::peak_path (string base) const
+Session::construct_peak_filepath (const string& filepath, const bool in_session, const bool old_peak_name) const
{
- if (Glib::path_is_absolute (base)) {
+ string interchange_dir_string = string (interchange_dir_name) + G_DIR_SEPARATOR;
+
+ if (Glib::path_is_absolute (filepath)) {
/* rip the session dir from the audiofile source */
string session_path;
- string interchange_dir_string = string (interchange_dir_name) + G_DIR_SEPARATOR;
bool in_another_session = true;
- if (base.find (interchange_dir_string) != string::npos) {
+ if (filepath.find (interchange_dir_string) != string::npos) {
- session_path = Glib::path_get_dirname (base); /* now ends in audiofiles */
+ session_path = Glib::path_get_dirname (filepath); /* now ends in audiofiles */
session_path = Glib::path_get_dirname (session_path); /* now ends in session name */
session_path = Glib::path_get_dirname (session_path); /* now ends in interchange */
session_path = Glib::path_get_dirname (session_path); /* now has session path */
if (in_another_session) {
SessionDirectory sd (session_path);
- return Glib::build_filename (sd.peak_path(), Glib::path_get_basename (base) + peakfile_suffix);
+ return peak_file_helper (sd.peak_path(), "", Glib::path_get_basename (filepath), !old_peak_name);
}
}
- base = Glib::path_get_basename (base);
- return Glib::build_filename (_session_dir->peak_path(), base + peakfile_suffix);
+ /* 1) if file belongs to this session
+ * it may be a relative path (interchange/...)
+ * or just basename (session_state, remove source)
+ * -> just use the basename
+ */
+ std::string filename = Glib::path_get_basename (filepath);
+ std::string path;
+
+ /* 2) if the file is outside our session dir:
+ * (imported but not copied) add the path for check-summming */
+ if (!in_session) {
+ path = Glib::path_get_dirname (filepath);
+ }
+
+ return peak_file_helper (_session_dir->peak_path(), path, Glib::path_get_basename (filepath), !old_peak_name);
}
string
Session::new_audio_source_path_for_embedded (const std::string& path)
{
- /* embedded source:
+ /* embedded source:
*
* we know that the filename is already unique because it exists
- * out in the filesystem.
+ * out in the filesystem.
*
* However, when we bring it into the session, we could get a
* collision.
*
* Eg. two embedded files:
- *
+ *
* /foo/bar/baz.wav
* /frob/nic/baz.wav
*
- * When merged into session, these collide.
+ * When merged into session, these collide.
*
* There will not be a conflict with in-memory sources
* because when the source was created we already picked
return newpath;
}
-/** Return true if there are no audio file sources that use @param name as
- * the filename component of their path.
+/** Return true if there are no audio file sources that use @param name as
+ * the filename component of their path.
*
* Return false otherwise.
*
- * This method MUST ONLY be used to check in-session, mono files since it
+ * This method MUST ONLY be used to check in-session, mono files since it
* hard-codes the channel of the audio file source we are looking for as zero.
- *
+ *
* If/when Ardour supports native files in non-mono formats, the logic here
* will need to be revisited.
*/
if (!path.empty()) {
return boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+ SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate(), true, true));
} else {
throw failed_constructor ();
}
Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
{
/* the caller passes in the track the source will be used in,
- so that we can keep the numbering sane.
-
+ so that we can keep the numbering sane.
+
Rationale: a track with the name "Foo" that has had N
captures carried out so far will ALREADY have a write source
named "Foo-N+1.mid" waiting to be used for the next capture.
-
+
If we call new_midi_source_name() we will get "Foo-N+2". But
there is no region corresponding to "Foo-N+1", so when
"Foo-N+2" appears in the track, the gap presents the user
with odd behaviour - why did it skip past Foo-N+1?
-
+
We could explain this to the user in some odd way, but
instead we rename "Foo-N+1.mid" as "Foo-N+2.mid", and then
use "Foo-N+1" here.
-
+
If that attempted rename fails, we get "Foo-N+2.mid" anyway.
*/
from a set_state() call or creating new tracks. Ditto for deletion.
*/
- if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _reconnecting_routes_in_progress) {
+ if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _reconnecting_routes_in_progress || _route_deletion_in_progress) {
return;
}
boost::shared_ptr<Region>
Session::write_one_track (Track& track, framepos_t start, framepos_t end,
bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
- InterThreadInfo& itt,
+ InterThreadInfo& itt,
boost::shared_ptr<Processor> endpoint, bool include_endpoint,
bool for_export, bool for_freeze)
{
break;
}
}
-
+
g_atomic_int_set (&_have_rec_disabled_track, i != rl->end () ? 1 : 0);
bool record_arm_state_changed = (old != g_atomic_int_get (&_have_rec_enabled_track) );
-
+
if (record_status() == Recording && record_arm_state_changed ) {
RecordArmStateChanged ();
}
}
void
-Session::add_session_range_location (framepos_t start, framepos_t end)
+Session::set_session_range_location (framepos_t start, framepos_t end)
{
_session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange);
_locations->add (_session_range_location);
return 0;
}
-uint32_t
+uint32_t
Session::next_control_id () const
{
int subtract = 0;
if (src != _("None") && !src.empty()) {
_ltc_input->nth (0)->connect (src);
}
+
+ if ( ARDOUR::Profile->get_trx () ) {
+ // Tracks need this signal to update timecode_source_dropdown
+ MtcOrLtcInputPortChanged (); //emit signal
+ }
}
}
{
if (_ltc_output) {
-#if 0
- string src = Config->get_ltc_sink_port();
+ string src = Config->get_ltc_output_port();
_ltc_output->disconnect (this);
if (src != _("None") && !src.empty()) {
_ltc_output->nth (0)->connect (src);
}
-#endif
}
}
Session::set_range_selection (framepos_t start, framepos_t end)
{
_range_selection = Evoral::Range<framepos_t> (start, end);
+#ifdef USE_TRACKS_CODE_FEATURES
follow_playhead_priority ();
+#endif
}
void
Session::set_object_selection (framepos_t start, framepos_t end)
{
_object_selection = Evoral::Range<framepos_t> (start, end);
+#ifdef USE_TRACKS_CODE_FEATURES
follow_playhead_priority ();
+#endif
}
void
Session::clear_range_selection ()
{
_range_selection = Evoral::Range<framepos_t> (-1,-1);
+#ifdef USE_TRACKS_CODE_FEATURES
follow_playhead_priority ();
+#endif
}
void
Session::clear_object_selection ()
{
_object_selection = Evoral::Range<framepos_t> (-1,-1);
+#ifdef USE_TRACKS_CODE_FEATURES
follow_playhead_priority ();
+#endif
}