X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=eb9e1c69ca3e15b84e089befd56780ec82dacdb6;hb=f7563c2b158252339f98e38719cfc3e092ef7ac7;hp=a8ab2204dc4aabcd0a855484af1e42d489389a39;hpb=50ee09e80ff91bf0146e89728578d5e31aba23b7;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index a8ab2204dc..eb9e1c69ca 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -32,24 +32,29 @@ #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 -#include #include #include #include @@ -59,26 +64,36 @@ #include #include #include +#include #include #include #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 +101,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) @@ -243,27 +263,41 @@ Session::Session (AudioEngine &eng, string* mix_template) : _engine (eng), + _scratch_buffers(new BufferSet()), + _silent_buffers(new BufferSet()), + _send_buffers(new BufferSet()), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), pending_events (2048), - midi_requests (128), // the size of this should match the midi request pool size + //midi_requests (128), // the size of this should match the midi request pool size + _send_smpte_update (false), + 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"; + destroy (); + throw failed_constructor (); + } } if (second_stage_init (new_session)) { + destroy (); throw failed_constructor (); } @@ -273,6 +307,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,51 +323,73 @@ 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), + _scratch_buffers(new BufferSet()), + _silent_buffers(new BufferSet()), + _send_buffers(new BufferSet()), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), pending_events (2048), - midi_requests (16), + //midi_requests (16), + _send_smpte_update (false), + 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)) { + destroy (); + 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)) { + destroy (); throw failed_constructor (); } @@ -347,6 +405,12 @@ Session::Session (AudioEngine &eng, } Session::~Session () +{ + destroy (); +} + +void +Session::destroy () { /* if we got to here, leaving pending capture state around is a mistake. @@ -356,11 +420,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_midi_thread (); if (click_data && click_data != default_click) { delete [] click_data; @@ -372,30 +450,11 @@ Session::~Session () clear_clicks (); - if (_click_io) { - delete _click_io; - } - + delete _scratch_buffers; + delete _silent_buffers; + delete _send_buffers; - if (auditioner) { - delete auditioner; - } - - for (vector::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) { - free(*i); - } - - for (vector::iterator i = _silent_buffers.begin(); i != _silent_buffers.end(); ++i) { - free(*i); - } - - for (vector::iterator i = _send_buffers.begin(); i != _send_buffers.end(); ++i) { - free(*i); - } - - for (map::iterator i = _conversion_buffers.begin(); i != _conversion_buffers.end(); ++i) { - delete [] (i->second); - } + AudioDiskstream::free_working_buffers(); #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION @@ -420,64 +479,88 @@ Session::~Session () tmp = i; ++tmp; - delete *i; + (*i)->drop_references (); i = tmp; } + + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { + PlaylistList::iterator tmp; -#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)->drop_references (); + i = tmp; } + playlists.clear (); + unused_playlists.clear (); + #ifdef TRACK_DESTRUCTION - cerr << "delete routes\n"; + cerr << "delete regions\n"; #endif /* TRACK_DESTRUCTION */ - for (RouteList::iterator i = routes.begin(); i != routes.end(); ) { - RouteList::iterator tmp; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { + RegionList::iterator tmp; + tmp = i; ++tmp; - delete *i; + + cerr << "dropping refs on a region (" << i->second->name() << " @ " << i->second << ") with UC = " << i->second.use_count() << endl; + i->second->drop_references (); + cerr << "AFTER: UC = " << i->second.use_count() << endl; + i = tmp; } + regions.clear (); + #ifdef TRACK_DESTRUCTION - cerr << "delete diskstreams\n"; + cerr << "delete routes\n"; #endif /* TRACK_DESTRUCTION */ - for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ) { - DiskStreamList::iterator tmp; - - tmp = 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 */ + } - delete *i; + routes.flush (); - i = tmp; - } +#ifdef TRACK_DESTRUCTION + cerr << "delete diskstreams\n"; +#endif /* TRACK_DESTRUCTION */ + { + 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 (SourceMap::iterator i = sources.begin(); i != sources.end(); ) { + SourceMap::iterator tmp; tmp = i; ++tmp; - delete (*i).second; + i->second->drop_references (); i = tmp; } + sources.clear (); + #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; #endif /* TRACK_DESTRUCTION */ @@ -533,14 +616,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; @@ -549,18 +628,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 @@ -570,14 +643,14 @@ Session::when_engine_running () /* we don't want to run execute this again */ - first_time_running.disconnect (); - 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 (); @@ -592,7 +665,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) { @@ -600,7 +673,7 @@ Session::when_engine_running () if (_click_io->set_state (*child->children().front()) == 0) { - _clicking = click_requested; + _clicking = Config->get_clicking (); } else { @@ -612,13 +685,13 @@ Session::when_engine_running () /* default state for Click */ - first_physical_output = _engine.get_nth_physical_output (0); - + first_physical_output = _engine.get_nth_physical_output (DataType::AUDIO, 0); + if (first_physical_output.length()) { if (_click_io->add_output_port (first_physical_output, this)) { // relax, even though its an error } else { - _clicking = click_requested; + _clicking = Config->get_clicking (); } } } @@ -628,27 +701,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 */ - } - - if (auditioner == 0) { - - /* we delay creating the auditioner till now because - it makes its own connections to ports named - in the ARDOUR_RC config file. the engine has - to be running for this to work. - */ - - try { - auditioner = new Auditioner (*this); - } - - catch (failed_constructor& err) { - warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; - } + // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } /* Create a set of Connection objects that map @@ -664,7 +720,7 @@ Session::when_engine_running () Connection* c = new OutputConnection (buf, true); c->add_port (); - c->add_connection (0, _engine.get_nth_physical_output (np)); + c->add_connection (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); add_connection (c); } @@ -676,7 +732,7 @@ Session::when_engine_running () Connection* c = new InputConnection (buf, true); c->add_port (); - c->add_connection (0, _engine.get_nth_physical_input (np)); + c->add_connection (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); add_connection (c); } @@ -691,8 +747,8 @@ Session::when_engine_running () c->add_port (); c->add_port (); - c->add_connection (0, _engine.get_nth_physical_output (np)); - c->add_connection (1, _engine.get_nth_physical_output (np+1)); + c->add_connection (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + c->add_connection (1, _engine.get_nth_physical_output (DataType::AUDIO, np+1)); add_connection (c); } @@ -705,8 +761,8 @@ Session::when_engine_running () c->add_port (); c->add_port (); - c->add_connection (0, _engine.get_nth_physical_input (np)); - c->add_connection (1, _engine.get_nth_physical_input (np+1)); + c->add_connection (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + c->add_connection (1, _engine.get_nth_physical_input (DataType::AUDIO, np+1)); add_connection (c); } @@ -730,16 +786,18 @@ Session::when_engine_running () _master_out->defer_pan_reset (); - while ((int) _master_out->n_inputs() < _master_out->input_maximum()) { - if (_master_out->add_input_port ("", this)) { + while (_master_out->n_inputs().get(DataType::AUDIO) + < _master_out->input_maximum().get(DataType::AUDIO)) { + if (_master_out->add_input_port ("", this, DataType::AUDIO)) { error << _("cannot setup master inputs") << endmsg; break; } } n = 0; - while ((int) _master_out->n_outputs() < _master_out->output_maximum()) { - if (_master_out->add_output_port (_engine.get_nth_physical_output (n), this)) { + while (_master_out->n_outputs().get(DataType::AUDIO) + < _master_out->output_maximum().get(DataType::AUDIO)) { + if (_master_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this, DataType::AUDIO)) { error << _("cannot setup master outputs") << endmsg; break; @@ -753,7 +811,7 @@ Session::when_engine_running () Connection* c = new OutputConnection (_("Master Out"), true); - for (uint32_t n = 0; n < _master_out->n_inputs (); ++n) { + for (uint32_t n = 0; n < _master_out->n_inputs ().get_total(); ++n) { c->add_port (); c->add_connection ((int) n, _master_out->input(n)->name()); } @@ -788,15 +846,18 @@ Session::when_engine_running () } } + _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; @@ -812,6 +873,22 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); + if (auditioner == 0) { + + /* we delay creating the auditioner till now because + it makes its own connections to ports. + the engine has to be running for this to work. + */ + + try { + auditioner.reset (new Auditioner (*this)); + } + + catch (failed_constructor& err) { + warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; + } + } + /* Tell all IO objects to create their ports */ IO::enable_ports (); @@ -819,7 +896,7 @@ Session::hookup_io () if (_control_out) { uint32_t n; - while ((int) _control_out->n_inputs() < _control_out->input_maximum()) { + while (_control_out->n_inputs().get(DataType::AUDIO) < _control_out->input_maximum().get(DataType::AUDIO)) { if (_control_out->add_input_port ("", this)) { error << _("cannot setup control inputs") << endmsg; @@ -827,8 +904,8 @@ Session::hookup_io () } } n = 0; - while ((int) _control_out->n_outputs() < _control_out->output_maximum()) { - if (_control_out->add_output_port (_engine.get_nth_physical_output (n), this)) { + while (_control_out->n_outputs().get(DataType::AUDIO) < _control_out->output_maximum().get(DataType::AUDIO)) { + if (_control_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this)) { error << _("cannot set up master outputs") << endmsg; break; @@ -863,7 +940,7 @@ Session::hookup_io () } void -Session::playlist_length_changed (Playlist* pl) +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, @@ -875,12 +952,12 @@ Session::playlist_length_changed (Playlist* pl) } void -Session::diskstream_playlist_changed (DiskStream* dstream) +Session::diskstream_playlist_changed (boost::shared_ptr dstream) { - Playlist *playlist; + boost::shared_ptr playlist; if ((playlist = dstream->playlist()) != 0) { - playlist->LengthChanged.connect (sigc::bind (mem_fun (this, &Session::playlist_length_changed), playlist)); + playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); } /* see comment in playlist_length_changed () */ @@ -895,132 +972,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); } @@ -1029,7 +1017,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); } @@ -1037,7 +1025,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; @@ -1049,7 +1037,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()) { @@ -1060,7 +1048,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 @@ -1117,48 +1105,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) { @@ -1247,19 +1193,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); + deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location); - 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); } @@ -1275,26 +1216,26 @@ 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); + // 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_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); } @@ -1312,17 +1253,13 @@ Session::disable_record (bool rt_context, bool force) 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); } @@ -1333,32 +1270,32 @@ 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 { - send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordPause); + deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame); RecordStateChanged (); /* EMIT SIGNAL */ } 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 @@ -1412,27 +1349,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 @@ -1441,41 +1382,11 @@ 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; current_block_size = nframes; - for (np = 0, i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i, ++np) { - free (*i); - } - - for (vector::iterator i = _silent_buffers.begin(); i != _silent_buffers.end(); ++i) { - free (*i); - } - - _passthru_buffers.clear (); - _silent_buffers.clear (); - - ensure_passthru_buffers (np); - - for (vector::iterator i = _send_buffers.begin(); i != _send_buffers.end(); ++i) { - free(*i); - - Sample *buf; -#ifdef NO_POSIX_MEMALIGN - buf = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&buf,16,current_block_size * 4); -#endif - *i = buf; - - memset (*i, 0, sizeof (Sample) * current_block_size); - } + ensure_buffers(_scratch_buffers->available()); - if (_gain_automation_buffer) { delete [] _gain_automation_buffer; } @@ -1483,15 +1394,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 (); } } @@ -1499,7 +1413,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 */ @@ -1510,7 +1424,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); } @@ -1519,7 +1433,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); } @@ -1533,7 +1447,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()) { @@ -1555,9 +1469,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; @@ -1566,13 +1480,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 @@ -1602,7 +1516,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. @@ -1612,304 +1526,451 @@ 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); + #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_midi_track (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 */ + /* count existing midi 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(); + channels_used += (*i)->n_inputs().get(DataType::MIDI); } } } } - /* 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) - */ + while (how_many) { - do { - snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, n+1); - if (route_by_name (track_name) == 0) { - break; - } - n++; + /* check for duplicate route names, since we might have pre-existing + routes with this name (e.g. create Midi1, Midi2, delete Midi1, + save, close,restart,add new route - first named route is now + Midi2) + */ + - } while (n < (UINT_MAX-1)); + do { + ++track_id; - if (input_auto_connect & AutoConnectPhysical) { - nphysical_in = n_physical_inputs; - } else { - nphysical_in = 0; - } + snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id); - if (output_auto_connect & AutoConnectPhysical) { - nphysical_out = n_physical_outputs; - } else { - nphysical_out = 0; - } + if (route_by_name (track_name) == 0) { + break; + } + + } while (track_id < (UINT_MAX-1)); - try { - track = new AudioTrack (*this, track_name, Route::Flag (0), mode); + try { + shared_ptr track (new MidiTrack (*this, track_name, Route::Flag (0), mode)); + + if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::MIDI, 1), false, this)) { + error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + } + + channels_used += track->n_inputs ().get(DataType::MIDI); - 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; + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); + track->set_remote_control_id (ntracks()); + + new_routes.push_back (track); + ret.push_back (track); } - if (nphysical_in) { - for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) { - - port = ""; - - if (input_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((channels_used+x)%nphysical_in); - } - - if (port.length() && track->connect_input (track->input (x), port, this)) { - break; - } - } + catch (failed_constructor &err) { + error << _("Session: could not create new midi track.") << endmsg; + // XXX should we delete the tracks already created? + ret.clear (); + return ret; } - for (uint32_t x = 0; x < track->n_outputs(); ++x) { - - port = ""; + --how_many; + } - 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 (!new_routes.empty()) { + add_routes (new_routes, false); + save_state (_current_snapshot_name); + } - if (port.length() && track->connect_output (track->output (x), port, this)) { - break; + return ret; +} + +list > +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many) +{ + char track_name[32]; + uint32_t track_id = 0; + uint32_t n = 0; + uint32_t channels_used = 0; + string port; + RouteList new_routes; + list > ret; + uint32_t control_id; + + /* count existing audio tracks */ + + { + 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().get(DataType::AUDIO); + } } } + } - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs(); + vector physinputs; + vector physoutputs; + uint32_t nphysical_in; + uint32_t nphysical_out; + + _engine.get_physical_outputs (physoutputs); + _engine.get_physical_inputs (physinputs); + control_id = 0; + + while (how_many) { + + /* 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) + */ + + + do { + ++track_id; - for (n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); + snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id); + + if (route_by_name (track_name) == 0) { + break; } + + } while (track_id < (UINT_MAX-1)); - track->set_control_outs (cports); + 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 (ChanCount(DataType::AUDIO, input_channels), 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; + } + + if (nphysical_in) { + for (uint32_t x = 0; x < track->n_inputs().get(DataType::AUDIO) && 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().get(DataType::MIDI); ++x) { + + port = ""; + + 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().get(DataType::AUDIO))->name(); + } + } + + if (port.length() && track->connect_output (track->output (x), port, this)) { + break; + } + } + + channels_used += track->n_inputs ().get(DataType::AUDIO); - track->diskstream_changed.connect (mem_fun (this, &Session::resort_routes)); + if (_control_out) { + vector cports; + uint32_t ni = _control_out->n_inputs().get(DataType::AUDIO); + + 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() + control_id + 1); + ++control_id; - add_route (track); + new_routes.push_back (track); + ret.push_back (track); + } - track->set_remote_control_id (ntracks()); + 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; + } + + --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) { + } while (bus_id < (UINT_MAX-1)); + + try { + shared_ptr bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); + + if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), 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; + } - port = ""; + for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().get(DataType::AUDIO); ++x) { + + port = ""; - 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(); + 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; } } - - if (port.length() && bus->connect_output (bus->output (x), port, this)) { - break; + + for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs().get(DataType::AUDIO); ++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().get(DataType::AUDIO))->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().get(DataType::AUDIO); + + 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)); @@ -1917,65 +1978,83 @@ 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; + Track* t; + boost::shared_ptr ds; + + if ((t = dynamic_cast(route.get())) != 0) { + ds = t->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 (); + + /* try to cause everyone to drop their references */ - save_state (_current_snapshot_name); + route->drop_references (); - delete &route; + /* save the new state of the world */ + + if (save_state (_current_snapshot_name)) { + save_history (_current_snapshot_name); + } } void @@ -1985,19 +2064,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 */ @@ -2005,7 +2092,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; } @@ -2013,7 +2100,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; } } @@ -2029,7 +2116,7 @@ Session::route_solo_changed (void* src, Route* route) then leave it as it is. */ - if (_solo_latched) { + if (Config->get_solo_latched()) { continue; } } @@ -2046,10 +2133,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; @@ -2078,16 +2165,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 () { @@ -2100,11 +2177,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; @@ -2120,7 +2199,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); } @@ -2141,13 +2220,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 { @@ -2159,7 +2240,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()) { @@ -2191,36 +2272,49 @@ 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); } -Route * +shared_ptr Session::route_by_remote_id (uint32_t id) { - 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)->remote_control_id() == id) { - return* i; + return *i; } } - return 0; + return shared_ptr ((Route*) 0); } void @@ -2230,7 +2324,7 @@ Session::find_current_end () return; } - jack_nframes_t max = get_maximum_extent (); + nframes_t max = get_maximum_extent (); if (max > end_location->end()) { end_location->set_end (max); @@ -2239,18 +2333,16 @@ Session::find_current_end () } } -jack_nframes_t +nframes_t Session::get_maximum_extent () const { - jack_nframes_t max = 0; - jack_nframes_t me; + nframes_t max = 0; + nframes_t me; - /* Don't take the diskstream lock. Caller must have other ways to - ensure atomicity. - */ + boost::shared_ptr dsl = diskstreams.reader(); - for (DiskStreamList::const_iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - Playlist* pl = (*i)->playlist(); + for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { + boost::shared_ptr pl = (*i)->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; } @@ -2259,35 +2351,35 @@ Session::get_maximum_extent () const return max; } -DiskStream * +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 */ +/* Region management */ string Session::new_region_name (string old) @@ -2313,7 +2405,7 @@ Session::new_region_name (string old) while (number < (UINT_MAX-1)) { - AudioRegionList::const_iterator i; + RegionList::const_iterator i; string sbuf; number++; @@ -2321,13 +2413,13 @@ Session::new_region_name (string old) snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number); sbuf = buf; - for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { - if ((*i).second->name() == sbuf) { + for (i = regions.begin(); i != regions.end(); ++i) { + if (i->second->name() == sbuf) { break; } } - if (i == audio_regions.end()) { + if (i == regions.end()) { break; } } @@ -2346,11 +2438,13 @@ Session::region_name (string& result, string base, bool newlevel) const char buf[16]; string subbase; + assert(base.find("/") == string::npos); + if (base == "") { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); - snprintf (buf, sizeof (buf), "%d", (int)audio_regions.size() + 1); + snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1); result = "region."; @@ -2376,7 +2470,7 @@ Session::region_name (string& result, string base, bool newlevel) const bool name_taken = true; { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); for (int n = 1; n < 5000; ++n) { @@ -2386,8 +2480,8 @@ 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) { + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + if (i->second->name() == result) { name_taken = true; break; } @@ -2408,52 +2502,42 @@ Session::region_name (string& result, string base, bool newlevel) const } void -Session::add_region (Region* region) +Session::add_region (boost::shared_ptr region) { - AudioRegion* ar = 0; - AudioRegion* oar = 0; + boost::shared_ptr other; bool added = false; { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); - if ((ar = dynamic_cast (region)) != 0) { + RegionList::iterator x; - AudioRegionList::iterator x; + for (x = regions.begin(); x != regions.end(); ++x) { - for (x = audio_regions.begin(); x != audio_regions.end(); ++x) { + other = x->second; - oar = dynamic_cast (x->second); - - if (ar->region_list_equivalent (*oar)) { - break; - } + if (region->region_list_equivalent (other)) { + break; } + } - if (x == audio_regions.end()) { + if (x == regions.end()) { - pair entry; - - entry.first = region->id(); - entry.second = ar; + pair entry; - pair x = audio_regions.insert (entry); - - if (!x.second) { - return; - } + entry.first = region->id(); + entry.second = region; - added = true; - } + pair x = regions.insert (entry); - } else { - fatal << _("programming error: ") - << X_("unknown region type passed to Session::add_region()") - << endmsg; - /*NOTREACHED*/ + if (!x.second) { + return; + } + + added = true; + } - } } /* mark dirty because something has changed even if we didn't @@ -2463,15 +2547,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)); - AudioRegionAdded (ar); /* EMIT SIGNAL */ + 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))); + RegionAdded (region); /* 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); @@ -2479,31 +2569,23 @@ Session::region_changed (Change what_changed, Region* region) } void -Session::region_renamed (Region* region) +Session::remove_region (boost::weak_ptr weak_region) { - add_region (region); -} + RegionList::iterator i; + boost::shared_ptr region (weak_region.lock ()); + + if (!region) { + return; + } -void -Session::remove_region (Region* region) -{ - AudioRegionList::iterator i; - AudioRegion* ar = 0; bool removed = false; { - LockMonitor lm (region_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (region_lock); - if ((ar = dynamic_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; - /*NOTREACHED*/ + if ((i = regions.find (region->id())) != regions.end()) { + regions.erase (i); + removed = true; } } @@ -2514,71 +2596,75 @@ Session::remove_region (Region* region) set_dirty(); if (removed) { - AudioRegionRemoved(ar); /* EMIT SIGNAL */ + RegionRemoved(region); /* 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__); + RegionList::iterator i; + boost::shared_ptr region; + + Glib::Mutex::Lock lm (region_lock); - for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { + for (i = regions.begin(); i != 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"; } } @@ -2586,9 +2672,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; @@ -2597,12 +2683,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()); @@ -2615,83 +2701,87 @@ 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; + pair entry; + pair result; + entry.first = source->id(); + entry.second = source; + { - LockMonitor lm (source_lock, __LINE__, __FILE__); - entry.first = source->id(); - entry.second = source; - sources.insert (entry); + Glib::Mutex::Lock lm (source_lock); + result = sources.insert (entry); + } + + if (result.second) { + source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); + set_dirty(); } - - 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; + SourceMap::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 (source_lock); + + { + Glib::Mutex::Lock lm (source_lock); + + if ((i = sources.find (source->id())) != sources.end()) { + 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 (source_lock); + SourceMap::iterator i; + boost::shared_ptr source; if ((i = sources.find (id)) != sources.end()) { - source = (*i).second; + source = i->second; } + /* XXX search MIDI or other searches here */ + return source; } string -Session::peak_path_from_audio_path (string audio_path) +Session::peak_path_from_audio_path (string audio_path) const { - /* XXX hardly bombproof! fix me */ - string res; - res = PBD::dirname (audio_path); - res = PBD::dirname (res); - res += '/'; - res += peak_dir_name; - res += '/'; + res = peak_dir (); res += PBD::basename_nosuffix (audio_path); res += ".peak"; @@ -2702,7 +2792,7 @@ string Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive) { string look_for; - string old_basename = basename_nosuffix (oldname); + 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 */ @@ -2752,37 +2842,290 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, 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 */ - string dir; - string suffix; - 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 ""; + } + + 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; + char buf[PATH_MAX+1]; + const uint32_t limit = 10000; + string legalized; + + buf[0] = '\0'; + legalized = legalize_for_path (name); + + /* find a "version" of the file name that doesn't exist in + any of the possible directories. + */ + + for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { + + vector::iterator i; + uint32_t existing = 0; + + for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { + + spath = (*i).path; + + spath += sound_dir (false); + + if (destructive) { + 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/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); + } + + } else { + + 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 (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, name) << endmsg; + destroy (); + throw failed_constructor(); + } + } + + /* we now have a unique name for the file, but figure out where to + actually put it. + */ + + string foo = buf; + + spath = discover_best_sound_dir (); + spath += '/'; + + string::size_type pos = foo.find_last_of ('/'); + + if (pos == string::npos) { + spath += foo; + } else { + spath += foo.substr (pos + 1); + } + + return spath; +} + +boost::shared_ptr +Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) +{ + string spath = audio_path_from_name (ds.name(), ds.n_channels().get(DataType::AUDIO), chan, destructive); + return boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate())); +} + +// FIXME: _terrible_ code duplication +string +Session::change_midi_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 += ".mid"; /* 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 ((slash = path.find_last_of ('/')) == string::npos) { + if (postfix != string::npos) { + suffix = suffix.substr (postfix); + } else { + error << "Logic error in Session::change_midi_path_by_name(), please report to the developers" << endl; return ""; } - dir = path.substr (0, slash+1); + const uint32_t limit = 10000; + char buf[PATH_MAX+1]; - /* '-' is not a legal character for the NAME part of the path */ + for (uint32_t cnt = 1; cnt <= limit; ++cnt) { - if ((dash = path.find_last_of ('-')) == string::npos) { - return ""; + 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 = ""; } - suffix = path.substr (dash); + if (path == "") { + error << "FATAL ERROR! Could not find a " << endl; + } - path = dir; - path += new_legalized; - path += suffix; } return path; } string -Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive) +Session::midi_path_from_name (string name) { string spath; uint32_t cnt; @@ -2797,60 +3140,23 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool any of the possible directories. */ - for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { - + for (cnt = 1; cnt <= limit; ++cnt) { + vector::iterator i; uint32_t existing = 0; - + for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - - spath = (*i).path; - - if (destructive) { - spath += tape_dir_name; - } else { - spath += sound_dir_name; - } - - if (destructive) { - 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/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); - } - } else { - - 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); - } - } + + // FIXME: different directory from audio? + spath = (*i).path + sound_dir_name + "/" + legalized; + + snprintf (buf, sizeof(buf), "%s-%u.mid", spath.c_str(), cnt); if (access (buf, F_OK) == 0) { existing++; } } - + if (existing == 0) { break; } @@ -2867,11 +3173,8 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool string foo = buf; - if (destructive) { - spath = tape_dir (); - } else { - spath = discover_best_sound_dir (); - } + // FIXME: different directory than audio? + spath = discover_best_sound_dir (); string::size_type pos = foo.find_last_of ('/'); @@ -2884,38 +3187,21 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool return spath; } -FileSource * -Session::create_file_source (DiskStream& ds, int32_t chan, bool destructive) +boost::shared_ptr +Session::create_midi_source_for_session (MidiDiskstream& ds) { - string spath = audio_path_from_name (ds.name(), ds.n_channels(), chan, destructive); - - /* this might throw failed_constructor(), which is OK */ - - if (destructive) { - return new DestructiveFileSource (spath, frame_rate(), false, Config->get_native_file_data_format()); - } else { - return new FileSource (spath, frame_rate(), false, Config->get_native_file_data_format()); - } + string spath = midi_path_from_name (ds.name()); + + return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, spath, false, frame_rate())); } -/* Playlist management */ - -Playlist * -Session::get_playlist (string name) -{ - Playlist* ret = 0; - - if ((ret = playlist_by_name (name)) == 0) { - ret = new AudioPlaylist (*this, name); - } - return ret; -} +/* Playlist management */ -Playlist * +boost::shared_ptr 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; @@ -2926,23 +3212,23 @@ Session::playlist_by_name (string name) return* i; } } - return 0; + + return boost::shared_ptr(); } void -Session::add_playlist (Playlist* playlist) +Session::add_playlist (boost::shared_ptr playlist) { if (playlist->hidden()) { return; } { - 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->InUse.connect (sigc::bind (mem_fun (*this, &Session::track_playlist), boost::weak_ptr(playlist))); + playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), boost::weak_ptr(playlist))); } } @@ -2952,15 +3238,39 @@ Session::add_playlist (Playlist* playlist) } void -Session::track_playlist (Playlist* pl, bool inuse) +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 (bool inuse, boost::weak_ptr wpl) { + boost::shared_ptr pl(wpl.lock()); + + if (!pl) { + return; + } + PlaylistList::iterator x; + if (pl->hidden()) { + /* its not supposed to be visible */ + return; + } + { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); + Glib::Mutex::Lock lm (playlist_lock); if (!inuse) { - //cerr << "shifting playlist to unused: " << pl->name() << endl; unused_playlists.insert (pl); @@ -2970,8 +3280,7 @@ Session::track_playlist (Playlist* pl, bool inuse) } else { - //cerr << "shifting playlist to used: " << pl->name() << endl; - + playlists.insert (pl); if ((x = unused_playlists.find (pl)) != unused_playlists.end()) { @@ -2982,20 +3291,24 @@ Session::track_playlist (Playlist* pl, bool inuse) } void -Session::remove_playlist (Playlist* playlist) +Session::remove_playlist (boost::weak_ptr weak_playlist) { if (_state_of_the_state & Deletion) { return; } + boost::shared_ptr playlist (weak_playlist.lock()); + + if (!playlist) { + return; + } + { - LockMonitor lm (playlist_lock, __LINE__, __FILE__); - // cerr << "removing playlist: " << playlist->name() << endl; + Glib::Mutex::Lock lm (playlist_lock); PlaylistList::iterator i; i = find (playlists.begin(), playlists.end(), playlist); - if (i != playlists.end()) { playlists.erase (i); } @@ -3013,7 +3326,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); @@ -3021,30 +3334,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); } @@ -3053,12 +3366,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")); } @@ -3066,17 +3379,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 (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()); @@ -3104,13 +3435,11 @@ Session::is_auditioning () const 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); } } @@ -3120,13 +3449,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); } } @@ -3136,10 +3463,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++; } @@ -3147,17 +3475,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 () { @@ -3168,17 +3485,22 @@ Session::graph_reordered () if (_state_of_the_state & InitialConnecting) { return; } + + /* every track/bus asked for this to be handled but it was deferred because + we were connecting. do it now. + */ - RWLockMonitor lm1 (route_lock, true, __LINE__, __FILE__); - RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__); + request_input_change_handling (); - 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 (); } } @@ -3198,12 +3520,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) { - AudioTrack* at; + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + Track* at; - if ((at = dynamic_cast(*i)) != 0) { + if ((at = dynamic_cast((*i).get())) != 0) { at->set_record_enable (yn, this); } } @@ -3235,7 +3557,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(); } @@ -3247,18 +3569,28 @@ Session::remove_redirect (Redirect* redirect) Insert* insert; PortInsert* port_insert; PluginInsert* plugin_insert; - + if ((insert = dynamic_cast (redirect)) != 0) { if ((port_insert = dynamic_cast (insert)) != 0) { - _port_inserts.remove (port_insert); + list::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert); + if (x != _port_inserts.end()) { + insert_bitset[port_insert->bit_slot()] = false; + _port_inserts.erase (x); + } } 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) { - _sends.remove (send); + list::iterator x = find (_sends.begin(), _sends.end(), send); + if (x != _sends.end()) { + send_bitset[send->bit_slot()] = false; + _sends.erase (x); + } } else { fatal << _("programming error: unknown type of Redirect deleted!") << endmsg; /*NOTREACHED*/ @@ -3267,23 +3599,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); } @@ -3298,7 +3649,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()) { @@ -3317,7 +3668,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) { @@ -3328,23 +3679,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) { @@ -3352,56 +3686,84 @@ Session::tempo_map_changed (Change ignored) set_dirty (); } +/** Ensures that all buffers (scratch, send, silent, etc) are allocated for + * the given count with the current block size. + */ void -Session::ensure_passthru_buffers (uint32_t howmany) +Session::ensure_buffers (ChanCount howmany) { - while (howmany > _passthru_buffers.size()) { - Sample *p; -#ifdef NO_POSIX_MEMALIGN - p = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&p,16,current_block_size * 4); -#endif - _passthru_buffers.push_back (p); + // FIXME: NASTY assumption (midi block size == audio block size) + _scratch_buffers->ensure_buffers(howmany, current_block_size); + _send_buffers->ensure_buffers(howmany, current_block_size); + _silent_buffers->ensure_buffers(howmany, current_block_size); + + allocate_pan_automation_buffers (current_block_size, howmany.get(DataType::AUDIO), false); +} - *p = 0; +uint32_t +Session::next_insert_id () +{ + /* this doesn't really loop forever. just think about it */ + + while (true) { + for (boost::dynamic_bitset::size_type n = 0; n < insert_bitset.size(); ++n) { + if (!insert_bitset[n]) { + insert_bitset[n] = true; + cerr << "Returning " << n << " as insert ID\n"; + return n; + + } + } -#ifdef NO_POSIX_MEMALIGN - p = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&p,16,current_block_size * 4); -#endif - memset (p, 0, sizeof (Sample) * current_block_size); - _silent_buffers.push_back (p); + /* none available, so resize and try again */ - *p = 0; - -#ifdef NO_POSIX_MEMALIGN - p = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&p,16,current_block_size * 4); -#endif - memset (p, 0, sizeof (Sample) * current_block_size); - _send_buffers.push_back (p); + insert_bitset.resize (insert_bitset.size() + 16, false); + } +} + +uint32_t +Session::next_send_id () +{ + /* this doesn't really loop forever. just think about it */ + + while (true) { + for (boost::dynamic_bitset::size_type n = 0; n < send_bitset.size(); ++n) { + if (!send_bitset[n]) { + send_bitset[n] = true; + cerr << "Returning " << n << " as send ID\n"; + return n; + + } + } + /* none available, so resize and try again */ + + send_bitset.resize (send_bitset.size() + 16, false); } - allocate_pan_automation_buffers (current_block_size, howmany, false); } -string -Session::next_send_name () +void +Session::mark_send_id (uint32_t id) { - char buf[32]; - snprintf (buf, sizeof (buf), "send %" PRIu32, ++send_cnt); - return buf; + if (id >= send_bitset.size()) { + send_bitset.resize (id+16, false); + } + if (send_bitset[id]) { + warning << string_compose (_("send ID %1 appears to be in use already"), id) << endmsg; + } + send_bitset[id] = true; } -string -Session::next_insert_name () +void +Session::mark_insert_id (uint32_t id) { - char buf[32]; - snprintf (buf, sizeof (buf), "insert %" PRIu32, ++insert_cnt); - return buf; + if (id >= insert_bitset.size()) { + insert_bitset.resize (id+16, false); + } + if (insert_bitset[id]) { + warning << string_compose (_("insert ID %1 appears to be in use already"), id) << endmsg; + } + insert_bitset[id] = true; } /* Named Selection management */ @@ -3409,7 +3771,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; @@ -3422,7 +3784,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); } @@ -3437,7 +3799,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); @@ -3457,11 +3819,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); } } @@ -3469,9 +3829,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; } @@ -3480,31 +3840,15 @@ Session::route_name_unique (string n) const return true; } -int -Session::remove_file_source (FileSource& fs) -{ - 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; @@ -3528,23 +3872,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; + Track *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. */ @@ -3556,28 +3893,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 playlist; + 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; - vector buffers; - char * workbuf = 0; - const jack_nframes_t chunk_size = (256 * 1024)/4; + ChanCount nchans(track.audio_diskstream()->n_channels()); + nframes_t position; + nframes_t this_chunk; + nframes_t to_do; + BufferSet buffers; - 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; } @@ -3586,12 +3924,10 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ if (track.has_external_redirects()) { goto out; } - - nchans = track.disk_stream().n_channels(); dir = discover_best_sound_dir (); - for (uint32_t chan_n=0; chan_n < nchans; ++chan_n) { + for (uint32_t chan_n=0; chan_n < nchans.get(DataType::AUDIO); ++chan_n) { for (x = 0; x < 99999; ++x) { snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", dir.c_str(), playlist->name().c_str(), chan_n, x+1); @@ -3606,7 +3942,8 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ } try { - fsource = new FileSource (buf, frame_rate(), false, Config->get_native_file_data_format()); + fsource = boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -3614,7 +3951,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 */ @@ -3623,31 +3960,25 @@ Session::write_one_track (AudioTrack& track, jack_nframes_t start, jack_nframes_ to_do = len; /* create a set of reasonably-sized buffers */ + buffers.ensure_buffers(nchans, chunk_size); + buffers.set_count(nchans); - for (vector::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) { - Sample* b; -#ifdef NO_POSIX_MEMALIGN - b = (Sample *) malloc(chunk_size * sizeof(Sample)); -#else - posix_memalign((void **)&b,16,chunk_size * 4); -#endif - 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, 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.get_audio(n).data(this_chunk), this_chunk) != this_chunk) { + goto out; + } } } @@ -3665,59 +3996,90 @@ 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(), true)); + 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); - for (vector::iterator i = buffers.begin(); i != buffers.end(); ++i) { - free(*i); - } + if (afs) { + afs->mark_for_remove (); + } - if (workbuf) { - delete [] workbuf; + (*src)->drop_references (); + } } - - atomic_set (&processing_prohibited, 0); + + g_atomic_int_set (&processing_prohibited, 0); itt.done = true; return ret; } -vector& -Session::get_silent_buffers (uint32_t howmany) +BufferSet& +Session::get_silent_buffers (ChanCount count) { - for (uint32_t i = 0; i < howmany; ++i) { - memset (_silent_buffers[i], 0, sizeof (Sample) * current_block_size); + assert(_silent_buffers->available() >= count); + _silent_buffers->set_count(count); + + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + for (size_t i=0; i < count.get(*t); ++i) { + _silent_buffers->get(*t, i).clear(); + } } - return _silent_buffers; + + return *_silent_buffers; +} + +BufferSet& +Session::get_scratch_buffers (ChanCount count) +{ + assert(_scratch_buffers->available() >= count); + _scratch_buffers->set_count(count); + return *_scratch_buffers; +} + +BufferSet& +Session::get_send_buffers (ChanCount count) +{ + assert(_send_buffers->available() >= count); + _send_buffers->set_count(count); + return *_send_buffers; } 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; } } @@ -3729,10 +4091,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; } } @@ -3741,22 +4103,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; }