#include "pbd/search_path.h"
#include "pbd/stacktrace.h"
#include "pbd/stl_delete.h"
+#include "pbd/replace_all.h"
#include "pbd/unwind.h"
#include "ardour/amp.h"
#include "ardour/data_type.h"
#include "ardour/debug.h"
#include "ardour/directory_names.h"
+#ifdef USE_TRACKS_CODE_FEATURES
+#include "ardour/engine_state_controller.h"
+#endif
#include "ardour/filename_extensions.h"
#include "ardour/graph.h"
#include "ardour/midiport_manager.h"
#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)
+ , _ltc_active (false)
, post_export_sync (false)
, post_export_position (0)
, _exporting (false)
, click_emphasis_length (0)
, _clicks_cleared (0)
, _play_range (false)
+ , _range_selection (-1,-1)
+ , _object_selection (-1,-1)
, main_outs (0)
, first_file_data_format_reset (true)
, first_file_header_format_reset (true)
pre_engine_init (fullpath);
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
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 */
_engine.set_session (this);
_engine.reset_timebase ();
- BootMessage (_("Session loading complete"));
+#ifdef USE_TRACKS_CODE_FEATURES
+
+ EngineStateController::instance()->set_session(this);
+
+ if (_is_new ) {
+ if ( ARDOUR::Profile->get_trx () ) {
+
+ /* Waves Tracks: fill session with tracks basing on the amount of inputs.
+ * each available input must have corresponding track when session starts.
+ */
+
+ uint32_t how_many (0);
+
+ std::vector<std::string> inputs;
+ EngineStateController::instance()->get_physical_audio_inputs(inputs);
+
+ how_many = inputs.size();
+
+ list<boost::shared_ptr<AudioTrack> > tracks;
+
+ // 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) {
+ string track_name;
+ track_name = inputs[i];
+ replace_all (track_name, "system:capture", "");
+
+ 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());
+ }
+
+ if (tracks.size() != how_many) {
+ destroy ();
+ throw failed_constructor ();
+ }
+ }
+ }
+#endif
+
+ _is_new = false;
+ session_loaded ();
+ BootMessage (_("Session loading complete"));
}
Session::~Session ()
_engine.remove_session ();
+#ifdef USE_TRACKS_CODE_FEATURES
+ EngineStateController::instance()->remove_session();
+#endif
+
/* deregister all ports - there will be no process or any other
* callbacks from the engine any more.
*/
/* 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);
}
if (!_master_out || !Config->get_auto_connect_standard_busses() || _monitor_out) {
return;
}
-
+
+ // Waves Tracks: Do not connect master bas for Tracks if AutoConnectMaster option is not set
+ // In this case it means "Multi Out" output mode
+ if (ARDOUR::Profile->get_trx() && !(Config->get_output_auto_connect() & AutoConnectMaster) ) {
+ return;
+ }
+
/* if requested auto-connect the outputs to the first N physical ports.
*/
#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);
clear_events (SessionEvent::AutoLoop);
}
+ /* possibly move playhead if not rolling; if we are rolling we'll move
+ to the loop start on stop if that is appropriate.
+ */
+
+ framepos_t pos;
+
+ if (!transport_rolling() && select_playhead_priority_target (pos)) {
+ if (pos == location->start()) {
+ request_locate (pos);
+ }
+ }
+
+
last_loopend = location->end();
set_dirty ();
}
location->StartChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
location->EndChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
location->Changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
+ location->FlagsChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
location->set_auto_loop (true, this);
+ if (Config->get_loop_is_mode() && play_loop && Config->get_seamless_loop()) {
+ // set all tracks to use internal looping
+ 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->hidden()) {
+ tr->set_loop (location);
+ }
+ }
+ }
+
/* take care of our stuff first */
auto_loop_changed (location);
auto_loop_location_changed (location);
}
-void
-Session::update_loop (Location*)
-{
- set_dirty ();
-}
-
void
Session::update_marks (Location*)
{
void
Session::update_skips (Location* loc, bool consolidate)
{
- if (_ignore_skips_updates) {
- return;
- }
-
+ if (_ignore_skips_updates) {
+ return;
+ }
+
Locations::LocationList skips;
if (consolidate) {
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.
*/
-
- for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
- location_added (*i);
- }
+
+ {
+ PBD::Unwinder<bool> protect_ignore_skip_updates (_ignore_skips_updates, true);
+ for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ location_added (*i);
+ }
+ }
+
+ update_skips (NULL, false);
}
void
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 ();
sync_time_vars();
clear_clicks ();
-
+ reset_write_sources (false);
+
// XXX we need some equivalent to this, somehow
// SndFileSource::setup_standard_crossfades (frames_per_second);
* and \a id do not reflect a free route name.
*/
bool
-Session::find_route_name (string const & base, uint32_t& id, char* name, size_t name_len, bool definitely_add_number)
-{
+Session::find_route_name (string const & base, uint32_t& id, string& name, bool definitely_add_number)
+{
+ /* it is unfortunate that we need to include reserved names here that
+ refer to control surfaces. But there's no way to ensure a complete
+ lack of collisions without doing this, since the control surface
+ support may not even be active. Without adding an API to control
+ surface support that would list their port names, we do have to
+ list them here.
+ */
+
+ char const * const reserved[] = {
+ _("Monitor"),
+ _("Master"),
+ _("Control"),
+ _("Click"),
+ _("Mackie"),
+ 0
+ };
+
+ /* 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 (int n = 0; reserved[n]; ++n) {
+ if (base == reserved[n]) {
+ definitely_add_number = true;
+ if (id < 1) {
+ id = 1;
+ }
+ break;
+ }
+ }
+
if (!definitely_add_number && route_by_name (base) == 0) {
/* juse use the base */
- snprintf (name, name_len, "%s", base.c_str());
+ name = base;
return true;
}
do {
- snprintf (name, name_len, "%s %" PRIu32, base.c_str(), id);
+ name = string_compose ("%1 %2", base, id);
if (route_by_name (name) == 0) {
return true;
}
}
+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
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)
{
- char track_name[32];
+ string track_name;
uint32_t track_id = 0;
string port;
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, sizeof(track_name), use_number)) {
+ if (!find_route_name (name_template.empty() ? _("MIDI") : name_template, ++track_id, track_name, use_number)) {
error << "cannot find name for new midi track" << endmsg;
goto failed;
}
void
Session::reconnect_midi_scene_ports(bool inputs)
{
- if (inputs) {
- scene_in()->disconnect_all ();
+ if (inputs ) {
- std::vector<EngineStateController::MidiPortState> midi_port_states;
- EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
+ 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 {
- std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+ boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
- for (; state_iter != midi_port_states.end(); ++state_iter) {
- if (state_iter->active && state_iter->available && state_iter->connected) {
- scene_in()->connect (state_iter->name);
- }
- }
+ if (scene_out_ptr ) {
+ scene_out_ptr->disconnect_all ();
- } else {
- scene_out()->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);
+ }
+ }
+ }
+ }
+}
+
+void
+Session::reconnect_mtc_ports ()
+{
+ boost::shared_ptr<MidiPort> mtc_in_ptr = _midi_ports->mtc_input_port();
+
+ if (!mtc_in_ptr) {
+ return;
+ }
+
+ mtc_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->available && state_iter->mtc_in) {
+ mtc_in_ptr->connect (state_iter->name);
+ }
+ }
+
+ if (!_midi_ports->mtc_input_port ()->connected () &&
+ config.get_external_sync () &&
+ (Config->get_sync_source () == MTC) ) {
+ config.set_external_sync (false);
+ }
+
+ if ( ARDOUR::Profile->get_trx () ) {
+ // Tracks need this signal to update timecode_source_dropdown
+ MtcOrLtcInputPortChanged (); //emit signal
+ }
+}
- std::vector<EngineStateController::MidiPortState> midi_port_states;
- EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
+void
+Session::reconnect_mmc_ports(bool inputs)
+{
+ if (inputs ) { // get all enabled midi input ports
- std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+ 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
- for (; state_iter != midi_port_states.end(); ++state_iter) {
- if (state_iter->active && state_iter->available && state_iter->connected) {
- scene_out()->connect (state_iter->name);
+ 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);
}
}
-
}
}
Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,
uint32_t how_many, string name_template)
{
- char track_name[32];
+ string track_name;
uint32_t track_id = 0;
string port;
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) {
- if (!find_route_name (name_template.empty() ? _(name_pattern.c_str()) : name_template, ++track_id, track_name, sizeof(track_name), use_number)) {
+ if (!find_route_name (name_template.empty() ? _(name_pattern.c_str()) : name_template, ++track_id, track_name, use_number)) {
error << "cannot find name for new audio track" << endmsg;
goto failed;
}
goto failed;
}
+ if (ARDOUR::Profile->get_trx ()) {
+ // TRACKS considers it's not a USE CASE, it's
+ // a piece of behavior of the session model:
+ //
+ // Gain for a newly created route depends on
+ // the current output_auto_connect mode:
+ //
+ // 0 for Stereo Out mode
+ // 0 Multi Out mode
+ if (Config->get_output_auto_connect() & AutoConnectMaster) {
+ track->set_gain (dB_to_coefficient (0), 0);
+ }
+ }
+
track->use_new_diskstream();
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
RouteList
Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template)
{
- char bus_name[32];
+ string bus_name;
uint32_t bus_id = 0;
string port;
RouteList ret;
bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Bus");
while (how_many) {
- if (!find_route_name (name_template.empty () ? _("Bus") : name_template, ++bus_id, bus_name, sizeof(bus_name), use_number)) {
+ if (!find_route_name (name_template.empty () ? _("Bus") : name_template, ++bus_id, bus_name, use_number)) {
error << "cannot find name for new audio bus" << endmsg;
goto failure;
}
node_copy.remove_property_recursively (X_("id"));
try {
- char name[32];
+ string name;
if (!name_base.empty()) {
* numbered, via the final parameter.
*/
- if (!find_route_name (name_base.c_str(), ++number, name, sizeof(name), (being_added > 1))) {
+ if (!find_route_name (name_base.c_str(), ++number, name, (being_added > 1))) {
fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
/*NOTREACHDE*/
}
string const route_name = node_copy.property(X_("name"))->value ();
/* generate a new name by adding a number to the end of the template name */
- if (!find_route_name (route_name.c_str(), ++number, name, sizeof(name), true)) {
+ if (!find_route_name (route_name.c_str(), ++number, name, true)) {
fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
abort(); /*NOTREACHED*/
}
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 ();
_monitor_out.reset ();
}
- update_route_solo_state ();
-
// We need to disconnect the route's inputs and outputs
(*iter)->input()->disconnect (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();
#ifdef USE_TRACKS_CODE_FEATURES
reconnect_existing_routes(true, false);
#else
+ routes.flush (); // maybe unsafe, see below.
resort_routes ();
#endif
/* 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 ();
}
}
+void
+Session::routes_solo_changed (boost::shared_ptr<RouteList> solo_change_routes)
+{
+ if (solo_update_disabled) {
+ // We know already
+ DEBUG_TRACE (DEBUG::Solo, "solo update disabled - changed ignored\n");
+ return;
+ }
+
+ if (solo_change_routes->empty() ) {
+ return;
+ }
+
+ boost::shared_ptr<RouteList> non_solo_change_routes (new RouteList);
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ int32_t delta;
+
+ std::set_difference (r->begin(), r->end(),
+ solo_change_routes->begin(), solo_change_routes->end(),
+ std::back_inserter(*non_solo_change_routes) );
+
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate solo change, delta = %1\n", delta));
+
+ solo_update_disabled = true;
+ RouteList uninvolved;
+
+ for (RouteList::iterator route = solo_change_routes->begin(); route != solo_change_routes->end(); ++route) {
+
+ if ((*route)->self_soloed() ) {
+ delta = 1;
+ } else {
+ delta = -1;
+ }
+
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1\n", (*route)->name()));
+
+ for (RouteList::iterator i = non_solo_change_routes->begin(); i != non_solo_change_routes->end(); ++i) {
+ bool via_sends_only;
+ bool in_signal_flow;
+
+ if ((*i) == *route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ) {
+ continue;
+ }
+
+ in_signal_flow = false;
+
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed from %1\n", (*i)->name()));
+
+ if ((*i)->feeds (*route, &via_sends_only)) {
+ 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);
+ }
+ } else {
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a send-only feed from %1\n", (*i)->name()));
+ }
+ in_signal_flow = true;
+ } else {
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("\tno feed from %1\n", (*i)->name()));
+ }
+
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed to %1\n", (*i)->name()));
+
+ if ((*route)->feeds (*i, &via_sends_only)) {
+ /* propagate solo upstream only if routing other than
+ sends is involved, but do consider the other route
+ (*i) to be part of the signal flow even if only
+ 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()));
+ if (!via_sends_only) {
+ if (!(*route)->soloed_by_others_downstream()) {
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta));
+ (*i)->mod_solo_by_others_upstream (delta);
+ } else {
+ DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others downstream\n");
+ }
+ } else {
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name()));
+ }
+ in_signal_flow = true;
+ } else {
+ DEBUG_TRACE (DEBUG::Solo, "\tno feed to\n");
+ }
+
+ if (!in_signal_flow) {
+ uninvolved.push_back (*i);
+ }
+ }
+ }
+ solo_update_disabled = false;
+ DEBUG_TRACE (DEBUG::Solo, "propagation complete\n");
+
+ update_route_solo_state ();
+
+ /* now notify that the mute state of the routes not involved in the signal
+ pathway of the just-solo-changed route may have altered.
+ */
+
+ for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1\n", (*i)->name() ));
+ (*i)->mute_changed (this);
+ }
+
+ SoloChanged (); /* EMIT SIGNAL */
+ set_dirty();
+}
+
void
Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr<Route> wpr)
{
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
ostringstream sstr;
const string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
- if (destructive) {
+ if (Profile->get_trx() && destructive) {
sstr << 'T';
sstr << setfill ('0') << setw (4) << cnt;
sstr << legalized_base;
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 ();
}
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;
}
Session::reset_native_file_format ()
{
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) {
/* don't save state as we do this, there's no point
*/
-
_state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
tr->reset_write_sources (false);
_state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
}
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);
subtract++;
}
+ /* the same about masterbus in Waves Tracks */
+
+ if (Profile->get_trx() && _master_out) {
+ subtract++;
+ }
+
return nroutes() - subtract;
}
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
}
}
+
+void
+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
+}