/*
- Copyright (C) 1999-2004 Paul Davis
+ Copyright (C) 1999-2010 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
+#include <stdint.h>
+
#include <algorithm>
#include <string>
#include <vector>
#include <unistd.h>
#include <limits.h>
-
#include <glibmm/thread.h>
#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
-#include <glibmm/thread.h>
+
+#include <boost/algorithm/string/erase.hpp>
#include "pbd/error.h"
#include "pbd/boost_debug.h"
#include "pbd/stacktrace.h"
#include "pbd/file_utils.h"
#include "pbd/convert.h"
+#include "pbd/strsplit.h"
#include "ardour/amp.h"
#include "ardour/analyser.h"
#include "ardour/audioplaylist.h"
#include "ardour/audioregion.h"
#include "ardour/auditioner.h"
+#include "ardour/buffer_manager.h"
#include "ardour/buffer_set.h"
#include "ardour/bundle.h"
#include "ardour/butler.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/named_selection.h"
+#include "ardour/process_thread.h"
#include "ardour/playlist.h"
#include "ardour/plugin_insert.h"
#include "ardour/port_insert.h"
#include "ardour/tape_file_matcher.h"
#include "ardour/tempo.h"
#include "ardour/utils.h"
+#include "ardour/graph.h"
+#include "ardour/vbap_speakers.h"
-#include "midi++/jack.h"
+#include "midi++/port.h"
+#include "midi++/mmc.h"
+#include "midi++/manager.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
-using boost::shared_ptr;
-using boost::weak_ptr;
bool Session::_disable_all_loaded_plugins = false;
PBD::Signal1<void,std::string> Session::Dialog;
PBD::Signal0<int> Session::AskAboutPendingState;
-PBD::Signal2<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
+PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
PBD::Signal0<void> Session::SendFeedback;
+PBD::Signal3<int,Session*,std::string,DataType> Session::MissingFile;
-PBD::Signal0<void> Session::TimecodeOffsetChanged;
-PBD::Signal0<void> Session::StartTimeChanged;
-PBD::Signal0<void> Session::EndTimeChanged;
+PBD::Signal1<void, framepos_t> Session::StartTimeChanged;
+PBD::Signal1<void, framepos_t> Session::EndTimeChanged;
PBD::Signal0<void> Session::AutoBindingOn;
PBD::Signal0<void> Session::AutoBindingOff;
PBD::Signal2<void,std::string, std::string> Session::Exported;
PBD::Signal1<int,boost::shared_ptr<Playlist> > Session::AskAboutPlaylistDeletion;
+PBD::Signal0<void> Session::Quit;
static void clean_up_session_event (SessionEvent* ev) { delete ev; }
const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
BusProfile* bus_profile,
string mix_template)
- : _engine (eng),
- _target_transport_speed (0.0),
- _requested_return_frame (-1),
- _scratch_buffers(new BufferSet()),
- _silent_buffers(new BufferSet()),
- _mix_buffers(new BufferSet()),
- mmc (0),
- _mmc_port (default_mmc_port),
- _mtc_port (default_mtc_port),
- _midi_port (default_midi_port),
- _midi_clock_port (default_midi_clock_port),
- _session_dir (new SessionDirectory(fullpath)),
- state_tree (0),
- _butler (new Butler (*this)),
- _post_transport_work (0),
- _send_timecode_update (false),
- diskstreams (new DiskstreamList),
- routes (new RouteList),
- _total_free_4k_blocks (0),
- _bundles (new BundleList),
- _bundle_xml_node (0),
- _click_io ((IO*) 0),
- click_data (0),
- click_emphasis_data (0),
- main_outs (0),
- _metadata (new SessionMetadata()),
- _have_rec_enabled_diskstream (false)
-
+ : _engine (eng)
+ , _target_transport_speed (0.0)
+ , _requested_return_frame (-1)
+ , _session_dir (new SessionDirectory(fullpath))
+ , state_tree (0)
+ , _state_of_the_state (Clean)
+ , _butler (new Butler (*this))
+ , _post_transport_work (0)
+ , _send_timecode_update (false)
+ , _all_route_group (new RouteGroup (*this, "all"))
+ , route_graph (new Graph(*this))
+ , routes (new RouteList)
+ , _total_free_4k_blocks (0)
+ , _bundles (new BundleList)
+ , _bundle_xml_node (0)
+ , _click_io ((IO*) 0)
+ , click_data (0)
+ , click_emphasis_data (0)
+ , main_outs (0)
+ , _metadata (new SessionMetadata())
+ , _have_rec_enabled_track (false)
+ , _suspend_timecode_transmission (0)
{
+ _locations = new Locations (*this);
+
playlists.reset (new SessionPlaylists);
-
+
+ _all_route_group->set_active (true, this);
+
interpolation.add_channel_to (0, 0);
if (!eng.connected()) {
throw failed_constructor();
}
- n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO);
- n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO);
+ n_physical_outputs = _engine.n_physical_outputs ();
+ n_physical_inputs = _engine.n_physical_inputs ();
first_stage_init (fullpath, snapshot_name);
- _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
if (_is_new) {
- if (create (mix_template, compute_initial_length(), bus_profile)) {
+ if (create (mix_template, bus_profile)) {
destroy ();
throw failed_constructor ();
}
- }
+ }
if (second_stage_init ()) {
destroy ();
DirtyChanged (); /* EMIT SIGNAL */
}
+ 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;
}
delete state_tree;
+ /* remove all stubfiles that might still be lurking */
+
+ cleanup_stubfiles ();
+
/* reset dynamic state version back to default */
Stateful::loading_state_version = 0;
+ _butler->drop_references ();
delete _butler;
delete midi_control_ui;
+ delete _all_route_group;
if (click_data != default_click) {
delete [] click_data;
clear_clicks ();
- delete _scratch_buffers;
- delete _silent_buffers;
- delete _mix_buffers;
-
/* clear out any pending dead wood from RCU managed objects */
routes.flush ();
- diskstreams.flush ();
_bundles.flush ();
AudioDiskstream::free_working_buffers();
RegionFactory::delete_all_regions ();
DEBUG_TRACE (DEBUG::Destruction, "delete routes\n");
-
+
/* reset these three references to special routes before we do the usual route delete thing */
auditioner.reset ();
boost::shared_ptr<RouteList> r = routes.reader ();
- DEBUG_TRACE (DEBUG::Destruction, "delete diskstreams\n");
- {
- RCUWriter<DiskstreamList> dwriter (diskstreams);
- boost::shared_ptr<DiskstreamList> dsl = dwriter.get_copy();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for diskstream %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
- (*i)->drop_references ();
- }
-
- dsl->clear ();
- }
- diskstreams.flush ();
-
DEBUG_TRACE (DEBUG::Destruction, "delete sources\n");
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for source %1 ; pre-ref = %2\n", i->second->path(), i->second.use_count()));
Crossfade::set_buffer_size (0);
- delete mmc;
-
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
playlists.reset ();
boost_debug_list_ptrs ();
+ delete _locations;
+ delete _speakers;
+
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
}
/* default state for Click: dual-mono to first 2 physical outputs */
- for (int physport = 0; physport < 2; ++physport) {
- string physical_output = _engine.get_nth_physical_output (DataType::AUDIO, physport);
-
- if (physical_output.length()) {
- if (_click_io->add_port (physical_output, this)) {
- // relax, even though its an error
- }
- }
- }
+ vector<string> outs;
+ _engine.get_physical_outputs (DataType::AUDIO, outs);
- if (_click_io->n_ports () > ChanCount::ZERO) {
- _clicking = Config->get_clicking ();
- }
+ 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 (_("Set up standard connections"));
+ 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]);
+ }
+
/* Create a set of Bundle objects that map
to the physical I/O currently available. We create both
mono and stereo bundles, so that the common cases of mono
/* mono output bundles */
- for (uint32_t np = 0; np < n_physical_outputs; ++np) {
+ for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); ++np) {
char buf[32];
snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
- shared_ptr<Bundle> c (new Bundle (buf, true));
- c->add_channel (_("mono"));
- c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+ boost::shared_ptr<Bundle> c (new Bundle (buf, true));
+ c->add_channel (_("mono"), DataType::AUDIO);
+ c->set_port (0, outputs[DataType::AUDIO][np]);
add_bundle (c);
}
/* stereo output bundles */
- for (uint32_t np = 0; np < n_physical_outputs; np += 2) {
- if (np + 1 < n_physical_outputs) {
+ for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); np += 2) {
+ if (np + 1 < outputs[DataType::AUDIO].size()) {
char buf[32];
snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2);
- shared_ptr<Bundle> c (new Bundle (buf, true));
- c->add_channel (_("L"));
- c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
- c->add_channel (_("R"));
- c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
+ boost::shared_ptr<Bundle> c (new Bundle (buf, true));
+ c->add_channel (_("L"), DataType::AUDIO);
+ c->set_port (0, outputs[DataType::AUDIO][np]);
+ c->add_channel (_("R"), DataType::AUDIO);
+ c->set_port (1, outputs[DataType::AUDIO][np + 1]);
add_bundle (c);
}
/* mono input bundles */
- for (uint32_t np = 0; np < n_physical_inputs; ++np) {
+ for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); ++np) {
char buf[32];
snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
- shared_ptr<Bundle> c (new Bundle (buf, false));
- c->add_channel (_("mono"));
- c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+ boost::shared_ptr<Bundle> c (new Bundle (buf, false));
+ c->add_channel (_("mono"), DataType::AUDIO);
+ c->set_port (0, inputs[DataType::AUDIO][np]);
add_bundle (c);
}
/* stereo input bundles */
- for (uint32_t np = 0; np < n_physical_inputs; np += 2) {
- if (np + 1 < n_physical_inputs) {
+ for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); np += 2) {
+ if (np + 1 < inputs[DataType::AUDIO].size()) {
char buf[32];
snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2);
- shared_ptr<Bundle> c (new Bundle (buf, false));
- c->add_channel (_("L"));
- c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
- c->add_channel (_("R"));
- c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
+ boost::shared_ptr<Bundle> c (new Bundle (buf, false));
+ c->add_channel (_("L"), DataType::AUDIO);
+ c->set_port (0, inputs[DataType::AUDIO][np]);
+ c->add_channel (_("R"), DataType::AUDIO);
+ c->set_port (1, inputs[DataType::AUDIO][np + 1]);
add_bundle (c);
}
}
+ /* MIDI input bundles */
+
+ for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) {
+ string n = inputs[DataType::MIDI][np];
+ boost::erase_first (n, X_("alsa_pcm:"));
+
+ boost::shared_ptr<Bundle> c (new Bundle (n, false));
+ c->add_channel ("", DataType::MIDI);
+ c->set_port (0, inputs[DataType::MIDI][np]);
+ add_bundle (c);
+ }
+
+ /* MIDI output bundles */
+
+ for (uint32_t np = 0; np < outputs[DataType::MIDI].size(); ++np) {
+ string n = outputs[DataType::MIDI][np];
+ boost::erase_first (n, X_("alsa_pcm:"));
+
+ boost::shared_ptr<Bundle> c (new Bundle (n, true));
+ c->add_channel ("", DataType::MIDI);
+ c->set_port (0, outputs[DataType::MIDI][np]);
+ add_bundle (c);
+ }
+
BootMessage (_("Setup signal flow and plugins"));
hookup_io ();
if (_is_new && !no_auto_connect()) {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock());
+
/* don't connect the master bus outputs if there is a monitor bus */
if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) {
for (uint32_t n = 0; n < limit; ++n) {
Port* p = _master_out->output()->nth (n);
- string connect_to = _engine.get_nth_physical_output (DataType (p->type()), n);
+ string connect_to;
+ if (outputs[p->type()].size() > n) {
+ connect_to = outputs[p->type()][n];
+ }
if (!connect_to.empty() && p->connected_to (connect_to) == false) {
if (_master_out->output()->connect (p, connect_to, this)) {
} else {
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
- uint32_t mod = _engine.n_physical_outputs (*t);
+ uint32_t mod = n_physical_outputs.get (*t);
uint32_t limit = _monitor_out->n_outputs().get(*t);
for (uint32_t n = 0; n < limit; ++n) {
Port* p = _monitor_out->output()->ports().port(*t, n);
- string connect_to = _engine.get_nth_physical_output (*t, (n % mod));
+ string connect_to;
+ if (outputs[*t].size() > (n % mod)) {
+ connect_to = outputs[*t][n % mod];
+ }
if (!connect_to.empty()) {
if (_monitor_out->output()->connect (p, connect_to, this)) {
*/
try {
- Auditioner* a = new Auditioner (*this);
- if (a->init()) {
- delete a;
- throw failed_constructor();
- }
- a->use_new_diskstream ();
+ Auditioner* a = new Auditioner (*this);
+ if (a->init()) {
+ delete a;
+ throw failed_constructor();
+ }
+ a->use_new_diskstream ();
auditioner.reset (a);
}
/* Tell all IO objects to connect themselves together */
IO::enable_connecting ();
- MIDI::JACK_MidiPort::MakeConnections ();
+ MIDI::Port::MakeConnections ();
/* Now reset all panners */
they connect to the control out specifically.
*/
- if (_monitor_out) {
+ if (_monitor_out) {
boost::shared_ptr<RouteList> r = routes.reader ();
- for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
+ for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
- if ((*x)->is_monitor()) {
+ if ((*x)->is_monitor()) {
- /* relax */
+ /* relax */
- } else if ((*x)->is_master()) {
+ } else if ((*x)->is_master()) {
- /* relax */
+ /* relax */
- } else {
+ } else {
- (*x)->listen_via (_monitor_out,
- (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
- false, false);
- }
- }
- }
+ (*x)->listen_via (_monitor_out,
+ (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
+ false, false);
+ }
+ }
+ }
/* Anyone who cares about input state, wake up and do something */
}
void
-Session::playlist_length_changed ()
-{
- /* we can't just increase end_location->end() if pl->get_maximum_extent()
- if larger. if the playlist used to be the longest playlist,
- and its now shorter, we have to decrease end_location->end(). hence,
- we have to iterate over all diskstreams and check the
- playlists currently in use.
- */
- find_current_end ();
-}
-
-void
-Session::diskstream_playlist_changed (boost::weak_ptr<Diskstream> wp)
+Session::track_playlist_changed (boost::weak_ptr<Track> wp)
{
- boost::shared_ptr<Diskstream> dstream = wp.lock ();
- if (!dstream) {
+ boost::shared_ptr<Track> track = wp.lock ();
+ if (!track) {
return;
}
boost::shared_ptr<Playlist> playlist;
- if ((playlist = dstream->playlist()) != 0) {
- playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::playlist_length_changed, this));
+ if ((playlist = track->playlist()) != 0) {
+ playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::update_session_range_location_marker, this));
+ playlist->RangesMoved.connect_same_thread (*this, boost::bind (&Session::update_session_range_location_marker, this));
}
- /* see comment in playlist_length_changed () */
- find_current_end ();
+ update_session_range_location_marker ();
}
bool
{
if (transport_rolling()) {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+ 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->record_enabled ()) {
//cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input());
+ tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input());
}
}
+
} else {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+
+ 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->record_enabled ()) {
//cerr << "switching to input = " << !Config->get_auto_input() << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring);
+ tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring);
}
}
}
void
Session::auto_punch_end_changed (Location* location)
{
- nframes_t when_to_stop = location->end();
+ framepos_t when_to_stop = location->end();
// when_to_stop += _worst_output_latency + _worst_input_latency;
replace_event (SessionEvent::PunchOut, when_to_stop);
}
void
Session::auto_punch_changed (Location* location)
{
- nframes_t when_to_stop = location->end();
+ framepos_t when_to_stop = location->end();
replace_event (SessionEvent::PunchIn, location->start());
//when_to_stop += _worst_output_latency + _worst_input_latency;
{
Location* existing;
- if ((existing = _locations.auto_punch_location()) != 0 && existing != location) {
+ if ((existing = _locations->auto_punch_location()) != 0 && existing != location) {
punch_connections.drop_connections();
existing->set_auto_punch (false, this);
remove_event (existing->start(), SessionEvent::PunchIn);
{
Location* existing;
- if ((existing = _locations.auto_loop_location()) != 0 && existing != location) {
+ if ((existing = _locations->auto_loop_location()) != 0 && existing != location) {
loop_connections.drop_connections ();
existing->set_auto_loop (false, this);
remove_event (existing->end(), SessionEvent::AutoLoop);
void
Session::locations_changed ()
{
- _locations.apply (*this, &Session::handle_locations_changed);
+ _locations->apply (*this, &Session::handle_locations_changed);
}
void
set_loop = true;
}
- if (location->is_start()) {
- start_location = location;
- }
- if (location->is_end()) {
- end_location = location;
+ if (location->is_session_range()) {
+ _session_range_location = location;
}
}
void
Session::enable_record ()
{
- /* XXX really atomic compare+swap here */
- if (g_atomic_int_get (&_record_status) != Recording) {
- g_atomic_int_set (&_record_status, Recording);
- _last_record_location = _transport_frame;
- deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location);
+ while (1) {
+ RecordState rs = (RecordState) g_atomic_int_get (&_record_status);
+
+ if (rs == Recording) {
+ break;
+ }
- if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
- (*i)->monitor_input (true);
+ if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) {
+
+ _last_record_location = _transport_frame;
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
+
+ if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
+
+ 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->record_enabled ()) {
+ tr->monitor_input (true);
+ }
}
}
+
+ RecordStateChanged ();
+ break;
}
-
- RecordStateChanged ();
}
}
if ((!Config->get_latched_record_enable () && !play_loop) || force) {
g_atomic_int_set (&_record_status, Disabled);
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
} else {
if (rs == Recording) {
g_atomic_int_set (&_record_status, Enabled);
}
}
- // FIXME: timestamp correct? [DR]
- // FIXME FIXME FIXME: rt_context? this must be called in the process thread.
- // does this /need/ to be sent in all cases?
- if (rt_context) {
- deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame);
- }
-
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
- (*i)->monitor_input (false);
+ 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->record_enabled ()) {
+ tr->monitor_input (false);
}
}
}
void
Session::step_back_from_record ()
{
- /* XXX really atomic compare+swap here */
- if (g_atomic_int_get (&_record_status) == Recording) {
- g_atomic_int_set (&_record_status, Enabled);
+ if (g_atomic_int_compare_and_exchange (&_record_status, Recording, Enabled)) {
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+ 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->record_enabled ()) {
//cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (false);
+ tr->monitor_input (false);
}
}
}
void
Session::maybe_enable_record ()
{
+ if (_step_editors > 0) {
+ return;
+ }
+
g_atomic_int_set (&_record_status, Enabled);
- /* this function is currently called from somewhere other than an RT thread.
- this save_state() call therefore doesn't impact anything.
+ /* This function is currently called from somewhere other than an RT thread.
+ This save_state() call therefore doesn't impact anything. Doing it here
+ means that we save pending state of which sources the next record will use,
+ which gives us some chance of recovering from a crash during the record.
*/
-
+
save_state ("", true);
-
+
if (_transport_speed) {
if (!config.get_punch_in()) {
enable_record ();
}
} else {
- deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
RecordStateChanged (); /* EMIT SIGNAL */
}
set_dirty();
}
-nframes64_t
+framepos_t
Session::audible_frame () const
{
- nframes64_t ret;
- nframes64_t tf;
- nframes_t offset;
+ framepos_t ret;
+ framepos_t tf;
+ framecnt_t offset;
/* the first of these two possible settings for "offset"
mean that the audible frame is stationary until
/* MOVING */
- /* check to see if we have passed the first guaranteed
+ /* 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 or changed transport direction.
*/
if (_transport_speed > 0.0f) {
if (!play_loop || !have_looped) {
- if (tf < _last_roll_location + offset) {
- return _last_roll_location;
+ if (tf < _last_roll_or_reversal_location + offset) {
+ return _last_roll_or_reversal_location;
}
}
/* XXX wot? no backward looping? */
- if (tf > _last_roll_location - offset) {
- return _last_roll_location;
+ if (tf > _last_roll_or_reversal_location - offset) {
+ return _last_roll_or_reversal_location;
} else {
/* backwards */
ret += offset;
}
void
-Session::set_frame_rate (nframes_t frames_per_second)
+Session::set_frame_rate (framecnt_t frames_per_second)
{
- /** \fn void Session::set_frame_size(nframes_t)
+ /** \fn void Session::set_frame_size(framecnt_t)
the AudioEngine object that calls this guarantees
that it will not be called while we are also in
::process(). Its fine to do things that block
sync_time_vars();
- Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval())));
+ Automatable::set_automation_interval (ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval())));
clear_clicks ();
}
void
-Session::set_block_size (nframes_t nframes)
+Session::set_block_size (pframes_t nframes)
{
/* the AudioEngine guarantees
that it will not be called while we are also in
{
current_block_size = nframes;
- ensure_buffers(_scratch_buffers->available());
-
- delete [] _gain_automation_buffer;
- _gain_automation_buffer = new gain_t[nframes];
-
- allocate_pan_automation_buffers (nframes, _npan_buffers, true);
+ ensure_buffers ();
boost::shared_ptr<RouteList> r = routes.reader ();
(*i)->set_block_size (nframes);
}
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->set_block_size (nframes);
+ 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_block_size (nframes);
+ }
}
set_worst_io_latencies ();
Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/)
{
#if 0
- nframes_t fade_frames;
+ framecnt_t fade_frames;
/* Don't allow fade of less 1 frame */
} else {
- fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001);
+ fade_frames = (framecnt_t) floor (fade_msecs * _current_frame_rate * 0.001);
}
}
struct RouteSorter {
+ /** @return true to run r1 before r2, otherwise false */
bool operator() (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> r2) {
- if (r1->fed_by.find (r2) != r1->fed_by.end()) {
+ if (r2->feeds (r1)) {
+ /* r1 fed by r2; run r2 early */
return false;
- } else if (r2->fed_by.find (r1) != r2->fed_by.end()) {
+ } else if (r1->feeds (r2)) {
+ /* r2 fed by r1; run r1 early */
return true;
} else {
- if (r1->fed_by.empty()) {
- if (r2->fed_by.empty()) {
+ if (r1->not_fed ()) {
+ if (r2->not_fed ()) {
/* no ardour-based connections inbound to either route. just use signal order */
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
} else {
return true;
}
} else {
- return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
+ if (r2->not_fed()) {
+ /* r1 has connections, r2 does not; run r2 early */
+ return false;
+ } else {
+ /* both r1 and r2 have connections, but not to each other. just use signal order */
+ return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
+ }
}
}
}
};
static void
-trace_terminal (shared_ptr<Route> r1, shared_ptr<Route> rbase)
+trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
{
- shared_ptr<Route> r2;
+ boost::shared_ptr<Route> r2;
- if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) {
+ if (r1->feeds (rbase) && rbase->feeds (r1)) {
info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg;
return;
}
/* make a copy of the existing list of routes that feed r1 */
- set<weak_ptr<Route> > existing = r1->fed_by;
-
+ Route::FedBy existing (r1->fed_by());
+
/* for each route that feeds r1, recurse, marking it as feeding
rbase as well.
*/
- for (set<weak_ptr<Route> >::iterator i = existing.begin(); i != existing.end(); ++i) {
- if (!(r2 = (*i).lock ())) {
+ for (Route::FedBy::iterator i = existing.begin(); i != existing.end(); ++i) {
+ if (!(r2 = i->r.lock ())) {
/* (*i) went away, ignore it */
continue;
}
base as being fed by r2
*/
- rbase->fed_by.insert (r2);
+ rbase->add_fed_by (r2, i->sends_only);
if (r2 != rbase) {
stop here.
*/
- if ((r1->fed_by.find (r2) != r1->fed_by.end()) && (r2->fed_by.find (r1) != r2->fed_by.end())) {
+ if (r1->feeds (r2) && r2->feeds (r1)) {
continue;
}
return;
}
-
{
-
RCUWriter<RouteList> writer (routes);
- shared_ptr<RouteList> r = writer.get_copy ();
+ boost::shared_ptr<RouteList> r = writer.get_copy ();
resort_routes_using (r);
/* writer goes out of scope and forces update */
}
+ //route_graph->dump(1);
+
+#ifndef NDEBUG
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
+
+ const Route::FedBy& fb ((*i)->fed_by());
+
+ for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
+ boost::shared_ptr<Route> sf = f->r.lock();
+ if (sf) {
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+ }
+ }
+ }
+#endif
+
}
void
-Session::resort_routes_using (shared_ptr<RouteList> r)
+Session::resort_routes_using (boost::shared_ptr<RouteList> r)
{
RouteList::iterator i, j;
for (i = r->begin(); i != r->end(); ++i) {
- (*i)->fed_by.clear ();
+ (*i)->clear_fed_by ();
for (j = r->begin(); j != r->end(); ++j) {
continue;
}
- if ((*j)->feeds (*i)) {
- (*i)->fed_by.insert (*j);
+ bool via_sends_only;
+
+ if ((*j)->direct_feeds (*i, &via_sends_only)) {
+ (*i)->add_fed_by (*j, via_sends_only);
}
}
}
RouteSorter cmp;
r->sort (cmp);
-#if 0
- cerr << "finished route resort\n";
+ route_graph->rechain (r);
+#ifndef NDEBUG
+ DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl;
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n",
+ (*i)->name(), (*i)->order_key ("signal")));
}
- cerr << endl;
#endif
}
return false;
}
+/** Count the total ins and outs of all non-hidden routes in the session and return them in in and out */
void
Session::count_existing_route_channels (ChanCount& in, ChanCount& out)
{
in = ChanCount::ZERO;
out = ChanCount::ZERO;
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) {
- in += (*i)->n_inputs();
- out += (*i)->n_outputs();
+ in += (*i)->n_inputs();
+ out += (*i)->n_outputs();
}
}
}
+/** Caller must not hold process lock */
list<boost::shared_ptr<MidiTrack> >
Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many)
{
goto failed;
}
- shared_ptr<MidiTrack> track;
-
+ boost::shared_ptr<MidiTrack> track;
+
try {
MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode);
- if (mt->init ()) {
- delete mt;
- goto failed;
- }
+ if (mt->init ()) {
+ delete mt;
+ goto failed;
+ }
- mt->use_new_diskstream();
+ mt->use_new_diskstream();
boost_debug_shared_ptr_mark_interesting (mt, "Track");
track = boost::shared_ptr<MidiTrack>(mt);
- if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
- error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
- goto failed;
- }
-
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+ error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+ goto failed;
+ }
- if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
- error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
- goto failed;
+ if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+ error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+ goto failed;
+ }
}
- auto_connect_route (track, existing_inputs, existing_outputs);
+ auto_connect_route (track.get(), existing_inputs, existing_outputs);
+
+ track->non_realtime_input_change();
- track->midi_diskstream()->non_realtime_input_change();
if (route_group) {
route_group->add (track);
}
catch (failed_constructor &err) {
error << _("Session: could not create new midi track.") << endmsg;
-
- if (track) {
- /* we need to get rid of this, since the track failed to be created */
- /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
-
- {
- RCUWriter<DiskstreamList> writer (diskstreams);
- boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
- ds->remove (track->midi_diskstream());
- }
- }
-
goto failed;
}
catch (AudioEngine::PortRegistrationFailure& pfe) {
error << string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with ports if you need this many tracks."), PROGRAM_NAME) << endmsg;
-
- if (track) {
- /* we need to get rid of this, since the track failed to be created */
- /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */
-
- {
- RCUWriter<DiskstreamList> writer (diskstreams);
- boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
- ds->remove (track->midi_diskstream());
- }
- }
-
goto failed;
}
return ret;
}
+/** Caller must hold process lock.
+ * @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs.
+ * @param input_start Where to start from when auto-connecting inputs; e.g. if this is 0, auto-connect starting from input 0.
+ * @param output_start As \a input_start, but for outputs.
+ */
void
-Session::auto_connect_route (boost::shared_ptr<Route> route,
- ChanCount& existing_inputs, ChanCount& existing_outputs)
+Session::auto_connect_route (
+ Route* route, ChanCount& existing_inputs, ChanCount& existing_outputs, bool connect_inputs, ChanCount input_start, ChanCount output_start
+ )
{
/* If both inputs and outputs are auto-connected to physical ports,
use the max of input and output offsets to ensure auto-connected
port number). Otherwise just use the lowest input or output
offset possible.
*/
+
const bool in_out_physical =
(Config->get_input_auto_connect() & AutoConnectPhysical)
- && (Config->get_output_auto_connect() & AutoConnectPhysical);
+ && (Config->get_output_auto_connect() & AutoConnectPhysical)
+ && connect_inputs;
const ChanCount in_offset = in_out_physical
? ChanCount::max(existing_inputs, existing_outputs)
? ChanCount::max(existing_inputs, existing_outputs)
: existing_outputs;
- static string empty_string;
- string& port = empty_string;
-
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
vector<string> physinputs;
vector<string> physoutputs;
_engine.get_physical_outputs (*t, physoutputs);
_engine.get_physical_inputs (*t, physinputs);
- if (!physinputs.empty()) {
+ if (!physinputs.empty() && connect_inputs) {
uint32_t nphysical_in = physinputs.size();
- for (uint32_t i = 0; i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
- port = empty_string;
+ for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
+ string port;
if (Config->get_input_auto_connect() & AutoConnectPhysical) {
port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
if (!physoutputs.empty()) {
uint32_t nphysical_out = physoutputs.size();
- for (uint32_t i = 0; i < route->n_outputs().get(*t); ++i) {
- port = empty_string;
+ for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
+ string port;
if (Config->get_output_auto_connect() & AutoConnectPhysical) {
port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
existing_outputs += route->n_outputs();
}
+/** Caller must not hold process lock */
list< boost::shared_ptr<AudioTrack> >
Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many)
{
goto failed;
}
- shared_ptr<AudioTrack> track;
-
+ boost::shared_ptr<AudioTrack> track;
+
try {
AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode);
- if (at->init ()) {
- delete at;
- goto failed;
- }
+ if (at->init ()) {
+ delete at;
+ goto failed;
+ }
- at->use_new_diskstream();
+ at->use_new_diskstream();
boost_debug_shared_ptr_mark_interesting (at, "Track");
track = boost::shared_ptr<AudioTrack>(at);
- if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
- error << string_compose (
- _("cannot configure %1 in/%2 out configuration for new audio track"),
- input_channels, output_channels)
- << endmsg;
- goto failed;
- }
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
+ error << string_compose (
+ _("cannot configure %1 in/%2 out configuration for new audio track"),
+ input_channels, output_channels)
+ << endmsg;
+ goto failed;
+ }
+
+ if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
+ error << string_compose (
+ _("cannot configure %1 in/%2 out configuration for new audio track"),
+ input_channels, output_channels)
+ << endmsg;
+ goto failed;
+ }
- if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
- error << string_compose (
- _("cannot configure %1 in/%2 out configuration for new audio track"),
- input_channels, output_channels)
- << endmsg;
- goto failed;
+ auto_connect_route (track.get(), existing_inputs, existing_outputs);
}
- auto_connect_route (track, existing_inputs, existing_outputs);
-
if (route_group) {
route_group->add (track);
}
- track->audio_diskstream()->non_realtime_input_change();
+ track->non_realtime_input_change();
track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
track->set_remote_control_id (control_id);
catch (failed_constructor &err) {
error << _("Session: could not create new audio track.") << endmsg;
-
- if (track) {
- /* we need to get rid of this, since the track failed to be created */
- /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
-
- {
- RCUWriter<DiskstreamList> writer (diskstreams);
- boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
- ds->remove (track->audio_diskstream());
- }
- }
-
goto failed;
}
catch (AudioEngine::PortRegistrationFailure& pfe) {
error << pfe.what() << endmsg;
-
- if (track) {
- /* we need to get rid of this, since the track failed to be created */
- /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
-
- {
- RCUWriter<DiskstreamList> writer (diskstreams);
- boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
- ds->remove (track->audio_diskstream());
- }
- }
-
goto failed;
}
RemoteModel m = Config->get_remote_model();
bool emit_signal = false;
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (MixerOrdered == m) {
- long order = (*i)->order_key(N_("signal"));
+ int32_t order = (*i)->order_key(N_("signal"));
(*i)->set_remote_control_id (order+1, false);
emit_signal = true;
} else if (EditorOrdered == m) {
- long order = (*i)->order_key(N_("editor"));
+ int32_t order = (*i)->order_key(N_("editor"));
(*i)->set_remote_control_id (order+1, false);
emit_signal = true;
} else if (UserOrdered == m) {
}
}
-
+/** Caller must not hold process lock */
RouteList
Session::new_audio_route (bool aux, int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many)
{
try {
Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO);
- if (rt->init ()) {
- delete rt;
- goto failure;
- }
+ if (rt->init ()) {
+ delete rt;
+ goto failure;
+ }
boost_debug_shared_ptr_mark_interesting (rt, "Route");
- shared_ptr<Route> bus (rt);
+ boost::shared_ptr<Route> bus (rt);
- if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
- error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
- input_channels, output_channels)
- << endmsg;
- goto failure;
- }
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
+ error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+ input_channels, output_channels)
+ << endmsg;
+ goto failure;
+ }
+
+
+ if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
+ error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+ input_channels, output_channels)
+ << endmsg;
+ goto failure;
+ }
- if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
- error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
- input_channels, output_channels)
- << endmsg;
- goto failure;
+ auto_connect_route (bus.get(), existing_inputs, existing_outputs, false);
}
- auto_connect_route (bus, existing_inputs, existing_outputs);
-
if (route_group) {
route_group->add (bus);
}
/*NOTREACHED*/
}
- IO::set_name_in_state (*node_copy.children().front(), name);
+ /* set IO children to use the new name */
+ XMLNodeList const & children = node_copy.children ();
+ for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == IO::state_node_name) {
+ IO::set_name_in_state (**i, name);
+ }
+ }
Track::zero_diskstream_id_in_xml (node_copy);
try {
- shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
-
+ boost::shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
+
if (route == 0) {
error << _("Session: cannot create track/bus from template description") << endmsg;
goto out;
picks up the configuration of the route. During session
loading this normally happens in a different way.
*/
- route->input()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
- route->output()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
+
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ IOChange change (IOChange::Type (IOChange::ConfigurationChanged | IOChange::ConnectionsChanged));
+ change.after = route->input()->n_ports();
+ route->input()->changed (change, this);
+ change.after = route->output()->n_ports();
+ route->output()->changed (change, this);
}
route->set_remote_control_id (control_id);
{
{
RCUWriter<RouteList> writer (routes);
- shared_ptr<RouteList> r = writer.get_copy ();
+ boost::shared_ptr<RouteList> r = writer.get_copy ();
r->insert (r->end(), new_routes.begin(), new_routes.end());
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, 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, _1, wpr));
r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1));
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->route_group_changed.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
+ r->order_key_changed.connect_same_thread (*this, boost::bind (&Session::route_order_key_changed, this));
if (r->is_master()) {
_master_out = r;
if (r->is_monitor()) {
_monitor_out = r;
}
+
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (r);
+ if (tr) {
+ tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr<Track> (tr)));
+ track_playlist_changed (boost::weak_ptr<Track> (tr));
+ tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this));
+
+ boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
+ if (mt) {
+ mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1));
+ }
+ }
}
if (_monitor_out && IO::connecting_legal) {
for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
if ((*x)->is_monitor()) {
- /* relax */
- } else if ((*x)->is_master()) {
- /* relax */
+ /* relax */
+ } else if ((*x)->is_master()) {
+ /* relax */
} else {
- (*x)->listen_via (_monitor_out,
- (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
- false, false);
- }
+ (*x)->listen_via (_monitor_out,
+ (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
+ false, false);
+ }
}
resort_routes ();
boost::shared_ptr<RouteList> r = routes.reader ();
boost::shared_ptr<Send> s;
- /* only tracks */
-
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (boost::dynamic_pointer_cast<Track>(*i)) {
- if ((s = (*i)->internal_send_for (dest)) != 0) {
- s->amp()->gain_control()->set_value (0.0);
- }
+ if ((s = (*i)->internal_send_for (dest)) != 0) {
+ s->amp()->gain_control()->set_value (0.0);
}
}
}
boost::shared_ptr<RouteList> r = routes.reader ();
boost::shared_ptr<Send> s;
- /* only tracks */
-
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (boost::dynamic_pointer_cast<Track>(*i)) {
- if ((s = (*i)->internal_send_for (dest)) != 0) {
- s->amp()->gain_control()->set_value (1.0);
- }
+ if ((s = (*i)->internal_send_for (dest)) != 0) {
+ s->amp()->gain_control()->set_value (1.0);
}
}
}
boost::shared_ptr<RouteList> r = routes.reader ();
boost::shared_ptr<Send> s;
- /* only tracks */
-
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (boost::dynamic_pointer_cast<Track>(*i)) {
- if ((s = (*i)->internal_send_for (dest)) != 0) {
- s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value());
- }
+ if ((s = (*i)->internal_send_for (dest)) != 0) {
+ s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value());
}
}
}
}
void
-Session::add_diskstream (boost::shared_ptr<Diskstream> dstream)
+Session::remove_route (boost::shared_ptr<Route> route)
{
- /* need to do this in case we're rolling at the time, to prevent false underruns */
- dstream->do_refill_with_alloc ();
-
- dstream->set_block_size (current_block_size);
-
- {
- RCUWriter<DiskstreamList> writer (diskstreams);
- boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
- ds->push_back (dstream);
- /* writer goes out of scope, copies ds back to main */
+ if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) {
+ return;
}
- dstream->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::diskstream_playlist_changed, this, boost::weak_ptr<Diskstream> (dstream)));
- /* this will connect to future changes, and check the current length */
- diskstream_playlist_changed (boost::weak_ptr<Diskstream> (dstream));
-
- dstream->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_diskstream, this));
-}
+ route->set_solo (false, this);
-void
-Session::remove_route (shared_ptr<Route> route)
-{
{
RCUWriter<RouteList> writer (routes);
- shared_ptr<RouteList> rs = writer.get_copy ();
+ boost::shared_ptr<RouteList> rs = writer.get_copy ();
rs->remove (route);
*/
if (route == _master_out) {
- _master_out = shared_ptr<Route> ();
+ _master_out = boost::shared_ptr<Route> ();
}
if (route == _monitor_out) {
_monitor_out.reset ();
}
- update_route_solo_state ();
-
/* writer goes out of scope, forces route list update */
}
- boost::shared_ptr<Track> t;
- boost::shared_ptr<Diskstream> ds;
-
- if ((t = boost::dynamic_pointer_cast<Track>(route)) != 0) {
- ds = t->diskstream();
- }
-
- if (ds) {
-
- {
- RCUWriter<DiskstreamList> dsl (diskstreams);
- boost::shared_ptr<DiskstreamList> d = dsl.get_copy();
- d->remove (ds);
- }
- }
-
- find_current_end ();
+ update_route_solo_state ();
+ update_session_range_location_marker ();
- // We need to disconnect the routes inputs and outputs
+ // We need to disconnect the route's inputs and outputs
route->input()->disconnect (0);
route->output()->disconnect (0);
}
}
+ boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
+ if (mt && mt->step_editing()) {
+ if (_step_editors > 0) {
+ _step_editors--;
+ }
+ }
+
update_latency_compensation (false, false);
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.
+ */
+
+ resort_routes ();
+ route_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) */
}
if (route->listening()) {
+
+ if (Config->get_exclusive_solo()) {
+ /* new listen: disable all other listen */
+ 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_hidden()) {
+ continue;
+ }
+ (*i)->set_listen (false, this);
+ }
+ }
+
_listen_cnt++;
+
} else if (_listen_cnt > 0) {
+
_listen_cnt--;
}
}
+void
+Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+{
+ 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_changed")) << endmsg;
+ return;
+ }
+
+ bool send_changed = false;
+
+ if (route->solo_isolated()) {
+ if (_solo_isolated_cnt == 0) {
+ send_changed = true;
+ }
+ _solo_isolated_cnt++;
+ } else if (_solo_isolated_cnt > 0) {
+ _solo_isolated_cnt--;
+ if (_solo_isolated_cnt == 0) {
+ send_changed = true;
+ }
+ }
+ if (send_changed) {
+ IsolatedChanged (); /* EMIT SIGNAL */
+ }
+}
+
void
-Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr<Route> wpr)
{
+ if (!self_solo_change) {
+ // session doesn't care about changes to soloed-by-others
+ return;
+ }
+
if (solo_update_disabled) {
// We know already
return;
error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg;
return;
}
-
- shared_ptr<RouteList> r = routes.reader ();
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
int32_t delta;
if (route->self_soloed()) {
} else {
delta = -1;
}
-
- /* now mod the solo level of all other routes except master & control outs
- so that they will be silent if appropriate.
- */
+
+ if (delta == 1 && Config->get_exclusive_solo()) {
+ /* new solo: disable all other solos */
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
+ continue;
+ }
+ (*i)->set_solo (false, this);
+ }
+ }
solo_update_disabled = true;
-
+
+ RouteList uninvolved;
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
bool via_sends_only;
+ bool in_signal_flow;
if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
continue;
- } else if ((*i)->feeds (route, &via_sends_only)) {
+ }
+
+ in_signal_flow = false;
+
+ if ((*i)->feeds (route, &via_sends_only)) {
if (!via_sends_only) {
- (*i)->mod_solo_by_others (delta);
+ if (!route->soloed_by_others_upstream()) {
+ (*i)->mod_solo_by_others_downstream (delta);
+ }
+ in_signal_flow = true;
}
}
+
+ if (route->feeds (*i, &via_sends_only)) {
+ (*i)->mod_solo_by_others_upstream (delta);
+ in_signal_flow = true;
+ }
+
+ if (!in_signal_flow) {
+ uninvolved.push_back (*i);
+ }
}
- /* make sure master is never muted by solo */
+ solo_update_disabled = false;
+ update_route_solo_state (r);
- if (_master_out && route != _master_out && _master_out->soloed_by_others() == 0 && !_master_out->soloed()) {
- _master_out->mod_solo_by_others (1);
- }
-
- /* ditto for control outs make sure master is never muted by solo */
+ /* now notify that the mute state of the routes not involved in the signal
+ pathway of the just-solo-changed route may have altered.
+ */
- if (_monitor_out && route != _monitor_out && _monitor_out && _monitor_out->soloed_by_others() == 0) {
- _monitor_out->mod_solo_by_others (1);
+ for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
+ (*i)->mute_changed (this);
}
- solo_update_disabled = false;
- update_route_solo_state (r);
SoloChanged (); /* EMIT SIGNAL */
set_dirty();
}
/* now figure out if anything that matters is soloed (or is "listening")*/
bool something_soloed = false;
- uint32_t listeners = 0;
+ uint32_t listeners = 0;
+ uint32_t isolated = 0;
if (!r) {
r = routes.reader();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_hidden() && (*i)->self_soloed()) {
something_soloed = true;
- break;
}
- if (!(*i)->is_hidden() && (*i)->listening()) {
- listeners++;
- }
+ if (!(*i)->is_hidden() && (*i)->listening()) {
+ if (Config->get_solo_control_is_listen_control()) {
+ listeners++;
+ } else {
+ (*i)->set_listen (false, this);
+ }
+ }
+
+ if ((*i)->solo_isolated()) {
+ isolated++;
+ }
}
- if (something_soloed != _non_soloed_outs_muted) {
- _non_soloed_outs_muted = something_soloed;
- SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
- }
+ if (something_soloed != _non_soloed_outs_muted) {
+ _non_soloed_outs_muted = something_soloed;
+ SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
+ }
- if (listeners) {
- _listen_cnt = listeners;
- }
+ _listen_cnt = listeners;
+
+ if (isolated != _solo_isolated_cnt) {
+ _solo_isolated_cnt = isolated;
+ IsolatedChanged (); /* EMIT SIGNAL */
+ }
}
boost::shared_ptr<RouteList>
Session::get_routes_with_internal_returns() const
{
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
boost::shared_ptr<RouteList> rl (new RouteList);
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
return rl;
}
-shared_ptr<Route>
+bool
+Session::io_name_is_legal (const std::string& name)
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->name() == name) {
+ return false;
+ }
+
+ if ((*i)->has_io_processor_named (name)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+boost::shared_ptr<Route>
Session::route_by_name (string name)
{
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->name() == name) {
}
}
- return shared_ptr<Route> ((Route*) 0);
+ return boost::shared_ptr<Route> ((Route*) 0);
}
-shared_ptr<Route>
+boost::shared_ptr<Route>
Session::route_by_id (PBD::ID id)
{
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->id() == id) {
}
}
- return shared_ptr<Route> ((Route*) 0);
+ return boost::shared_ptr<Route> ((Route*) 0);
}
-shared_ptr<Route>
+boost::shared_ptr<Route>
Session::route_by_remote_id (uint32_t id)
{
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->remote_control_id() == id) {
}
}
- return shared_ptr<Route> ((Route*) 0);
+ return boost::shared_ptr<Route> ((Route*) 0);
}
+/** If either end of the session range location marker lies inside the current
+ * session extent, move it to the corresponding session extent.
+ */
void
-Session::find_current_end ()
+Session::update_session_range_location_marker ()
{
if (_state_of_the_state & Loading) {
return;
}
- nframes_t max = get_maximum_extent ();
+ pair<framepos_t, framepos_t> const ext = get_extent ();
- if (max > end_location->end()) {
- end_location->set_end (max);
- set_dirty();
- DurationChanged(); /* EMIT SIGNAL */
+ if (_session_range_location == 0) {
+ /* we don't have a session range yet; use this one (provided it is valid) */
+ if (ext.first != max_framepos) {
+ add_session_range_location (ext.first, ext.second);
+ }
+ } else {
+ /* update the existing session range */
+ if (ext.first < _session_range_location->start()) {
+ _session_range_location->set_start (ext.first);
+ set_dirty ();
+ }
+
+ if (ext.second > _session_range_location->end()) {
+ _session_range_location->set_end (ext.second);
+ set_dirty ();
+ }
+
}
}
-nframes_t
-Session::get_maximum_extent () const
+/** @return Extent of the session's contents; if the session is empty, the first value of
+ * the pair will equal max_framepos.
+ */
+pair<framepos_t, framepos_t>
+Session::get_extent () const
{
- nframes_t max = 0;
- nframes_t me;
-
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->destructive()) //ignore tape tracks when getting max extents
+ pair<framepos_t, framepos_t> ext (max_framepos, 0);
+
+ 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->destructive()) {
+ // ignore tape tracks when getting extents
continue;
- boost::shared_ptr<Playlist> pl = (*i)->playlist();
- if ((me = pl->get_maximum_extent()) > max) {
- max = me;
}
- }
-
- return max;
-}
-
-boost::shared_ptr<Diskstream>
-Session::diskstream_by_name (string name)
-{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->name() == name) {
- return *i;
+ pair<framepos_t, framepos_t> e = tr->playlist()->get_extent ();
+ if (e.first < ext.first) {
+ ext.first = e.first;
}
- }
-
- return boost::shared_ptr<Diskstream>((Diskstream*) 0);
-}
-
-boost::shared_ptr<Diskstream>
-Session::diskstream_by_id (const PBD::ID& id)
-{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->id() == id) {
- return *i;
+ if (e.second > ext.second) {
+ ext.second = e.second;
}
}
- return boost::shared_ptr<Diskstream>((Diskstream*) 0);
+ return ext;
}
/* Region management */
boost::shared_ptr<Region>
Session::find_whole_file_parent (boost::shared_ptr<Region const> child) const
{
- const RegionFactory::RegionMap& regions (RegionFactory::regions());
+ const RegionFactory::RegionMap& regions (RegionFactory::regions());
RegionFactory::RegionMap::const_iterator i;
boost::shared_ptr<Region> region;
}
int
-Session::destroy_region (boost::shared_ptr<Region> region)
+Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
{
- vector<boost::shared_ptr<Source> > srcs;
+ set<boost::shared_ptr<Region> > relevant_regions;
- {
- if (region->playlist()) {
- region->playlist()->destroy_region (region);
- }
-
- for (uint32_t n = 0; n < region->n_channels(); ++n) {
- srcs.push_back (region->source (n));
- }
+ for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ++s) {
+ RegionFactory::get_regions_using_source (*s, relevant_regions);
}
- region->drop_references ();
+ cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl;
- for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
+ for (set<boost::shared_ptr<Region> >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) {
+ set<boost::shared_ptr<Region> >::iterator tmp;
- (*i)->mark_for_remove ();
- (*i)->drop_references ();
-
- cerr << "source was not used by any playlist\n";
+ tmp = r;
+ ++tmp;
+
+ cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl;
+
+ playlists->destroy_region (*r);
+ RegionFactory::map_remove (*r);
+
+ (*r)->drop_sources ();
+ (*r)->drop_references ();
+
+ cerr << "\tdone UC = " << (*r).use_count() << endl;
+
+ relevant_regions.erase (r);
+
+ r = tmp;
}
- return 0;
-}
+ for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ) {
+
+ {
+ Glib::Mutex::Lock ls (source_lock);
+ /* remove from the main source list */
+ sources.erase ((*s)->id());
+ }
-int
-Session::destroy_regions (list<boost::shared_ptr<Region> > regions)
-{
- for (list<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
- destroy_region (*i);
+ (*s)->mark_for_remove ();
+ (*s)->drop_references ();
+
+ s = srcs.erase (s);
}
+
return 0;
}
int
Session::remove_last_capture ()
{
- list<boost::shared_ptr<Region> > r;
-
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ list<boost::shared_ptr<Source> > srcs;
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- list<boost::shared_ptr<Region> >& l = (*i)->last_capture_regions();
+ 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) {
+ continue;
+ }
+
+ list<boost::shared_ptr<Source> >& l = tr->last_capture_sources();
if (!l.empty()) {
- r.insert (r.end(), l.begin(), l.end());
+ srcs.insert (srcs.end(), l.begin(), l.end());
l.clear ();
}
}
- destroy_regions (r);
+ destroy_sources (srcs);
save_state (_current_snapshot_name);
}
if (result.second) {
- set_dirty();
- }
- boost::shared_ptr<AudioFileSource> afs;
+ /* yay, new source */
- if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
- if (Config->get_auto_analyse_audio()) {
- Analyser::queue_source_for_analysis (source, false);
+ set_dirty();
+
+ boost::shared_ptr<AudioFileSource> afs;
+
+ if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (source, false);
+ }
}
- }
+ }
}
void
Glib::Mutex::Lock lm (source_lock);
if ((i = sources.find (source->id())) != sources.end()) {
+ cerr << "Removing source " << source->name() << endl;
sources.erase (i);
}
}
}
boost::shared_ptr<Source>
-Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
+Session::source_by_path_and_channel (const string& path, uint16_t chn)
{
Glib::Mutex::Lock lm (source_lock);
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
- cerr << "comparing " << path << " with " << i->second->name() << endl;
boost::shared_ptr<AudioFileSource> afs
= boost::dynamic_pointer_cast<AudioFileSource>(i->second);
return boost::shared_ptr<Source>();
}
+uint32_t
+Session::count_sources_by_origin (const string& path)
+{
+ uint32_t cnt = 0;
+ Glib::Mutex::Lock lm (source_lock);
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+ boost::shared_ptr<FileSource> fs
+ = boost::dynamic_pointer_cast<FileSource>(i->second);
+
+ if (fs && fs->origin() == path) {
+ ++cnt;
+ }
+ }
+
+ return cnt;
+}
+
string
Session::change_source_path_by_name (string path, string oldname, string newname, bool destructive)
the task here is to replace NAME with the new name.
*/
- /* find last slash */
-
string dir;
string prefix;
- string::size_type slash;
string::size_type dash;
- if ((slash = path.find_last_of ('/')) == string::npos) {
- return "";
- }
-
- dir = path.substr (0, slash+1);
+ dir = Glib::path_get_dirname (path);
+ path = Glib::path_get_basename (path);
/* '-' is not a legal character for the NAME part of the path */
return "";
}
- prefix = path.substr (slash+1, dash-(slash+1));
+ prefix = path.substr (0, dash);
- path = dir;
path += prefix;
path += '-';
path += new_legalized;
- path += ".wav"; /* XXX gag me with a spoon */
+ path += native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+ path = Glib::build_filename (dir, path);
} else {
string dir;
string suffix;
- string::size_type slash;
string::size_type dash;
string::size_type postfix;
- /* find last slash */
-
- if ((slash = path.find_last_of ('/')) == string::npos) {
- return "";
- }
-
- dir = path.substr (0, slash+1);
+ dir = Glib::path_get_dirname (path);
+ path = Glib::path_get_basename (path);
/* '-' is not a legal character for the NAME part of the path */
for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
- snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
+ snprintf (buf, sizeof(buf), "%s-%u%s", newname.c_str(), cnt, suffix.c_str());
- if (access (buf, F_OK) != 0) {
- path = buf;
+ if (!matching_unsuffixed_filename_exists_in (dir, buf)) {
+ path = Glib::build_filename (dir, buf);
break;
}
+
path = "";
}
- if (path == "") {
- error << "FATAL ERROR! Could not find a " << endl;
+ if (path.empty()) {
+ fatal << string_compose (_("FATAL ERROR! Could not find a suitable version of %1 for a rename"),
+ newname) << endl;
+ /*NOTREACHED*/
}
-
}
return path;
* (e.g. as returned by new_*_source_name)
*/
string
-Session::new_source_path_from_name (DataType type, const string& name)
+Session::new_source_path_from_name (DataType type, const string& name, bool as_stub)
{
assert(name.find("/") == string::npos);
sys::path p;
if (type == DataType::AUDIO) {
- p = sdir.sound_path();
+ p = (as_stub ? sdir.sound_stub_path() : sdir.sound_path());
} else if (type == DataType::MIDI) {
- p = sdir.midi_path();
+ p = (as_stub ? sdir.midi_stub_path() : sdir.midi_path());
} else {
error << "Unknown source type, unable to create file path" << endmsg;
return "";
return p.to_string();
}
-Glib::ustring
-Session::peak_path (Glib::ustring base) const
+string
+Session::peak_path (string base) const
{
sys::path peakfile_path(_session_dir->peak_path());
- peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
+ peakfile_path /= base + peakfile_suffix;
return peakfile_path.to_string();
}
string
Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
{
- string spath;
uint32_t cnt;
char buf[PATH_MAX+1];
const uint32_t limit = 10000;
string legalized;
+ string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
buf[0] = '\0';
legalized = legalize_for_path (base);
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
- SessionDirectory sdir((*i).path);
-
- spath = sdir.sound_path().to_string();
-
if (destructive) {
if (nchan < 2) {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
- spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "T%04d-%s%s",
+ cnt, legalized.c_str(), ext.c_str());
} else if (nchan == 2) {
if (chan == 0) {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav",
- spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "T%04d-%s%%L%s",
+ cnt, legalized.c_str(), ext.c_str());
} else {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav",
- spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "T%04d-%s%%R%s",
+ cnt, legalized.c_str(), ext.c_str());
}
} else if (nchan < 26) {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav",
- spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
+ snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s",
+ cnt, legalized.c_str(), 'a' + chan, ext.c_str());
} else {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
- spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "T%04d-%s%s",
+ cnt, legalized.c_str(), ext.c_str());
}
} else {
- spath += '/';
- spath += legalized;
-
if (nchan < 2) {
- snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt);
+ snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
} else if (nchan == 2) {
if (chan == 0) {
- snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt);
+ snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str());
} else {
- snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt);
+ snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str());
}
} else if (nchan < 26) {
- snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan);
+ snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str());
} else {
- snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt);
+ snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
}
}
- if (sys::exists(buf)) {
+ SessionDirectory sdir((*i).path);
+
+ string spath = sdir.sound_path().to_string();
+ string spath_stubs = sdir.sound_stub_path().to_string();
+
+ /* note that we search *without* the extension so that
+ we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf"
+ in the event that this new name is required for
+ a file format change.
+ */
+
+ if (matching_unsuffixed_filename_exists_in (spath, buf) ||
+ matching_unsuffixed_filename_exists_in (spath_stubs, buf)) {
existing++;
+ break;
}
-
}
if (existing == 0) {
throw failed_constructor();
}
}
-
- return Glib::path_get_basename(buf);
+
+ return Glib::path_get_basename (buf);
}
/** Create a new within-session audio source */
boost::shared_ptr<AudioFileSource>
-Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive)
+Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive, bool as_stub)
{
- const size_t n_chans = ds.n_channels().n_audio();
- const string name = new_audio_source_name (ds.name(), n_chans, chan, destructive);
- const string path = new_source_path_from_name(DataType::AUDIO, name);
+ const string name = new_audio_source_name (n, n_chans, chan, destructive);
+ const string path = new_source_path_from_name(DataType::AUDIO, name, as_stub);
return boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+ SourceFactory::createWritable (DataType::AUDIO, *this, path, string(), destructive, frame_rate()));
}
/** Return a unique name based on \a base for a new internal MIDI source */
/** Create a new within-session MIDI source */
boost::shared_ptr<MidiSource>
-Session::create_midi_source_for_session (MidiDiskstream& ds)
+Session::create_midi_source_for_session (Track* track, string const & n, bool as_stub)
{
- const string name = new_midi_source_name (ds.name());
- const string path = new_source_path_from_name (DataType::MIDI, name);
+ /* try to use the existing write source for the track, to keep numbering sane
+ */
+
+ if (track) {
+ /*MidiTrack* mt = dynamic_cast<Track*> (track);
+ assert (mt);
+ */
+
+ list<boost::shared_ptr<Source> > l = track->steal_write_sources ();
+
+ if (!l.empty()) {
+ assert (boost::dynamic_pointer_cast<MidiSource> (l.front()));
+ return boost::dynamic_pointer_cast<MidiSource> (l.front());
+ }
+ }
+
+ const string name = new_midi_source_name (n);
+ const string path = new_source_path_from_name (DataType::MIDI, name, as_stub);
return boost::dynamic_pointer_cast<SMFSource> (
- SourceFactory::createWritable (
- DataType::MIDI, *this, path, false, frame_rate()));
+ SourceFactory::createWritable (
+ DataType::MIDI, *this, path, string(), false, frame_rate()));
}
bool
Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
{
- if (a->is_monitor()) {
- return true;
- }
- if (b->is_monitor()) {
- return false;
- }
- return a->order_key(N_("signal")) < b->order_key(N_("signal"));
-}
-
-void
-Session::remove_empty_sounds ()
-{
- vector<string> audio_filenames;
-
- get_files_in_directory (_session_dir->sound_path(), audio_filenames);
-
- Glib::Mutex::Lock lm (source_lock);
-
- TapeFileMatcher tape_file_matcher;
-
- remove_if (audio_filenames.begin(), audio_filenames.end(),
- boost::bind (&TapeFileMatcher::matches, &tape_file_matcher, _1));
-
- for (vector<string>::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) {
-
- sys::path audio_file_path (_session_dir->sound_path());
-
- audio_file_path /= *i;
-
- if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) {
-
- try
- {
- sys::remove (audio_file_path);
- const string peakfile = peak_path (audio_file_path.to_string());
- sys::remove (peakfile);
- }
- catch (const sys::filesystem_error& err)
- {
- error << err.what() << endmsg;
- }
- }
+ if (a->is_monitor()) {
+ return true;
}
+ if (b->is_monitor()) {
+ return false;
+ }
+ return a->order_key(N_("signal")) < b->order_key(N_("signal"));
}
bool
}
}
-uint32_t
-Session::n_diskstreams () const
-{
- uint32_t n = 0;
-
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->hidden()) {
- n++;
- }
- }
- return n;
-}
-
void
Session::graph_reordered ()
{
reflect any changes in latencies within the graph.
*/
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->set_capture_offset ();
+ 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_capture_offset ();
+ }
}
}
-nframes_t
+framecnt_t
Session::available_capture_duration ()
{
float sample_bytes_on_disk = 4.0; // keep gcc happy
double scale = 4096.0 / sample_bytes_on_disk;
- if (_total_free_4k_blocks * scale > (double) max_frames) {
- return max_frames;
+ if (_total_free_4k_blocks * scale > (double) max_framecnt) {
+ return max_framecnt;
}
-
- return (nframes_t) floor (_total_free_4k_blocks * scale);
+
+ return (framecnt_t) floor (_total_free_4k_blocks * scale);
}
void
-Session::add_bundle (shared_ptr<Bundle> bundle)
+Session::add_bundle (boost::shared_ptr<Bundle> bundle)
{
{
RCUWriter<BundleList> writer (_bundles);
}
void
-Session::remove_bundle (shared_ptr<Bundle> bundle)
+Session::remove_bundle (boost::shared_ptr<Bundle> bundle)
{
bool removed = false;
set_dirty();
}
-shared_ptr<Bundle>
+boost::shared_ptr<Bundle>
Session::bundle_by_name (string name) const
{
boost::shared_ptr<BundleList> b = _bundles.reader ();
playlists->update_after_tempo_map_change ();
+ _locations->apply (*this, &Session::update_locations_after_tempo_map_change);
+
set_dirty ();
}
+void
+Session::update_locations_after_tempo_map_change (Locations::LocationList& loc)
+{
+ for (Locations::LocationList::iterator i = loc.begin(); i != loc.end(); ++i) {
+ (*i)->recompute_frames_from_bbt ();
+ }
+}
+
/** Ensures that all buffers (scratch, send, silent, etc) are allocated for
* the given count with the current block size.
*/
void
Session::ensure_buffers (ChanCount howmany)
{
- if (current_block_size == 0) {
- return; // too early? (is this ok?)
- }
-
- for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
- size_t count = std::max(_scratch_buffers->available().get(*t), howmany.get(*t));
- _scratch_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t));
- _mix_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t));
- _silent_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t));
- }
-
- allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false);
+ BufferManager::ensure_buffers (howmany);
}
void
void
Session::mark_send_id (uint32_t id)
{
- cerr << "Marking send ID " << id << " in use\n";
-
if (id >= send_bitset.size()) {
send_bitset.resize (id+16, false);
}
Session::unmark_send_id (uint32_t id)
{
if (id < send_bitset.size()) {
- send_bitset[id] = false;
- }
+ send_bitset[id] = false;
+ }
}
void
Session::unmark_return_id (uint32_t id)
{
if (id < return_bitset.size()) {
- return_bitset[id] = false;
- }
+ return_bitset[id] = false;
+ }
}
void
Session::unmark_insert_id (uint32_t id)
{
if (id < insert_bitset.size()) {
- insert_bitset[id] = false;
- }
+ insert_bitset[id] = false;
+ }
}
void
Session::reset_native_file_format ()
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ 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
+ */
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->reset_write_sources (false);
+ _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);
+ }
}
}
bool
Session::route_name_unique (string n) const
{
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->name() == n) {
return false;
}
-void
-Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force)
-{
- if (!force && howmany <= _npan_buffers) {
- return;
- }
-
- if (_pan_automation_buffer) {
-
- for (uint32_t i = 0; i < _npan_buffers; ++i) {
- delete [] _pan_automation_buffer[i];
- }
-
- delete [] _pan_automation_buffer;
- }
-
- _pan_automation_buffer = new pan_t*[howmany];
-
- for (uint32_t i = 0; i < howmany; ++i) {
- _pan_automation_buffer[i] = new pan_t[nframes];
- }
-
- _npan_buffers = howmany;
-}
-
int
Session::freeze_all (InterThreadInfo& itt)
{
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
}
boost::shared_ptr<Region>
-Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
+Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
InterThreadInfo& itt, bool enable_processing)
{
boost::shared_ptr<AudioFileSource> fsource;
uint32_t x;
char buf[PATH_MAX+1];
- ChanCount nchans(track.audio_diskstream()->n_channels());
- nframes_t position;
- nframes_t this_chunk;
- nframes_t to_do;
+ ChanCount diskstream_channels (track.n_channels());
+ framepos_t position;
+ framecnt_t this_chunk;
+ framepos_t to_do;
BufferSet buffers;
SessionDirectory sdir(get_best_session_directory_for_new_source ());
const string sound_dir = sdir.sound_path().to_string();
- nframes_t len = end - start;
+ framepos_t len = end - start;
+ bool need_block_size_reset = false;
+ string ext;
+ ChanCount const max_proc = track.max_processor_streams ();
if (end <= start) {
error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
return result;
}
- const nframes_t chunk_size = (256 * 1024)/4;
+ const framecnt_t chunk_size = (256 * 1024)/4;
// block all process callback handling
/* call tree *MUST* hold route_lock */
- if ((playlist = track.diskstream()->playlist()) == 0) {
+ if ((playlist = track.playlist()) == 0) {
goto out;
}
goto out;
}
- for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) {
+ ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+
+ for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) {
for (x = 0; x < 99999; ++x) {
- snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1);
+ snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str());
if (access (buf, F_OK) != 0) {
break;
}
try {
fsource = boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
+ SourceFactory::createWritable (DataType::AUDIO, *this, buf, string(), false, frame_rate()));
}
catch (failed_constructor& err) {
srcs.push_back (fsource);
}
+ /* tell redirects that care that we are about to use a much larger blocksize */
+
+ need_block_size_reset = true;
+ track.set_block_size (chunk_size);
+
/* XXX need to flush all redirects */
position = start;
to_do = len;
/* create a set of reasonably-sized buffers */
- buffers.ensure_buffers(DataType::AUDIO, nchans.n_audio(), chunk_size);
- buffers.set_count(nchans);
+ buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size);
+ buffers.set_count (max_proc);
- for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
+ for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
if (afs)
afs->prepare_for_peakfile_writes ();
}
}
+
+ if (need_block_size_reset) {
+ track.set_block_size (get_block_size());
+ }
+
unblock_processing ();
return result;
}
+gain_t*
+Session::gain_automation_buffer() const
+{
+ return ProcessThread::gain_automation_buffer ();
+}
+
+pan_t**
+Session::pan_automation_buffer() const
+{
+ return ProcessThread::pan_automation_buffer ();
+}
+
BufferSet&
Session::get_silent_buffers (ChanCount count)
{
+ return ProcessThread::get_silent_buffers (count);
+#if 0
assert(_silent_buffers->available() >= count);
_silent_buffers->set_count(count);
}
return *_silent_buffers;
+#endif
}
BufferSet&
Session::get_scratch_buffers (ChanCount count)
{
+ return ProcessThread::get_scratch_buffers (count);
+#if 0
if (count != ChanCount::ZERO) {
assert(_scratch_buffers->available() >= count);
_scratch_buffers->set_count(count);
}
return *_scratch_buffers;
+#endif
}
BufferSet&
Session::get_mix_buffers (ChanCount count)
{
+ return ProcessThread::get_mix_buffers (count);
+#if 0
assert(_mix_buffers->available() >= count);
_mix_buffers->set_count(count);
return *_mix_buffers;
+#endif
}
uint32_t
Session::ntracks () const
{
uint32_t n = 0;
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
if (boost::dynamic_pointer_cast<Track> (*i)) {
Session::nbusses () const
{
uint32_t n = 0;
- shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
if (boost::dynamic_pointer_cast<Track>(*i) == 0) {
automation_lists[al->id()] = al;
}
-nframes_t
-Session::compute_initial_length ()
-{
- return _engine.frame_rate() * 60 * 5;
-}
-
void
Session::sync_order_keys (std::string const & base)
{
set_remote_control_ids ();
}
-/** @return true if there is at least one record-enabled diskstream, otherwise false */
+/** @return true if there is at least one record-enabled track, otherwise false */
bool
-Session::have_rec_enabled_diskstream () const
+Session::have_rec_enabled_track () const
{
- return g_atomic_int_get (&_have_rec_enabled_diskstream) == 1;
+ return g_atomic_int_get (&_have_rec_enabled_track) == 1;
}
-/** Update the state of our rec-enabled diskstreams flag */
+/** Update the state of our rec-enabled tracks flag */
void
-Session::update_have_rec_enabled_diskstream ()
+Session::update_have_rec_enabled_track ()
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader ();
- DiskstreamList::iterator i = dsl->begin ();
- while (i != dsl->end () && (*i)->record_enabled () == false) {
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ RouteList::iterator i = rl->begin();
+ while (i != rl->end ()) {
+
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->record_enabled ()) {
+ break;
+ }
+
++i;
}
- int const old = g_atomic_int_get (&_have_rec_enabled_diskstream);
+ int const old = g_atomic_int_get (&_have_rec_enabled_track);
- g_atomic_int_set (&_have_rec_enabled_diskstream, i != dsl->end () ? 1 : 0);
+ g_atomic_int_set (&_have_rec_enabled_track, i != rl->end () ? 1 : 0);
- if (g_atomic_int_get (&_have_rec_enabled_diskstream) != old) {
+ if (g_atomic_int_get (&_have_rec_enabled_track) != old) {
RecordStateChanged (); /* EMIT SIGNAL */
}
}
{
/* 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()) {
+ set_solo (get_routes(), false);
+ } else if (listening()) {
+ set_listen (get_routes(), false);
+ }
}
+/** Called when anything about any of our route groups changes (membership, state etc.) */
void
Session::route_group_changed ()
{
vector<SyncSource> ret;
ret.push_back (JACK);
-
- if (mtc_port()) {
- ret.push_back (MTC);
- }
-
- if (midi_clock_port()) {
- ret.push_back (MIDIClock);
- }
+ ret.push_back (MTC);
+ ret.push_back (MIDIClock);
return ret;
}
boost::shared_ptr<RouteList>
-Session::get_routes_with_regions_at (nframes64_t const p) const
+Session::get_routes_with_regions_at (framepos_t const p) const
{
- shared_ptr<RouteList> r = routes.reader ();
- shared_ptr<RouteList> rl (new RouteList);
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<RouteList> rl (new RouteList);
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
continue;
}
- boost::shared_ptr<Diskstream> ds = tr->diskstream ();
- if (!ds) {
- continue;
- }
-
- boost::shared_ptr<Playlist> pl = ds->playlist ();
+ boost::shared_ptr<Playlist> pl = tr->playlist ();
if (!pl) {
continue;
}
return rl;
}
+
+void
+Session::goto_end ()
+{
+ if (_session_range_location) {
+ request_locate (_session_range_location->end(), false);
+ } else {
+ request_locate (0, false);
+ }
+}
+
+void
+Session::goto_start ()
+{
+ if (_session_range_location) {
+ request_locate (_session_range_location->start(), false);
+ } else {
+ request_locate (0, false);
+ }
+}
+
+framepos_t
+Session::current_start_frame () const
+{
+ return _session_range_location ? _session_range_location->start() : 0;
+}
+
+framepos_t
+Session::current_end_frame () const
+{
+ return _session_range_location ? _session_range_location->end() : 0;
+}
+
+void
+Session::add_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);
+}
+
+/** Called when one of our routes' order keys has changed */
+void
+Session::route_order_key_changed ()
+{
+ RouteOrderKeyChanged (); /* EMIT SIGNAL */
+}
+
+void
+Session::step_edit_status_change (bool yn)
+{
+ bool send = false;
+
+ bool val = false;
+ if (yn) {
+ send = (_step_editors == 0);
+ val = true;
+
+ _step_editors++;
+ } else {
+ send = (_step_editors == 1);
+ val = false;
+
+ if (_step_editors > 0) {
+ _step_editors--;
+ }
+ }
+
+ if (send) {
+ StepEditStatusChange (val);
+ }
+}
+
+
+void
+Session::start_time_changed (framepos_t old)
+{
+ /* Update the auto loop range to match the session range
+ (unless the auto loop range has been changed by the user)
+ */
+
+ Location* s = _locations->session_range_location ();
+ Location* l = _locations->auto_loop_location ();
+
+ if (l->start() == old) {
+ l->set_start (s->start(), true);
+ }
+}
+
+void
+Session::end_time_changed (framepos_t old)
+{
+ /* Update the auto loop range to match the session range
+ (unless the auto loop range has been changed by the user)
+ */
+
+ Location* s = _locations->session_range_location ();
+ Location* l = _locations->auto_loop_location ();
+
+ if (l->end() == old) {
+ l->set_end (s->end(), true);
+ }
+}
+
+string
+Session::source_search_path (DataType type) const
+{
+ string search_path;
+
+ if (session_dirs.size() == 1) {
+ switch (type) {
+ case DataType::AUDIO:
+ search_path = _session_dir->sound_path().to_string();
+ break;
+ case DataType::MIDI:
+ search_path = _session_dir->midi_path().to_string();
+ break;
+ }
+ } else {
+ for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+ SessionDirectory sdir (i->path);
+ if (!search_path.empty()) {
+ search_path += ':';
+ }
+ switch (type) {
+ case DataType::AUDIO:
+ search_path += sdir.sound_path().to_string();
+ break;
+ case DataType::MIDI:
+ search_path += sdir.midi_path().to_string();
+ break;
+ }
+ }
+ }
+
+ /* now add user-specified locations
+ */
+
+ vector<string> dirs;
+
+ switch (type) {
+ case DataType::AUDIO:
+ split (config.get_audio_search_path (), dirs, ':');
+ break;
+ case DataType::MIDI:
+ split (config.get_midi_search_path (), dirs, ':');
+ break;
+ }
+
+ for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
+ search_path += ':';
+ search_path += *i;
+
+ }
+
+ return search_path;
+}
+
+void
+Session::ensure_search_path_includes (const string& path, DataType type)
+{
+ string search_path;
+ vector<string> dirs;
+
+ if (path == ".") {
+ return;
+ }
+
+ switch (type) {
+ case DataType::AUDIO:
+ search_path = config.get_audio_search_path ();
+ break;
+ case DataType::MIDI:
+ search_path = config.get_midi_search_path ();
+ break;
+ }
+
+ split (search_path, dirs, ':');
+
+ for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
+ if (*i == path) {
+ return;
+ }
+ }
+
+ if (!search_path.empty()) {
+ search_path += ':';
+ }
+
+ search_path += path;
+
+ switch (type) {
+ case DataType::AUDIO:
+ config.set_audio_search_path (search_path);
+ break;
+ case DataType::MIDI:
+ config.set_midi_search_path (search_path);
+ break;
+ }
+}
+
+Speakers&
+Session::get_speakers()
+{
+ if (!_speakers) {
+ _speakers = new Speakers;
+ }
+
+ return *_speakers;
+}
+
+list<string>
+Session::unknown_processors () const
+{
+ list<string> p;
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ list<string> t = (*i)->unknown_processors ();
+ copy (t.begin(), t.end(), back_inserter (p));
+ }
+
+ p.sort ();
+ p.unique ();
+
+ return p;
+}