X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=67ea01b8bfdf0402de53afaa68f5dd51b9028a1a;hb=bed58e9f372a6c2671e9f072c19a4c77d06c4292;hp=d775e9c69b479ebe8218b47c1d347b4c7fe28faf;hpb=959a7909c1adca430a63f783fd16687242a7be3d;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index d775e9c69b..67ea01b8bf 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 1999-2004 Paul Davis + Copyright (C) 1999-2004 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -40,10 +41,13 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -55,10 +59,11 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include +#include #include #include #include @@ -72,6 +77,11 @@ #include #include #include +#include +#include +#include +#include +#include #ifdef HAVE_LIBLO #include @@ -90,199 +100,54 @@ static const int CPU_CACHE_ALIGN = 64; 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"); -const char* Session::old_sound_dir_name = X_("sounds"); -const char* Session::sound_dir_name = X_("audiofiles"); -const char* Session::peak_dir_name = X_("peaks"); -const char* Session::dead_sound_dir_name = X_("dead_sounds"); -const char* Session::interchange_dir_name = X_("interchange"); -const char* Session::export_dir_name = X_("export"); - -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; +bool Session::_disable_all_loaded_plugins = false; +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; - -int -Session::find_session (string str, string& path, string& snapshot, bool& isnew) -{ - struct stat statbuf; - char buf[PATH_MAX+1]; - - isnew = false; - - if (!realpath (str.c_str(), buf) && (errno != ENOENT && errno != ENOTDIR)) { - error << string_compose (_("Could not resolve path: %1 (%2)"), buf, strerror(errno)) << endmsg; - return -1; - } - - str = buf; - - /* check to see if it exists, and what it is */ - - if (stat (str.c_str(), &statbuf)) { - if (errno == ENOENT) { - isnew = true; - } else { - error << string_compose (_("cannot check session path %1 (%2)"), str, strerror (errno)) - << endmsg; - return -1; - } - } - - if (!isnew) { - - /* it exists, so it must either be the name - of the directory, or the name of the statefile - within it. - */ - - if (S_ISDIR (statbuf.st_mode)) { - - string::size_type slash = str.find_last_of ('/'); - - if (slash == string::npos) { - - /* a subdirectory of cwd, so statefile should be ... */ - - string tmp; - tmp = str; - tmp += '/'; - tmp += str; - tmp += _statefile_suffix; - - /* is it there ? */ - - if (stat (tmp.c_str(), &statbuf)) { - error << string_compose (_("cannot check statefile %1 (%2)"), tmp, strerror (errno)) - << endmsg; - return -1; - } - - path = str; - snapshot = str; - - } else { - - /* some directory someplace in the filesystem. - the snapshot name is the directory name - itself. - */ - - path = str; - snapshot = str.substr (slash+1); - - } - - } else if (S_ISREG (statbuf.st_mode)) { - - string::size_type slash = str.find_last_of ('/'); - string::size_type suffix; - - /* remove the suffix */ - - if (slash != string::npos) { - snapshot = str.substr (slash+1); - } else { - snapshot = str; - } - - suffix = snapshot.find (_statefile_suffix); - - if (suffix == string::npos) { - error << string_compose (_("%1 is not an Ardour snapshot file"), str) << endmsg; - return -1; - } - - /* remove suffix */ - - snapshot = snapshot.substr (0, suffix); - - if (slash == string::npos) { - - /* we must be in the directory where the - statefile lives. get it using cwd(). - */ - - char cwd[PATH_MAX+1]; - - if (getcwd (cwd, sizeof (cwd)) == 0) { - error << string_compose (_("cannot determine current working directory (%1)"), strerror (errno)) - << endmsg; - return -1; - } - - path = cwd; - - } else { - - /* full path to the statefile */ - - path = str.substr (0, slash); - } - - } else { - - /* what type of file is it? */ - error << string_compose (_("unknown file type for session %1"), str) << endmsg; - return -1; - } - - } else { - - /* its the name of a new directory. get the name - as "dirname" does. - */ - - string::size_type slash = str.find_last_of ('/'); - - if (slash == string::npos) { - - /* no slash, just use the name, but clean it up */ - - path = legalize_for_path (str); - snapshot = path; - - } else { - - path = str; - snapshot = str.substr (slash+1); - } - } - - return 0; -} +sigc::signal Session::AutoBindingOn; +sigc::signal Session::AutoBindingOff; +sigc::signal Session::Exported; Session::Session (AudioEngine &eng, - string fullpath, - string snapshot_name, - string* mix_template) + const string& fullpath, + const string& snapshot_name, + string mix_template) : _engine (eng), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), - _send_buffers(new BufferSet()), + _mix_buffers(new BufferSet()), + mmc (0), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), + _midi_clock_port (default_midi_clock_port), + _session_dir (new SessionDirectory(fullpath)), pending_events (2048), - //midi_requests (128), // the size of this should match the midi request pool size + state_tree (0), + butler_mixdown_buffer (0), + butler_gain_buffer (0), + post_transport_work((PostTransportWork)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), + _bundle_xml_node (0), _click_io ((IO*) 0), - main_outs (0) + click_data (0), + click_emphasis_data (0), + main_outs (0), + _metadata (new SessionMetadata()) + { bool new_session; @@ -292,27 +157,27 @@ Session::Session (AudioEngine &eng, 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_outputs = _engine.n_physical_outputs(DataType::AUDIO); + n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO); first_stage_init (fullpath, snapshot_name); - - new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + + new_session = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + if (new_session) { if (create (new_session, mix_template, compute_initial_length())) { - cerr << "create failed\n"; destroy (); throw failed_constructor (); } } - + if (second_stage_init (new_session)) { destroy (); throw failed_constructor (); } - + store_recent_sessions(_name, _path); - + bool was_dirty = dirty(); _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); @@ -338,17 +203,31 @@ Session::Session (AudioEngine &eng, : _engine (eng), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), - _send_buffers(new BufferSet()), + _mix_buffers(new BufferSet()), + mmc (0), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), + _midi_clock_port (default_midi_clock_port), + _session_dir ( new SessionDirectory(fullpath)), pending_events (2048), - //midi_requests (16), + state_tree (0), + butler_mixdown_buffer (0), + butler_gain_buffer (0), + post_transport_work((PostTransportWork)0), _send_smpte_update (false), + midi_thread (pthread_t (0)), + midi_requests (16), diskstreams (new DiskstreamList), routes (new RouteList), - main_outs (0) - + auditioner ((Auditioner *) 0), + _total_free_4k_blocks (0), + _bundle_xml_node (0), + _click_io ((IO *) 0), + click_data (0), + click_emphasis_data (0), + main_outs (0), + _metadata (new SessionMetadata()) { bool new_session; @@ -358,8 +237,8 @@ Session::Session (AudioEngine &eng, cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; - n_physical_outputs = _engine.n_physical_outputs(); - n_physical_inputs = _engine.n_physical_inputs(); + n_physical_outputs = _engine.n_physical_outputs (DataType::AUDIO); + n_physical_inputs = _engine.n_physical_inputs (DataType::AUDIO); if (n_physical_inputs) { n_physical_inputs = max (requested_physical_in, n_physical_inputs); @@ -374,7 +253,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 (); } @@ -382,31 +261,31 @@ Session::Session (AudioEngine &eng, { /* set up Master Out and Control Out if necessary */ - + RouteList rl; 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); + add_routes (rl, false); } - + } Config->set_input_auto_connect (input_ac); @@ -416,18 +295,12 @@ Session::Session (AudioEngine &eng, destroy (); throw failed_constructor (); } - - store_recent_sessions(_name, _path); - - bool was_dirty = dirty (); + + store_recent_sessions (_name, _path); _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 */ - } } Session::~Session () @@ -445,10 +318,11 @@ Session::destroy () remove_pending_capture_state (); _state_of_the_state = StateOfTheState (CannotSave|Deletion); + _engine.remove_session (); GoingAway (); /* EMIT SIGNAL */ - + /* do this */ notify_callbacks (); @@ -458,19 +332,17 @@ 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; } @@ -478,10 +350,12 @@ Session::destroy () delete _scratch_buffers; delete _silent_buffers; - delete _send_buffers; + delete _mix_buffers; AudioDiskstream::free_working_buffers(); - + + Route::SyncOrderKeys.clear(); + #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION cerr << "delete named selections\n"; @@ -506,10 +380,10 @@ Session::destroy () ++tmp; (*i)->drop_references (); - + i = tmp; } - + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { PlaylistList::iterator tmp; @@ -517,26 +391,24 @@ Session::destroy () ++tmp; (*i)->drop_references (); - + i = tmp; } - + playlists.clear (); unused_playlists.clear (); #ifdef TRACK_DESTRUCTION cerr << "delete regions\n"; #endif /* TRACK_DESTRUCTION */ - + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { RegionList::iterator tmp; tmp = i; ++tmp; - cerr << "dropping refs on a region (" << i->second->name() << " @ " << i->second << ") with UC = " << i->second.use_count() << endl; i->second->drop_references (); - cerr << "AFTER: UC = " << i->second.use_count() << endl; i = tmp; } @@ -546,7 +418,7 @@ Session::destroy () #ifdef TRACK_DESTRUCTION cerr << "delete routes\n"; #endif /* TRACK_DESTRUCTION */ - { + { RCUWriter writer (routes); boost::shared_ptr r = writer.get_copy (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -584,7 +456,6 @@ Session::destroy () i = tmp; } - sources.clear (); #ifdef TRACK_DESTRUCTION @@ -606,20 +477,6 @@ Session::destroy () #endif /* TRACK_DESTRUCTION */ for (list::iterator i = edit_groups.begin(); i != edit_groups.end(); ) { list::iterator tmp; - - tmp = i; - ++tmp; - - delete *i; - - i = tmp; - } - -#ifdef TRACK_DESTRUCTION - cerr << "delete connections\n"; -#endif /* TRACK_DESTRUCTION */ - for (ConnectionList::iterator i = _connections.begin(); i != _connections.end(); ) { - ConnectionList::iterator tmp; tmp = i; ++tmp; @@ -629,19 +486,12 @@ Session::destroy () i = tmp; } - if (butler_mixdown_buffer) { - delete [] butler_mixdown_buffer; - } - - if (butler_gain_buffer) { - delete [] butler_gain_buffer; - } + delete [] butler_mixdown_buffer; + delete [] butler_gain_buffer; Crossfade::set_buffer_size (0); - if (mmc) { - delete mmc; - } + delete mmc; } void @@ -655,7 +505,7 @@ Session::set_worst_io_latencies () } boost::shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); @@ -669,9 +519,13 @@ Session::when_engine_running () /* we don't want to run execute this again */ + BootMessage (_("Set block size and sample rate")); + set_block_size (_engine.frames_per_cycle()); set_frame_rate (_engine.frame_rate()); + BootMessage (_("Using configuration")); + Config->map_parameters (mem_fun (*this, &Session::config_changed)); /* every time we reconnect, recompute worst case output latencies */ @@ -690,15 +544,15 @@ Session::when_engine_running () try { XMLNode* child = 0; - + _click_io.reset (new ClickIO (*this, "click", 0, 0, -1, -1)); if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { /* existing state for Click */ - + if (_click_io->set_state (*child->children().front()) == 0) { - + _clicking = Config->get_clicking (); } else { @@ -708,7 +562,7 @@ Session::when_engine_running () } } else { - + /* default state for Click */ first_physical_output = _engine.get_nth_physical_output (DataType::AUDIO, 0); @@ -727,40 +581,42 @@ 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? } - /* Create a set of Connection objects that map + /* Create a set of Bundle 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) { char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - Connection* c = new OutputConnection (buf, true); + shared_ptr c (new Bundle (buf, true)); + c->set_nchannels (1); + c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - c->add_port (); - c->add_connection (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - - add_connection (c); + add_bundle (c); } for (uint32_t np = 0; np < n_physical_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 (DataType::AUDIO, np)); + shared_ptr c (new Bundle (buf, false)); + c->set_nchannels (1); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - add_connection (c); + add_bundle (c); } /* TWO: STEREO */ @@ -769,28 +625,24 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2); - Connection* c = new OutputConnection (buf, true); + shared_ptr c (new Bundle (buf, true)); + c->set_nchannels (2); + c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1)); - c->add_port (); - c->add_port (); - c->add_connection (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - c->add_connection (1, _engine.get_nth_physical_output (DataType::AUDIO, np+1)); - - add_connection (c); + add_bundle (c); } for (uint32_t np = 0; np < n_physical_inputs; np +=2) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2); - Connection* c = new InputConnection (buf, true); - - c->add_port (); - c->add_port (); - c->add_connection (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - c->add_connection (1, _engine.get_nth_physical_input (DataType::AUDIO, np+1)); + shared_ptr c (new Bundle (buf, false)); + c->set_nchannels (2); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1)); - add_connection (c); + add_bundle (c); } /* THREE MASTER */ @@ -798,31 +650,31 @@ Session::when_engine_running () if (_master_out) { /* create master/control ports */ - + if (_master_out) { uint32_t n; /* force the master to ignore any later call to this */ - + if (_master_out->pending_state_node) { _master_out->ports_became_legal(); } /* no panner resets till we are through */ - + _master_out->defer_pan_reset (); - - while (_master_out->n_inputs().get(DataType::AUDIO) - < _master_out->input_maximum().get(DataType::AUDIO)) { + + while (_master_out->n_inputs().n_audio() + < _master_out->input_maximum().n_audio()) { if (_master_out->add_input_port ("", this, DataType::AUDIO)) { - error << _("cannot setup master inputs") + error << _("cannot setup master inputs") << endmsg; break; } } n = 0; - while (_master_out->n_outputs().get(DataType::AUDIO) - < _master_out->output_maximum().get(DataType::AUDIO)) { + while (_master_out->n_outputs().n_audio() + < _master_out->output_maximum().n_audio()) { if (_master_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this, DataType::AUDIO)) { error << _("cannot setup master outputs") << endmsg; @@ -832,24 +684,20 @@ Session::when_engine_running () } _master_out->allow_pan_reset (); - - } - Connection* c = new OutputConnection (_("Master Out"), true); - - for (uint32_t n = 0; n < _master_out->n_inputs ().get_total(); ++n) { - c->add_port (); - c->add_connection ((int) n, _master_out->input(n)->name()); } - 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) { uint32_t id; @@ -864,7 +712,7 @@ Session::when_engine_running () for (list::iterator i = _sends.begin(); i != _sends.end(); ++i) { uint32_t id; - + if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { if (id > send_cnt) { send_cnt = id; @@ -872,22 +720,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 @@ -899,17 +748,18 @@ 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; } @@ -921,8 +771,9 @@ Session::hookup_io () if (_control_out) { uint32_t n; + vector cports; - while (_control_out->n_inputs().get(DataType::AUDIO) < _control_out->input_maximum().get(DataType::AUDIO)) { + while (_control_out->n_inputs().n_audio() < _control_out->input_maximum().n_audio()) { if (_control_out->add_input_port ("", this)) { error << _("cannot setup control inputs") << endmsg; @@ -930,7 +781,7 @@ Session::hookup_io () } } n = 0; - while (_control_out->n_outputs().get(DataType::AUDIO) < _control_out->output_maximum().get(DataType::AUDIO)) { + while (_control_out->n_outputs().n_audio() < _control_out->output_maximum().n_audio()) { if (_control_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this)) { error << _("cannot set up master outputs") << endmsg; @@ -938,6 +789,25 @@ Session::hookup_io () } n++; } + + + uint32_t ni = _control_out->n_inputs().get (DataType::AUDIO); + + 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); + } + } + + /* load bundles, which we may have postponed earlier on */ + if (_bundle_xml_node) { + load_bundles (*_bundle_xml_node); + delete _bundle_xml_node; } /* Tell all IO objects to connect themselves together */ @@ -954,6 +824,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. */ @@ -968,10 +839,10 @@ Session::hookup_io () void Session::playlist_length_changed () { - /* we can't just increase end_location->end() if pl->get_maximum_extent() + /* we can't just increase end_location->end() if pl->get_maximum_extent() if larger. if the playlist used to be the longest playlist, and its now shorter, we have to decrease end_location->end(). hence, - we have to iterate over all diskstreams and check the + we have to iterate over all diskstreams and check the playlists currently in use. */ find_current_end (); @@ -985,7 +856,7 @@ Session::diskstream_playlist_changed (boost::shared_ptr dstream) if ((playlist = dstream->playlist()) != 0) { playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); } - + /* see comment in playlist_length_changed () */ find_current_end (); } @@ -1038,7 +909,7 @@ Session::auto_punch_start_changed (Location* location) /* capture start has been changed, so save new pending state */ save_state ("", true); } -} +} void Session::auto_punch_end_changed (Location* location) @@ -1046,7 +917,7 @@ Session::auto_punch_end_changed (Location* location) nframes_t when_to_stop = location->end(); // when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (Event::PunchOut, when_to_stop); -} +} void Session::auto_punch_changed (Location* location) @@ -1056,7 +927,7 @@ Session::auto_punch_changed (Location* location) replace_event (Event::PunchIn, location->start()); //when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (Event::PunchOut, when_to_stop); -} +} void Session::auto_loop_changed (Location* location) @@ -1070,12 +941,12 @@ Session::auto_loop_changed (Location* location) if (_transport_frame > location->end()) { // relocate to beginning of loop clear_events (Event::LocateRoll); - + request_locate (location->start(), true); } else if (Config->get_seamless_loop() && !loop_changing) { - + // schedule a locate-roll to refill the diskstreams at the // previous loop end loop_changing = true; @@ -1087,10 +958,9 @@ Session::auto_loop_changed (Location* location) } } - } + } last_loopend = location->end(); - } void @@ -1113,7 +983,7 @@ Session::set_auto_punch_location (Location* location) if (location == 0) { return; } - + if (location->end() <= location->start()) { error << _("Session: you can't use that location for auto punch (start <= end)") << endmsg; return; @@ -1122,12 +992,16 @@ Session::set_auto_punch_location (Location* location) auto_punch_start_changed_connection.disconnect(); auto_punch_end_changed_connection.disconnect(); auto_punch_changed_connection.disconnect(); - + auto_punch_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_punch_start_changed)); auto_punch_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_punch_end_changed)); 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); } @@ -1144,7 +1018,7 @@ Session::set_auto_loop_location (Location* location) remove_event (existing->end(), Event::AutoLoop); auto_loop_location_changed (0); } - + set_dirty(); if (location == 0) { @@ -1157,16 +1031,23 @@ Session::set_auto_loop_location (Location* location) } last_loopend = location->end(); - + auto_loop_start_changed_connection.disconnect(); auto_loop_end_changed_connection.disconnect(); auto_loop_changed_connection.disconnect(); - + auto_loop_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_loop_changed)); auto_loop_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_loop_changed)); 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); } @@ -1202,7 +1083,13 @@ Session::handle_locations_changed (Locations::LocationList& locations) set_auto_loop_location (location); set_loop = true; } - + + if (location->is_start()) { + start_location = location; + } + if (location->is_end()) { + end_location = location; + } } if (!set_loop) { @@ -1213,7 +1100,7 @@ Session::handle_locations_changed (Locations::LocationList& locations) } set_dirty(); -} +} void Session::enable_record () @@ -1228,7 +1115,7 @@ Session::enable_record () boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - (*i)->monitor_input (true); + (*i)->monitor_input (true); } } } @@ -1244,7 +1131,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) { @@ -1260,14 +1147,14 @@ Session::disable_record (bool rt_context, bool force) if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { boost::shared_ptr dsl = diskstreams.reader(); - + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - (*i)->monitor_input (false); + (*i)->monitor_input (false); } } } - + RecordStateChanged (); /* emit signal */ if (!rt_context) { @@ -1279,17 +1166,20 @@ 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); + } + } + } } } @@ -1307,7 +1197,7 @@ Session::maybe_enable_record () if (_transport_speed) { if (!Config->get_punch_in()) { enable_record (); - } + } } else { deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame); RecordStateChanged (); /* EMIT SIGNAL */ @@ -1323,8 +1213,12 @@ 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 + mean that the audible frame is stationary until audio emerges from the latency compensation "pseudo-pipeline". @@ -1337,7 +1231,7 @@ Session::audible_frame () const if (offset > current_block_size) { offset -= current_block_size; - } else { + } else { /* XXX is this correct? if we have no external physical connections and everything is internal then surely this is zero? still, how @@ -1351,24 +1245,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; @@ -1378,7 +1291,7 @@ void Session::set_frame_rate (nframes_t frames_per_second) { /** \fn void Session::set_frame_size(nframes_t) - the AudioEngine object that calls this guarantees + the AudioEngine object that calls this guarantees that it will not be called while we are also in ::process(). Its fine to do things that block here. @@ -1388,7 +1301,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)); + Automatable::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); @@ -1401,21 +1316,19 @@ Session::set_frame_rate (nframes_t frames_per_second) void Session::set_block_size (nframes_t nframes) { - /* the AudioEngine guarantees + /* the AudioEngine guarantees that it will not be called while we are also in ::process(). It is therefore fine to do things that block here. */ - { - + { + current_block_size = nframes; ensure_buffers(_scratch_buffers->available()); - if (_gain_automation_buffer) { - delete [] _gain_automation_buffer; - } + delete [] _gain_automation_buffer; _gain_automation_buffer = new gain_t[nframes]; allocate_pan_automation_buffers (nframes, _npan_buffers, true); @@ -1425,7 +1338,7 @@ Session::set_block_size (nframes_t nframes) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_block_size (nframes); } - + boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { (*i)->set_block_size (nframes); @@ -1440,18 +1353,18 @@ Session::set_default_fade (float steepness, float fade_msecs) { #if 0 nframes_t fade_frames; - + /* Don't allow fade of less 1 frame */ - + if (fade_msecs < (1000.0 * (1.0/_current_frame_rate))) { fade_msecs = 0; fade_frames = 0; } else { - + fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001); - + } default_fade_msecs = fade_msecs; @@ -1466,7 +1379,7 @@ Session::set_default_fade (float steepness, float fade_msecs) set_dirty(); /* XXX have to do this at some point */ - /* foreach region using default fade, reset, then + /* foreach region using default fade, reset, then refill_all_diskstream_buffers (); */ #endif @@ -1502,7 +1415,7 @@ trace_terminal (shared_ptr r1, shared_ptr rbase) if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) { info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg; return; - } + } /* make a copy of the existing list of routes that feed r1 */ @@ -1566,45 +1479,45 @@ void Session::resort_routes_using (shared_ptr r) { RouteList::iterator i, j; - + for (i = r->begin(); i != r->end(); ++i) { - + (*i)->fed_by.clear (); - + for (j = r->begin(); j != r->end(); ++j) { - + /* although routes can feed themselves, it will cause an endless recursive descent if we detect it. so don't bother checking for self-feeding. */ - + if (*j == *i) { continue; } - + if ((*j)->feeds (*i)) { (*i)->fed_by.insert (*j); - } + } } } - + for (i = r->begin(); i != r->end(); ++i) { trace_terminal (*i, *i); } - + RouteSorter cmp; r->sort (cmp); - + #if 0 cerr << "finished route resort\n"; - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl; } cerr << endl; #endif - + } list > @@ -1613,10 +1526,12 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) char track_name[32]; uint32_t track_id = 0; uint32_t n = 0; - uint32_t channels_used = 0; string port; RouteList new_routes; list > ret; + //uint32_t control_id; + + // FIXME: need physical I/O and autoconnect stuff for MIDI /* count existing midi tracks */ @@ -1625,22 +1540,30 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (dynamic_cast((*i).get()) != 0) { - if (!(*i)->hidden()) { + if (!(*i)->is_hidden()) { n++; - channels_used += (*i)->n_inputs().get(DataType::MIDI); + //channels_used += (*i)->n_inputs().n_midi(); } } } } + vector physinputs; + vector physoutputs; + + _engine.get_physical_outputs (DataType::MIDI, physoutputs); + _engine.get_physical_inputs (DataType::MIDI, physinputs); + + // control_id = ntracks() + nbusses(); + while (how_many) { /* check for duplicate route names, since we might have pre-existing - routes with this name (e.g. create Midi1, Midi2, delete Midi1, + routes with this name (e.g. create Audio1, Audio2, delete Audio1, save, close,restart,add new route - first named route is now - Midi2) + Audio2) */ - + do { ++track_id; @@ -1650,20 +1573,60 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) if (route_by_name (track_name) == 0) { break; } - + } while (track_id < (UINT_MAX-1)); + shared_ptr track; + try { - shared_ptr track (new MidiTrack (*this, track_name, Route::Flag (0), mode)); - - if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::MIDI, 1), false, this)) { + track = boost::shared_ptr((new MidiTrack (*this, track_name, Route::Flag (0), mode))); + + if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) { error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; } - - channels_used += track->n_inputs ().get(DataType::MIDI); + + /* + if (nphysical_in) { + for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) { + + port = ""; + + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + port = physinputs[(channels_used+x)%nphysical_in]; + } + + if (port.length() && track->connect_input (track->input (x), port, this)) { + break; + } + } + } + + for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) { + + port = ""; + + if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { + port = physoutputs[(channels_used+x)%nphysical_out]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs().n_midi())->name(); + } + } + + if (port.length() && track->connect_output (track->output (x), port, this)) { + break; + } + } + + channels_used += track->n_inputs ().n_midi(); + + */ + + track->midi_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); new_routes.push_back (track); ret.push_back (track); @@ -1671,14 +1634,43 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) catch (failed_constructor &err) { error << _("Session: could not create new midi track.") << endmsg; - // XXX should we delete the tracks already created? - ret.clear (); - return ret; + + 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->midi_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, MidiTrack::MidiTrack should not do the Session::add_diskstream() */ + + { + RCUWriter writer (diskstreams); + boost::shared_ptr ds = writer.get_copy(); + ds->remove (track->midi_diskstream()); + } + } + + goto failed; + } + --how_many; } + failed: if (!new_routes.empty()) { add_routes (new_routes, false); save_state (_current_snapshot_name); @@ -1706,9 +1698,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (dynamic_cast((*i).get()) != 0) { - if (!(*i)->hidden()) { + if (!(*i)->is_hidden()) { n++; - channels_used += (*i)->n_inputs().get(DataType::AUDIO); + channels_used += (*i)->n_inputs().n_audio(); } } } @@ -1716,11 +1708,10 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod vector physinputs; vector physoutputs; - uint32_t nphysical_in; - uint32_t nphysical_out; - _engine.get_physical_outputs (physoutputs); - _engine.get_physical_inputs (physinputs); + _engine.get_physical_outputs (DataType::AUDIO, physoutputs); + _engine.get_physical_inputs (DataType::AUDIO, physinputs); + control_id = ntracks() + nbusses() + 1; while (how_many) { @@ -1730,7 +1721,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod save, close,restart,add new route - first named route is now Audio2) */ - + do { ++track_id; @@ -1740,26 +1731,14 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod if (route_by_name (track_name) == 0) { break; } - - } while (track_id < (UINT_MAX-1)); - if (Config->get_input_auto_connect() & AutoConnectPhysical) { - nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size()); - } else { - nphysical_in = 0; - } - - if (Config->get_output_auto_connect() & AutoConnectPhysical) { - nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size()); - } else { - nphysical_out = 0; - } + } while (track_id < (UINT_MAX-1)); shared_ptr track; - + try { track = boost::shared_ptr((new AudioTrack (*this, track_name, Route::Flag (0), mode))); - + if (track->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) @@ -1767,55 +1746,48 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod goto failed; } - if (nphysical_in) { - for (uint32_t x = 0; x < track->n_inputs().get(DataType::AUDIO) && x < nphysical_in; ++x) { - + if (!physinputs.empty()) { + uint32_t nphysical_in = physinputs.size(); + + for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) { + port = ""; - + if (Config->get_input_auto_connect() & AutoConnectPhysical) { port = physinputs[(channels_used+x)%nphysical_in]; - } - + } + if (port.length() && track->connect_input (track->input (x), port, this)) { break; } } } - - for (uint32_t x = 0; x < track->n_outputs().get(DataType::MIDI); ++x) { - - port = ""; - - if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { - port = physoutputs[(channels_used+x)%nphysical_out]; - } else if (Config->get_output_auto_connect() & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs().get(DataType::AUDIO))->name(); + + if (!physoutputs.empty()) { + uint32_t nphysical_out = physoutputs.size(); + + for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) { + + port = ""; + + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + port = physoutputs[(channels_used+x)%nphysical_out]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); + } + } + + if (port.length() && track->connect_output (track->output (x), port, this)) { + break; } - } - - if (port.length() && track->connect_output (track->output (x), port, this)) { - break; } } - - channels_used += track->n_inputs ().get(DataType::AUDIO); - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs().get(DataType::AUDIO); - - for (n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - - track->set_control_outs (cports); - } + channels_used += track->n_inputs ().n_audio(); - // assert (current_thread != RT_thread) - track->audio_diskstream()->non_realtime_input_change(); - + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); track->set_remote_control_id (control_id); ++control_id; @@ -1831,7 +1803,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod /* 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()); @@ -1849,7 +1821,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod /* 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()); @@ -1864,8 +1836,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod failed: if (!new_routes.empty()) { - add_routes (new_routes, false); - save_state (_current_snapshot_name); + add_routes (new_routes, true); } return ret; @@ -1879,14 +1850,14 @@ Session::set_remote_control_ids () shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ( MixerOrdered == m) { + 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 + //do nothing ... only changes to remote id's are initiated by user } } } @@ -1898,6 +1869,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ char bus_name[32]; uint32_t bus_id = 1; uint32_t n = 0; + uint32_t channels_used = 0; string port; RouteList ret; uint32_t control_id; @@ -1908,9 +1880,12 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (dynamic_cast((*i).get()) == 0) { - if (!(*i)->hidden() && (*i)->name() != _("master")) { + if (boost::dynamic_pointer_cast(*i) == 0) { + /* its a bus ? */ + if (!(*i)->is_hidden() && (*i)->name() != _("master")) { bus_id++; + n++; + channels_used += (*i)->n_inputs().n_audio(); } } } @@ -1919,8 +1894,12 @@ 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_outputs (DataType::AUDIO, physoutputs); + _engine.get_physical_inputs (DataType::AUDIO, physinputs); + + n_physical_audio_outputs = physoutputs.size(); + n_physical_audio_inputs = physinputs.size(); + control_id = ntracks() + nbusses() + 1; while (how_many) { @@ -1928,68 +1907,65 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ do { snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id); + bus_id++; + if (route_by_name (bus_name) == 0) { break; } - } while (++bus_id < (UINT_MAX-1)); + } while (bus_id < (UINT_MAX-1)); try { shared_ptr bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); - + if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; goto failure; } - - for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().get(DataType::AUDIO); ++x) { - - port = ""; + + + /* + 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().get(DataType::AUDIO); ++x) { - + */ + + for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) { port = ""; - + if (Config->get_output_auto_connect() & AutoConnectPhysical) { port = physoutputs[((n+x)%n_physical_outputs)]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs().get(DataType::AUDIO))->name(); + port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); } } - + if (port.length() && bus->connect_output (bus->output (x), port, this)) { break; } } - - if (_control_out) { - vector cports; - uint32_t ni = _control_out->n_inputs().get(DataType::AUDIO); - - for (uint32_t n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - bus->set_control_outs (cports); - } + + channels_used += bus->n_inputs ().n_audio(); bus->set_remote_control_id (control_id); ++control_id; ret.push_back (bus); } - + catch (failed_constructor &err) { error << _("Session: could not create new audio route.") << endmsg; @@ -2007,8 +1983,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ failure: if (!ret.empty()) { - add_routes (ret, false); - save_state (_current_snapshot_name); + add_routes (ret, true); } return ret; @@ -2018,7 +1993,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ void Session::add_routes (RouteList& new_routes, bool save) { - { + { RCUWriter writer (routes); shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); @@ -2026,21 +2001,42 @@ Session::add_routes (RouteList& new_routes, bool save) } for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - + boost::weak_ptr wpr (*x); (*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr)); (*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed)); (*x)->output_changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); - (*x)->redirects_changed.connect (mem_fun (*this, &Session::update_latency_compensation_proxy)); - - if ((*x)->master()) { + (*x)->processors_changed.connect (bind (mem_fun (*this, &Session::update_latency_compensation), false, false)); + + if ((*x)->is_master()) { _master_out = (*x); } - - if ((*x)->control()) { + + if ((*x)->is_control()) { _control_out = (*x); } + + /* only busses get automatic bundles formed */ + + if (!boost::dynamic_pointer_cast (*x)) { + add_bundle ((*x)->bundle_for_inputs()); + add_bundle ((*x)->bundle_for_outputs()); + } + } + + if (_control_out && IO::connecting_legal) { + + vector cports; + uint32_t ni = _control_out->n_inputs().n_audio(); + + for (uint32_t 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(); @@ -2057,7 +2053,7 @@ 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->set_block_size (current_block_size); { @@ -2065,7 +2061,7 @@ Session::add_diskstream (boost::shared_ptr dstream) boost::shared_ptr ds = writer.get_copy(); ds->push_back (dstream); /* writer goes out of scope, copies ds back to main */ - } + } dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream)); /* this will connect to future changes, and check the current length */ @@ -2078,10 +2074,10 @@ Session::add_diskstream (boost::shared_ptr dstream) void Session::remove_route (shared_ptr route) { - { + { RCUWriter writer (routes); shared_ptr rs = writer.get_copy (); - + rs->remove (route); /* deleting the master out seems like a dumb @@ -2106,17 +2102,17 @@ Session::remove_route (shared_ptr route) } update_route_solo_state (); - + /* writer goes out of scope, forces route list update */ } Track* t; boost::shared_ptr ds; - + if ((t = dynamic_cast(route.get())) != 0) { ds = t->diskstream(); } - + if (ds) { { @@ -2127,14 +2123,15 @@ Session::remove_route (shared_ptr route) } 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) */ @@ -2145,12 +2142,14 @@ 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)) { save_history (_current_snapshot_name); } -} +} void Session::route_mute_changed (void* src) @@ -2160,12 +2159,12 @@ Session::route_mute_changed (void* src) void Session::route_solo_changed (void* src, boost::weak_ptr wpr) -{ +{ if (solo_update_disabled) { // We know already return; } - + bool is_track; boost::shared_ptr route = wpr.lock (); @@ -2176,46 +2175,46 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) } is_track = (boost::dynamic_pointer_cast(route) != 0); - + shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - + /* soloing a track mutes all other tracks, soloing a bus mutes all other busses */ - + if (is_track) { - + /* don't mess with busses */ - + if (dynamic_cast((*i).get()) == 0) { continue; } - + } else { - + /* don't mess with tracks */ - + if (dynamic_cast((*i).get()) != 0) { continue; } } - + if ((*i) != route && ((*i)->mix_group () == 0 || (*i)->mix_group () != route->mix_group () || !route->mix_group ()->is_active())) { - + if ((*i)->soloed()) { - + /* if its already soloed, and solo latching is enabled, then leave it as it is. */ - + if (Config->get_solo_latched()) { continue; - } + } } - + /* do it */ solo_update_disabled = true; @@ -2223,7 +2222,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) solo_update_disabled = false; } } - + bool something_soloed = false; bool same_thing_soloed = false; bool signal = false; @@ -2245,12 +2244,12 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) break; } } - + if (something_soloed != currently_soloing) { signal = true; currently_soloing = something_soloed; } - + modify_solo_mute (is_track, same_thing_soloed); if (signal) { @@ -2269,15 +2268,13 @@ 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. */ - + shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->soloed()) { mute = true; if (dynamic_cast((*i).get())) { @@ -2299,7 +2296,7 @@ Session::update_route_solo_state () for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_solo_mute (false); } - + if (signal) { SoloActive (false); } @@ -2320,11 +2317,11 @@ Session::modify_solo_mute (bool is_track, bool mute) shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - + if (is_track) { - + /* only alter track solo mute */ - + if (dynamic_cast((*i).get())) { if ((*i)->soloed()) { (*i)->set_solo_mute (!mute); @@ -2348,7 +2345,7 @@ Session::modify_solo_mute (bool is_track, bool mute) /* don't mute master or control outs in response to another bus solo */ - + if ((*i) != _master_out && (*i) != _control_out) { (*i)->set_solo_mute (mute); @@ -2358,7 +2355,7 @@ Session::modify_solo_mute (bool is_track, bool mute) } } -} +} void @@ -2371,7 +2368,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) { @@ -2434,11 +2448,13 @@ nframes_t Session::get_maximum_extent () const { nframes_t max = 0; - nframes_t me; + nframes_t me; 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; @@ -2487,7 +2503,7 @@ Session::new_region_name (string old) char buf[len]; if ((last_period = old.find_last_of ('.')) == string::npos) { - + /* no period present - add one explicitly */ old += '.'; @@ -2515,7 +2531,7 @@ Session::new_region_name (string old) break; } } - + if (i == regions.end()) { break; } @@ -2523,14 +2539,14 @@ Session::new_region_name (string old) if (number != (UINT_MAX-1)) { return buf; - } + } error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg; return 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; @@ -2538,19 +2554,15 @@ Session::region_name (string& result, string base, bool newlevel) const assert(base.find("/") == string::npos); if (base == "") { - + Glib::Mutex::Lock lm (region_lock); snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1); - - result = "region."; result += buf; } else { - /* XXX this is going to be slow. optimize me later */ - if (newlevel) { subbase = base; } else { @@ -2564,89 +2576,137 @@ Session::region_name (string& result, string base, bool newlevel) const } - 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 (RegionList::const_iterator i = regions.begin(); i != 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) { - boost::shared_ptr other; + vector > v; + v.push_back (region); + add_regions (v); +} + +void +Session::add_regions (vector >& new_regions) +{ bool added = false; - { + { Glib::Mutex::Lock lm (region_lock); - RegionList::iterator x; + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { - for (x = regions.begin(); x != regions.end(); ++x) { + boost::shared_ptr region = *ii; - other = x->second; + if (region == 0) { - if (region->region_list_equivalent (other)) { - break; - } - } + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; + + } else { - if (x == regions.end()) { + RegionList::iterator x; - pair entry; + for (x = regions.begin(); x != regions.end(); ++x) { - entry.first = region->id(); - entry.second = region; + if (region->region_list_equivalent (x->second)) { + break; + } + } - pair x = regions.insert (entry); + if (x == regions.end()) { + pair entry; - if (!x.second) { - return; - } + entry.first = region->id(); + entry.second = region; + + pair x = regions.insert (entry); - added = true; - } + if (!x.second) { + return; + } + added = true; + } + } + } } /* mark dirty because something has changed even if we didn't add the region to the region list. */ - - set_dirty(); - + + set_dirty (); + if (added) { - region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr(region))); - region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr(region))); - RegionAdded (region); /* EMIT SIGNAL */ + + vector > v; + boost::shared_ptr first_r; + + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { + + boost::shared_ptr region = *ii; + + if (region == 0) { + + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; + + } else { + v.push_back (region); + + if (!first_r) { + first_r = region; + } + } + + 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()) { + RegionsAdded (v); /* EMIT SIGNAL */ + } + } +} + +void +Session::update_region_name_map (boost::shared_ptr region) +{ + string::size_type last_period = region->name().find_last_of ('.'); + + 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); } } @@ -2663,6 +2723,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 @@ -2677,7 +2741,7 @@ Session::remove_region (boost::weak_ptr weak_region) bool removed = false; - { + { Glib::Mutex::Lock lm (region_lock); if ((i = regions.find (region->id())) != regions.end()) { @@ -2702,7 +2766,7 @@ Session::find_whole_file_parent (boost::shared_ptr child) { RegionList::iterator i; boost::shared_ptr region; - + Glib::Mutex::Lock lm (region_lock); for (i = regions.begin(); i != regions.end(); ++i) { @@ -2715,10 +2779,10 @@ Session::find_whole_file_parent (boost::shared_ptr child) return region; } } - } + } return boost::shared_ptr (); -} +} void Session::find_equivalent_playlist_regions (boost::shared_ptr region, vector >& result) @@ -2731,20 +2795,14 @@ int Session::destroy_region (boost::shared_ptr region) { vector > srcs; - + { - boost::shared_ptr aregion; - - if ((aregion = boost::dynamic_pointer_cast (region)) == 0) { - return 0; + if (region->playlist()) { + region->playlist()->destroy_region (region); } - - if (aregion->playlist()) { - aregion->playlist()->destroy_region (region); - } - - for (uint32_t n = 0; n < aregion->n_channels(); ++n) { - srcs.push_back (aregion->source (n)); + + for (uint32_t n = 0; n < region->n_channels(); ++n) { + srcs.push_back (region->source (n)); } } @@ -2752,17 +2810,10 @@ Session::destroy_region (boost::shared_ptr region) for (vector >::iterator i = srcs.begin(); i != srcs.end(); ++i) { - if (!(*i)->used()) { - boost::shared_ptr afs = boost::dynamic_pointer_cast(*i); - - if (afs) { - (afs)->mark_for_remove (); - } - + (*i)->mark_for_remove (); (*i)->drop_references (); - + cerr << "source was not used by any playlist\n"; - } } return 0; @@ -2781,12 +2832,12 @@ int Session::remove_last_capture () { list > r; - + boost::shared_ptr dsl = diskstreams.reader(); - + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { list >& l = (*i)->last_capture_regions(); - + if (!l.empty()) { r.insert (r.end(), l.begin(), l.end()); l.clear (); @@ -2794,6 +2845,9 @@ Session::remove_last_capture () } destroy_regions (r); + + save_state (_current_snapshot_name); + return 0; } @@ -2813,7 +2867,7 @@ Session::add_source (boost::shared_ptr source) entry.first = source->id(); entry.second = source; - + { Glib::Mutex::Lock lm (source_lock); result = sources.insert (entry); @@ -2823,6 +2877,14 @@ Session::add_source (boost::shared_ptr source) source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); set_dirty(); } + + boost::shared_ptr afs; + + if ((afs = boost::dynamic_pointer_cast(source)) != 0) { + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } + } } void @@ -2833,26 +2895,22 @@ Session::remove_source (boost::weak_ptr src) if (!source) { return; - } + } - { + { Glib::Mutex::Lock lm (source_lock); - - { - Glib::Mutex::Lock lm (source_lock); - - if ((i = sources.find (source->id())) != sources.end()) { - sources.erase (i); - } + + if ((i = sources.find (source->id())) != sources.end()) { + sources.erase (i); } } - + if (!_state_of_the_state & InCleanup) { - + /* save state so we don't end up with a session file referring to non-existent sources. */ - + save_state (_current_snapshot_name); } } @@ -2868,8 +2926,6 @@ Session::source_by_id (const PBD::ID& id) source = i->second; } - /* XXX search MIDI or other searches here */ - return source; } @@ -2885,22 +2941,18 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) if (afs && afs->path() == path && chn == afs->channel()) { return afs; - } - + } + } return boost::shared_ptr(); } -string -Session::peak_path_from_audio_path (string audio_path) const +Glib::ustring +Session::peak_path (Glib::ustring base) const { - string res; - - res = peak_dir (); - res += PBD::basename_nosuffix (audio_path); - res += ".peak"; - - return res; + sys::path peakfile_path(_session_dir->peak_path()); + peakfile_path /= basename_nosuffix (base) + peakfile_suffix; + return peakfile_path.to_string(); } string @@ -2913,14 +2965,14 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, /* note: we know (or assume) the old path is already valid */ if (destructive) { - + /* destructive file sources have a name of the form: /path/to/Tnnnn-NAME(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + /* find last slash */ string dir; @@ -2947,16 +2999,16 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, path += '-'; path += new_legalized; path += ".wav"; /* XXX gag me with a spoon */ - + } else { - + /* non-destructive file sources have a name of the form: /path/to/NAME-nnnnn(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + string dir; string suffix; string::size_type slash; @@ -2978,7 +3030,7 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, } suffix = path.substr (dash+1); - + // Suffix is now everything after the dash. Now we need to eliminate // the nnnnn part, which is done by either finding a '%' or a '.' @@ -3040,9 +3092,9 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - spath = (*i).path; + SessionDirectory sdir((*i).path); - spath += sound_dir (false); + spath = sdir.sound_path().to_string(); if (destructive) { if (nchan < 2) { @@ -3079,9 +3131,9 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool } } - if (g_file_test (buf, G_FILE_TEST_EXISTS)) { + if (sys::exists(buf)) { existing++; - } + } } @@ -3102,11 +3154,13 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool string foo = buf; - spath = discover_best_sound_dir (); + SessionDirectory sdir(get_best_session_directory_for_new_source ()); + + spath = sdir.sound_path().to_string(); spath += '/'; string::size_type pos = foo.find_last_of ('/'); - + if (pos == string::npos) { spath += foo; } else { @@ -3119,7 +3173,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool boost::shared_ptr Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) { - string spath = audio_path_from_name (ds.name(), ds.n_channels().get(DataType::AUDIO), chan, destructive); + string spath = audio_path_from_name (ds.name(), ds.n_channels().n_audio(), chan, destructive); return boost::dynamic_pointer_cast ( SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate())); } @@ -3135,14 +3189,14 @@ Session::change_midi_path_by_name (string path, string oldname, string newname, /* note: we know (or assume) the old path is already valid */ if (destructive) { - + /* destructive file sources have a name of the form: /path/to/Tnnnn-NAME(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + /* find last slash */ string dir; @@ -3169,16 +3223,16 @@ Session::change_midi_path_by_name (string path, string oldname, string newname, path += '-'; path += new_legalized; path += ".mid"; /* XXX gag me with a spoon */ - + } else { - + /* non-destructive file sources have a name of the form: /path/to/NAME-nnnnn(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + string dir; string suffix; string::size_type slash; @@ -3200,7 +3254,7 @@ Session::change_midi_path_by_name (string path, string oldname, string newname, } suffix = path.substr (dash+1); - + // Suffix is now everything after the dash. Now we need to eliminate // the nnnnn part, which is done by either finding a '%' or a '.' @@ -3262,16 +3316,19 @@ Session::midi_path_from_name (string name) for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - spath = (*i).path; + SessionDirectory sdir((*i).path); + + sys::path p = sdir.midi_path(); - // FIXME: different directory from audio? - spath += sound_dir(false) + "/" + legalized; + p /= legalized; + + spath = p.to_string(); snprintf (buf, sizeof(buf), "%s-%u.mid", spath.c_str(), cnt); - if (g_file_test (buf, G_FILE_TEST_EXISTS)) { + if (sys::exists (buf)) { existing++; - } + } } if (existing == 0) { @@ -3290,12 +3347,13 @@ Session::midi_path_from_name (string name) string foo = buf; - // FIXME: different directory than audio? - spath = discover_best_sound_dir (); + SessionDirectory sdir(get_best_session_directory_for_new_source ()); + + spath = sdir.midi_path().to_string(); spath += '/'; string::size_type pos = foo.find_last_of ('/'); - + if (pos == string::npos) { spath += foo; } else { @@ -3308,9 +3366,9 @@ Session::midi_path_from_name (string name) boost::shared_ptr Session::create_midi_source_for_session (MidiDiskstream& ds) { - string spath = midi_path_from_name (ds.name()); - - return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, spath, false, frame_rate())); + string mpath = midi_path_from_name (ds.name()); + + return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, mpath, false, frame_rate())); } @@ -3335,13 +3393,29 @@ Session::playlist_by_name (string name) } void -Session::add_playlist (boost::shared_ptr playlist) +Session::unassigned_playlists (std::list > & list) +{ + Glib::Mutex::Lock lm (playlist_lock); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) { + list.push_back (*i); + } + } + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) { + list.push_back (*i); + } + } +} + +void +Session::add_playlist (boost::shared_ptr playlist, bool unused) { if (playlist->hidden()) { return; } - { + { Glib::Mutex::Lock lm (playlist_lock); if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) { playlists.insert (playlists.begin(), playlist); @@ -3350,6 +3424,10 @@ Session::add_playlist (boost::shared_ptr playlist) } } + if (unused) { + playlist->release(); + } + set_dirty(); PlaylistAdded (playlist); /* EMIT SIGNAL */ @@ -3358,7 +3436,7 @@ Session::add_playlist (boost::shared_ptr playlist) void 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); @@ -3385,22 +3463,22 @@ Session::track_playlist (bool inuse, boost::weak_ptr wpl) return; } - { + { Glib::Mutex::Lock lm (playlist_lock); if (!inuse) { unused_playlists.insert (pl); - + if ((x = playlists.find (pl)) != playlists.end()) { playlists.erase (x); } - + } else { playlists.insert (pl); - + if ((x = unused_playlists.find (pl)) != unused_playlists.end()) { unused_playlists.erase (x); } @@ -3421,7 +3499,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) return; } - { + { Glib::Mutex::Lock lm (playlist_lock); PlaylistList::iterator i; @@ -3435,7 +3513,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) if (i != unused_playlists.end()) { unused_playlists.erase (i); } - + } set_dirty(); @@ -3443,7 +3521,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) PlaylistRemoved (playlist); /* EMIT SIGNAL */ } -void +void Session::set_audition (boost::shared_ptr r) { pending_audition_region = r; @@ -3497,46 +3575,37 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: void Session::remove_empty_sounds () { - PathScanner scanner; + vector audio_filenames; + + get_files_in_directory (_session_dir->sound_path(), audio_filenames); - vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); - Glib::Mutex::Lock lm (source_lock); - - regex_t compiled_tape_track_pattern; - int err; - if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) { + TapeFileMatcher tape_file_matcher; - char msg[256]; - - regerror (err, &compiled_tape_track_pattern, msg, sizeof (msg)); - - error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg; - return; - } + remove_if (audio_filenames.begin(), audio_filenames.end(), + sigc::mem_fun (tape_file_matcher, &TapeFileMatcher::matches)); - for (vector::iterator i = possible_audiofiles->begin(); i != possible_audiofiles->end(); ++i) { - - /* never remove files that appear to be a tape track */ + for (vector::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) { - if (regexec (&compiled_tape_track_pattern, (*i)->c_str(), 0, 0, 0) == 0) { - delete *i; - continue; - } - - if (AudioFileSource::is_empty (*this, *(*i))) { + sys::path audio_file_path (_session_dir->sound_path()); - unlink ((*i)->c_str()); - - string peak_path = peak_path_from_audio_path (**i); - unlink (peak_path.c_str()); - } + audio_file_path /= *i; - delete* i; - } + if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) { - delete possible_audiofiles; + try + { + sys::remove (audio_file_path); + const string peakfile = peak_path (audio_file_path.to_string()); + sys::remove (peakfile); + } + catch (const sys::filesystem_error& err) + { + error << err.what() << endmsg; + } + } + } } bool @@ -3554,30 +3623,30 @@ 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()) { + if (!(*i)->is_hidden()) { (*i)->set_solo (yn, this); } } set_dirty(); } - + void Session::set_all_mute (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->hidden()) { + if (!(*i)->is_hidden()) { (*i)->set_mute (yn, this); } } set_dirty(); } - + uint32_t Session::n_diskstreams () const { @@ -3603,7 +3672,7 @@ Session::graph_reordered () if (_state_of_the_state & InitialConnecting) { return; } - + /* every track/bus asked for this to be handled but it was deferred because we were connecting. do it now. */ @@ -3612,10 +3681,10 @@ Session::graph_reordered () resort_routes (); - /* force all diskstreams to update their capture offset values to + /* force all diskstreams to update their capture offset values to reflect any changes in latencies within the graph. */ - + boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { @@ -3639,7 +3708,7 @@ void Session::record_enable_change_all (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { Track* at; @@ -3647,70 +3716,56 @@ Session::record_enable_change_all (bool yn) at->set_record_enable (yn, this); } } - + /* since we don't keep rec-enable state, don't mark session dirty */ } void -Session::add_redirect (Redirect* redirect) +Session::add_processor (Processor* processor) { Send* send; - Insert* insert; PortInsert* port_insert; PluginInsert* plugin_insert; - if ((insert = dynamic_cast (redirect)) != 0) { - if ((port_insert = dynamic_cast (insert)) != 0) { - _port_inserts.insert (_port_inserts.begin(), port_insert); - } else if ((plugin_insert = dynamic_cast (insert)) != 0) { - _plugin_inserts.insert (_plugin_inserts.begin(), plugin_insert); - } else { - fatal << _("programming error: unknown type of Insert created!") << endmsg; - /*NOTREACHED*/ - } - } else if ((send = dynamic_cast (redirect)) != 0) { + if ((port_insert = dynamic_cast (processor)) != 0) { + _port_inserts.insert (_port_inserts.begin(), port_insert); + } else if ((plugin_insert = dynamic_cast (processor)) != 0) { + _plugin_inserts.insert (_plugin_inserts.begin(), plugin_insert); + } else if ((send = dynamic_cast (processor)) != 0) { _sends.insert (_sends.begin(), send); } else { - fatal << _("programming error: unknown type of Redirect created!") << endmsg; + fatal << _("programming error: unknown type of Insert created!") << endmsg; /*NOTREACHED*/ } - redirect->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_redirect), redirect)); + processor->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_processor), processor)); set_dirty(); } void -Session::remove_redirect (Redirect* redirect) +Session::remove_processor (Processor* processor) { Send* send; - Insert* insert; PortInsert* port_insert; PluginInsert* plugin_insert; - - if ((insert = dynamic_cast (redirect)) != 0) { - if ((port_insert = dynamic_cast (insert)) != 0) { - 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 << string_compose (_("programming error: %1"), - X_("unknown type of Insert deleted!")) - << endmsg; - /*NOTREACHED*/ + + if ((port_insert = dynamic_cast (processor)) != 0) { + 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 ((send = dynamic_cast (redirect)) != 0) { + } else if ((plugin_insert = dynamic_cast (processor)) != 0) { + _plugin_inserts.remove (plugin_insert); + } else if ((send = dynamic_cast (processor)) != 0) { 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; + fatal << _("programming error: unknown type of Insert deleted!") << endmsg; /*NOTREACHED*/ } @@ -3731,7 +3786,11 @@ Session::available_capture_duration () sample_bytes_on_disk = 3.0; break; - default: + 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")) @@ -3744,63 +3803,72 @@ Session::available_capture_duration () if (_total_free_4k_blocks * scale > (double) max_frames) { return max_frames; } - + return (nframes_t) floor (_total_free_4k_blocks * scale); } void -Session::add_connection (ARDOUR::Connection* connection) +Session::add_bundle (shared_ptr bundle) { { - Glib::Mutex::Lock guard (connection_lock); - _connections.push_back (connection); + Glib::Mutex::Lock guard (bundle_lock); + _bundles.push_back (bundle); } - - ConnectionAdded (connection); /* EMIT SIGNAL */ + + BundleAdded (bundle); /* EMIT SIGNAL */ set_dirty(); } void -Session::remove_connection (ARDOUR::Connection* connection) +Session::remove_bundle (shared_ptr bundle) { bool removed = false; { - Glib::Mutex::Lock guard (connection_lock); - ConnectionList::iterator i = find (_connections.begin(), _connections.end(), connection); - - if (i != _connections.end()) { - _connections.erase (i); + Glib::Mutex::Lock guard (bundle_lock); + BundleList::iterator i = find (_bundles.begin(), _bundles.end(), bundle); + + if (i != _bundles.end()) { + _bundles.erase (i); removed = true; } } if (removed) { - ConnectionRemoved (connection); /* EMIT SIGNAL */ + BundleRemoved (bundle); /* EMIT SIGNAL */ } set_dirty(); } -ARDOUR::Connection * -Session::connection_by_name (string name) const +shared_ptr +Session::bundle_by_name (string name) const { - Glib::Mutex::Lock lm (connection_lock); + Glib::Mutex::Lock lm (bundle_lock); - for (ConnectionList::const_iterator i = _connections.begin(); i != _connections.end(); ++i) { + for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) { if ((*i)->name() == name) { return* i; } } - return 0; + return boost::shared_ptr (); } 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 (); } @@ -3810,12 +3878,20 @@ Session::tempo_map_changed (Change ignored) void Session::ensure_buffers (ChanCount howmany) { - // FIXME: NASTY assumption (midi block size == audio block size) + if (current_block_size == 0) + return; // too early? (is this ok?) + + // We need at least 2 MIDI scratch buffers to mix/merge + if (howmany.n_midi() < 2) + howmany.set_midi(2); + + // FIXME: JACK needs to tell us maximum MIDI buffer size + // Using nasty assumption (max # events == nframes) for now _scratch_buffers->ensure_buffers(howmany, current_block_size); - _send_buffers->ensure_buffers(howmany, current_block_size); + _mix_buffers->ensure_buffers(howmany, current_block_size); _silent_buffers->ensure_buffers(howmany, current_block_size); - - allocate_pan_automation_buffers (current_block_size, howmany.get(DataType::AUDIO), false); + + allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false); } uint32_t @@ -3828,10 +3904,10 @@ Session::next_insert_id () 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); @@ -3848,10 +3924,10 @@ Session::next_send_id () 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); @@ -3899,7 +3975,7 @@ Session::named_selection_by_name (string name) void Session::add_named_selection (NamedSelection* named_selection) { - { + { Glib::Mutex::Lock lm (named_selection_lock); named_selections.insert (named_selections.begin(), named_selection); } @@ -3918,7 +3994,7 @@ Session::remove_named_selection (NamedSelection* named_selection) { bool removed = false; - { + { Glib::Mutex::Lock lm (named_selection_lock); NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection); @@ -3950,13 +4026,13 @@ bool Session::route_name_unique (string n) const { shared_ptr r = routes.reader (); - + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == n) { return false; } } - + return true; } @@ -3984,7 +4060,7 @@ Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, b } _pan_automation_buffer = new pan_t*[howmany]; - + for (uint32_t i = 0; i < howmany; ++i) { _pan_automation_buffer[i] = new pan_t[nframes]; } @@ -4012,29 +4088,37 @@ 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_track (AudioTrack& track, nframes_t start, nframes_t end, + bool overwrite, vector >& srcs, InterThreadInfo& itt) { - int ret = -1; + boost::shared_ptr result; boost::shared_ptr playlist; boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; - string dir; ChanCount nchans(track.audio_diskstream()->n_channels()); nframes_t position; nframes_t this_chunk; nframes_t to_do; BufferSet buffers; + SessionDirectory sdir(get_best_session_directory_for_new_source ()); + const string sound_dir = sdir.sound_path().to_string(); + nframes_t len = end - start; + + 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); - + /* call tree *MUST* hold route_lock */ - + if ((playlist = track.diskstream()->playlist()) == 0) { goto out; } @@ -4044,28 +4128,26 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le if (track.has_external_redirects()) { goto out; } - - dir = discover_best_sound_dir (); - for (uint32_t chan_n=0; chan_n < nchans.get(DataType::AUDIO); ++chan_n) { + for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) { for (x = 0; x < 99999; ++x) { - snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", dir.c_str(), playlist->name().c_str(), chan_n, x+1); + snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1); if (access (buf, F_OK) != 0) { break; } } - + if (x == 99999) { error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg; goto out; } - + try { fsource = boost::dynamic_pointer_cast ( SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } - + catch (failed_constructor& err) { error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg; goto out; @@ -4075,7 +4157,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le } /* XXX need to flush all redirects */ - + position = start; to_do = len; @@ -4088,11 +4170,11 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le if (afs) afs->prepare_for_peakfile_writes (); } - + while (to_do && !itt.cancel) { - + this_chunk = min (to_do, chunk_size); - + if (track.export_stuff (buffers, start, this_chunk)) { goto out; } @@ -4100,61 +4182,59 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le uint32_t n = 0; for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) { - if (afs->write (buffers.get_audio(n).data(this_chunk), this_chunk) != this_chunk) { + if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) { goto out; } } } - + start += this_chunk; to_do -= this_chunk; - + itt.progress = (float) (1.0 - ((double) to_do / len)); } if (!itt.cancel) { - + time_t now; struct tm* xnow; time (&now); xnow = localtime (&now); - + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) { afs->update_header (position, *xnow, now); afs->flush_header (); } } - - /* 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)); + /* construct a region to represent the bounced material */ - 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) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) afs->done_with_peakfile_writes (); } @@ -4162,9 +4242,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le g_atomic_int_set (&processing_prohibited, 0); - itt.done = true; - - return ret; + return result; } BufferSet& @@ -4174,31 +4252,36 @@ Session::get_silent_buffers (ChanCount count) _silent_buffers->set_count(count); for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - for (size_t i=0; i < count.get(*t); ++i) { + for (size_t i= 0; i < count.get(*t); ++i) { _silent_buffers->get(*t, i).clear(); } } - + return *_silent_buffers; } BufferSet& Session::get_scratch_buffers (ChanCount count) { - assert(_scratch_buffers->available() >= count); - _scratch_buffers->set_count(count); + if (count != ChanCount::ZERO) { + assert(_scratch_buffers->available() >= count); + _scratch_buffers->set_count(count); + } else { + _scratch_buffers->set_count (_scratch_buffers->available()); + } + return *_scratch_buffers; } BufferSet& -Session::get_send_buffers (ChanCount count) +Session::get_mix_buffers (ChanCount count) { - assert(_send_buffers->available() >= count); - _send_buffers->set_count(count); - return *_send_buffers; + assert(_mix_buffers->available() >= count); + _mix_buffers->set_count(count); + return *_mix_buffers; } -uint32_t +uint32_t Session::ntracks () const { uint32_t n = 0; @@ -4213,7 +4296,7 @@ Session::ntracks () const return n; } -uint32_t +uint32_t Session::nbusses () const { uint32_t n = 0; @@ -4240,3 +4323,29 @@ 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 +} + +void +Session::foreach_bundle (sigc::slot > sl) +{ + Glib::Mutex::Lock lm (bundle_lock); + for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) { + sl (*i); + } +} +