X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=14c5bde9969045bfbaa674b54eaa1b945d3b1a50;hb=337cee7a8344c76edc9068bf733ee8489b1c9bef;hp=9b61b5d816f726a5503a45a3234daa3670afd08e;hpb=a8640ec0af8a477b35ddfd9b83d8704e63edf978;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 9b61b5d816..14c5bde996 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -32,24 +32,25 @@ #include #include +#include +#include + #include -#include +#include #include #include #include -#include +#include #include #include #include -#include +#include #include #include #include -#include -#include +#include #include -#include #include #include #include @@ -64,21 +65,29 @@ #include #include #include -#include +#include +#include +#include + +#ifdef HAVE_LIBLO +#include +#endif #include "i18n.h" using namespace std; using namespace ARDOUR; -//using namespace sigc; +using namespace PBD; +using boost::shared_ptr; const char* Session::_template_suffix = X_(".template"); const char* Session::_statefile_suffix = X_(".ardour"); const char* Session::_pending_suffix = X_(".pending"); -const char* Session::sound_dir_name = X_("sounds"); -const char* Session::tape_dir_name = X_("tapes"); +const char* Session::old_sound_dir_name = X_("sounds"); +const char* Session::sound_dir_name = X_("audiofiles"); const char* Session::peak_dir_name = X_("peaks"); const char* Session::dead_sound_dir_name = X_("dead_sounds"); +const char* Session::interchange_dir_name = X_("interchange"); Session::compute_peak_t Session::compute_peak = 0; Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0; @@ -86,6 +95,11 @@ Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0; Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0; sigc::signal Session::AskAboutPendingState; +sigc::signal Session::SendFeedback; + +sigc::signal Session::SMPTEOffsetChanged; +sigc::signal Session::StartTimeChanged; +sigc::signal Session::EndTimeChanged; int Session::find_session (string str, string& path, string& snapshot, bool& isnew) @@ -248,22 +262,31 @@ Session::Session (AudioEngine &eng, _midi_port (default_midi_port), pending_events (2048), midi_requests (128), // the size of this should match the midi request pool size + diskstreams (new DiskstreamList), + routes (new RouteList), + auditioner ((Auditioner*) 0), + _click_io ((IO*) 0), main_outs (0) { bool new_session; - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << endl; + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; n_physical_outputs = _engine.n_physical_outputs(); n_physical_inputs = _engine.n_physical_inputs(); first_stage_init (fullpath, snapshot_name); - if (create (new_session, mix_template, _engine.frame_rate() * 60 * 5)) { - throw failed_constructor (); + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + if (new_session) { + if (create (new_session, mix_template, compute_initial_length())) { + cerr << "create failed\n"; + throw failed_constructor (); + } } if (second_stage_init (new_session)) { + cerr << "2nd state failed\n"; throw failed_constructor (); } @@ -273,6 +296,8 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); + Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); + if (was_dirty) { DirtyChanged (); /* EMIT SIGNAL */ } @@ -287,7 +312,7 @@ Session::Session (AudioEngine &eng, uint32_t master_out_channels, uint32_t requested_physical_in, uint32_t requested_physical_out, - jack_nframes_t initial_length) + nframes_t initial_length) : _engine (eng), _mmc_port (default_mmc_port), @@ -295,41 +320,57 @@ Session::Session (AudioEngine &eng, _midi_port (default_midi_port), pending_events (2048), midi_requests (16), + diskstreams (new DiskstreamList), + routes (new RouteList), main_outs (0) { bool new_session; - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << endl; + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; + + n_physical_outputs = _engine.n_physical_outputs(); + n_physical_inputs = _engine.n_physical_inputs(); + + if (n_physical_inputs) { + n_physical_inputs = max (requested_physical_in, n_physical_inputs); + } - n_physical_outputs = max (requested_physical_out, _engine.n_physical_outputs()); - n_physical_inputs = max (requested_physical_in, _engine.n_physical_inputs()); + if (n_physical_outputs) { + n_physical_outputs = max (requested_physical_out, n_physical_outputs); + } first_stage_init (fullpath, snapshot_name); - - if (create (new_session, 0, initial_length)) { - throw failed_constructor (); + + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + + if (new_session) { + if (create (new_session, 0, initial_length)) { + throw failed_constructor (); + } } if (control_out_channels) { - Route* r; - r = new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut); - add_route (r); + shared_ptr r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); + RouteList rl; + rl.push_back (r); + add_routes (rl); _control_out = r; } if (master_out_channels) { - Route* r; - r = new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut); - add_route (r); + shared_ptr r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); + RouteList rl; + rl.push_back (r); + add_routes (rl); _master_out = r; } else { /* prohibit auto-connect to master, because there isn't one */ output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); } - input_auto_connect = input_ac; - output_auto_connect = output_ac; + Config->set_input_auto_connect (input_ac); + Config->set_output_auto_connect (output_ac); if (second_stage_init (new_session)) { throw failed_constructor (); @@ -356,12 +397,25 @@ Session::~Session () _state_of_the_state = StateOfTheState (CannotSave|Deletion); _engine.remove_session (); + + GoingAway (); /* EMIT SIGNAL */ - going_away (); /* EMIT SIGNAL */ + /* do this */ + + notify_callbacks (); + + /* clear history so that no references to objects are held any more */ + + _history.clear (); + + /* clear state tree so that no references to objects are held any more */ + if (state_tree) { + delete state_tree; + } + terminate_butler_thread (); terminate_midi_thread (); - terminate_feedback (); if (click_data && click_data != default_click) { delete [] click_data; @@ -373,15 +427,6 @@ Session::~Session () clear_clicks (); - if (_click_io) { - delete _click_io; - } - - - if (auditioner) { - delete auditioner; - } - for (vector::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) { free(*i); } @@ -394,9 +439,11 @@ Session::~Session () free(*i); } - for (map::iterator i = _conversion_buffers.begin(); i != _conversion_buffers.end(); ++i) { - delete [] (i->second); - } + AudioDiskstream::free_working_buffers(); + + /* this should cause deletion of the auditioner */ + + // auditioner.reset (); #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION @@ -429,56 +476,64 @@ Session::~Session () #ifdef TRACK_DESTRUCTION cerr << "delete audio regions\n"; #endif /* TRACK_DESTRUCTION */ + for (AudioRegionList::iterator i = audio_regions.begin(); i != audio_regions.end(); ) { AudioRegionList::iterator tmp; - tmp =i; + tmp = i; ++tmp; - delete (*i).second; + i->second->drop_references (); i = tmp; } + + audio_regions.clear (); #ifdef TRACK_DESTRUCTION cerr << "delete routes\n"; #endif /* TRACK_DESTRUCTION */ - for (RouteList::iterator i = routes.begin(); i != routes.end(); ) { - RouteList::iterator tmp; - tmp = i; - ++tmp; - delete *i; - i = tmp; + { + RCUWriter writer (routes); + boost::shared_ptr r = writer.get_copy (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->drop_references (); + } + r->clear (); + /* writer goes out of scope and updates master */ } + routes.flush (); + #ifdef TRACK_DESTRUCTION cerr << "delete diskstreams\n"; #endif /* TRACK_DESTRUCTION */ - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ) { - DiskStreamList::iterator tmp; - - tmp = i; - ++tmp; - - delete *i; - - i = tmp; - } + { + RCUWriter dwriter (diskstreams); + boost::shared_ptr dsl = dwriter.get_copy(); + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + (*i)->drop_references (); + } + dsl->clear (); + } + diskstreams.flush (); #ifdef TRACK_DESTRUCTION - cerr << "delete sources\n"; + cerr << "delete audio sources\n"; #endif /* TRACK_DESTRUCTION */ - for (SourceList::iterator i = sources.begin(); i != sources.end(); ) { - SourceList::iterator tmp; + for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ) { + AudioSourceList::iterator tmp; tmp = i; ++tmp; - delete (*i).second; + i->second->drop_references (); i = tmp; } + audio_sources.clear (); + #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; #endif /* TRACK_DESTRUCTION */ @@ -534,14 +589,10 @@ Session::~Session () if (mmc) { delete mmc; } - - if (state_tree) { - delete state_tree; - } } void -Session::set_worst_io_latencies (bool take_lock) +Session::set_worst_io_latencies () { _worst_output_latency = 0; _worst_input_latency = 0; @@ -550,18 +601,12 @@ Session::set_worst_io_latencies (bool take_lock) return; } - if (take_lock) { - route_lock.read_lock (); - } + boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); } - - if (take_lock) { - route_lock.unlock (); - } } void @@ -576,9 +621,11 @@ Session::when_engine_running () set_block_size (_engine.frames_per_cycle()); set_frame_rate (_engine.frame_rate()); + Config->map_parameters (mem_fun (*this, &Session::config_changed)); + /* every time we reconnect, recompute worst case output latencies */ - _engine.Running.connect (sigc::bind (mem_fun (*this, &Session::set_worst_io_latencies), true)); + _engine.Running.connect (mem_fun (*this, &Session::set_worst_io_latencies)); if (synced_to_jack()) { _engine.transport_stop (); @@ -593,7 +640,7 @@ Session::when_engine_running () try { XMLNode* child = 0; - _click_io = new ClickIO (*this, "click", 0, 0, -1, -1); + _click_io.reset (new ClickIO (*this, "click", 0, 0, -1, -1)); if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { @@ -601,7 +648,7 @@ Session::when_engine_running () if (_click_io->set_state (*child->children().front()) == 0) { - _clicking = click_requested; + _clicking = Config->get_clicking (); } else { @@ -619,7 +666,7 @@ Session::when_engine_running () if (_click_io->add_output_port (first_physical_output, this)) { // relax, even though its an error } else { - _clicking = click_requested; + _clicking = Config->get_clicking (); } } } @@ -629,10 +676,10 @@ Session::when_engine_running () error << _("cannot setup Click I/O") << endmsg; } - set_worst_io_latencies (true); + set_worst_io_latencies (); if (_clicking) { - ControlChanged (Clicking); /* EMIT SIGNAL */ + // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } if (auditioner == 0) { @@ -644,7 +691,7 @@ Session::when_engine_running () */ try { - auditioner = new Auditioner (*this); + auditioner.reset (new Auditioner (*this)); } catch (failed_constructor& err) { @@ -767,7 +814,7 @@ Session::when_engine_running () insert_cnt = 0; - for (slist::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { + for (list::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { uint32_t id; if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { @@ -779,7 +826,7 @@ Session::when_engine_running () send_cnt = 0; - for (slist::iterator i = _sends.begin(); i != _sends.end(); ++i) { + for (list::iterator i = _sends.begin(); i != _sends.end(); ++i) { uint32_t id; if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { @@ -789,12 +836,23 @@ Session::when_engine_running () } } + + /* its safe to do this now */ + + restore_history (snap_name()); + _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); /* hook us up to the engine */ _engine.set_session (this); +#ifdef HAVE_LIBLO + /* and to OSC */ + + osc->set_session (*this); +#endif + _state_of_the_state = Clean; DirtyChanged (); /* EMIT SIGNAL */ @@ -872,7 +930,7 @@ Session::playlist_length_changed (Playlist* pl) } void -Session::diskstream_playlist_changed (DiskStream* dstream) +Session::diskstream_playlist_changed (boost::shared_ptr dstream) { Playlist *playlist; @@ -892,132 +950,43 @@ Session::record_enabling_legal () const // return false; // } - if (all_safe) { + if (Config->get_all_safe()) { return false; } return true; } -void -Session::set_auto_play (bool yn) -{ - if (auto_play != yn) { - auto_play = yn; - set_dirty (); - ControlChanged (AutoPlay); - } -} - -void -Session::set_auto_return (bool yn) -{ - if (auto_return != yn) { - auto_return = yn; - set_dirty (); - ControlChanged (AutoReturn); - } -} - -void -Session::set_crossfades_active (bool yn) -{ - if (crossfades_active != yn) { - crossfades_active = yn; - set_dirty (); - ControlChanged (CrossFadesActive); - } -} - -void -Session::set_do_not_record_plugins (bool yn) -{ - if (do_not_record_plugins != yn) { - do_not_record_plugins = yn; - set_dirty (); - ControlChanged (RecordingPlugins); - } -} - -void -Session::set_auto_input (bool yn) -{ - if (auto_input != yn) { - auto_input = yn; - - if (Config->get_use_hardware_monitoring() && transport_rolling()) { - /* auto-input only makes a difference if we're rolling */ - - /* Even though this can called from RT context we are using - a non-tentative rwlock here, because the action must occur. - The rarity and short potential lock duration makes this "OK" - */ - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - if ((*i)->record_enabled ()) { - //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (!auto_input); - } - } - } - - set_dirty(); - ControlChanged (AutoInput); - } -} - void Session::reset_input_monitor_state () { if (transport_rolling()) { - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (Config->get_use_hardware_monitoring() && !auto_input); + (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !Config->get_auto_input()); } } } else { - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (Config->get_use_hardware_monitoring()); + //cerr << "switching to input = " << !Config->get_auto_input() << __FILE__ << __LINE__ << endl << endl; + (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring); } } } } - -void -Session::set_input_auto_connect (bool yn) -{ - if (yn) { - input_auto_connect = AutoConnectOption (input_auto_connect|AutoConnectPhysical); - } else { - input_auto_connect = AutoConnectOption (input_auto_connect|~AutoConnectPhysical); - } - set_dirty (); -} - -bool -Session::get_input_auto_connect () const -{ - return (input_auto_connect & AutoConnectPhysical); -} - -void -Session::set_output_auto_connect (AutoConnectOption aco) -{ - output_auto_connect = aco; - set_dirty (); -} - void Session::auto_punch_start_changed (Location* location) { replace_event (Event::PunchIn, location->start()); - if (get_record_enabled() && get_punch_in()) { + if (get_record_enabled() && Config->get_punch_in()) { /* capture start has been changed, so save new pending state */ save_state ("", true); } @@ -1026,7 +995,7 @@ Session::auto_punch_start_changed (Location* location) void Session::auto_punch_end_changed (Location* location) { - jack_nframes_t when_to_stop = location->end(); + nframes_t when_to_stop = location->end(); // when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (Event::PunchOut, when_to_stop); } @@ -1034,7 +1003,7 @@ Session::auto_punch_end_changed (Location* location) void Session::auto_punch_changed (Location* location) { - jack_nframes_t when_to_stop = location->end(); + nframes_t when_to_stop = location->end(); replace_event (Event::PunchIn, location->start()); //when_to_stop += _worst_output_latency + _worst_input_latency; @@ -1046,7 +1015,7 @@ Session::auto_loop_changed (Location* location) { replace_event (Event::AutoLoop, location->end(), location->start()); - if (transport_rolling() && get_auto_loop()) { + if (transport_rolling() && play_loop) { //if (_transport_frame < location->start() || _transport_frame > location->end()) { @@ -1057,7 +1026,7 @@ Session::auto_loop_changed (Location* location) request_locate (location->start(), true); } - else if (seamless_loop && !loop_changing) { + else if (Config->get_seamless_loop() && !loop_changing) { // schedule a locate-roll to refill the diskstreams at the // previous loop end @@ -1114,48 +1083,6 @@ Session::set_auto_punch_location (Location* location) auto_punch_location_changed (location); } -void -Session::set_punch_in (bool yn) -{ - if (punch_in == yn) { - return; - } - - Location* location; - - if ((location = _locations.auto_punch_location()) != 0) { - if ((punch_in = yn) == true) { - replace_event (Event::PunchIn, location->start()); - } else { - remove_event (location->start(), Event::PunchIn); - } - } - - set_dirty(); - ControlChanged (PunchIn); /* EMIT SIGNAL */ -} - -void -Session::set_punch_out (bool yn) -{ - if (punch_out == yn) { - return; - } - - Location* location; - - if ((location = _locations.auto_punch_location()) != 0) { - if ((punch_out = yn) == true) { - replace_event (Event::PunchOut, location->end()); - } else { - clear_events (Event::PunchOut); - } - } - - set_dirty(); - ControlChanged (PunchOut); /* EMIT SIGNAL */ -} - void Session::set_auto_loop_location (Location* location) { @@ -1244,19 +1171,14 @@ void Session::enable_record () { /* XXX really atomic compare+swap here */ - if (atomic_read (&_record_status) != Recording) { - atomic_set (&_record_status, Recording); + if (g_atomic_int_get (&_record_status) != Recording) { + g_atomic_int_set (&_record_status, Recording); _last_record_location = _transport_frame; send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordStrobe); - if (Config->get_use_hardware_monitoring() && auto_input) { - /* Even though this can be called from RT context we are using - a non-tentative rwlock here, because the action must occur. - The rarity and short potential lock duration makes this "OK" - */ - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); - - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { + boost::shared_ptr dsl = diskstreams.reader(); + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { (*i)->monitor_input (true); } @@ -1268,30 +1190,26 @@ Session::enable_record () } void -Session::disable_record (bool force) +Session::disable_record (bool rt_context, bool force) { RecordState rs; - if ((rs = (RecordState) atomic_read (&_record_status)) != Disabled) { + if ((rs = (RecordState) g_atomic_int_get (&_record_status)) != Disabled) { if (!Config->get_latched_record_enable () || force) { - atomic_set (&_record_status, Disabled); + g_atomic_int_set (&_record_status, Disabled); } else { if (rs == Recording) { - atomic_set (&_record_status, Enabled); + g_atomic_int_set (&_record_status, Enabled); } } send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordExit); - if (Config->get_use_hardware_monitoring() && auto_input) { - /* Even though this can be called from RT context we are using - a non-tentative rwlock here, because the action must occur. - The rarity and short potential lock duration makes this "OK" - */ - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); + if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { (*i)->monitor_input (false); } @@ -1299,24 +1217,23 @@ Session::disable_record (bool force) } RecordStateChanged (); /* emit signal */ - remove_pending_capture_state (); + + if (!rt_context) { + remove_pending_capture_state (); + } } } void Session::step_back_from_record () { - atomic_set (&_record_status, Enabled); + g_atomic_int_set (&_record_status, Enabled); - if (Config->get_use_hardware_monitoring()) { - /* Even though this can be called from RT context we are using - a non-tentative rwlock here, because the action must occur. - The rarity and short potential lock duration makes this "OK" - */ - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); + if (Config->get_monitoring_model() == HardwareMonitoring) { + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - if (auto_input && (*i)->record_enabled ()) { + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + if (Config->get_auto_input() && (*i)->record_enabled ()) { //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl; (*i)->monitor_input (false); } @@ -1327,16 +1244,16 @@ Session::step_back_from_record () void Session::maybe_enable_record () { - atomic_set (&_record_status, Enabled); + g_atomic_int_set (&_record_status, Enabled); - /* XXX this save should really happen in another thread. its needed so that - pending capture state can be recovered if we crash. + /* this function is currently called from somewhere other than an RT thread. + this save_state() call therefore doesn't impact anything. */ save_state ("", true); if (_transport_speed) { - if (!punch_in) { + if (!Config->get_punch_in()) { enable_record (); } } else { @@ -1347,12 +1264,12 @@ Session::maybe_enable_record () set_dirty(); } -jack_nframes_t +nframes_t Session::audible_frame () const { - jack_nframes_t ret; - jack_nframes_t offset; - jack_nframes_t tf; + nframes_t ret; + nframes_t offset; + nframes_t tf; /* the first of these two possible settings for "offset" mean that the audible frame is stationary until @@ -1406,27 +1323,31 @@ Session::audible_frame () const } void -Session::set_frame_rate (jack_nframes_t frames_per_second) +Session::set_frame_rate (nframes_t frames_per_second) { - /** \fn void Session::set_frame_size(jack_nframes_t) + /** \fn void Session::set_frame_size(nframes_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 here. */ - _current_frame_rate = frames_per_second; - _frames_per_smpte_frame = (double) _current_frame_rate / (double) smpte_frames_per_second; + _base_frame_rate = frames_per_second; + + sync_time_vars(); Route::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * 0.25)); + // XXX we need some equivalent to this, somehow + // SndFileSource::setup_standard_crossfades (frames_per_second); + set_dirty(); /* XXX need to reset/reinstantiate all LADSPA plugins */ } void -Session::set_block_size (jack_nframes_t nframes) +Session::set_block_size (nframes_t nframes) { /* the AudioEngine guarantees that it will not be called while we are also in @@ -1435,8 +1356,6 @@ Session::set_block_size (jack_nframes_t nframes) */ { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); - RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__); vector::iterator i; uint32_t np; @@ -1477,15 +1396,18 @@ Session::set_block_size (jack_nframes_t nframes) allocate_pan_automation_buffers (nframes, _npan_buffers, true); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_block_size (nframes); } - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + boost::shared_ptr dsl = diskstreams.reader(); + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { (*i)->set_block_size (nframes); } - set_worst_io_latencies (false); + set_worst_io_latencies (); } } @@ -1493,7 +1415,7 @@ void Session::set_default_fade (float steepness, float fade_msecs) { #if 0 - jack_nframes_t fade_frames; + nframes_t fade_frames; /* Don't allow fade of less 1 frame */ @@ -1504,7 +1426,7 @@ Session::set_default_fade (float steepness, float fade_msecs) } else { - fade_frames = (jack_nframes_t) floor (fade_msecs * _current_frame_rate * 0.001); + fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001); } @@ -1513,7 +1435,7 @@ Session::set_default_fade (float steepness, float fade_msecs) { // jlc, WTF is this! - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); + Glib::RWLock::ReaderLock lm (route_lock); AudioRegion::set_default_fade (steepness, fade_frames); } @@ -1527,7 +1449,7 @@ Session::set_default_fade (float steepness, float fade_msecs) } struct RouteSorter { - bool operator() (Route* r1, Route* r2) { + bool operator() (boost::shared_ptr r1, boost::shared_ptr r2) { if (r1->fed_by.find (r2) != r1->fed_by.end()) { return false; } else if (r2->fed_by.find (r1) != r2->fed_by.end()) { @@ -1549,9 +1471,9 @@ struct RouteSorter { }; static void -trace_terminal (Route* r1, Route* rbase) +trace_terminal (shared_ptr r1, shared_ptr rbase) { - Route* r2; + shared_ptr r2; if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) { info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg; @@ -1560,13 +1482,13 @@ trace_terminal (Route* r1, Route* rbase) /* make a copy of the existing list of routes that feed r1 */ - set existing = r1->fed_by; + set > existing = r1->fed_by; /* for each route that feeds r1, recurse, marking it as feeding rbase as well. */ - for (set::iterator i = existing.begin(); i != existing.end(); ++i) { + for (set >::iterator i = existing.begin(); i != existing.end(); ++i) { r2 =* i; /* r2 is a route that feeds r1 which somehow feeds base. mark @@ -1596,7 +1518,7 @@ trace_terminal (Route* r1, Route* rbase) } void -Session::resort_routes (void* src) +Session::resort_routes () { /* don't do anything here with signals emitted by Routes while we are being destroyed. @@ -1606,67 +1528,85 @@ Session::resort_routes (void* src) return; } - /* Caller MUST hold the route_lock */ - RouteList::iterator i, j; + { - for (i = routes.begin(); i != routes.end(); ++i) { + RCUWriter writer (routes); + shared_ptr r = writer.get_copy (); + resort_routes_using (r); + /* writer goes out of scope and forces update */ + } +} +void +Session::resort_routes_using (shared_ptr r) +{ + RouteList::iterator i, j; + + for (i = r->begin(); i != r->end(); ++i) { + (*i)->fed_by.clear (); - for (j = routes.begin(); j != routes.end(); ++j) { - + for (j = r->begin(); j != r->end(); ++j) { + /* although routes can feed themselves, it will cause an endless recursive descent if we detect it. so don't bother checking for self-feeding. */ - + if (*j == *i) { continue; } - + if ((*j)->feeds (*i)) { (*i)->fed_by.insert (*j); } } } - for (i = routes.begin(); i != routes.end(); ++i) { + for (i = r->begin(); i != r->end(); ++i) { trace_terminal (*i, *i); - } + } RouteSorter cmp; - routes.sort (cmp); + r->sort (cmp); + + /* don't leave dangling references to routes in Route::fed_by */ + + for (i = r->begin(); i != r->end(); ++i) { + (*i)->fed_by.clear (); + } #if 0 cerr << "finished route resort\n"; - for (i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl; } cerr << endl; #endif - + } -AudioTrack* -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode) +list > +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many) { - AudioTrack *track; char track_name[32]; + uint32_t track_id = 0; uint32_t n = 0; uint32_t channels_used = 0; string port; - uint32_t nphysical_in; - uint32_t nphysical_out; + RouteList new_routes; + list > ret; /* count existing audio tracks */ { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { - if (dynamic_cast(*i) != 0) { + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (dynamic_cast((*i).get()) != 0) { if (!(*i)->hidden()) { n++; channels_used += (*i)->n_inputs(); @@ -1675,235 +1615,287 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod } } - /* check for duplicate route names, since we might have pre-existing - routes with this name (e.g. create Audio1, Audio2, delete Audio1, - save, close,restart,add new route - first named route is now - Audio2) - */ + vector physinputs; + vector physoutputs; + uint32_t nphysical_in; + uint32_t nphysical_out; - do { - snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, n+1); - if (route_by_name (track_name) == 0) { - break; - } - n++; + _engine.get_physical_outputs (physoutputs); + _engine.get_physical_inputs (physinputs); - } while (n < (UINT_MAX-1)); + while (how_many) { - if (input_auto_connect & AutoConnectPhysical) { - nphysical_in = n_physical_inputs; - } else { - nphysical_in = 0; - } + /* check for duplicate route names, since we might have pre-existing + routes with this name (e.g. create Audio1, Audio2, delete Audio1, + save, close,restart,add new route - first named route is now + Audio2) + */ + - if (output_auto_connect & AutoConnectPhysical) { - nphysical_out = n_physical_outputs; - } else { - nphysical_out = 0; - } + do { + ++track_id; - try { - track = new AudioTrack (*this, track_name, Route::Flag (0), mode); + snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id); - if (track->ensure_io (input_channels, output_channels, false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - } + if (route_by_name (track_name) == 0) { + break; + } + + } while (track_id < (UINT_MAX-1)); - if (nphysical_in) { - for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) { + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size()); + } else { + nphysical_in = 0; + } + + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size()); + } else { + nphysical_out = 0; + } + + try { + shared_ptr track (new AudioTrack (*this, track_name, Route::Flag (0), mode)); + + if (track->ensure_io (input_channels, output_channels, false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + } + + if (nphysical_in) { + for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) { + + port = ""; + + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + port = physinputs[(channels_used+x)%nphysical_in]; + } + + if (port.length() && track->connect_input (track->input (x), port, this)) { + break; + } + } + } + + for (uint32_t x = 0; x < track->n_outputs(); ++x) { port = ""; - if (input_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((channels_used+x)%nphysical_in); - } + if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { + port = physoutputs[(channels_used+x)%nphysical_out]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs())->name(); + } + } - if (port.length() && track->connect_input (track->input (x), port, this)) { + if (port.length() && track->connect_output (track->output (x), port, this)) { break; } } - } - - for (uint32_t x = 0; x < track->n_outputs(); ++x) { - port = ""; + channels_used += track->n_inputs (); - if (nphysical_out && (output_auto_connect & AutoConnectPhysical)) { - port = _engine.get_nth_physical_output ((channels_used+x)%nphysical_out); - } else if (output_auto_connect & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs())->name(); + if (_control_out) { + vector cports; + uint32_t ni = _control_out->n_inputs(); + + for (n = 0; n < ni; ++n) { + cports.push_back (_control_out->input(n)->name()); } + + track->set_control_outs (cports); } + + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); + track->set_remote_control_id (ntracks()); - if (port.length() && track->connect_output (track->output (x), port, this)) { - break; - } + new_routes.push_back (track); + ret.push_back (track); } - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs(); - - for (n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - - track->set_control_outs (cports); + catch (failed_constructor &err) { + error << _("Session: could not create new audio track.") << endmsg; + // XXX should we delete the tracks already created? + ret.clear (); + return ret; } - - track->diskstream_changed.connect (mem_fun (this, &Session::resort_routes)); - - add_route (track); - - track->set_remote_control_id (ntracks()); + + --how_many; } - catch (failed_constructor &err) { - error << _("Session: could not create new audio track.") << endmsg; - return 0; + if (!new_routes.empty()) { + add_routes (new_routes, false); + save_state (_current_snapshot_name); } - return track; + return ret; } -Route* -Session::new_audio_route (int input_channels, int output_channels) +Session::RouteList +Session::new_audio_route (int input_channels, int output_channels, uint32_t how_many) { - Route *bus; char bus_name[32]; + uint32_t bus_id = 1; uint32_t n = 0; string port; + RouteList ret; /* count existing audio busses */ { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { - if (dynamic_cast(*i) == 0) { + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (dynamic_cast((*i).get()) == 0) { if (!(*i)->hidden()) { - n++; + bus_id++; } } } } - do { - snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, n+1); - if (route_by_name (bus_name) == 0) { - break; - } - n++; + vector physinputs; + vector physoutputs; - } while (n < (UINT_MAX-1)); + _engine.get_physical_outputs (physoutputs); + _engine.get_physical_inputs (physinputs); - try { - bus = new Route (*this, bus_name, -1, -1, -1, -1); + while (how_many) { - if (bus->ensure_io (input_channels, output_channels, false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - } + do { + ++bus_id; - for (uint32_t x = 0; x < bus->n_inputs(); ++x) { - - port = ""; + snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id); - if (input_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((n+x)%n_physical_inputs); - } - - if (port.length() && bus->connect_input (bus->input (x), port, this)) { + if (route_by_name (bus_name) == 0) { break; } - } - for (uint32_t x = 0; x < bus->n_outputs(); ++x) { - - port = ""; + } while (bus_id < (UINT_MAX-1)); - if (output_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((n+x)%n_physical_outputs); - } else if (output_auto_connect & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs())->name(); - } + try { + shared_ptr bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); + + if (bus->ensure_io (input_channels, output_channels, false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; } + + for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs(); ++x) { + + port = ""; - if (port.length() && bus->connect_output (bus->output (x), port, this)) { - break; + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + port = physinputs[((n+x)%n_physical_inputs)]; + } + + if (port.length() && bus->connect_input (bus->input (x), port, this)) { + break; + } + } + + for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs(); ++x) { + + port = ""; + + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + port = physoutputs[((n+x)%n_physical_outputs)]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs())->name(); + } + } + + if (port.length() && bus->connect_output (bus->output (x), port, this)) { + break; + } + } + + if (_control_out) { + vector cports; + uint32_t ni = _control_out->n_inputs(); + + for (uint32_t n = 0; n < ni; ++n) { + cports.push_back (_control_out->input(n)->name()); + } + bus->set_control_outs (cports); } - } - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs(); + ret.push_back (bus); + } + - for (uint32_t n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - bus->set_control_outs (cports); + catch (failed_constructor &err) { + error << _("Session: could not create new audio route.") << endmsg; + ret.clear (); + return ret; } - - add_route (bus); + + --how_many; } - catch (failed_constructor &err) { - error << _("Session: could not create new route.") << endmsg; - return 0; + if (!ret.empty()) { + add_routes (ret, false); + save_state (_current_snapshot_name); } - return bus; + return ret; + } void -Session::add_route (Route* route) +Session::add_routes (RouteList& new_routes, bool save) { { - RWLockMonitor lm (route_lock, true, __LINE__, __FILE__); - routes.push_front (route); - resort_routes(0); + RCUWriter writer (routes); + shared_ptr r = writer.get_copy (); + r->insert (r->end(), new_routes.begin(), new_routes.end()); + resort_routes_using (r); } - route->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), route)); - route->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed)); - route->output_changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); - route->redirects_changed.connect (mem_fun (*this, &Session::update_latency_compensation_proxy)); - - if (route->master()) { - _master_out = route; - } + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { + + boost::weak_ptr wpr (*x); - if (route->control()) { - _control_out = route; + (*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr)); + (*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed)); + (*x)->output_changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); + (*x)->redirects_changed.connect (mem_fun (*this, &Session::update_latency_compensation_proxy)); + + if ((*x)->master()) { + _master_out = (*x); + } + + if ((*x)->control()) { + _control_out = (*x); + } } set_dirty(); - save_state (_current_snapshot_name); - RouteAdded (route); /* EMIT SIGNAL */ + if (save) { + save_state (_current_snapshot_name); + } + + RouteAdded (new_routes); /* EMIT SIGNAL */ } void -Session::add_diskstream (DiskStream* dstream) +Session::add_diskstream (boost::shared_ptr dstream) { /* need to do this in case we're rolling at the time, to prevent false underruns */ - dstream->do_refill(0, 0, 0); + dstream->do_refill_with_alloc(); { - RWLockMonitor lm (diskstream_lock, true, __LINE__, __FILE__); - diskstreams.push_back (dstream); + RCUWriter writer (diskstreams); + boost::shared_ptr ds = writer.get_copy(); + ds->push_back (dstream); } - /* take a reference to the diskstream, preventing it from - ever being deleted until the session itself goes away, - or chooses to remove it for its own purposes. - */ - - dstream->ref(); dstream->set_block_size (current_block_size); dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream)); @@ -1911,65 +1903,84 @@ Session::add_diskstream (DiskStream* dstream) diskstream_playlist_changed (dstream); dstream->prepare (); - - set_dirty(); - save_state (_current_snapshot_name); - - DiskStreamAdded (dstream); /* EMIT SIGNAL */ } void -Session::remove_route (Route& route) +Session::remove_route (shared_ptr route) { { - RWLockMonitor lm (route_lock, true, __LINE__, __FILE__); - routes.remove (&route); + RCUWriter writer (routes); + shared_ptr rs = writer.get_copy (); + rs->remove (route); + /* deleting the master out seems like a dumb idea, but its more of a UI policy issue than our concern. */ - if (&route == _master_out) { - _master_out = 0; + if (route == _master_out) { + _master_out = shared_ptr (); } - if (&route == _control_out) { - _control_out = 0; + if (route == _control_out) { + _control_out = shared_ptr (); /* cancel control outs for all routes */ vector empty; - for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) { (*r)->set_control_outs (empty); } } update_route_solo_state (); + + /* writer goes out of scope, forces route list update */ } - { - RWLockMonitor lm (diskstream_lock, true, __LINE__, __FILE__); - - AudioTrack* at; + // FIXME: audio specific + AudioTrack* at; + boost::shared_ptr ds; + + if ((at = dynamic_cast(route.get())) != 0) { + ds = at->audio_diskstream(); + } + + if (ds) { - if ((at = dynamic_cast(&route)) != 0) { - diskstreams.remove (&at->disk_stream()); - at->disk_stream().unref (); + { + RCUWriter dsl (diskstreams); + boost::shared_ptr d = dsl.get_copy(); + d->remove (ds); } - - find_current_end (); } + + find_current_end (); update_latency_compensation (false, false); set_dirty(); + + // We need to disconnect the routes inputs and outputs + route->disconnect_inputs(NULL); + route->disconnect_outputs(NULL); - /* XXX should we disconnect from the Route's signals ? */ + /* get rid of it from the dead wood collection in the route list manager */ + + /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */ + + routes.flush (); - save_state (_current_snapshot_name); + /* try to cause everyone to drop their references */ - delete &route; + route->drop_references (); + + /* save the new state of the world */ + + if (save_state (_current_snapshot_name)) { + save_history (_current_snapshot_name); + } } void @@ -1979,19 +1990,27 @@ Session::route_mute_changed (void* src) } void -Session::route_solo_changed (void* src, Route* route) +Session::route_solo_changed (void* src, boost::weak_ptr wpr) { if (solo_update_disabled) { // We know already return; } - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); bool is_track; + boost::shared_ptr 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; + } + + is_track = (boost::dynamic_pointer_cast(route) != 0); - is_track = (dynamic_cast(route) != 0); - - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { /* soloing a track mutes all other tracks, soloing a bus mutes all other busses */ @@ -1999,7 +2018,7 @@ Session::route_solo_changed (void* src, Route* route) /* don't mess with busses */ - if (dynamic_cast(*i) == 0) { + if (dynamic_cast((*i).get()) == 0) { continue; } @@ -2007,7 +2026,7 @@ Session::route_solo_changed (void* src, Route* route) /* don't mess with tracks */ - if (dynamic_cast(*i) != 0) { + if (dynamic_cast((*i).get()) != 0) { continue; } } @@ -2023,7 +2042,7 @@ Session::route_solo_changed (void* src, Route* route) then leave it as it is. */ - if (_solo_latched) { + if (Config->get_solo_latched()) { continue; } } @@ -2040,10 +2059,10 @@ Session::route_solo_changed (void* src, Route* route) bool same_thing_soloed = false; bool signal = false; - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->soloed()) { something_soloed = true; - if (dynamic_cast(*i)) { + if (dynamic_cast((*i).get())) { if (is_track) { same_thing_soloed = true; break; @@ -2072,16 +2091,6 @@ Session::route_solo_changed (void* src, Route* route) set_dirty(); } -void -Session::set_solo_latched (bool yn) -{ - if (yn != _solo_latched) { - _solo_latched = yn; - set_dirty (); - ControlChanged (SoloLatch); - } -} - void Session::update_route_solo_state () { @@ -2094,11 +2103,13 @@ Session::update_route_solo_state () /* this is where we actually implement solo by changing the solo mute setting of each track. */ - - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->soloed()) { mute = true; - if (dynamic_cast(*i)) { + if (dynamic_cast((*i).get())) { is_track = true; } break; @@ -2114,7 +2125,7 @@ Session::update_route_solo_state () /* nothing is soloed */ - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_solo_mute (false); } @@ -2135,13 +2146,15 @@ Session::update_route_solo_state () void Session::modify_solo_mute (bool is_track, bool mute) { - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (is_track) { /* only alter track solo mute */ - if (dynamic_cast(*i)) { + if (dynamic_cast((*i).get())) { if ((*i)->soloed()) { (*i)->set_solo_mute (!mute); } else { @@ -2153,7 +2166,7 @@ Session::modify_solo_mute (bool is_track, bool mute) /* only alter bus solo mute */ - if (!dynamic_cast(*i)) { + if (!dynamic_cast((*i).get())) { if ((*i)->soloed()) { @@ -2185,45 +2198,60 @@ Session::catch_up_on_solo () basis, but needs the global overview that only the session has. */ - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); update_route_solo_state(); } -Route * +shared_ptr Session::route_by_name (string name) { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); + shared_ptr r = routes.reader (); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == name) { - return* i; + return *i; } } - return 0; + return shared_ptr ((Route*) 0); +} + +shared_ptr +Session::route_by_id (PBD::ID id) +{ + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->id() == id) { + return *i; + } + } + + return shared_ptr ((Route*) 0); +} + +shared_ptr +Session::route_by_remote_id (uint32_t id) +{ + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->remote_control_id() == id) { + return *i; + } + } + + return shared_ptr ((Route*) 0); } void Session::find_current_end () { - jack_nframes_t max = 0; - jack_nframes_t me; - if (_state_of_the_state & Loading) { return; } - /* Don't take the diskstream lock. Caller must have other ways to - ensure atomicity. - */ + nframes_t max = get_maximum_extent (); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - Playlist* pl = (*i)->playlist(); - if ((me = pl->get_maximum_extent()) > max) { - max = me; - } - } - if (max > end_location->end()) { end_location->set_end (max); set_dirty(); @@ -2231,32 +2259,50 @@ Session::find_current_end () } } -DiskStream * +nframes_t +Session::get_maximum_extent () const +{ + nframes_t max = 0; + nframes_t me; + + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { + Playlist* pl = (*i)->playlist(); + if ((me = pl->get_maximum_extent()) > max) { + max = me; + } + } + + return max; +} + +boost::shared_ptr Session::diskstream_by_name (string name) { - RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__); + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->name() == name) { - return* i; + return *i; } } - return 0; + return boost::shared_ptr((Diskstream*) 0); } -DiskStream * -Session::diskstream_by_id (id_t id) +boost::shared_ptr +Session::diskstream_by_id (const PBD::ID& id) { - RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__); + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->id() == id) { return *i; } } - return 0; + return boost::shared_ptr((Diskstream*) 0); } /* AudioRegion management */ @@ -2294,7 +2340,7 @@ Session::new_region_name (string old) sbuf = buf; for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { - if ((*i).second->name() == sbuf) { + if (i->second->name() == sbuf) { break; } } @@ -2320,7 +2366,7 @@ Session::region_name (string& result, string base, bool newlevel) const if (base == "") { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); snprintf (buf, sizeof (buf), "%d", (int)audio_regions.size() + 1); @@ -2337,19 +2383,18 @@ Session::region_name (string& result, string base, bool newlevel) const } else { string::size_type pos; - if ((pos = base.find_last_of ('-')) == string::npos) { - pos = base.find_last_of ('.'); - } + pos = base.find_last_of ('.'); /* pos may be npos, but then we just use entire base */ subbase = base.substr (0, pos); + } - + bool name_taken = true; { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); for (int n = 1; n < 5000; ++n) { @@ -2360,7 +2405,7 @@ Session::region_name (string& result, string base, bool newlevel) const name_taken = false; for (AudioRegionList::const_iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) { - if ((*i).second->name() == result) { + if (i->second->name() == result) { name_taken = true; break; } @@ -2377,41 +2422,41 @@ Session::region_name (string& result, string base, bool newlevel) const /*NOTREACHED*/ } } - return 0; } void -Session::add_region (Region* region) +Session::add_region (boost::shared_ptr region) { - AudioRegion* ar = 0; - AudioRegion* oar = 0; + boost::shared_ptr ar; + boost::shared_ptr oar; bool added = false; { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); - if ((ar = dynamic_cast (region)) != 0) { + if ((ar = boost::dynamic_pointer_cast (region)) != 0) { AudioRegionList::iterator x; for (x = audio_regions.begin(); x != audio_regions.end(); ++x) { - oar = dynamic_cast (x->second); + oar = boost::dynamic_pointer_cast (x->second); - if (ar->region_list_equivalent (*oar)) { + if (ar->region_list_equivalent (oar)) { break; } } if (x == audio_regions.end()) { - pair entry; - + pair entry; + entry.first = region->id(); entry.second = ar; pair x = audio_regions.insert (entry); + if (!x.second) { return; @@ -2437,15 +2482,21 @@ Session::add_region (Region* region) set_dirty(); if (added) { - region->GoingAway.connect (mem_fun (*this, &Session::remove_region)); - region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), region)); + region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr(region))); + region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr(region))); AudioRegionAdded (ar); /* EMIT SIGNAL */ } } void -Session::region_changed (Change what_changed, Region* region) +Session::region_changed (Change what_changed, boost::weak_ptr weak_region) { + boost::shared_ptr region (weak_region.lock ()); + + if (!region) { + return; + } + if (what_changed & Region::HiddenChanged) { /* relay hidden changes */ RegionHiddenChange (region); @@ -2453,27 +2504,29 @@ Session::region_changed (Change what_changed, Region* region) } void -Session::region_renamed (Region* region) -{ - add_region (region); -} - -void -Session::remove_region (Region* region) +Session::remove_region (boost::weak_ptr weak_region) { AudioRegionList::iterator i; - AudioRegion* ar = 0; + boost::shared_ptr region (weak_region.lock ()); + + if (!region) { + return; + } + + boost::shared_ptr ar; bool removed = false; { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); - if ((ar = dynamic_cast (region)) != 0) { + if ((ar = boost::dynamic_pointer_cast (region)) != 0) { if ((i = audio_regions.find (region->id())) != audio_regions.end()) { audio_regions.erase (i); removed = true; - } + } + } else { + fatal << _("programming error: ") << X_("unknown region type passed to Session::remove_region()") << endmsg; @@ -2488,71 +2541,74 @@ Session::remove_region (Region* region) set_dirty(); if (removed) { - AudioRegionRemoved(ar); /* EMIT SIGNAL */ + AudioRegionRemoved (ar); /* EMIT SIGNAL */ } } -AudioRegion* -Session::find_whole_file_parent (AudioRegion& child) +boost::shared_ptr +Session::find_whole_file_parent (boost::shared_ptr child) { AudioRegionList::iterator i; - AudioRegion* region; - LockMonitor lm (region_lock, __LINE__, __FILE__); + boost::shared_ptr region; + Glib::Mutex::Lock lm (region_lock); for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { - region = (*i).second; + region = i->second; if (region->whole_file()) { - if (child.source_equivalent (*region)) { + if (child->source_equivalent (region)) { return region; } } } - return 0; + return boost::shared_ptr (); } void -Session::find_equivalent_playlist_regions (AudioRegion& region, vector& result) +Session::find_equivalent_playlist_regions (boost::shared_ptr region, vector >& result) { - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { - - AudioPlaylist* pl; - - if ((pl = dynamic_cast(*i)) == 0) { - continue; - } - - pl->get_region_list_equivalent_regions (region, result); - } + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) + (*i)->get_region_list_equivalent_regions (region, result); } int -Session::destroy_region (Region* region) +Session::destroy_region (boost::shared_ptr region) { - AudioRegion* aregion; - - if ((aregion = dynamic_cast (region)) == 0) { - return 0; + vector > srcs; + + { + boost::shared_ptr aregion; + + if ((aregion = boost::dynamic_pointer_cast (region)) == 0) { + return 0; + } + + if (aregion->playlist()) { + aregion->playlist()->destroy_region (region); + } + + for (uint32_t n = 0; n < aregion->n_channels(); ++n) { + srcs.push_back (aregion->source (n)); + } } - if (aregion->playlist()) { - aregion->playlist()->destroy_region (region); - } + region->drop_references (); - vector srcs; - - for (uint32_t n = 0; n < aregion->n_channels(); ++n) { - srcs.push_back (&aregion->source (n)); - } + for (vector >::iterator i = srcs.begin(); i != srcs.end(); ++i) { - for (vector::iterator i = srcs.begin(); i != srcs.end(); ++i) { - - if ((*i)->use_cnt() == 0) { - (*i)->mark_for_remove (); - delete *i; + if (!(*i)->used()) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*i); + + if (afs) { + (afs)->mark_for_remove (); + } + + (*i)->drop_references (); + + cerr << "source was not used by any playlist\n"; } } @@ -2560,9 +2616,9 @@ Session::destroy_region (Region* region) } int -Session::destroy_regions (list regions) +Session::destroy_regions (list > regions) { - for (list::iterator i = regions.begin(); i != regions.end(); ++i) { + for (list >::iterator i = regions.begin(); i != regions.end(); ++i) { destroy_region (*i); } return 0; @@ -2571,12 +2627,12 @@ Session::destroy_regions (list regions) int Session::remove_last_capture () { - list r; - - RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__); + list > r; + + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - list& l = (*i)->last_capture_regions(); + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + list >& l = (*i)->last_capture_regions(); if (!l.empty()) { r.insert (r.end(), l.begin(), l.end()); @@ -2589,73 +2645,219 @@ Session::remove_last_capture () } int -Session::remove_region_from_region_list (Region& r) +Session::remove_region_from_region_list (boost::shared_ptr r) { - remove_region (&r); + remove_region (r); return 0; } /* Source Management */ void -Session::add_source (Source* source) +Session::add_source (boost::shared_ptr source) { - pair entry; - - { - LockMonitor lm (source_lock, __LINE__, __FILE__); + boost::shared_ptr afs; + + if ((afs = boost::dynamic_pointer_cast(source)) != 0) { + + pair entry; + pair result; + entry.first = source->id(); - entry.second = source; - sources.insert (entry); + entry.second = afs; + + { + Glib::Mutex::Lock lm (audio_source_lock); + result = audio_sources.insert (entry); + } + + if (!result.second) { + cerr << "\tNOT inserted ? " << result.second << endl; + } + + source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); + set_dirty(); + + SourceAdded (source); /* EMIT SIGNAL */ + } else { + cerr << "\tNOT AUDIO FILE\n"; } - - source->GoingAway.connect (mem_fun (this, &Session::remove_source)); - set_dirty(); - - SourceAdded (source); /* EMIT SIGNAL */ } void -Session::remove_source (Source* source) +Session::remove_source (boost::weak_ptr src) { - SourceList::iterator i; + AudioSourceList::iterator i; + boost::shared_ptr source = src.lock(); - { - LockMonitor lm (source_lock, __LINE__, __FILE__); + if (!source) { + return; + } - if ((i = sources.find (source->id())) != sources.end()) { - sources.erase (i); - } + { + Glib::Mutex::Lock lm (audio_source_lock); + + if ((i = audio_sources.find (source->id())) != audio_sources.end()) { + audio_sources.erase (i); + } } - + if (!_state_of_the_state & InCleanup) { - + /* save state so we don't end up with a session file referring to non-existent sources. */ save_state (_current_snapshot_name); } - + SourceRemoved(source); /* EMIT SIGNAL */ } -Source * -Session::get_source (ARDOUR::id_t id) +boost::shared_ptr +Session::source_by_id (const PBD::ID& id) { - LockMonitor lm (source_lock, __LINE__, __FILE__); - SourceList::iterator i; - Source* source = 0; + Glib::Mutex::Lock lm (audio_source_lock); + AudioSourceList::iterator i; + boost::shared_ptr source; - if ((i = sources.find (id)) != sources.end()) { - source = (*i).second; + if ((i = audio_sources.find (id)) != audio_sources.end()) { + source = i->second; } + /* XXX search MIDI or other searches here */ + return source; } -FileSource * -Session::create_file_source (DiskStream& ds, int32_t chan, bool destructive) +string +Session::peak_path_from_audio_path (string audio_path) const +{ + string res; + + res = peak_dir (); + res += PBD::basename_nosuffix (audio_path); + res += ".peak"; + + return res; +} + +string +Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive) +{ + string look_for; + string old_basename = PBD::basename_nosuffix (oldname); + string new_legalized = legalize_for_path (newname); + + /* note: we know (or assume) the old path is already valid */ + + if (destructive) { + + /* destructive file sources have a name of the form: + + /path/to/Tnnnn-NAME(%[LR])?.wav + + 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); + + /* '-' is not a legal character for the NAME part of the path */ + + if ((dash = path.find_last_of ('-')) == string::npos) { + return ""; + } + + prefix = path.substr (slash+1, dash-(slash+1)); + + path = dir; + path += prefix; + path += '-'; + path += new_legalized; + path += ".wav"; /* XXX gag me with a spoon */ + + } else { + + /* non-destructive file sources have a name of the form: + + /path/to/NAME-nnnnn(%[LR])?.wav + + the task here is to replace NAME with the new name. + */ + + 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); + + /* '-' is not a legal character for the NAME part of the path */ + + if ((dash = path.find_last_of ('-')) == string::npos) { + return ""; + } + + suffix = path.substr (dash+1); + + // Suffix is now everything after the dash. Now we need to eliminate + // the nnnnn part, which is done by either finding a '%' or a '.' + + postfix = suffix.find_last_of ("%"); + if (postfix == string::npos) { + postfix = suffix.find_last_of ('.'); + } + + if (postfix != string::npos) { + suffix = suffix.substr (postfix); + } else { + error << "Logic error in Session::change_audio_path_by_name(), please report to the developers" << endl; + return ""; + } + + const uint32_t limit = 10000; + char buf[PATH_MAX+1]; + + 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()); + + if (access (buf, F_OK) != 0) { + path = buf; + break; + } + path = ""; + } + + if (path == "") { + error << "FATAL ERROR! Could not find a " << endl; + } + + } + + return path; +} + +string +Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive) { string spath; uint32_t cnt; @@ -2664,13 +2866,13 @@ Session::create_file_source (DiskStream& ds, int32_t chan, bool destructive) string legalized; buf[0] = '\0'; - legalized = legalize_for_path (ds.name()); + legalized = legalize_for_path (name); /* find a "version" of the file name that doesn't exist in any of the possible directories. */ - - for (cnt = 1; cnt <= limit; ++cnt) { + + for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { vector::iterator i; uint32_t existing = 0; @@ -2679,41 +2881,56 @@ Session::create_file_source (DiskStream& ds, int32_t chan, bool destructive) spath = (*i).path; + spath += sound_dir (false); + if (destructive) { - spath += tape_dir_name; - } else { - spath += sound_dir_name; - } - spath += '/'; - spath += legalized; - - if (ds.n_channels() < 2) { - snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); - } else if (ds.n_channels() == 2) { - if (chan == 0) { - snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt); + if (nchan < 2) { + snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.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()); + } else { + snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", spath.c_str(), cnt, legalized.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); } else { - snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt); + snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); } - } else if (ds.n_channels() < 26) { - snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan); } else { - snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + + spath += '/'; + spath += legalized; + + if (nchan < 2) { + snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + } else if (nchan == 2) { + if (chan == 0) { + snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt); + } else { + snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt); + } + } else if (nchan < 26) { + snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan); + } else { + snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + } } - if (access (buf, F_OK) == 0) { + if (g_file_test (buf, G_FILE_TEST_EXISTS)) { existing++; - } + } + } if (existing == 0) { break; } - } - if (cnt > limit) { - error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, ds.name()) << endmsg; - throw failed_constructor(); + if (cnt > limit) { + error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg; + throw failed_constructor(); + } } /* we now have a unique name for the file, but figure out where to @@ -2732,33 +2949,22 @@ Session::create_file_source (DiskStream& ds, int32_t chan, bool destructive) spath += foo.substr (pos + 1); } - /* this might throw failed_constructor(), which is OK */ - - if (destructive) { - return new DestructiveFileSource (spath, frame_rate()); - } else { - return new FileSource (spath, frame_rate()); - } + return spath; } -/* Playlist management */ - -Playlist * -Session::get_playlist (string name) +boost::shared_ptr +Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) { - Playlist* ret = 0; - - if ((ret = playlist_by_name (name)) == 0) { - ret = new AudioPlaylist (*this, name); - } - - return ret; + string spath = audio_path_from_name (ds.name(), ds.n_channels(), chan, destructive); + return boost::dynamic_pointer_cast (SourceFactory::createWritable (*this, spath, destructive, frame_rate())); } +/* Playlist management */ + Playlist * Session::playlist_by_name (string name) { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (playlist_lock); for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { if ((*i)->name() == name) { return* i; @@ -2780,12 +2986,12 @@ Session::add_playlist (Playlist* playlist) } { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (playlist_lock); if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) { playlists.insert (playlists.begin(), playlist); // playlist->ref(); playlist->InUse.connect (mem_fun (*this, &Session::track_playlist)); - playlist->GoingAway.connect (mem_fun (*this, &Session::remove_playlist)); + playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), playlist)); } } @@ -2794,13 +3000,27 @@ Session::add_playlist (Playlist* playlist) PlaylistAdded (playlist); /* EMIT SIGNAL */ } +void +Session::get_playlists (vector& s) +{ + { + Glib::Mutex::Lock lm (playlist_lock); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + s.push_back (*i); + } + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + s.push_back (*i); + } + } +} + void Session::track_playlist (Playlist* pl, bool inuse) { PlaylistList::iterator x; { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (playlist_lock); if (!inuse) { //cerr << "shifting playlist to unused: " << pl->name() << endl; @@ -2832,7 +3052,7 @@ Session::remove_playlist (Playlist* playlist) } { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (playlist_lock); // cerr << "removing playlist: " << playlist->name() << endl; PlaylistList::iterator i; @@ -2856,7 +3076,7 @@ Session::remove_playlist (Playlist* playlist) } void -Session::set_audition (AudioRegion* r) +Session::set_audition (boost::shared_ptr r) { pending_audition_region = r; post_transport_work = PostTransportWork (post_transport_work | PostTransportAudition); @@ -2864,30 +3084,30 @@ Session::set_audition (AudioRegion* r) } void -Session::non_realtime_set_audition () +Session::audition_playlist () { - if (pending_audition_region == (AudioRegion*) 0xfeedface) { - auditioner->audition_current_playlist (); - } else if (pending_audition_region) { - auditioner->audition_region (*pending_audition_region); - } - pending_audition_region = 0; - AuditionActive (true); /* EMIT SIGNAL */ + Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); + ev->region.reset (); + queue_event (ev); } void -Session::audition_playlist () +Session::non_realtime_set_audition () { - Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); - ev->set_ptr ((void*) 0xfeedface); - queue_event (ev); + if (!pending_audition_region) { + auditioner->audition_current_playlist (); + } else { + auditioner->audition_region (pending_audition_region); + pending_audition_region.reset (); + } + AuditionActive (true); /* EMIT SIGNAL */ } void -Session::audition_region (AudioRegion& r) +Session::audition_region (boost::shared_ptr r) { Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); - ev->set_ptr (&r); + ev->region = r; queue_event (ev); } @@ -2896,12 +3116,12 @@ Session::cancel_audition () { if (auditioner->active()) { auditioner->cancel_audition (); - AuditionActive (false); /* EMIT SIGNAL */ + AuditionActive (false); /* EMIT SIGNAL */ } } bool -Session::RoutePublicOrderSorter::operator() (Route* a, Route* b) +Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost::shared_ptr b) { return a->order_key(N_("signal")) < b->order_key(N_("signal")); } @@ -2909,17 +3129,35 @@ Session::RoutePublicOrderSorter::operator() (Route* a, Route* b) void Session::remove_empty_sounds () { - PathScanner scanner; - string dir; - dir = sound_dir (); - - vector* possible_audiofiles = scanner (dir, "\\.wav$", false, true); + vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); + + Glib::Mutex::Lock lm (audio_source_lock); + regex_t compiled_tape_track_pattern; + int err; + + if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) { + + char msg[256]; + + regerror (err, &compiled_tape_track_pattern, msg, sizeof (msg)); + + error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg; + return; + } + for (vector::iterator i = possible_audiofiles->begin(); i != possible_audiofiles->end(); ++i) { + + /* never remove files that appear to be a tape track */ - if (FileSource::is_empty (*(*i))) { + if (regexec (&compiled_tape_track_pattern, (*i)->c_str(), 0, 0, 0) == 0) { + delete *i; + continue; + } + + if (AudioFileSource::is_empty (*this, *(*i))) { unlink ((*i)->c_str()); @@ -2944,50 +3182,14 @@ Session::is_auditioning () const } } - -string -Session::peak_path_from_audio_path (string audio_path) -{ - /* XXX hardly bombproof! fix me */ - - string res; - - res = PBD::dirname (audio_path); - res = PBD::dirname (res); - res += '/'; - res += peak_dir_name; - res += '/'; - res += PBD::basename_nosuffix (audio_path); - res += ".peak"; - - return res; -} - -string -Session::old_peak_path_from_audio_path (string audio_path) -{ - /* This is a hangover from when audio and peak files - lived in the same directory. We need it to to - be able to open old sessions. - */ - - /* XXX hardly bombproof! fix me */ - - string res = audio_path.substr (0, audio_path.find_last_of ('.')); - res += ".peak"; - return res; -} - void Session::set_all_solo (bool yn) { - { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); - - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { - if (!(*i)->hidden()) { - (*i)->set_solo (yn, this); - } + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->hidden()) { + (*i)->set_solo (yn, this); } } @@ -2997,13 +3199,11 @@ Session::set_all_solo (bool yn) void Session::set_all_mute (bool yn) { - { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); - - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { - if (!(*i)->hidden()) { - (*i)->set_mute (yn, this); - } + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->hidden()) { + (*i)->set_mute (yn, this); } } @@ -3013,10 +3213,11 @@ Session::set_all_mute (bool yn) uint32_t Session::n_diskstreams () const { - RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__); uint32_t n = 0; - for (DiskStreamList::const_iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { if (!(*i)->hidden()) { n++; } @@ -3024,17 +3225,6 @@ Session::n_diskstreams () const return n; } -void -Session::foreach_diskstream (void (DiskStream::*func)(void)) -{ - RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - if (!(*i)->hidden()) { - ((*i)->*func)(); - } - } -} - void Session::graph_reordered () { @@ -3046,16 +3236,15 @@ Session::graph_reordered () return; } - RWLockMonitor lm1 (route_lock, true, __LINE__, __FILE__); - RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__); - - resort_routes (0); + resort_routes (); /* force all diskstreams to update their capture offset values to reflect any changes in latencies within the graph. */ - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { (*i)->set_capture_offset (); } } @@ -3075,12 +3264,12 @@ Session::record_enable_all () void Session::record_enable_change_all (bool yn) { - RWLockMonitor lm1 (route_lock, false, __LINE__, __FILE__); + shared_ptr r = routes.reader (); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { AudioTrack* at; - if ((at = dynamic_cast(*i)) != 0) { + if ((at = dynamic_cast((*i).get())) != 0) { at->set_record_enable (yn, this); } } @@ -3112,7 +3301,7 @@ Session::add_redirect (Redirect* redirect) /*NOTREACHED*/ } - redirect->GoingAway.connect (mem_fun (*this, &Session::remove_redirect)); + redirect->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_redirect), redirect)); set_dirty(); } @@ -3131,7 +3320,9 @@ Session::remove_redirect (Redirect* redirect) } else if ((plugin_insert = dynamic_cast (insert)) != 0) { _plugin_inserts.remove (plugin_insert); } else { - fatal << _("programming error: unknown type of Insert deleted!") << endmsg; + fatal << string_compose (_("programming error: %1"), + X_("unknown type of Insert deleted!")) + << endmsg; /*NOTREACHED*/ } } else if ((send = dynamic_cast (redirect)) != 0) { @@ -3144,23 +3335,42 @@ Session::remove_redirect (Redirect* redirect) set_dirty(); } -jack_nframes_t +nframes_t Session::available_capture_duration () { - const double scale = 4096.0 / sizeof (Sample); - + float sample_bytes_on_disk; + + switch (Config->get_native_file_data_format()) { + case FormatFloat: + sample_bytes_on_disk = 4; + break; + + case FormatInt24: + sample_bytes_on_disk = 3; + break; + + default: + /* impossible, but keep some gcc versions happy */ + fatal << string_compose (_("programming error: %1"), + X_("illegal native file data format")) + << endmsg; + /*NOTREACHED*/ + } + + double scale = 4096.0 / sample_bytes_on_disk; + if (_total_free_4k_blocks * scale > (double) max_frames) { return max_frames; } - return (jack_nframes_t) floor (_total_free_4k_blocks * scale); + return (nframes_t) floor (_total_free_4k_blocks * scale); } void Session::add_connection (ARDOUR::Connection* connection) { { - LockMonitor (connection_lock, __LINE__, __FILE__); + Glib::Mutex::Lock guard (connection_lock); _connections.push_back (connection); } @@ -3175,7 +3385,7 @@ Session::remove_connection (ARDOUR::Connection* connection) bool removed = false; { - LockMonitor (connection_lock, __LINE__, __FILE__); + Glib::Mutex::Lock guard (connection_lock); ConnectionList::iterator i = find (_connections.begin(), _connections.end(), connection); if (i != _connections.end()) { @@ -3194,7 +3404,7 @@ Session::remove_connection (ARDOUR::Connection* connection) ARDOUR::Connection * Session::connection_by_name (string name) const { - LockMonitor lm (connection_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (connection_lock); for (ConnectionList::const_iterator i = _connections.begin(); i != _connections.end(); ++i) { if ((*i)->name() == name) { @@ -3205,23 +3415,6 @@ Session::connection_by_name (string name) const return 0; } -void -Session::set_edit_mode (EditMode mode) -{ - _edit_mode = mode; - - { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); - - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { - (*i)->set_edit_mode (mode); - } - } - - set_dirty (); - ControlChanged (EditingMode); /* EMIT SIGNAL */ -} - void Session::tempo_map_changed (Change ignored) { @@ -3268,9 +3461,15 @@ Session::ensure_passthru_buffers (uint32_t howmany) string Session::next_send_name () { - char buf[32]; - snprintf (buf, sizeof (buf), "send %" PRIu32, ++send_cnt); - return buf; + uint32_t cnt = 0; + + shared_ptr r = routes.reader (); + + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { + cnt += (*i)->count_sends (); + } + + return string_compose (_("send %1"), ++cnt); } string @@ -3286,7 +3485,7 @@ Session::next_insert_name () NamedSelection * Session::named_selection_by_name (string name) { - LockMonitor lm (named_selection_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (named_selection_lock); for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) { if ((*i)->name == name) { return* i; @@ -3299,7 +3498,7 @@ void Session::add_named_selection (NamedSelection* named_selection) { { - LockMonitor lm (named_selection_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (named_selection_lock); named_selections.insert (named_selections.begin(), named_selection); } @@ -3314,7 +3513,7 @@ Session::remove_named_selection (NamedSelection* named_selection) bool removed = false; { - LockMonitor lm (named_selection_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (named_selection_lock); NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection); @@ -3334,11 +3533,9 @@ Session::remove_named_selection (NamedSelection* named_selection) void Session::reset_native_file_format () { - // jlc - WHY take routelock? - //RWLockMonitor lm1 (route_lock, true, __LINE__, __FILE__); - RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__); + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { (*i)->reset_write_sources (false); } } @@ -3346,9 +3543,9 @@ Session::reset_native_file_format () bool Session::route_name_unique (string n) const { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); + shared_ptr r = routes.reader (); - for (RouteList::const_iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == n) { return false; } @@ -3358,30 +3555,20 @@ Session::route_name_unique (string n) const } int -Session::remove_file_source (FileSource& fs) +Session::cleanup_audio_file_source (boost::shared_ptr fs) { - return fs.move_to_trash (dead_sound_dir_name); + return fs->move_to_trash (dead_sound_dir_name); } uint32_t Session::n_playlists () const { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (playlist_lock); return playlists.size(); } void -Session::set_solo_model (SoloModel sm) -{ - if (sm != _solo_model) { - _solo_model = sm; - ControlChanged (SoloingModel); - set_dirty (); - } -} - -void -Session::allocate_pan_automation_buffers (jack_nframes_t nframes, uint32_t howmany, bool force) +Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force) { if (!force && howmany <= _npan_buffers) { return; @@ -3405,23 +3592,16 @@ Session::allocate_pan_automation_buffers (jack_nframes_t nframes, uint32_t howma _npan_buffers = howmany; } -void -Session::add_instant_xml (XMLNode& node, const std::string& dir) -{ - Stateful::add_instant_xml (node, dir); - Config->add_instant_xml (node, get_user_ardour_path()); -} - int Session::freeze (InterThreadInfo& itt) { - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); + shared_ptr r = routes.reader (); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { AudioTrack *at; - if ((at = dynamic_cast(*i)) != 0) { + if ((at = dynamic_cast((*i).get())) != 0) { /* XXX this is wrong because itt.progress will keep returning to zero at the start of every track. */ @@ -3433,28 +3613,29 @@ Session::freeze (InterThreadInfo& itt) } int -Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_t len, bool overwrite, vector& srcs, - InterThreadInfo& itt) +Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len, + bool overwrite, vector >& srcs, InterThreadInfo& itt) { int ret = -1; Playlist* playlist; - FileSource* fsource; + boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; string dir; uint32_t nchans; - jack_nframes_t position; - jack_nframes_t this_chunk; - jack_nframes_t to_do; + nframes_t position; + nframes_t this_chunk; + nframes_t to_do; vector buffers; - char * workbuf = 0; - const jack_nframes_t chunk_size = (256 * 1024)/4; - atomic_set (&processing_prohibited, 1); + // any bigger than this seems to cause stack overflows in called functions + const nframes_t chunk_size = (128 * 1024)/4; + + g_atomic_int_set (&processing_prohibited, 1); /* call tree *MUST* hold route_lock */ - if ((playlist = track.disk_stream().playlist()) == 0) { + if ((playlist = track.diskstream()->playlist()) == 0) { goto out; } @@ -3464,7 +3645,7 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ goto out; } - nchans = track.disk_stream().n_channels(); + nchans = track.audio_diskstream()->n_channels(); dir = discover_best_sound_dir (); @@ -3483,7 +3664,7 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ } try { - fsource = new FileSource (buf, frame_rate()); + fsource = boost::dynamic_pointer_cast (SourceFactory::createWritable (*this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -3491,7 +3672,7 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ goto out; } - srcs.push_back(fsource); + srcs.push_back (fsource); } /* XXX need to flush all redirects */ @@ -3511,20 +3692,22 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ buffers.push_back (b); } - workbuf = new char[chunk_size * 4]; - while (to_do && !itt.cancel) { this_chunk = min (to_do, chunk_size); - if (track.export_stuff (buffers, workbuf, nchans, start, this_chunk)) { + if (track.export_stuff (buffers, nchans, start, this_chunk)) { goto out; } uint32_t n = 0; - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { - if ((*src)->write (buffers[n], this_chunk, workbuf) != this_chunk) { - goto out; + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + + if (afs) { + if (afs->write (buffers[n], this_chunk) != this_chunk) { + goto out; + } } } @@ -3542,24 +3725,41 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ time (&now); xnow = localtime (&now); - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src) { - dynamic_cast((*src))->update_header (position, *xnow, now); + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + + if (afs) { + afs->update_header (position, *xnow, now); + } } /* build peakfile for new source */ - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src) { - dynamic_cast(*src)->build_peaks (); + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + if (afs) { + afs->build_peaks (); + } } - + + /* construct a region to represent the bounced material */ + + boost::shared_ptr aregion = RegionFactory::create (srcs, 0, srcs.front()->length(), + region_name_from_path (srcs.front()->name())); + ret = 0; } out: if (ret) { - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src) { - dynamic_cast(*src)->mark_for_remove (); - delete *src; + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + + if (afs) { + afs->mark_for_remove (); + } + + (*src)->drop_references (); } } @@ -3567,11 +3767,7 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ free(*i); } - if (workbuf) { - delete [] workbuf; - } - - atomic_set (&processing_prohibited, 0); + g_atomic_int_set (&processing_prohibited, 0); itt.done = true; @@ -3591,10 +3787,10 @@ uint32_t Session::ntracks () const { uint32_t n = 0; - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); + shared_ptr r = routes.reader (); - for (RouteList::const_iterator i = routes.begin(); i != routes.end(); ++i) { - if (dynamic_cast (*i)) { + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { + if (dynamic_cast ((*i).get())) { ++n; } } @@ -3606,10 +3802,10 @@ uint32_t Session::nbusses () const { uint32_t n = 0; - RWLockMonitor lm (route_lock, false, __LINE__, __FILE__); + shared_ptr r = routes.reader (); - for (RouteList::const_iterator i = routes.begin(); i != routes.end(); ++i) { - if (dynamic_cast (*i) == 0) { + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { + if (dynamic_cast ((*i).get()) == 0) { ++n; } } @@ -3618,22 +3814,14 @@ Session::nbusses () const } void -Session::set_layer_model (LayerModel lm) +Session::add_automation_list(AutomationList *al) { - if (lm != layer_model) { - layer_model = lm; - set_dirty (); - ControlChanged (LayeringModel); - } + automation_lists[al->id()] = al; } -void -Session::set_xfade_model (CrossfadeModel xm) +nframes_t +Session::compute_initial_length () { - if (xm != xfade_model) { - xfade_model = xm; - set_dirty (); - ControlChanged (CrossfadingModel); - } + return _engine.frame_rate() * 60 * 5; }