X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=5b8d2f850677abea63df09685b996c7960d0e8f7;hb=803b502bf176c417a70498a98173fb09691a80b7;hp=962e8487bf0227724c8db1baf0927a45019c11ba;hpb=afa29d319007ce20bd1546c343e9277c58c80c1a;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 962e8487bf..5b8d2f8506 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include @@ -28,12 +27,14 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -45,12 +46,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -80,6 +81,12 @@ using namespace ARDOUR; using namespace PBD; using boost::shared_ptr; +#ifdef __x86_64__ +static const int CPU_CACHE_ALIGN = 64; +#else +static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */ +#endif + const char* Session::_template_suffix = X_(".template"); const char* Session::_statefile_suffix = X_(".ardour"); const char* Session::_pending_suffix = X_(".pending"); @@ -88,19 +95,31 @@ 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"); +const char* Session::export_dir_name = X_("export"); -Session::compute_peak_t Session::compute_peak = 0; +bool Session::_disable_all_loaded_plugins = false; + +Session::compute_peak_t Session::compute_peak = 0; +Session::find_peaks_t Session::find_peaks = 0; Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0; 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::Dialog; sigc::signal Session::AskAboutPendingState; +sigc::signal Session::AskAboutSampleRateMismatch; sigc::signal Session::SendFeedback; sigc::signal Session::SMPTEOffsetChanged; sigc::signal Session::StartTimeChanged; sigc::signal Session::EndTimeChanged; +sigc::signal Session::AutoBindingOn; +sigc::signal Session::AutoBindingOff; + + +sigc::signal Session::Exported; + int Session::find_session (string str, string& path, string& snapshot, bool& isnew) { @@ -252,9 +271,9 @@ Session::find_session (string str, string& path, string& snapshot, bool& isnew) } Session::Session (AudioEngine &eng, - string fullpath, - string snapshot_name, - string* mix_template) + const string& fullpath, + const string& snapshot_name, + string mix_template) : _engine (eng), _mmc_port (default_mmc_port), @@ -270,23 +289,28 @@ Session::Session (AudioEngine &eng, { bool new_session; + if (!eng.connected()) { + throw failed_constructor(); + } + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; - n_physical_outputs = _engine.n_physical_outputs(); - n_physical_inputs = _engine.n_physical_inputs(); + n_physical_audio_outputs = _engine.n_physical_audio_outputs(); + n_physical_audio_inputs = _engine.n_physical_audio_inputs(); first_stage_init (fullpath, snapshot_name); - new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + new_session = !Glib::file_test (_path, Glib::FileTest (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)) { - cerr << "2nd state failed\n"; + destroy (); throw failed_constructor (); } @@ -327,17 +351,21 @@ Session::Session (AudioEngine &eng, { bool new_session; + if (!eng.connected()) { + throw failed_constructor(); + } + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; - n_physical_outputs = _engine.n_physical_outputs(); - n_physical_inputs = _engine.n_physical_inputs(); + n_physical_audio_outputs = _engine.n_physical_audio_outputs(); + n_physical_audio_inputs = _engine.n_physical_audio_inputs(); - if (n_physical_inputs) { - n_physical_inputs = max (requested_physical_in, n_physical_inputs); + if (n_physical_audio_inputs) { + n_physical_audio_inputs = max (requested_physical_in, n_physical_audio_inputs); } - if (n_physical_outputs) { - n_physical_outputs = max (requested_physical_out, n_physical_outputs); + if (n_physical_audio_outputs) { + n_physical_audio_outputs = max (requested_physical_out, n_physical_audio_outputs); } first_stage_init (fullpath, snapshot_name); @@ -345,49 +373,64 @@ Session::Session (AudioEngine &eng, 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)) { + if (create (new_session, string(), initial_length)) { + destroy (); throw failed_constructor (); } } - if (control_out_channels) { - 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) { - shared_ptr r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); + { + /* set up Master Out and Control Out if necessary */ + 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); + int control_id = 1; + + if (control_out_channels) { + shared_ptr r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); + r->set_remote_control_id (control_id++); + + rl.push_back (r); + } + + if (master_out_channels) { + shared_ptr r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); + r->set_remote_control_id (control_id); + + rl.push_back (r); + } else { + /* prohibit auto-connect to master, because there isn't one */ + output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); + } + + if (!rl.empty()) { + add_routes (rl, false); + } + } Config->set_input_auto_connect (input_ac); Config->set_output_auto_connect (output_ac); if (second_stage_init (new_session)) { + destroy (); throw failed_constructor (); } - store_recent_sessions(_name, _path); + store_recent_sessions (_name, _path); - bool was_dirty = dirty (); - _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - if (was_dirty) { - DirtyChanged (); /* EMIT SIGNAL */ - } + + Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); } Session::~Session () +{ + destroy (); +} + +void +Session::destroy () { /* if we got to here, leaving pending capture state around is a mistake. @@ -396,6 +439,7 @@ Session::~Session () remove_pending_capture_state (); _state_of_the_state = StateOfTheState (CannotSave|Deletion); + _engine.remove_session (); GoingAway (); /* EMIT SIGNAL */ @@ -540,14 +584,13 @@ Session::~Session () tmp = i; ++tmp; - + i->second->drop_references (); - + i = tmp; } - audio_sources.clear (); - + #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; #endif /* TRACK_DESTRUCTION */ @@ -630,9 +673,13 @@ Session::when_engine_running () /* we don't want to run execute this again */ + BootMessage (_("Set block size and sample rate")); + set_block_size (_engine.frames_per_cycle()); set_frame_rate (_engine.frame_rate()); + BootMessage (_("Using configuration")); + Config->map_parameters (mem_fun (*this, &Session::config_changed)); /* every time we reconnect, recompute worst case output latencies */ @@ -672,7 +719,7 @@ Session::when_engine_running () /* default state for Click */ - first_physical_output = _engine.get_nth_physical_output (0); + first_physical_output = _engine.get_nth_physical_audio_output (0); if (first_physical_output.length()) { if (_click_io->add_output_port (first_physical_output, this)) { @@ -688,6 +735,8 @@ Session::when_engine_running () error << _("cannot setup Click I/O") << endmsg; } + BootMessage (_("Compute I/O Latencies")); + set_worst_io_latencies (); if (_clicking) { @@ -698,35 +747,37 @@ Session::when_engine_running () to the physical outputs currently available */ + BootMessage (_("Set up standard connections")); + /* ONE: MONO */ - for (uint32_t np = 0; np < n_physical_outputs; ++np) { + for (uint32_t np = 0; np < n_physical_audio_outputs; ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); 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_audio_output (np)); add_connection (c); } - for (uint32_t np = 0; np < n_physical_inputs; ++np) { + for (uint32_t np = 0; np < n_physical_audio_inputs; ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); 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_audio_input (np)); add_connection (c); } /* TWO: STEREO */ - for (uint32_t np = 0; np < n_physical_outputs; np +=2) { + for (uint32_t np = 0; np < n_physical_audio_outputs; np +=2) { char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2); @@ -734,13 +785,13 @@ 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_audio_output (np)); + c->add_connection (1, _engine.get_nth_physical_audio_output (np+1)); add_connection (c); } - for (uint32_t np = 0; np < n_physical_inputs; np +=2) { + for (uint32_t np = 0; np < n_physical_audio_inputs; np +=2) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2); @@ -748,8 +799,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_audio_input (np)); + c->add_connection (1, _engine.get_nth_physical_audio_input (np+1)); add_connection (c); } @@ -782,7 +833,7 @@ Session::when_engine_running () } 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)) { + if (_master_out->add_output_port (_engine.get_nth_physical_audio_output (n), this)) { error << _("cannot setup master outputs") << endmsg; break; @@ -802,11 +853,15 @@ Session::when_engine_running () } add_connection (c); } + + BootMessage (_("Setup signal flow and plugins")); hookup_io (); /* catch up on send+insert cnts */ + BootMessage (_("Catch up with send/insert state")); + insert_cnt = 0; for (list::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { @@ -836,17 +891,18 @@ Session::when_engine_running () /* hook us up to the engine */ + BootMessage (_("Connect to engine")); + _engine.set_session (this); #ifdef HAVE_LIBLO /* and to OSC */ + BootMessage (_("OSC startup")); + osc->set_session (*this); #endif - _state_of_the_state = Clean; - - DirtyChanged (); /* EMIT SIGNAL */ } void @@ -858,6 +914,7 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); + if (auditioner == 0) { /* we delay creating the auditioner till now because @@ -880,6 +937,7 @@ Session::hookup_io () if (_control_out) { uint32_t n; + vector cports; while ((int) _control_out->n_inputs() < _control_out->input_maximum()) { if (_control_out->add_input_port ("", this)) { @@ -890,14 +948,27 @@ 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)) { + if (_control_out->add_output_port (_engine.get_nth_physical_audio_output (n), this)) { error << _("cannot set up master outputs") << endmsg; break; } n++; } - } + + + uint32_t ni = _control_out->n_inputs(); + + for (n = 0; n < ni; ++n) { + cports.push_back (_control_out->input(n)->name()); + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { + (*x)->set_control_outs (cports); + } + } /* Tell all IO objects to connect themselves together */ @@ -913,6 +984,7 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state & ~InitialConnecting); + /* now handle the whole enchilada as if it was one graph reorder event. */ @@ -937,8 +1009,15 @@ Session::playlist_length_changed () } void -Session::diskstream_playlist_changed (boost::shared_ptr dstream) +Session::diskstream_playlist_changed (boost::weak_ptr wptr) { + boost::shared_ptr dstream = wptr.lock(); + + if (!dstream) { + return; + + } + boost::shared_ptr playlist; if ((playlist = dstream->playlist()) != 0) { @@ -1049,7 +1128,6 @@ Session::auto_loop_changed (Location* location) } last_loopend = location->end(); - } void @@ -1087,6 +1165,10 @@ Session::set_auto_punch_location (Location* location) auto_punch_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_punch_changed)); location->set_auto_punch (true, this); + + + auto_punch_changed (location); + auto_punch_location_changed (location); } @@ -1126,6 +1208,13 @@ Session::set_auto_loop_location (Location* location) auto_loop_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_loop_changed)); location->set_auto_loop (true, this); + + /* take care of our stuff first */ + + auto_loop_changed (location); + + /* now tell everyone else */ + auto_loop_location_changed (location); } @@ -1203,7 +1292,7 @@ Session::disable_record (bool rt_context, bool force) if ((rs = (RecordState) g_atomic_int_get (&_record_status)) != Disabled) { - if (!Config->get_latched_record_enable () || force) { + if ((!Config->get_latched_record_enable () && !play_loop) || force) { g_atomic_int_set (&_record_status, Disabled); } else { if (rs == Recording) { @@ -1234,15 +1323,18 @@ Session::disable_record (bool rt_context, bool force) void Session::step_back_from_record () { - g_atomic_int_set (&_record_status, Enabled); + /* XXX really atomic compare+swap here */ + if (g_atomic_int_get (&_record_status) == Recording) { + g_atomic_int_set (&_record_status, Enabled); - if (Config->get_monitoring_model() == HardwareMonitoring) { - boost::shared_ptr dsl = diskstreams.reader(); - - 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); + 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 ()) { + //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl; + (*i)->monitor_input (false); + } } } } @@ -1343,7 +1435,9 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); - Route::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * 0.25)); + IO::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); + + clear_clicks (); // XXX we need some equivalent to this, somehow // SndFileSource::setup_standard_crossfades (frames_per_second); @@ -1367,7 +1461,7 @@ Session::set_block_size (nframes_t nframes) uint32_t np; current_block_size = nframes; - + for (np = 0, i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i, ++np) { free (*i); } @@ -1388,7 +1482,7 @@ Session::set_block_size (nframes_t nframes) #ifdef NO_POSIX_MEMALIGN buf = (Sample *) malloc(current_block_size * sizeof(Sample)); #else - posix_memalign((void **)&buf,16,current_block_size * 4); + posix_memalign((void **)&buf,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)); #endif *i = buf; @@ -1606,6 +1700,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod string port; RouteList new_routes; list > ret; + uint32_t control_id; /* count existing audio tracks */ @@ -1627,8 +1722,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod uint32_t nphysical_in; uint32_t nphysical_out; - _engine.get_physical_outputs (physoutputs); - _engine.get_physical_inputs (physinputs); + _engine.get_physical_audio_outputs (physoutputs); + _engine.get_physical_audio_inputs (physinputs); + control_id = ntracks() + nbusses() + 1; while (how_many) { @@ -1651,26 +1747,29 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod } while (track_id < (UINT_MAX-1)); if (Config->get_input_auto_connect() & AutoConnectPhysical) { - nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size()); + nphysical_in = min (n_physical_audio_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()); + nphysical_out = min (n_physical_audio_outputs, (uint32_t) physinputs.size()); } else { nphysical_out = 0; } + + shared_ptr track; try { - shared_ptr track (new AudioTrack (*this, track_name, Route::Flag (0), mode)); + track = boost::shared_ptr((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; + goto failed; } - + if (nphysical_in) { for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) { @@ -1705,42 +1804,84 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod channels_used += track->n_inputs (); - 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->audio_diskstream()->non_realtime_input_change(); track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); - track->set_remote_control_id (ntracks()); + track->set_remote_control_id (control_id); + ++control_id; new_routes.push_back (track); ret.push_back (track); + } 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; + + if (track) { + /* we need to get rid of this, since the track failed to be created */ + /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ + + { + RCUWriter writer (diskstreams); + boost::shared_ptr ds = writer.get_copy(); + ds->remove (track->audio_diskstream()); + } + } + + goto failed; } - + + catch (AudioEngine::PortRegistrationFailure& pfe) { + + error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg; + + if (track) { + /* we need to get rid of this, since the track failed to be created */ + /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ + + { + RCUWriter writer (diskstreams); + boost::shared_ptr ds = writer.get_copy(); + ds->remove (track->audio_diskstream()); + } + } + + goto failed; + } + --how_many; } + failed: if (!new_routes.empty()) { - add_routes (new_routes, false); - save_state (_current_snapshot_name); + add_routes (new_routes, true); } return ret; } +void +Session::set_remote_control_ids () +{ + RemoteModel m = Config->get_remote_model(); + + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ( MixerOrdered == m) { + long order = (*i)->order_key(N_("signal")); + (*i)->set_remote_control_id( order+1 ); + } else if ( EditorOrdered == m) { + long order = (*i)->order_key(N_("editor")); + (*i)->set_remote_control_id( order+1 ); + } else if ( UserOrdered == m) { + //do nothing ... only changes to remote id's are initiated by user + } + } +} + + Session::RouteList Session::new_audio_route (int input_channels, int output_channels, uint32_t how_many) { @@ -1749,6 +1890,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ uint32_t n = 0; string port; RouteList ret; + uint32_t control_id; /* count existing audio busses */ @@ -1757,7 +1899,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (dynamic_cast((*i).get()) == 0) { - if (!(*i)->hidden()) { + if (!(*i)->hidden() && (*i)->name() != _("master")) { bus_id++; } } @@ -1767,16 +1909,17 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ vector physinputs; vector physoutputs; - _engine.get_physical_outputs (physoutputs); - _engine.get_physical_inputs (physinputs); + _engine.get_physical_audio_outputs (physoutputs); + _engine.get_physical_audio_inputs (physinputs); + control_id = ntracks() + nbusses() + 1; while (how_many) { do { - ++bus_id; - snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id); + bus_id++; + if (route_by_name (bus_name) == 0) { break; } @@ -1790,14 +1933,15 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; + goto failure; } - for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs(); ++x) { + for (uint32_t x = 0; n_physical_audio_inputs && x < bus->n_inputs(); ++x) { port = ""; if (Config->get_input_auto_connect() & AutoConnectPhysical) { - port = physinputs[((n+x)%n_physical_inputs)]; + port = physinputs[((n+x)%n_physical_audio_inputs)]; } if (port.length() && bus->connect_input (bus->input (x), port, this)) { @@ -1805,12 +1949,12 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } } - for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs(); ++x) { + for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs(); ++x) { port = ""; if (Config->get_output_auto_connect() & AutoConnectPhysical) { - port = physoutputs[((n+x)%n_physical_outputs)]; + port = physoutputs[((n+x)%n_physical_audio_outputs)]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { if (_master_out) { port = _master_out->input (x%_master_out->n_inputs())->name(); @@ -1822,15 +1966,8 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } } - 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); - } + bus->set_remote_control_id (control_id); + ++control_id; ret.push_back (bus); } @@ -1838,16 +1975,21 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ catch (failed_constructor &err) { error << _("Session: could not create new audio route.") << endmsg; - ret.clear (); - return ret; + goto failure; + } + + catch (AudioEngine::PortRegistrationFailure& pfe) { + error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg; + goto failure; } + --how_many; } + failure: if (!ret.empty()) { - add_routes (ret, false); - save_state (_current_snapshot_name); + add_routes (ret, true); } return ret; @@ -1879,9 +2021,24 @@ Session::add_routes (RouteList& new_routes, bool save) if ((*x)->control()) { _control_out = (*x); - } + } } + if (_control_out && IO::connecting_legal) { + + vector cports; + uint32_t ni = _control_out->n_inputs(); + uint32_t n; + + for (n = 0; n < ni; ++n) { + cports.push_back (_control_out->input(n)->name()); + } + + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { + (*x)->set_control_outs (cports); + } + } + set_dirty(); if (save) { @@ -1895,17 +2052,19 @@ void 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(); + dstream->do_refill_with_alloc (); - { + dstream->set_block_size (current_block_size); + + { RCUWriter writer (diskstreams); boost::shared_ptr ds = writer.get_copy(); ds->push_back (dstream); - } - - dstream->set_block_size (current_block_size); + /* writer goes out of scope, copies ds back to main */ + } - dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream)); + dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), + boost::weak_ptr (dstream))); /* this will connect to future changes, and check the current length */ diskstream_playlist_changed (dstream); @@ -1962,17 +2121,20 @@ Session::remove_route (shared_ptr route) boost::shared_ptr d = dsl.get_copy(); d->remove (ds); } + + diskstreams.flush (); } find_current_end (); + // We need to disconnect the routes inputs and outputs + + route->disconnect_inputs (0); + route->disconnect_outputs (0); + update_latency_compensation (false, false); set_dirty(); - // We need to disconnect the routes inputs and outputs - route->disconnect_inputs(NULL); - route->disconnect_outputs(NULL); - /* 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) */ @@ -2092,9 +2254,11 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) modify_solo_mute (is_track, same_thing_soloed); if (signal) { - SoloActive (currently_soloing); + SoloActive (currently_soloing); /* EMIT SIGNAL */ } + SoloChanged (); /* EMIT SIGNAL */ + set_dirty(); } @@ -2434,6 +2598,14 @@ Session::region_name (string& result, string base, bool newlevel) const void Session::add_region (boost::shared_ptr region) +{ + vector > v; + v.push_back (region); + add_regions (v); +} + +void +Session::add_regions (vector >& new_regions) { boost::shared_ptr ar; boost::shared_ptr oar; @@ -2442,43 +2614,52 @@ Session::add_region (boost::shared_ptr region) { Glib::Mutex::Lock lm (region_lock); - if ((ar = boost::dynamic_pointer_cast (region)) != 0) { - - AudioRegionList::iterator x; - - for (x = audio_regions.begin(); x != audio_regions.end(); ++x) { - - oar = boost::dynamic_pointer_cast (x->second); - - if (ar->region_list_equivalent (oar)) { - break; - } - } - - if (x == audio_regions.end()) { - - pair entry; - - entry.first = region->id(); - entry.second = ar; + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { + + boost::shared_ptr region = *ii; + + if (region == 0) { - pair x = audio_regions.insert (entry); + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; + } else if ((ar = boost::dynamic_pointer_cast (region)) != 0) { - if (!x.second) { - return; + AudioRegionList::iterator x; + + for (x = audio_regions.begin(); x != audio_regions.end(); ++x) { + + oar = boost::dynamic_pointer_cast (x->second); + + if (ar->region_list_equivalent (oar)) { + break; + } } + + if (x == audio_regions.end()) { + + pair entry; + + entry.first = region->id(); + entry.second = ar; + + pair x = audio_regions.insert (entry); + + + if (!x.second) { + return; + } + + added = true; + } - added = true; - } - - } else { - - fatal << _("programming error: ") - << X_("unknown region type passed to Session::add_region()") - << endmsg; - /*NOTREACHED*/ - + } else { + + fatal << _("programming error: ") + << X_("unknown region type passed to Session::add_region()") + << endmsg; + /*NOTREACHED*/ + + } } } @@ -2489,9 +2670,34 @@ Session::add_region (boost::shared_ptr region) set_dirty(); if (added) { - 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 */ + + vector > v; + boost::shared_ptr first_ar; + + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { + + boost::shared_ptr region = *ii; + boost::shared_ptr ar; + + if (region == 0) { + + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; + + } else if ((ar = boost::dynamic_pointer_cast (region)) != 0) { + v.push_back (ar); + + if (!first_ar) { + first_ar = ar; + } + } + + region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr(region))); + region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr(region))); + } + + if (!v.empty()) { + AudioRegionsAdded (v); /* EMIT SIGNAL */ + } } } @@ -2648,6 +2854,9 @@ Session::remove_last_capture () } destroy_regions (r); + + save_state (_current_snapshot_name); + return 0; } @@ -2682,6 +2891,10 @@ Session::add_source (boost::shared_ptr source) source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); set_dirty(); } + + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } } } @@ -2729,13 +2942,30 @@ Session::source_by_id (const PBD::ID& id) return source; } -string -Session::peak_path_from_audio_path (string audio_path) const + +boost::shared_ptr +Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) { - string res; + Glib::Mutex::Lock lm (audio_source_lock); + for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ++i) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(i->second); + + if (afs && afs->path() == path && chn == afs->channel()) { + return afs; + } + + } + return boost::shared_ptr(); +} + +Glib::ustring +Session::peak_path (Glib::ustring base) const +{ + Glib::ustring res; + res = peak_dir (); - res += PBD::basename_nosuffix (audio_path); + res += base; res += ".peak"; return res; @@ -2929,6 +3159,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool 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(); } } @@ -3069,19 +3300,16 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) { Glib::Mutex::Lock lm (playlist_lock); - cerr << "removing playlist: " << playlist->name() << endl; PlaylistList::iterator i; i = find (playlists.begin(), playlists.end(), playlist); if (i != playlists.end()) { - cerr << "\tfound it in used playlist\n"; playlists.erase (i); } i = find (unused_playlists.begin(), unused_playlists.end(), playlist); if (i != unused_playlists.end()) { - cerr << "\tfound it in unused playlist\n"; unused_playlists.erase (i); } @@ -3148,7 +3376,7 @@ Session::remove_empty_sounds () { PathScanner scanner; - vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); + vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64|L|R)$", false, true); Glib::Mutex::Lock lm (audio_source_lock); @@ -3174,12 +3402,12 @@ Session::remove_empty_sounds () continue; } - if (AudioFileSource::is_empty (*this, *(*i))) { + if (AudioFileSource::is_empty (*this, **i)) { unlink ((*i)->c_str()); - string peak_path = peak_path_from_audio_path (**i); - unlink (peak_path.c_str()); + Glib::ustring peakpath = peak_path (PBD::basename_nosuffix (**i)); + unlink (peakpath.c_str()); } delete* i; @@ -3246,7 +3474,7 @@ void Session::graph_reordered () { /* don't do this stuff if we are setting up connections - from a set_state() call. + from a set_state() call or creating new tracks. */ if (_state_of_the_state & InitialConnecting) { @@ -3336,10 +3564,14 @@ 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 { @@ -3349,7 +3581,11 @@ Session::remove_redirect (Redirect* redirect) /*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*/ @@ -3361,15 +3597,19 @@ Session::remove_redirect (Redirect* redirect) nframes_t Session::available_capture_duration () { - float sample_bytes_on_disk; + float sample_bytes_on_disk = 4.0; // keep gcc happy switch (Config->get_native_file_data_format()) { case FormatFloat: - sample_bytes_on_disk = 4; + sample_bytes_on_disk = 4.0; break; case FormatInt24: - sample_bytes_on_disk = 3; + sample_bytes_on_disk = 3.0; + break; + + case FormatInt16: + sample_bytes_on_disk = 2.0; break; default: @@ -3442,18 +3682,36 @@ void Session::tempo_map_changed (Change ignored) { clear_clicks (); + + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + (*i)->update_after_tempo_map_change (); + } + + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + (*i)->update_after_tempo_map_change (); + } + set_dirty (); } void Session::ensure_passthru_buffers (uint32_t howmany) { + if (current_block_size == 0) { + return; + } + 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); + if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)) != 0) { + fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"), + current_block_size, sizeof (Sample), strerror (errno)) + << endmsg; + /*NOTREACHED*/ + } #endif _passthru_buffers.push_back (p); @@ -3462,7 +3720,12 @@ Session::ensure_passthru_buffers (uint32_t howmany) #ifdef NO_POSIX_MEMALIGN p = (Sample *) malloc(current_block_size * sizeof(Sample)); #else - posix_memalign((void **)&p,16,current_block_size * 4); + if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4) != 0) { + fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"), + current_block_size, sizeof (Sample), strerror (errno)) + << endmsg; + /*NOTREACHED*/ + } #endif memset (p, 0, sizeof (Sample) * current_block_size); _silent_buffers.push_back (p); @@ -3472,7 +3735,7 @@ Session::ensure_passthru_buffers (uint32_t howmany) #ifdef NO_POSIX_MEMALIGN p = (Sample *) malloc(current_block_size * sizeof(Sample)); #else - posix_memalign((void **)&p,16,current_block_size * 4); + posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)); #endif memset (p, 0, sizeof (Sample) * current_block_size); _send_buffers.push_back (p); @@ -3481,26 +3744,68 @@ Session::ensure_passthru_buffers (uint32_t howmany) allocate_pan_automation_buffers (current_block_size, howmany, false); } -string -Session::next_send_name () +uint32_t +Session::next_insert_id () { - uint32_t cnt = 0; - - shared_ptr r = routes.reader (); - - for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { - cnt += (*i)->count_sends (); + /* 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; + return n; + + } + } + + /* none available, so resize and try again */ + + 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; + return n; + + } + } + + /* none available, so resize and try again */ + + send_bitset.resize (send_bitset.size() + 16, false); } +} - return string_compose (_("send %1"), ++cnt); +void +Session::mark_send_id (uint32_t id) +{ + 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 */ @@ -3525,9 +3830,13 @@ Session::add_named_selection (NamedSelection* named_selection) named_selections.insert (named_selections.begin(), named_selection); } + for (list >::iterator i = named_selection->playlists.begin(); i != named_selection->playlists.end(); ++i) { + add_playlist (*i); + } + set_dirty(); - NamedSelectionAdded (); /* EMIT SIGNAL */ + NamedSelectionAdded (); /* EMIT SIGNAL */ } void @@ -3577,12 +3886,6 @@ Session::route_name_unique (string n) const return true; } -int -Session::cleanup_audio_file_source (boost::shared_ptr fs) -{ - return fs->move_to_trash (dead_sound_dir_name); -} - uint32_t Session::n_playlists () const { @@ -3710,11 +4013,15 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le #ifdef NO_POSIX_MEMALIGN b = (Sample *) malloc(chunk_size * sizeof(Sample)); #else - posix_memalign((void **)&b,16,chunk_size * 4); + posix_memalign((void **)&b,4096,chunk_size * sizeof(Sample)); #endif buffers.push_back (b); } + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + (*src)->prepare_for_peakfile_writes (); + } + while (to_do && !itt.cancel) { this_chunk = min (to_do, chunk_size); @@ -3753,18 +4060,10 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le if (afs) { afs->update_header (position, *xnow, now); + afs->flush_header (); } } - /* build peakfile for new source */ - - 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(), @@ -3784,6 +4083,11 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le (*src)->drop_references (); } + + } else { + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { + (*src)->done_with_peakfile_writes (); + } } for (vector::iterator i = buffers.begin(); i != buffers.end(); ++i) { @@ -3800,9 +4104,37 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le vector& Session::get_silent_buffers (uint32_t howmany) { + if (howmany > _silent_buffers.size()) { + + error << string_compose (_("Programming error: get_silent_buffers() called for %1 buffers but only %2 exist"), + howmany, _silent_buffers.size()) << endmsg; + + if (howmany > 1000) { + cerr << "ABSURD: more than 1000 silent buffers requested!\n"; + abort (); + } + + while (howmany > _silent_buffers.size()) { + Sample *p = 0; + +#ifdef NO_POSIX_MEMALIGN + p = (Sample *) malloc(current_block_size * sizeof(Sample)); +#else + if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4) != 0) { + fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"), + current_block_size, sizeof (Sample), strerror (errno)) + << endmsg; + /*NOTREACHED*/ + } +#endif + _silent_buffers.push_back (p); + } + } + for (uint32_t i = 0; i < howmany; ++i) { memset (_silent_buffers[i], 0, sizeof (Sample) * current_block_size); } + return _silent_buffers; } @@ -3848,3 +4180,19 @@ Session::compute_initial_length () return _engine.frame_rate() * 60 * 5; } +void +Session::sync_order_keys () +{ + if (!Config->get_sync_all_route_ordering()) { + /* leave order keys as they are */ + return; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->sync_order_keys (); + } + + Route::SyncOrderKeys (); // EMIT SIGNAL +}