X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Fsession.cc;h=5b8d2f850677abea63df09685b996c7960d0e8f7;hb=803b502bf176c417a70498a98173fb09691a80b7;hp=3bcaaf3aca5af26500b3ae2c6bca110f677293e6;hpb=a46aa719a67722864dbf36cb51a5e5bebc47bb83;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 3bcaaf3aca..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,28 +27,31 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -79,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"); @@ -87,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"); + +bool Session::_disable_all_loaded_plugins = false; -Session::compute_peak_t Session::compute_peak = 0; +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) { @@ -251,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), @@ -269,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, _engine.frame_rate() * 60 * 5)) { - cerr << "create failed\n"; + if (create (new_session, mix_template, compute_initial_length())) { + destroy (); throw failed_constructor (); } } if (second_stage_init (new_session)) { - cerr << "2nd state failed\n"; + destroy (); throw failed_constructor (); } @@ -326,58 +351,86 @@ 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 = max (requested_physical_out, _engine.n_physical_outputs()); - n_physical_inputs = max (requested_physical_in, _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_audio_inputs) { + n_physical_audio_inputs = max (requested_physical_in, n_physical_audio_inputs); + } + + if (n_physical_audio_outputs) { + n_physical_audio_outputs = max (requested_physical_out, n_physical_audio_outputs); + } first_stage_init (fullpath, snapshot_name); 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. @@ -386,6 +439,7 @@ Session::~Session () remove_pending_capture_state (); _state_of_the_state = StateOfTheState (CannotSave|Deletion); + _engine.remove_session (); GoingAway (); /* EMIT SIGNAL */ @@ -396,7 +450,7 @@ Session::~Session () /* clear history so that no references to objects are held any more */ - history.clear (); + _history.clear (); /* clear state tree so that no references to objects are held any more */ @@ -430,6 +484,10 @@ Session::~Session () } AudioDiskstream::free_working_buffers(); + + /* this should cause deletion of the auditioner */ + + // auditioner.reset (); #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION @@ -454,17 +512,38 @@ Session::~Session () tmp = i; ++tmp; - delete *i; + (*i)->drop_references (); i = tmp; } + + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { + PlaylistList::iterator tmp; + + tmp = i; + ++tmp; + + (*i)->drop_references (); + + i = tmp; + } + + playlists.clear (); + unused_playlists.clear (); #ifdef TRACK_DESTRUCTION cerr << "delete audio regions\n"; #endif /* TRACK_DESTRUCTION */ - for (AudioRegionList::iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) { + for (AudioRegionList::iterator i = audio_regions.begin(); i != audio_regions.end(); ) { + AudioRegionList::iterator tmp; + + tmp = i; + ++tmp; + i->second->drop_references (); + + i = tmp; } audio_regions.clear (); @@ -505,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 */ @@ -595,11 +673,13 @@ Session::when_engine_running () /* we don't want to run execute this again */ - first_time_running.disconnect (); + 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 */ @@ -639,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)) { @@ -655,62 +735,49 @@ Session::when_engine_running () error << _("cannot setup Click I/O") << endmsg; } + BootMessage (_("Compute I/O Latencies")); + set_worst_io_latencies (); if (_clicking) { // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } - if (auditioner == 0) { - - /* we delay creating the auditioner till now because - it makes its own connections to ports named - in the ARDOUR_RC config file. the engine has - to be running for this to work. - */ - - try { - auditioner.reset (new Auditioner (*this)); - } - - catch (failed_constructor& err) { - warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; - } - } - /* Create a set of Connection objects that map 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); @@ -718,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); @@ -732,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); } @@ -766,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; @@ -786,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) { @@ -815,21 +886,23 @@ Session::when_engine_running () } } + _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); /* 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 @@ -841,12 +914,30 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); + + if (auditioner == 0) { + + /* we delay creating the auditioner till now because + it makes its own connections to ports. + the engine has to be running for this to work. + */ + + try { + auditioner.reset (new Auditioner (*this)); + } + + catch (failed_constructor& err) { + warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; + } + } + /* Tell all IO objects to create their ports */ IO::enable_ports (); 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)) { @@ -857,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 */ @@ -880,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. */ @@ -892,7 +997,7 @@ Session::hookup_io () } void -Session::playlist_length_changed (Playlist* pl) +Session::playlist_length_changed () { /* we can't just increase end_location->end() if pl->get_maximum_extent() if larger. if the playlist used to be the longest playlist, @@ -904,12 +1009,19 @@ Session::playlist_length_changed (Playlist* pl) } void -Session::diskstream_playlist_changed (boost::shared_ptr dstream) +Session::diskstream_playlist_changed (boost::weak_ptr wptr) { - Playlist *playlist; + boost::shared_ptr dstream = wptr.lock(); + + if (!dstream) { + return; + + } + + boost::shared_ptr playlist; if ((playlist = dstream->playlist()) != 0) { - playlist->LengthChanged.connect (sigc::bind (mem_fun (this, &Session::playlist_length_changed), playlist)); + playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); } /* see comment in playlist_length_changed () */ @@ -1016,7 +1128,6 @@ Session::auto_loop_changed (Location* location) } last_loopend = location->end(); - } void @@ -1054,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); } @@ -1093,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); } @@ -1170,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) { @@ -1201,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); + } } } } @@ -1220,8 +1345,8 @@ 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); @@ -1310,10 +1435,12 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); - Route::set_automation_interval ((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 - // DestructiveFileSource::setup_standard_crossfades (frames_per_second); + // SndFileSource::setup_standard_crossfades (frames_per_second); set_dirty(); @@ -1334,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); } @@ -1355,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; @@ -1541,11 +1668,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"; @@ -1567,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 */ @@ -1588,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) { @@ -1612,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) { @@ -1666,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) { @@ -1710,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 */ @@ -1718,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++; } } @@ -1728,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; } @@ -1751,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; 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)) { @@ -1766,12 +1949,12 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } } - for (uint32_t x = 0; 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(); @@ -1783,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); } @@ -1799,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; @@ -1840,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) { @@ -1856,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); @@ -1879,8 +2077,9 @@ Session::remove_route (shared_ptr route) { RCUWriter writer (routes); shared_ptr rs = writer.get_copy (); - rs->remove (route); + rs->remove (route); + /* deleting the master out seems like a dumb idea, but its more of a UI policy issue than our concern. @@ -1922,13 +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(); - + /* 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) */ @@ -2048,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(); } @@ -2231,7 +2439,7 @@ Session::get_maximum_extent () const boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { - Playlist* pl = (*i)->playlist(); + boost::shared_ptr pl = (*i)->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; } @@ -2390,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; @@ -2398,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*/ + + } } } @@ -2445,15 +2670,46 @@ Session::add_region (boost::shared_ptr region) set_dirty(); if (added) { - region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), region)); - region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), 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 */ + } } } void -Session::region_changed (Change what_changed, boost::shared_ptr 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); @@ -2461,18 +2717,18 @@ Session::region_changed (Change what_changed, boost::shared_ptr region) } void -Session::region_renamed (boost::shared_ptr region) -{ - add_region (region); -} - -void -Session::remove_region (boost::shared_ptr region) +Session::remove_region (boost::weak_ptr weak_region) { AudioRegionList::iterator i; + boost::shared_ptr region (weak_region.lock ()); + + if (!region) { + return; + } + boost::shared_ptr ar; bool removed = false; - + { Glib::Mutex::Lock lm (region_lock); @@ -2498,12 +2754,12 @@ Session::remove_region (boost::shared_ptr region) set_dirty(); if (removed) { - AudioRegionRemoved(ar); /* EMIT SIGNAL */ + AudioRegionRemoved (ar); /* EMIT SIGNAL */ } } boost::shared_ptr -Session::find_whole_file_parent (boost::shared_ptr child) +Session::find_whole_file_parent (boost::shared_ptr child) { AudioRegionList::iterator i; boost::shared_ptr region; @@ -2521,7 +2777,7 @@ Session::find_whole_file_parent (boost::shared_ptr child) } } - return boost::shared_ptr ((AudioRegion*) 0); + return boost::shared_ptr (); } void @@ -2534,32 +2790,38 @@ Session::find_equivalent_playlist_regions (boost::shared_ptr region, vec int Session::destroy_region (boost::shared_ptr region) { - boost::shared_ptr aregion; - - if ((aregion = boost::dynamic_pointer_cast (region)) == 0) { - return 0; - } - - if (aregion->playlist()) { - aregion->playlist()->destroy_region (region); - } - vector > srcs; - - for (uint32_t n = 0; n < aregion->n_channels(); ++n) { - srcs.push_back (aregion->source (n)); + + { + boost::shared_ptr aregion; + + if ((aregion = boost::dynamic_pointer_cast (region)) == 0) { + return 0; + } + + if (aregion->playlist()) { + aregion->playlist()->destroy_region (region); + } + + for (uint32_t n = 0; n < aregion->n_channels(); ++n) { + srcs.push_back (aregion->source (n)); + } } + region->drop_references (); + for (vector >::iterator i = srcs.begin(); i != srcs.end(); ++i) { - - if ((*i).use_count() == 1) { - boost::shared_ptr afs = boost::dynamic_pointer_cast(*i); + if (!(*i)->used()) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*i); + if (afs) { (afs)->mark_for_remove (); } (*i)->drop_references (); + + cerr << "source was not used by any playlist\n"; } } @@ -2592,6 +2854,9 @@ Session::remove_last_capture () } destroy_regions (r); + + save_state (_current_snapshot_name); + return 0; } @@ -2622,17 +2887,15 @@ Session::add_source (boost::shared_ptr source) result = audio_sources.insert (entry); } - if (!result.second) { - cerr << "\tNOT inserted ? " << result.second << endl; + if (result.second) { + source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); + set_dirty(); } - 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"; - } + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } + } } void @@ -2642,28 +2905,24 @@ Session::remove_source (boost::weak_ptr src) boost::shared_ptr source = src.lock(); if (!source) { - cerr << "removing a source DEAD\n"; - } else { - cerr << "removing a source " << source->name () << endl; + return; + } + + { + Glib::Mutex::Lock lm (audio_source_lock); - { - Glib::Mutex::Lock lm (audio_source_lock); - - if ((i = audio_sources.find (source->id())) != audio_sources.end()) { - audio_sources.erase (i); - } - } + if ((i = audio_sources.find (source->id())) != audio_sources.end()) { + audio_sources.erase (i); + } + } + + if (!_state_of_the_state & InCleanup) { - 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); - } + /* save state so we don't end up with a session file + referring to non-existent sources. + */ - SourceRemoved(source); /* EMIT SIGNAL */ + save_state (_current_snapshot_name); } } @@ -2683,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; @@ -2850,6 +3126,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool } else { snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); } + } else { spath += '/'; @@ -2882,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(); } } @@ -2893,6 +3171,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool string foo = buf; spath = discover_best_sound_dir (); + spath += '/'; string::size_type pos = foo.find_last_of ('/'); @@ -2914,7 +3193,7 @@ Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bo /* Playlist management */ -Playlist * +boost::shared_ptr Session::playlist_by_name (string name) { Glib::Mutex::Lock lm (playlist_lock); @@ -2928,11 +3207,12 @@ Session::playlist_by_name (string name) return* i; } } - return 0; + + return boost::shared_ptr(); } void -Session::add_playlist (Playlist* playlist) +Session::add_playlist (boost::shared_ptr playlist) { if (playlist->hidden()) { return; @@ -2942,9 +3222,8 @@ Session::add_playlist (Playlist* playlist) Glib::Mutex::Lock lm (playlist_lock); if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) { playlists.insert (playlists.begin(), playlist); - // playlist->ref(); - playlist->InUse.connect (mem_fun (*this, &Session::track_playlist)); - playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), playlist)); + playlist->InUse.connect (sigc::bind (mem_fun (*this, &Session::track_playlist), boost::weak_ptr(playlist))); + playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), boost::weak_ptr(playlist))); } } @@ -2954,15 +3233,39 @@ Session::add_playlist (Playlist* playlist) } void -Session::track_playlist (Playlist* pl, bool inuse) +Session::get_playlists (vector >& s) +{ + { + Glib::Mutex::Lock lm (playlist_lock); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + s.push_back (*i); + } + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + s.push_back (*i); + } + } +} + +void +Session::track_playlist (bool inuse, boost::weak_ptr wpl) { + boost::shared_ptr pl(wpl.lock()); + + if (!pl) { + return; + } + PlaylistList::iterator x; + if (pl->hidden()) { + /* its not supposed to be visible */ + return; + } + { Glib::Mutex::Lock lm (playlist_lock); if (!inuse) { - //cerr << "shifting playlist to unused: " << pl->name() << endl; unused_playlists.insert (pl); @@ -2972,8 +3275,7 @@ Session::track_playlist (Playlist* pl, bool inuse) } else { - //cerr << "shifting playlist to used: " << pl->name() << endl; - + playlists.insert (pl); if ((x = unused_playlists.find (pl)) != unused_playlists.end()) { @@ -2984,20 +3286,24 @@ Session::track_playlist (Playlist* pl, bool inuse) } void -Session::remove_playlist (Playlist* playlist) +Session::remove_playlist (boost::weak_ptr weak_playlist) { if (_state_of_the_state & Deletion) { return; } + boost::shared_ptr playlist (weak_playlist.lock()); + + if (!playlist) { + return; + } + { 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()) { playlists.erase (i); } @@ -3070,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); @@ -3096,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; @@ -3168,13 +3474,19 @@ 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) { return; } + /* every track/bus asked for this to be handled but it was deferred because + we were connecting. do it now. + */ + + request_input_change_handling (); + resort_routes (); /* force all diskstreams to update their capture offset values to @@ -3252,18 +3564,28 @@ Session::remove_redirect (Redirect* redirect) Insert* insert; PortInsert* port_insert; PluginInsert* plugin_insert; - + if ((insert = dynamic_cast (redirect)) != 0) { if ((port_insert = dynamic_cast (insert)) != 0) { - _port_inserts.remove (port_insert); + list::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert); + if (x != _port_inserts.end()) { + insert_bitset[port_insert->bit_slot()] = false; + _port_inserts.erase (x); + } } else if ((plugin_insert = dynamic_cast (insert)) != 0) { _plugin_inserts.remove (plugin_insert); } else { - fatal << _("programming error: unknown type of Insert deleted!") << endmsg; + fatal << string_compose (_("programming error: %1"), + X_("unknown type of Insert deleted!")) + << endmsg; /*NOTREACHED*/ } } else if ((send = dynamic_cast (redirect)) != 0) { - _sends.remove (send); + list::iterator x = find (_sends.begin(), _sends.end(), send); + if (x != _sends.end()) { + send_bitset[send->bit_slot()] = false; + _sends.erase (x); + } } else { fatal << _("programming error: unknown type of Redirect deleted!") << endmsg; /*NOTREACHED*/ @@ -3275,7 +3597,30 @@ Session::remove_redirect (Redirect* redirect) nframes_t Session::available_capture_duration () { - const double scale = 4096.0 / sizeof (Sample); + float sample_bytes_on_disk = 4.0; // keep gcc happy + + switch (Config->get_native_file_data_format()) { + case FormatFloat: + sample_bytes_on_disk = 4.0; + break; + + case FormatInt24: + sample_bytes_on_disk = 3.0; + break; + + case FormatInt16: + sample_bytes_on_disk = 2.0; + break; + + default: + /* impossible, but keep some gcc versions happy */ + fatal << string_compose (_("programming error: %1"), + X_("illegal native file data format")) + << endmsg; + /*NOTREACHED*/ + } + + double scale = 4096.0 / sample_bytes_on_disk; if (_total_free_4k_blocks * scale > (double) max_frames) { return max_frames; @@ -3337,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); @@ -3357,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); @@ -3367,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); @@ -3376,20 +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 () { - char buf[32]; - snprintf (buf, sizeof (buf), "send %" PRIu32, ++send_cnt); - return buf; + /* 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); + } } -string -Session::next_insert_name () +uint32_t +Session::next_send_id () { - char buf[32]; - snprintf (buf, sizeof (buf), "insert %" PRIu32, ++insert_cnt); - return buf; + /* 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); + } +} + +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; +} + +void +Session::mark_insert_id (uint32_t id) +{ + 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 */ @@ -3414,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 @@ -3466,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 { @@ -3529,7 +3943,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le bool overwrite, vector >& srcs, InterThreadInfo& itt) { int ret = -1; - Playlist* playlist; + boost::shared_ptr playlist; boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; @@ -3599,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); @@ -3642,22 +4060,14 @@ 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(), - region_name_from_path (srcs.front()->name())); + region_name_from_path (srcs.front()->name(), true)); ret = 0; } @@ -3673,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) { @@ -3689,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; } @@ -3726,13 +4169,30 @@ Session::nbusses () const } void -Session::add_curve(Curve *curve) +Session::add_automation_list(AutomationList *al) +{ + automation_lists[al->id()] = al; +} + +nframes_t +Session::compute_initial_length () { - curves[curve->id()] = curve; + return _engine.frame_rate() * 60 * 5; } void -Session::add_automation_list(AutomationList *al) +Session::sync_order_keys () { - automation_lists[al->id()] = al; + 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 }