X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=c2141e62e630f39a64a39c4ba18220c4a069aa39;hb=b2c5b3b518479a35ba87764d1150b7bdac073cdc;hp=4b0d618a3087727959e2c3313a22ffcc8280e3ea;hpb=92aaa4ebc96c51873b9bd2468e5ac2caeb42d7bd;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 4b0d618a30..c2141e62e6 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -27,12 +27,14 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -44,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -78,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,18 +97,29 @@ 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) { @@ -251,20 +271,27 @@ 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 (0), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), pending_events (2048), + state_tree (0), + _send_smpte_update (false), + midi_thread (pthread_t (0)), midi_requests (128), // the size of this should match the midi request pool size diskstreams (new DiskstreamList), routes (new RouteList), auditioner ((Auditioner*) 0), + _total_free_4k_blocks (0), _click_io ((IO*) 0), + click_data (0), + click_emphasis_data (0), main_outs (0) { bool new_session; @@ -273,17 +300,17 @@ Session::Session (AudioEngine &eng, throw failed_constructor(); } - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; + info << "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 (); } @@ -319,13 +346,22 @@ Session::Session (AudioEngine &eng, nframes_t initial_length) : _engine (eng), + mmc (0), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), pending_events (2048), + state_tree (0), + _send_smpte_update (false), + midi_thread (pthread_t (0)), midi_requests (16), diskstreams (new DiskstreamList), routes (new RouteList), + auditioner ((Auditioner *) 0), + _total_free_4k_blocks (0), + _click_io ((IO *) 0), + click_data (0), + click_emphasis_data (0), main_outs (0) { @@ -335,17 +371,17 @@ Session::Session (AudioEngine &eng, throw failed_constructor(); } - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; + info << "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); @@ -353,7 +389,7 @@ 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 (); } @@ -383,7 +419,7 @@ Session::Session (AudioEngine &eng, } if (!rl.empty()) { - add_routes (rl); + add_routes (rl, false); } } @@ -396,17 +432,12 @@ Session::Session (AudioEngine &eng, 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); - Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); - if (was_dirty) { - DirtyChanged (); /* EMIT SIGNAL */ - } + Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); } Session::~Session () @@ -424,6 +455,7 @@ Session::destroy () remove_pending_capture_state (); _state_of_the_state = StateOfTheState (CannotSave|Deletion); + _engine.remove_session (); GoingAway (); /* EMIT SIGNAL */ @@ -437,19 +469,16 @@ Session::destroy () _history.clear (); /* clear state tree so that no references to objects are held any more */ - - if (state_tree) { - delete state_tree; - } + delete state_tree; terminate_butler_thread (); terminate_midi_thread (); - if (click_data && click_data != default_click) { + if (click_data != default_click) { delete [] click_data; } - if (click_emphasis_data && click_emphasis_data != default_click_emphasis) { + if (click_emphasis_data != default_click_emphasis) { delete [] click_emphasis_data; } @@ -568,14 +597,13 @@ Session::destroy () tmp = i; ++tmp; - + i->second->drop_references (); - + i = tmp; } - audio_sources.clear (); - + #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; #endif /* TRACK_DESTRUCTION */ @@ -618,19 +646,9 @@ Session::destroy () i = tmp; } - if (butler_mixdown_buffer) { - delete [] butler_mixdown_buffer; - } - - if (butler_gain_buffer) { - delete [] butler_gain_buffer; - } - Crossfade::set_buffer_size (0); - if (mmc) { - delete mmc; - } + delete mmc; } void @@ -654,13 +672,15 @@ Session::set_worst_io_latencies () void Session::when_engine_running () { - string first_physical_output; - /* 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 */ @@ -698,17 +718,21 @@ Session::when_engine_running () } else { - /* default state for Click */ - - first_physical_output = _engine.get_nth_physical_output (0); + /* default state for Click: dual-mono to first 2 physical outputs */ - if (first_physical_output.length()) { - if (_click_io->add_output_port (first_physical_output, this)) { - // relax, even though its an error - } else { - _clicking = Config->get_clicking (); + for (int physport = 0; physport < 2; ++physport) { + string physical_output = _engine.get_nth_physical_audio_output (physport); + + if (physical_output.length()) { + if (_click_io->add_output_port (physical_output, this)) { + // relax, even though its an error + } } } + + if (_click_io->n_outputs() > 0) { + _clicking = Config->get_clicking (); + } } } @@ -716,6 +740,8 @@ Session::when_engine_running () error << _("cannot setup Click I/O") << endmsg; } + BootMessage (_("Compute I/O Latencies")); + set_worst_io_latencies (); if (_clicking) { @@ -726,35 +752,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); @@ -762,13 +790,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); @@ -776,8 +804,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); } @@ -810,7 +838,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; @@ -830,11 +858,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) { @@ -864,17 +896,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 @@ -886,6 +919,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 @@ -908,6 +942,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)) { @@ -918,14 +953,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 */ @@ -941,6 +989,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. */ @@ -965,8 +1014,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) { @@ -1052,9 +1108,9 @@ Session::auto_loop_changed (Location* location) if (transport_rolling() && play_loop) { - //if (_transport_frame < location->start() || _transport_frame > location->end()) { + // if (_transport_frame > location->end()) { - if (_transport_frame > location->end()) { + if (_transport_frame < location->start() || _transport_frame > location->end()) { // relocate to beginning of loop clear_events (Event::LocateRoll); @@ -1077,7 +1133,6 @@ Session::auto_loop_changed (Location* location) } last_loopend = location->end(); - } void @@ -1115,6 +1170,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); } @@ -1154,6 +1213,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); } @@ -1190,6 +1256,12 @@ Session::handle_locations_changed (Locations::LocationList& locations) set_loop = true; } + if (location->is_start()) { + start_location = location; + } + if (location->is_end()) { + end_location = location; + } } if (!set_loop) { @@ -1231,7 +1303,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) { @@ -1262,15 +1334,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); + } } } } @@ -1306,6 +1381,10 @@ Session::audible_frame () const nframes_t offset; nframes_t tf; + if (_transport_speed == 0.0f && non_realtime_work_pending()) { + return last_stop_frame; + } + /* the first of these two possible settings for "offset" mean that the audible frame is stationary until audio emerges from the latency compensation @@ -1334,24 +1413,43 @@ Session::audible_frame () const } else { tf = _transport_frame; } - - if (_transport_speed == 0) { - return tf; - } - - if (tf < offset) { - return 0; - } - + ret = tf; if (!non_realtime_work_pending()) { /* MOVING */ - /* take latency into account */ - - ret -= offset; + /* check to see if we have passed the first guaranteed + audible frame past our last stopping position. if not, + the return that last stopping point because in terms + of audible frames, we have not moved yet. + */ + + if (_transport_speed > 0.0f) { + + if (!play_loop || !have_looped) { + if (tf < last_stop_frame + offset) { + return last_stop_frame; + + } + } + + + /* forwards */ + ret -= offset; + + } else if (_transport_speed < 0.0f) { + + /* XXX wot? no backward looping? */ + + if (tf > last_stop_frame - offset) { + return last_stop_frame; + } else { + /* backwards */ + ret += offset; + } + } } return ret; @@ -1371,7 +1469,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); @@ -1395,7 +1495,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); } @@ -1416,7 +1516,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; @@ -1656,8 +1756,8 @@ 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) { @@ -1681,26 +1781,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) { @@ -1735,19 +1838,6 @@ 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); - } - - // assert (current_thread != RT_thread) - track->audio_diskstream()->non_realtime_input_change(); track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); @@ -1756,21 +1846,50 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod 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 << pfe.what() << 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; @@ -1814,7 +1933,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++; } } @@ -1824,17 +1943,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; } @@ -1848,27 +1967,28 @@ 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)) { break; } } - - 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(); @@ -1880,16 +2000,6 @@ 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; @@ -1899,20 +2009,133 @@ 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 << pfe.what() << endmsg; + goto failure; } + --how_many; } + failure: if (!ret.empty()) { - add_routes (ret, false); - save_state (_current_snapshot_name); + add_routes (ret, true); + } + + return ret; + +} + +Session::RouteList +Session::new_route_from_template (uint32_t how_many, const std::string& template_path) +{ + char name[32]; + RouteList ret; + uint32_t control_id; + XMLTree tree; + uint32_t number = 1; + + if (!tree.read (template_path.c_str())) { + return ret; + } + + XMLNode* node = tree.root(); + + control_id = ntracks() + nbusses() + 1; + + while (how_many) { + + XMLNode node_copy (*node); // make a copy so we can change the name if we need to + + std::string node_name = IO::name_from_state (*node_copy.children().front()); + + /* generate a new name by adding a number to the end of the template name */ + + do { + snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number); + + number++; + + if (route_by_name (name) == 0) { + break; + } + + } while (number < UINT_MAX); + + if (number == UINT_MAX) { + fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; + /*NOTREACHED*/ + } + + IO::set_name_in_state (*node_copy.children().front(), name); + + Track::zero_diskstream_id_in_xml (node_copy); + + try { + shared_ptr route (XMLRouteFactory (node_copy)); + + if (route == 0) { + error << _("Session: cannot create track/bus from template description") << endmsg; + goto out; + } + + if (boost::dynamic_pointer_cast(route)) { + /* force input/output change signals so that the new diskstream + picks up the configuration of the route. During session + loading this normally happens in a different way. + */ + route->input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); + route->output_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); + } + + route->set_remote_control_id (control_id); + ++control_id; + + ret.push_back (route); + } + + catch (failed_constructor &err) { + error << _("Session: could not create new route from template") << endmsg; + goto out; + } + + catch (AudioEngine::PortRegistrationFailure& pfe) { + error << pfe.what() << endmsg; + goto out; + } + + --how_many; + } + + out: + if (!ret.empty()) { + add_routes (ret, true); } return ret; +} + +boost::shared_ptr +Session::new_video_track (string name) +{ + uint32_t control_id = ntracks() + nbusses() + 1; + shared_ptr new_route ( + new Route ( *this, name, -1, -1, -1, -1, Route::Flag(0), ARDOUR::DataType::NIL)); + new_route->set_remote_control_id (control_id); + RouteList rl; + rl.push_back (new_route); + { + RCUWriter writer (routes); + shared_ptr r = writer.get_copy (); + r->insert (r->end(), rl.begin(), rl.end()); + resort_routes_using (r); + } + return new_route; } void @@ -1940,9 +2163,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) { @@ -1956,17 +2194,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); @@ -2023,17 +2263,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) */ @@ -2044,6 +2287,8 @@ Session::remove_route (shared_ptr route) route->drop_references (); + sync_order_keys (N_("session")); + /* save the new state of the world */ if (save_state (_current_snapshot_name)) { @@ -2086,7 +2331,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) /* don't mess with busses */ - if (dynamic_cast((*i).get()) == 0) { + if (boost::dynamic_pointer_cast(*i) == 0) { continue; } @@ -2094,7 +2339,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) /* don't mess with tracks */ - if (dynamic_cast((*i).get()) != 0) { + if (boost::dynamic_pointer_cast(*i) != 0) { continue; } } @@ -2168,8 +2413,6 @@ Session::update_route_solo_state () bool is_track = false; bool signal = false; - /* caller must hold RouteLock */ - /* this is where we actually implement solo by changing the solo mute setting of each track. */ @@ -2179,7 +2422,7 @@ Session::update_route_solo_state () for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->soloed()) { mute = true; - if (dynamic_cast((*i).get())) { + if (boost::dynamic_pointer_cast(*i)) { is_track = true; } break; @@ -2224,7 +2467,7 @@ Session::modify_solo_mute (bool is_track, bool mute) /* only alter track solo mute */ - if (dynamic_cast((*i).get())) { + if (boost::dynamic_pointer_cast(*i)) { if ((*i)->soloed()) { (*i)->set_solo_mute (!mute); } else { @@ -2235,8 +2478,8 @@ Session::modify_solo_mute (bool is_track, bool mute) } else { /* only alter bus solo mute */ - - if (!dynamic_cast((*i).get())) { + + if (!boost::dynamic_pointer_cast(*i)) { if ((*i)->soloed()) { @@ -2270,7 +2513,24 @@ Session::catch_up_on_solo () */ update_route_solo_state(); } - + +void +Session::catch_up_on_solo_mute_override () +{ + if (Config->get_solo_model() != InverseMute) { + return; + } + + /* this is called whenever the param solo-mute-override is + changed. + */ + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->catch_up_on_solo_mute_override (); + } +} + shared_ptr Session::route_by_name (string name) { @@ -2338,6 +2598,8 @@ Session::get_maximum_extent () const boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { + if ((*i)->destructive()) //ignore tape tracks when getting max extents + continue; boost::shared_ptr pl = (*i)->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; @@ -2429,7 +2691,7 @@ Session::new_region_name (string old) } int -Session::region_name (string& result, string base, bool newlevel) const +Session::region_name (string& result, string base, bool newlevel) { char buf[16]; string subbase; @@ -2439,15 +2701,11 @@ Session::region_name (string& result, string base, bool newlevel) const Glib::Mutex::Lock lm (region_lock); snprintf (buf, sizeof (buf), "%d", (int)audio_regions.size() + 1); - - result = "region."; result += buf; } else { - /* XXX this is going to be slow. optimize me later */ - if (newlevel) { subbase = base; } else { @@ -2460,43 +2718,38 @@ Session::region_name (string& result, string base, bool newlevel) const subbase = base.substr (0, pos); } - - bool name_taken = true; { Glib::Mutex::Lock lm (region_lock); - - for (int n = 1; n < 5000; ++n) { - - result = subbase; - snprintf (buf, sizeof (buf), ".%d", n); + + map::iterator x; + + result = subbase; + + if ((x = region_name_map.find (subbase)) == region_name_map.end()) { + result += ".1"; + region_name_map[subbase] = 1; + } else { + x->second++; + snprintf (buf, sizeof (buf), ".%d", x->second); result += buf; - - name_taken = false; - - for (AudioRegionList::const_iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) { - if (i->second->name() == result) { - name_taken = true; - break; - } - } - - if (!name_taken) { - break; - } } } - - if (name_taken) { - fatal << string_compose(_("too many regions with names like %1"), base) << endmsg; - /*NOTREACHED*/ - } } + return 0; } 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; @@ -2505,56 +2758,110 @@ 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) { + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { + + boost::shared_ptr region = *ii; + + if (region == 0) { - oar = boost::dynamic_pointer_cast (x->second); + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; - if (ar->region_list_equivalent (oar)) { - break; + } else 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; + + pair x = audio_regions.insert (entry); + + if (!x.second) { + return; + } + + added = true; + } + + } else { + + fatal << _("programming error: ") + << X_("unknown region type passed to Session::add_region()") + << endmsg; + /*NOTREACHED*/ + } + } + } - if (x == audio_regions.end()) { + /* mark dirty because something has changed even if we didn't + add the region to the region list. + */ + + set_dirty (); + + if (added) { - pair entry; + vector > v; + boost::shared_ptr first_ar; - entry.first = region->id(); - entry.second = ar; + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { - pair x = audio_regions.insert (entry); + boost::shared_ptr region = *ii; + boost::shared_ptr ar; - - if (!x.second) { - return; - } + if (region == 0) { - added = true; - } + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; - } else { + } else if ((ar = boost::dynamic_pointer_cast (region)) != 0) { + v.push_back (ar); - fatal << _("programming error: ") - << X_("unknown region type passed to Session::add_region()") - << endmsg; - /*NOTREACHED*/ + 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))); + + update_region_name_map (region); + } + + if (!v.empty()) { + AudioRegionsAdded (v); /* EMIT SIGNAL */ } } +} - /* mark dirty because something has changed even if we didn't - add the region to the region list. - */ - - set_dirty(); +void +Session::update_region_name_map (boost::shared_ptr region) +{ + string::size_type last_period = region->name().find_last_of ('.'); - 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 */ + if (last_period != string::npos && last_period < region->name().length() - 1) { + + string base = region->name().substr (0, last_period); + string number = region->name().substr (last_period+1); + map::iterator x; + + /* note that if there is no number, we get zero from atoi, + which is just fine + */ + + region_name_map[base] = atoi (number); } } @@ -2571,6 +2878,10 @@ Session::region_changed (Change what_changed, boost::weak_ptr weak_regio /* relay hidden changes */ RegionHiddenChange (region); } + + if (what_changed & NameChanged) { + update_region_name_map (region); + } } void @@ -2711,6 +3022,9 @@ Session::remove_last_capture () } destroy_regions (r); + + save_state (_current_snapshot_name); + return 0; } @@ -2746,6 +3060,9 @@ Session::add_source (boost::shared_ptr source) set_dirty(); } + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } } } @@ -2793,16 +3110,27 @@ 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); - res = peak_dir (); - res += PBD::basename_nosuffix (audio_path); - res += ".peak"; + for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ++i) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(i->second); - return res; + if (afs && afs->path() == path && chn == afs->channel()) { + return afs; + } + + } + return boost::shared_ptr(); +} + +Glib::ustring +Session::peak_path (Glib::ustring base) const +{ + return Glib::build_filename(peak_dir (), base + ".peak"); } string @@ -3210,7 +3538,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); @@ -3236,12 +3564,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; @@ -3265,7 +3593,7 @@ void Session::set_all_solo (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->hidden()) { (*i)->set_solo (yn, this); @@ -3308,7 +3636,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) { @@ -3442,6 +3770,10 @@ Session::available_capture_duration () 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"), @@ -3512,18 +3844,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); @@ -3532,7 +3882,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 * 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 memset (p, 0, sizeof (Sample) * current_block_size); _silent_buffers.push_back (p); @@ -3542,7 +3897,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); @@ -3560,7 +3915,6 @@ Session::next_insert_id () for (boost::dynamic_bitset::size_type n = 0; n < insert_bitset.size(); ++n) { if (!insert_bitset[n]) { insert_bitset[n] = true; - cerr << "Returning " << n << " as insert ID\n"; return n; } @@ -3581,7 +3935,6 @@ Session::next_send_id () for (boost::dynamic_bitset::size_type n = 0; n < send_bitset.size(); ++n) { if (!send_bitset[n]) { send_bitset[n] = true; - cerr << "Returning " << n << " as send ID\n"; return n; } @@ -3747,11 +4100,11 @@ Session::freeze (InterThreadInfo& itt) return 0; } -int -Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len, - bool overwrite, vector >& srcs, InterThreadInfo& itt) +boost::shared_ptr +Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t end, + bool overwrite, vector >& srcs, InterThreadInfo& itt, bool enable_processing) { - int ret = -1; + boost::shared_ptr result; boost::shared_ptr playlist; boost::shared_ptr fsource; uint32_t x; @@ -3761,12 +4114,21 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le nframes_t position; nframes_t this_chunk; nframes_t to_do; + nframes_t len = end - start; vector buffers; + if (end <= start) { + error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"), + end, start) << endmsg; + return result; + } + // any bigger than this seems to cause stack overflows in called functions const nframes_t chunk_size = (128 * 1024)/4; - g_atomic_int_set (&processing_prohibited, 1); + // block all process callback handling + + block_processing (); /* call tree *MUST* hold route_lock */ @@ -3822,16 +4184,20 @@ 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); - if (track.export_stuff (buffers, nchans, start, this_chunk)) { + if (track.export_stuff (buffers, nchans, start, this_chunk, enable_processing)) { goto out; } @@ -3865,56 +4231,79 @@ 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(), true)); - - ret = 0; + result = RegionFactory::create (srcs, 0, srcs.front()->length(), + region_name_from_path (srcs.front()->name(), true)); } out: - if (ret) { + if (!result) { for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); if (afs) { afs->mark_for_remove (); } - + (*src)->drop_references (); } + + } 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) { - free(*i); + free (*i); } - g_atomic_int_set (&processing_prohibited, 0); + unblock_processing (); itt.done = true; - return ret; + return result; } 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 * 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 + _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; } @@ -3960,3 +4349,19 @@ Session::compute_initial_length () return _engine.frame_rate() * 60 * 5; } +void +Session::sync_order_keys (const char* base) +{ + 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 (base); + } + + Route::SyncOrderKeys (base); // EMIT SIGNAL +}