X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=d871f5a859e86ad5b6ff71a40cb45b4247237e55;hb=45d35d0732e18501762dc7c9bc87b519c2590447;hp=806ec3b17f09843cab6a7f3b441efd2874d6e6f5;hpb=79986643c0c904f6574bb5323e2233a43a9e622e;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 806ec3b17f..d871f5a859 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -65,6 +65,8 @@ #include #include #include +#include +#include #ifdef HAVE_LIBLO #include @@ -80,10 +82,11 @@ 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; @@ -91,9 +94,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::SMPTEOffsetChanged; 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) @@ -256,6 +261,7 @@ Session::Session (AudioEngine &eng, _midi_port (default_midi_port), pending_events (2048), midi_requests (128), // the size of this should match the midi request pool size + diskstreams (new DiskstreamList), routes (new RouteList), auditioner ((Auditioner*) 0), _click_io ((IO*) 0), @@ -263,18 +269,23 @@ Session::Session (AudioEngine &eng, { 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, _engine.frame_rate() * 60 * 5)) { + cerr << "create failed\n"; + throw failed_constructor (); + } } if (second_stage_init (new_session)) { + cerr << "2nd state failed\n"; throw failed_constructor (); } @@ -284,6 +295,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 */ } @@ -298,7 +311,7 @@ Session::Session (AudioEngine &eng, uint32_t master_out_channels, uint32_t requested_physical_in, uint32_t requested_physical_out, - jack_nframes_t initial_length) + nframes_t initial_length) : _engine (eng), _mmc_port (default_mmc_port), @@ -306,40 +319,49 @@ Session::Session (AudioEngine &eng, _midi_port (default_midi_port), pending_events (2048), midi_requests (16), + diskstreams (new DiskstreamList), routes (new RouteList), main_outs (0) { bool new_session; - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << endl; + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; n_physical_outputs = max (requested_physical_out, _engine.n_physical_outputs()); n_physical_inputs = max (requested_physical_in, _engine.n_physical_inputs()); first_stage_init (fullpath, snapshot_name); - - if (create (new_session, 0, initial_length)) { - throw failed_constructor (); + + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + + if (new_session) { + if (create (new_session, 0, initial_length)) { + throw failed_constructor (); + } } if (control_out_channels) { shared_ptr r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); - add_route (r); + RouteList rl; + rl.push_back (r); + add_routes (rl); _control_out = r; } if (master_out_channels) { shared_ptr r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); - add_route (r); + RouteList rl; + rl.push_back (r); + add_routes (rl); _master_out = r; } else { /* prohibit auto-connect to master, because there isn't one */ output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); } - input_auto_connect = input_ac; - output_auto_connect = output_ac; + Config->set_input_auto_connect (input_ac); + Config->set_output_auto_connect (output_ac); if (second_stage_init (new_session)) { throw failed_constructor (); @@ -366,9 +388,23 @@ 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 (); @@ -394,11 +430,11 @@ Session::~Session () free(*i); } - for (map::iterator i = _conversion_buffers.begin(); i != _conversion_buffers.end(); ++i) { - delete [] (i->second); - } - AudioDiskstream::free_working_buffers(); + + /* this should cause deletion of the auditioner */ + + // auditioner.reset (); #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION @@ -431,30 +467,47 @@ Session::~Session () #ifdef TRACK_DESTRUCTION cerr << "delete audio regions\n"; #endif /* TRACK_DESTRUCTION */ + for (AudioRegionList::iterator i = audio_regions.begin(); i != audio_regions.end(); ) { AudioRegionList::iterator tmp; - tmp =i; + tmp = i; ++tmp; - delete i->second; + i->second->drop_references (); i = tmp; } + + audio_regions.clear (); #ifdef TRACK_DESTRUCTION - cerr << "delete 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 audio sources\n"; @@ -465,11 +518,13 @@ Session::~Session () tmp = i; ++tmp; - delete i->second; + i->second->drop_references (); i = tmp; } + audio_sources.clear (); + #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; #endif /* TRACK_DESTRUCTION */ @@ -525,10 +580,6 @@ Session::~Session () if (mmc) { delete mmc; } - - if (state_tree) { - delete state_tree; - } } void @@ -561,6 +612,8 @@ Session::when_engine_running () set_block_size (_engine.frames_per_cycle()); set_frame_rate (_engine.frame_rate()); + Config->map_parameters (mem_fun (*this, &Session::config_changed)); + /* every time we reconnect, recompute worst case output latencies */ _engine.Running.connect (mem_fun (*this, &Session::set_worst_io_latencies)); @@ -586,7 +639,7 @@ Session::when_engine_running () if (_click_io->set_state (*child->children().front()) == 0) { - _clicking = click_requested; + _clicking = Config->get_clicking (); } else { @@ -604,7 +657,7 @@ Session::when_engine_running () if (_click_io->add_output_port (first_physical_output, this)) { // relax, even though its an error } else { - _clicking = click_requested; + _clicking = Config->get_clicking (); } } } @@ -617,7 +670,7 @@ Session::when_engine_running () set_worst_io_latencies (); if (_clicking) { - ControlChanged (Clicking); /* EMIT SIGNAL */ + // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } if (auditioner == 0) { @@ -863,7 +916,7 @@ Session::playlist_length_changed (Playlist* pl) } void -Session::diskstream_playlist_changed (Diskstream* dstream) +Session::diskstream_playlist_changed (boost::shared_ptr dstream) { Playlist *playlist; @@ -883,132 +936,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" - */ - Glib::RWLock::ReaderLock dsm (diskstream_lock); - 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()) { - Glib::RWLock::ReaderLock dsm (diskstream_lock); - 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 { - Glib::RWLock::ReaderLock dsm (diskstream_lock); - 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); } @@ -1017,7 +981,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); } @@ -1025,7 +989,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; @@ -1037,7 +1001,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()) { @@ -1048,7 +1012,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 @@ -1105,48 +1069,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) { @@ -1240,14 +1162,9 @@ Session::enable_record () _last_record_location = _transport_frame; send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordStrobe); - if (Config->get_use_hardware_monitoring() && auto_input) { - /* Even though this can be called from RT context we are using - a non-tentative rwlock here, because the action must occur. - The rarity and short potential lock duration makes this "OK" - */ - Glib::RWLock::ReaderLock dsm (diskstream_lock); - - 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,14 +1192,10 @@ Session::disable_record (bool rt_context, bool force) send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordExit); - if (Config->get_use_hardware_monitoring() && auto_input) { - /* Even though this can be called from RT context we are using - a non-tentative rwlock here, because the action must occur. - The rarity and short potential lock duration makes this "OK" - */ - Glib::RWLock::ReaderLock dsm (diskstream_lock); + 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); } @@ -1302,15 +1215,11 @@ Session::step_back_from_record () { 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" - */ - Glib::RWLock::ReaderLock dsm (diskstream_lock); + 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); } @@ -1323,14 +1232,14 @@ Session::maybe_enable_record () { 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 { @@ -1341,12 +1250,12 @@ Session::maybe_enable_record () set_dirty(); } -jack_nframes_t +nframes_t Session::audible_frame () const { - jack_nframes_t ret; - jack_nframes_t offset; - jack_nframes_t tf; + nframes_t ret; + nframes_t offset; + nframes_t tf; /* the first of these two possible settings for "offset" mean that the audible frame is stationary until @@ -1400,22 +1309,23 @@ 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 - // DestructiveFileSource::setup_standard_crossfades (frames_per_second); + // SndFileSource::setup_standard_crossfades (frames_per_second); set_dirty(); @@ -1423,7 +1333,7 @@ Session::set_frame_rate (jack_nframes_t frames_per_second) } 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 @@ -1432,7 +1342,6 @@ Session::set_block_size (jack_nframes_t nframes) */ { - Glib::RWLock::ReaderLock dsm (diskstream_lock); vector::iterator i; uint32_t np; @@ -1479,7 +1388,8 @@ Session::set_block_size (jack_nframes_t nframes) (*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); } @@ -1491,7 +1401,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 */ @@ -1502,7 +1412,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); } @@ -1643,11 +1553,17 @@ Session::resort_routes_using (shared_ptr r) for (i = r->begin(); i != r->end(); ++i) { trace_terminal (*i, *i); - } - + } + RouteSorter cmp; r->sort (cmp); + /* don't leave dangling references to routes in Route::fed_by */ + + for (i = r->begin(); i != r->end(); ++i) { + (*i)->fed_by.clear (); + } + #if 0 cerr << "finished route resort\n"; @@ -1659,15 +1575,16 @@ Session::resort_routes_using (shared_ptr r) } -shared_ptr -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode) +list > +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many) { 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 */ @@ -1684,105 +1601,133 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod } } - /* check for duplicate route names, since we might have pre-existing - routes with this name (e.g. create Audio1, Audio2, delete Audio1, - save, close,restart,add new route - first named route is now - Audio2) - */ + vector physinputs; + vector physoutputs; + uint32_t nphysical_in; + uint32_t nphysical_out; - do { - snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, n+1); - if (route_by_name (track_name) == 0) { - break; - } - n++; + _engine.get_physical_outputs (physoutputs); + _engine.get_physical_inputs (physinputs); - } while (n < (UINT_MAX-1)); + while (how_many) { - if (input_auto_connect & AutoConnectPhysical) { - nphysical_in = n_physical_inputs; - } else { - nphysical_in = 0; - } + /* check for duplicate route names, since we might have pre-existing + routes with this name (e.g. create Audio1, Audio2, delete Audio1, + save, close,restart,add new route - first named route is now + Audio2) + */ + - if (output_auto_connect & AutoConnectPhysical) { - nphysical_out = n_physical_outputs; - } else { - nphysical_out = 0; - } + do { + ++track_id; - try { - shared_ptr track (new AudioTrack (*this, track_name, Route::Flag (0), mode)); + snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id); - if (track->ensure_io (input_channels, output_channels, false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - } + if (route_by_name (track_name) == 0) { + break; + } + + } while (track_id < (UINT_MAX-1)); - if (nphysical_in) { - for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) { + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size()); + } else { + nphysical_in = 0; + } + + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size()); + } else { + nphysical_out = 0; + } + + try { + shared_ptr track (new AudioTrack (*this, track_name, Route::Flag (0), mode)); + + if (track->ensure_io (input_channels, output_channels, false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + } + + if (nphysical_in) { + for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) { + + port = ""; + + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + port = physinputs[(channels_used+x)%nphysical_in]; + } + + if (port.length() && track->connect_input (track->input (x), port, this)) { + break; + } + } + } + + for (uint32_t x = 0; x < track->n_outputs(); ++x) { port = ""; - if (input_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((channels_used+x)%nphysical_in); - } + if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { + port = physoutputs[(channels_used+x)%nphysical_out]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs())->name(); + } + } - if (port.length() && track->connect_input (track->input (x), port, this)) { + if (port.length() && track->connect_output (track->output (x), port, this)) { break; } } - } - - for (uint32_t x = 0; x < track->n_outputs(); ++x) { - port = ""; + channels_used += track->n_inputs (); - if (nphysical_out && (output_auto_connect & AutoConnectPhysical)) { - port = _engine.get_nth_physical_output ((channels_used+x)%nphysical_out); - } else if (output_auto_connect & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs())->name(); + if (_control_out) { + vector cports; + uint32_t ni = _control_out->n_inputs(); + + for (n = 0; n < ni; ++n) { + cports.push_back (_control_out->input(n)->name()); } + + track->set_control_outs (cports); } + + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); + track->set_remote_control_id (ntracks()); - if (port.length() && track->connect_output (track->output (x), port, this)) { - break; - } + new_routes.push_back (track); + ret.push_back (track); } - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs(); - - for (n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - - track->set_control_outs (cports); + catch (failed_constructor &err) { + error << _("Session: could not create new audio track.") << endmsg; + // XXX should we delete the tracks already created? + ret.clear (); + return ret; } - - track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); - - add_route (track); - - track->set_remote_control_id (ntracks()); - return track; + + --how_many; } - catch (failed_constructor &err) { - error << _("Session: could not create new audio track.") << endmsg; - return shared_ptr ((AudioTrack*) 0); + if (!new_routes.empty()) { + add_routes (new_routes, false); + save_state (_current_snapshot_name); } + + return ret; } -shared_ptr -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) { char bus_name[32]; + uint32_t bus_id = 1; uint32_t n = 0; string port; + RouteList ret; /* count existing audio busses */ @@ -1792,126 +1737,151 @@ Session::new_audio_route (int input_channels, int output_channels) 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 { - shared_ptr bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); + while (how_many) { - if (bus->ensure_io (input_channels, output_channels, false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - } + do { + ++bus_id; - for (uint32_t x = 0; x < bus->n_inputs(); ++x) { - - port = ""; + snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id); - if (input_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((n+x)%n_physical_inputs); - } - - if (port.length() && bus->connect_input (bus->input (x), port, this)) { + if (route_by_name (bus_name) == 0) { break; } - } - for (uint32_t x = 0; x < bus->n_outputs(); ++x) { - - port = ""; + } while (bus_id < (UINT_MAX-1)); - if (output_auto_connect & AutoConnectPhysical) { - port = _engine.get_nth_physical_input ((n+x)%n_physical_outputs); - } else if (output_auto_connect & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs())->name(); + try { + shared_ptr bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); + + if (bus->ensure_io (input_channels, output_channels, false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + } + + for (uint32_t x = 0; x < bus->n_inputs(); ++x) { + + port = ""; + + 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; x < bus->n_outputs(); ++x) { + + port = ""; + + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + port = physoutputs[((n+x)%n_physical_outputs)]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs())->name(); + } + } + + if (port.length() && bus->connect_output (bus->output (x), port, this)) { + break; + } + } + + if (_control_out) { + vector cports; + uint32_t ni = _control_out->n_inputs(); + + for (uint32_t n = 0; n < ni; ++n) { + cports.push_back (_control_out->input(n)->name()); + } + bus->set_control_outs (cports); } - } - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs(); + ret.push_back (bus); + } + - for (uint32_t n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - bus->set_control_outs (cports); + catch (failed_constructor &err) { + error << _("Session: could not create new audio route.") << endmsg; + ret.clear (); + return ret; } - - add_route (bus); - return bus; + + --how_many; } - catch (failed_constructor &err) { - error << _("Session: could not create new audio route.") << endmsg; - return shared_ptr ((Route*) 0); + if (!ret.empty()) { + add_routes (ret, false); + save_state (_current_snapshot_name); } + + return ret; + } void -Session::add_route (shared_ptr route) +Session::add_routes (RouteList& new_routes, bool save) { { RCUWriter writer (routes); shared_ptr r = writer.get_copy (); - r->push_front (route); + 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_with_alloc(); { - Glib::RWLock::WriterLock lm (diskstream_lock); - 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)); @@ -1919,11 +1889,6 @@ Session::add_diskstream (Diskstream* dstream) diskstream_playlist_changed (dstream); dstream->prepare (); - - set_dirty(); - save_state (_current_snapshot_name); - - DiskstreamAdded (dstream); /* EMIT SIGNAL */ } void @@ -1940,11 +1905,11 @@ Session::remove_route (shared_ptr route) */ if (route == _master_out) { - _master_out = shared_ptr ((Route*) 0); + _master_out = shared_ptr (); } if (route == _control_out) { - _control_out = shared_ptr ((Route*) 0); + _control_out = shared_ptr (); /* cancel control outs for all routes */ @@ -1962,32 +1927,45 @@ Session::remove_route (shared_ptr route) // FIXME: audio specific AudioTrack* at; - AudioDiskstream* ds = 0; + boost::shared_ptr ds; if ((at = dynamic_cast(route.get())) != 0) { - ds = &at->audio_diskstream(); + ds = at->audio_diskstream(); } if (ds) { { - Glib::RWLock::WriterLock lm (diskstream_lock); - diskstreams.remove (ds); + RCUWriter dsl (diskstreams); + boost::shared_ptr d = dsl.get_copy(); + d->remove (ds); } - - ds->unref (); } 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 (); - /* all shared ptrs to route should go out of scope here */ + /* save the new state of the world */ + + if (save_state (_current_snapshot_name)) { + save_history (_current_snapshot_name); + } } void @@ -1997,7 +1975,7 @@ Session::route_mute_changed (void* src) } void -Session::route_solo_changed (void* src, shared_ptr route) +Session::route_solo_changed (void* src, boost::weak_ptr wpr) { if (solo_update_disabled) { // We know already @@ -2005,8 +1983,15 @@ Session::route_solo_changed (void* src, shared_ptr route) } bool is_track; - - is_track = (dynamic_cast(route.get()) != 0); + 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); shared_ptr r = routes.reader (); @@ -2042,7 +2027,7 @@ Session::route_solo_changed (void* src, shared_ptr route) then leave it as it is. */ - if (_solo_latched) { + if (Config->get_solo_latched()) { continue; } } @@ -2091,16 +2076,6 @@ Session::route_solo_changed (void* src, shared_ptr 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 () { @@ -2225,6 +2200,20 @@ Session::route_by_name (string name) return shared_ptr ((Route*) 0); } +shared_ptr +Session::route_by_id (PBD::ID id) +{ + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->id() == id) { + return *i; + } + } + + return shared_ptr ((Route*) 0); +} + shared_ptr Session::route_by_remote_id (uint32_t id) { @@ -2246,7 +2235,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); @@ -2255,17 +2244,15 @@ 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) { + for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { Playlist* pl = (*i)->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; @@ -2275,32 +2262,32 @@ Session::get_maximum_extent () const return max; } -Diskstream * +boost::shared_ptr Session::diskstream_by_name (string name) { - Glib::RWLock::ReaderLock lm (diskstream_lock); + 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 * +boost::shared_ptr Session::diskstream_by_id (const PBD::ID& id) { - Glib::RWLock::ReaderLock lm (diskstream_lock); + 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 */ @@ -2424,24 +2411,24 @@ 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 ar; + boost::shared_ptr oar; bool added = false; { Glib::Mutex::Lock lm (region_lock); - if ((ar = dynamic_cast (region)) != 0) { + if ((ar = boost::dynamic_pointer_cast (region)) != 0) { AudioRegionList::iterator x; for (x = audio_regions.begin(); x != audio_regions.end(); ++x) { - oar = dynamic_cast (x->second); + oar = boost::dynamic_pointer_cast (x->second); - if (ar->region_list_equivalent (*oar)) { + if (ar->region_list_equivalent (oar)) { break; } } @@ -2454,6 +2441,7 @@ Session::add_region (Region* region) entry.second = ar; pair x = audio_regions.insert (entry); + if (!x.second) { return; @@ -2479,15 +2467,21 @@ Session::add_region (Region* region) set_dirty(); if (added) { - region->GoingAway.connect (mem_fun (*this, &Session::remove_region)); - region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), region)); + region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr(region))); + region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr(region))); AudioRegionAdded (ar); /* EMIT SIGNAL */ } } void -Session::region_changed (Change what_changed, Region* region) +Session::region_changed (Change what_changed, boost::weak_ptr weak_region) { + boost::shared_ptr region (weak_region.lock ()); + + if (!region) { + return; + } + if (what_changed & Region::HiddenChanged) { /* relay hidden changes */ RegionHiddenChange (region); @@ -2495,22 +2489,22 @@ Session::region_changed (Change what_changed, Region* region) } void -Session::region_renamed (Region* region) -{ - add_region (region); -} - -void -Session::remove_region (Region* region) +Session::remove_region (boost::weak_ptr weak_region) { AudioRegionList::iterator i; - AudioRegion* ar = 0; + boost::shared_ptr region (weak_region.lock ()); + + if (!region) { + return; + } + + boost::shared_ptr ar; bool removed = false; - + { Glib::Mutex::Lock lm (region_lock); - if ((ar = dynamic_cast (region)) != 0) { + if ((ar = boost::dynamic_pointer_cast (region)) != 0) { if ((i = audio_regions.find (region->id())) != audio_regions.end()) { audio_regions.erase (i); removed = true; @@ -2536,11 +2530,11 @@ Session::remove_region (Region* region) } } -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; + boost::shared_ptr region; Glib::Mutex::Lock lm (region_lock); for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { @@ -2549,49 +2543,51 @@ Session::find_whole_file_parent (AudioRegion& child) 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 (Region& region, vector& result) +Session::find_equivalent_playlist_regions (boost::shared_ptr region, vector >& 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; + boost::shared_ptr aregion; - if ((aregion = dynamic_cast (region)) == 0) { + if ((aregion = boost::dynamic_pointer_cast (region)) == 0) { return 0; } - + if (aregion->playlist()) { aregion->playlist()->destroy_region (region); } - vector srcs; + vector > srcs; for (uint32_t n = 0; n < aregion->n_channels(); ++n) { - srcs.push_back (&aregion->source (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) { - AudioFileSource* afs = dynamic_cast(*i); + if ((*i).use_count() == 1) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*i); + if (afs) { (afs)->mark_for_remove (); } - delete *i; + + (*i)->drop_references (); } } @@ -2599,9 +2595,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; @@ -2610,12 +2606,12 @@ Session::destroy_regions (list regions) int Session::remove_last_capture () { - list r; - - Glib::RWLock::ReaderLock lm (diskstream_lock); + list > r; - for (DiskstreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { - list& l = (*i)->last_capture_regions(); + boost::shared_ptr dsl = diskstreams.reader(); + + 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()); @@ -2628,63 +2624,81 @@ 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_audio_source (AudioSource* source) +Session::add_source (boost::shared_ptr source) { - pair entry; + boost::shared_ptr afs; + + if ((afs = boost::dynamic_pointer_cast(source)) != 0) { + + pair entry; + pair result; - { - Glib::Mutex::Lock lm (audio_source_lock); entry.first = source->id(); - entry.second = source; - audio_sources.insert (entry); - } - - source->GoingAway.connect (mem_fun (this, &Session::remove_source)); - set_dirty(); - - SourceAdded (source); /* EMIT SIGNAL */ + entry.second = afs; + + { + Glib::Mutex::Lock lm (audio_source_lock); + result = audio_sources.insert (entry); + } + + if (!result.second) { + cerr << "\tNOT inserted ? " << result.second << endl; + } + + source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); + set_dirty(); + + SourceAdded (source); /* EMIT SIGNAL */ + } else { + cerr << "\tNOT AUDIO FILE\n"; + } } void -Session::remove_source (Source* source) +Session::remove_source (boost::weak_ptr src) { AudioSourceList::iterator i; + boost::shared_ptr source = src.lock(); + + if (!source) { + return; + } { Glib::Mutex::Lock lm (audio_source_lock); - + if ((i = audio_sources.find (source->id())) != audio_sources.end()) { audio_sources.erase (i); } } - + if (!_state_of_the_state & InCleanup) { - + /* save state so we don't end up with a session file referring to non-existent sources. */ save_state (_current_snapshot_name); } - + SourceRemoved(source); /* EMIT SIGNAL */ } -Source * +boost::shared_ptr Session::source_by_id (const PBD::ID& id) { Glib::Mutex::Lock lm (audio_source_lock); AudioSourceList::iterator i; - Source* source = 0; + boost::shared_ptr source; if ((i = audio_sources.find (id)) != audio_sources.end()) { source = i->second; @@ -2696,17 +2710,11 @@ Session::source_by_id (const PBD::ID& id) } 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 = Glib::path_get_dirname (audio_path); - res = Glib::path_get_dirname (res); - res += '/'; - res += peak_dir_name; - res += '/'; + res = peak_dir (); res += PBD::basename_nosuffix (audio_path); res += ".peak"; @@ -2852,11 +2860,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool spath = (*i).path; - if (destructive) { - spath += tape_dir_name; - } else { - spath += sound_dir_name; - } + spath += sound_dir (false); if (destructive) { if (nchan < 2) { @@ -2892,9 +2896,10 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool } } - if (access (buf, F_OK) == 0) { + if (g_file_test (buf, G_FILE_TEST_EXISTS)) { existing++; - } + } + } if (existing == 0) { @@ -2913,11 +2918,7 @@ 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 (); - } + spath = discover_best_sound_dir (); string::size_type pos = foo.find_last_of ('/'); @@ -2930,24 +2931,11 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool return spath; } -AudioFileSource * +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(), chan, destructive); - - /* this might throw failed_constructor(), which is OK */ - - if (destructive) { - return new DestructiveFileSource (spath, - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - frame_rate()); - } else { - return new SndFileSource (spath, - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - frame_rate()); - } + return boost::dynamic_pointer_cast (SourceFactory::createWritable (*this, spath, destructive, frame_rate())); } /* Playlist management */ @@ -2982,7 +2970,7 @@ Session::add_playlist (Playlist* playlist) playlists.insert (playlists.begin(), playlist); // playlist->ref(); playlist->InUse.connect (mem_fun (*this, &Session::track_playlist)); - playlist->GoingAway.connect (mem_fun (*this, &Session::remove_playlist)); + playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), playlist)); } } @@ -3053,50 +3041,47 @@ 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); schedule_butler_transport_work (); } +void +Session::audition_playlist () +{ + Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); + ev->region.reset (); + queue_event (ev); +} + void Session::non_realtime_set_audition () { - if (pending_audition_region == (AudioRegion*) 0xfeedface) { + if (!pending_audition_region) { auditioner->audition_current_playlist (); - } else if (pending_audition_region) { - auditioner->audition_region (*pending_audition_region); + } else { + auditioner->audition_region (pending_audition_region); + pending_audition_region.reset (); } - pending_audition_region = 0; AuditionActive (true); /* EMIT SIGNAL */ } void -Session::audition_playlist () +Session::audition_region (boost::shared_ptr r) { Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); - ev->set_ptr ((void*) 0xfeedface); + ev->region = r; queue_event (ev); } -void -Session::audition_region (Region& r) -{ - AudioRegion* ar = dynamic_cast(&r); - if (ar) { - Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); - ev->set_ptr (ar); - queue_event (ev); - } -} - void Session::cancel_audition () { if (auditioner->active()) { auditioner->cancel_audition (); - AuditionActive (false); /* EMIT SIGNAL */ + AuditionActive (false); /* EMIT SIGNAL */ } } @@ -3109,17 +3094,35 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: void Session::remove_empty_sounds () { - PathScanner scanner; - string dir; - - dir = sound_dir (); - vector* possible_audiofiles = scanner (dir, "\\.wav$", false, true); + vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); + + Glib::Mutex::Lock lm (audio_source_lock); + regex_t compiled_tape_track_pattern; + int err; + + if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) { + + char msg[256]; + + regerror (err, &compiled_tape_track_pattern, msg, sizeof (msg)); + + error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg; + return; + } + for (vector::iterator i = possible_audiofiles->begin(); i != possible_audiofiles->end(); ++i) { + + /* never remove files that appear to be a tape track */ - if (AudioFileSource::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()); @@ -3175,10 +3178,11 @@ Session::set_all_mute (bool yn) uint32_t Session::n_diskstreams () const { - Glib::RWLock::ReaderLock lm (diskstream_lock); 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++; } @@ -3197,15 +3201,15 @@ Session::graph_reordered () return; } - Glib::RWLock::ReaderLock lm2 (diskstream_lock); - 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 (); } } @@ -3262,7 +3266,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(); } @@ -3294,16 +3298,28 @@ 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; + } + + 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 @@ -3355,23 +3371,6 @@ Session::connection_by_name (string name) const return 0; } -void -Session::set_edit_mode (EditMode mode) -{ - _edit_mode = mode; - - { - Glib::Mutex::Lock lm (playlist_lock); - - 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) { @@ -3484,11 +3483,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__); - Glib::RWLock::ReaderLock lm2 (diskstream_lock); + 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); } } @@ -3508,9 +3505,9 @@ Session::route_name_unique (string n) const } int -Session::cleanup_audio_file_source (AudioFileSource& fs) +Session::cleanup_audio_file_source (boost::shared_ptr fs) { - return fs.move_to_trash (dead_sound_dir_name); + return fs->move_to_trash (dead_sound_dir_name); } uint32_t @@ -3521,17 +3518,7 @@ Session::n_playlists () const } 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; @@ -3576,30 +3563,29 @@ Session::freeze (InterThreadInfo& itt) } int -Session::write_one_audio_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; - AudioFileSource* fsource; + boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; string dir; uint32_t nchans; - jack_nframes_t position; - jack_nframes_t this_chunk; - jack_nframes_t to_do; + nframes_t position; + nframes_t this_chunk; + nframes_t to_do; vector buffers; - char * workbuf = 0; // any bigger than this seems to cause stack overflows in called functions - const jack_nframes_t chunk_size = (128 * 1024)/4; + const nframes_t chunk_size = (128 * 1024)/4; g_atomic_int_set (&processing_prohibited, 1); /* call tree *MUST* hold route_lock */ - if ((playlist = track.diskstream().playlist()) == 0) { + if ((playlist = track.diskstream()->playlist()) == 0) { goto out; } @@ -3609,7 +3595,7 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf goto out; } - nchans = track.audio_diskstream().n_channels(); + nchans = track.audio_diskstream()->n_channels(); dir = discover_best_sound_dir (); @@ -3628,11 +3614,7 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf } try { - fsource = new SndFileSource (buf, - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - frame_rate()); - + fsource = boost::dynamic_pointer_cast (SourceFactory::createWritable (*this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -3640,7 +3622,7 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf goto out; } - srcs.push_back(fsource); + srcs.push_back (fsource); } /* XXX need to flush all redirects */ @@ -3660,22 +3642,20 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf buffers.push_back (b); } - workbuf = new char[chunk_size * 4]; - while (to_do && !itt.cancel) { this_chunk = min (to_do, chunk_size); - if (track.export_stuff (buffers, workbuf, nchans, start, this_chunk)) { + if (track.export_stuff (buffers, nchans, start, this_chunk)) { goto out; } uint32_t n = 0; - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { - AudioFileSource* afs = dynamic_cast(*src); - + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + if (afs) { - if (afs->write (buffers[n], this_chunk, workbuf) != this_chunk) { + if (afs->write (buffers[n], this_chunk) != this_chunk) { goto out; } } @@ -3695,8 +3675,9 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf time (&now); xnow = localtime (&now); - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src) { - AudioFileSource* afs = dynamic_cast(*src); + 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); } @@ -3704,24 +3685,31 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf /* build peakfile for new source */ - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src) { - AudioFileSource* afs = dynamic_cast(*src); + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); if (afs) { afs->build_peaks (); } } - + + /* construct a region to represent the bounced material */ + + boost::shared_ptr aregion = RegionFactory::create (srcs, 0, srcs.front()->length(), + region_name_from_path (srcs.front()->name())); + ret = 0; } out: if (ret) { - for (vector::iterator src=srcs.begin(); src != srcs.end(); ++src) { - AudioFileSource* afs = dynamic_cast(*src); + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + if (afs) { afs->mark_for_remove (); } - delete *src; + + (*src)->drop_references (); } } @@ -3729,10 +3717,6 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf free(*i); } - if (workbuf) { - delete [] workbuf; - } - g_atomic_int_set (&processing_prohibited, 0); itt.done = true; @@ -3780,22 +3764,7 @@ 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) -{ - if (xm != xfade_model) { - xfade_model = xm; - set_dirty (); - ControlChanged (CrossfadingModel); - } -} -