X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=14ef0e1ac5b346a5b6a48e40ea33569265c957da;hb=f8480d6392e6d0e61aa39e564904ba67822f711e;hp=8f61fc9ac111a0c2ea27399ab7362952d4cdef12;hpb=45d3ec1437cf661533bc7750c623865def4424df;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 8f61fc9ac1..14ef0e1ac5 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,32 +41,47 @@ #include #include #include +#include #include #include #include -#include +#include +#include #include +#include #include #include #include +#include +#include +#include +#include #include #include -#include +#include #include -#include -#include +#include +#include +#include +#include #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include +#include +#include +#include #ifdef HAVE_LIBLO #include @@ -84,195 +100,46 @@ 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()), + _mix_buffers(new BufferSet()), _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 + post_transport_work((PostTransportWork)0), + _send_smpte_update (false), + midi_requests (128), 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) + main_outs (0), + _metadata (new SessionMetadata()) { bool new_session; @@ -282,27 +149,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); @@ -326,13 +193,22 @@ Session::Session (AudioEngine &eng, nframes_t initial_length) : _engine (eng), + _scratch_buffers(new BufferSet()), + _silent_buffers(new BufferSet()), + _mix_buffers(new BufferSet()), _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), + post_transport_work((PostTransportWork)0), + _send_smpte_update (false), midi_requests (16), diskstreams (new DiskstreamList), routes (new RouteList), + _total_free_4k_blocks (0), + _bundle_xml_node (0), main_outs (0) { @@ -344,8 +220,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); @@ -360,7 +236,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 (); } @@ -368,31 +244,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); @@ -402,18 +278,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 () @@ -431,10 +301,11 @@ Session::destroy () remove_pending_capture_state (); _state_of_the_state = StateOfTheState (CannotSave|Deletion); + _engine.remove_session (); GoingAway (); /* EMIT SIGNAL */ - + /* do this */ notify_callbacks (); @@ -444,14 +315,14 @@ Session::destroy () _history.clear (); /* clear state tree so that no references to objects are held any more */ - + if (state_tree) { delete state_tree; } terminate_butler_thread (); - terminate_midi_thread (); - + //terminate_midi_thread (); + if (click_data && click_data != default_click) { delete [] click_data; } @@ -462,24 +333,14 @@ Session::destroy () clear_clicks (); - for (vector::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) { - free(*i); - } - - for (vector::iterator i = _silent_buffers.begin(); i != _silent_buffers.end(); ++i) { - free(*i); - } - - for (vector::iterator i = _send_buffers.begin(); i != _send_buffers.end(); ++i) { - free(*i); - } + delete _scratch_buffers; + delete _silent_buffers; + delete _mix_buffers; AudioDiskstream::free_working_buffers(); - /* this should cause deletion of the auditioner */ + Route::SyncOrderKeys.clear(); - // auditioner.reset (); - #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION cerr << "delete named selections\n"; @@ -504,10 +365,10 @@ Session::destroy () ++tmp; (*i)->drop_references (); - + i = tmp; } - + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { PlaylistList::iterator tmp; @@ -515,19 +376,19 @@ Session::destroy () ++tmp; (*i)->drop_references (); - + i = tmp; } - + playlists.clear (); unused_playlists.clear (); #ifdef TRACK_DESTRUCTION - cerr << "delete audio regions\n"; + cerr << "delete regions\n"; #endif /* TRACK_DESTRUCTION */ - - for (AudioRegionList::iterator i = audio_regions.begin(); i != audio_regions.end(); ) { - AudioRegionList::iterator tmp; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { + RegionList::iterator tmp; tmp = i; ++tmp; @@ -537,12 +398,12 @@ Session::destroy () i = tmp; } - audio_regions.clear (); - + regions.clear (); + #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) { @@ -570,8 +431,8 @@ Session::destroy () #ifdef TRACK_DESTRUCTION cerr << "delete audio sources\n"; #endif /* TRACK_DESTRUCTION */ - for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ) { - AudioSourceList::iterator tmp; + for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) { + SourceMap::iterator tmp; tmp = i; ++tmp; @@ -580,8 +441,7 @@ Session::destroy () i = tmp; } - - audio_sources.clear (); + sources.clear (); #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; @@ -602,20 +462,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; @@ -651,7 +497,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()); @@ -665,9 +511,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 */ @@ -686,15 +536,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 { @@ -704,11 +554,11 @@ Session::when_engine_running () } } else { - + /* default state for Click */ - first_physical_output = _engine.get_nth_physical_output (0); - + first_physical_output = _engine.get_nth_physical_output (DataType::AUDIO, 0); + if (first_physical_output.length()) { if (_click_io->add_output_port (first_physical_output, this)) { // relax, even though its an error @@ -723,40 +573,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); - - c->add_port (); - c->add_connection (0, _engine.get_nth_physical_output (np)); + shared_ptr c (new AutoBundle (buf, true)); + c->set_channels (1); + c->set_port (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); + shared_ptr c (new AutoBundle (buf, false)); + c->set_channels (1); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - c->add_port (); - c->add_connection (0, _engine.get_nth_physical_input (np)); - - add_connection (c); + add_bundle (c); } /* TWO: STEREO */ @@ -765,28 +617,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); - - 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)); + shared_ptr c (new AutoBundle (buf, true)); + c->set_channels (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)); - 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); + shared_ptr c (new AutoBundle (buf, false)); + c->set_channels (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)); - 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)); - - add_connection (c); + add_bundle (c); } /* THREE MASTER */ @@ -794,30 +642,32 @@ 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 ((int) _master_out->n_inputs() < _master_out->input_maximum()) { - if (_master_out->add_input_port ("", this)) { - error << _("cannot setup master inputs") + + 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") << endmsg; break; } } 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)) { + 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; break; @@ -826,24 +676,28 @@ Session::when_engine_running () } _master_out->allow_pan_reset (); - + } - Connection* c = new OutputConnection (_("Master Out"), true); + shared_ptr c (new AutoBundle (_("Master Out"), true)); - for (uint32_t n = 0; n < _master_out->n_inputs (); ++n) { - c->add_port (); - c->add_connection ((int) n, _master_out->input(n)->name()); - } - add_connection (c); - } + c->set_channels (_master_out->n_inputs().n_total()); + for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) { + c->set_port (n, _master_out->input(n)->name()); + } + add_bundle (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; @@ -858,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; @@ -866,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 @@ -893,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; } @@ -915,8 +771,9 @@ Session::hookup_io () if (_control_out) { uint32_t n; + vector cports; - while ((int) _control_out->n_inputs() < _control_out->input_maximum()) { + 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; @@ -924,14 +781,33 @@ 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)) { + 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; break; } 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 */ @@ -948,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. */ @@ -962,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 (); @@ -979,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 (); } @@ -1032,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) @@ -1040,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) @@ -1050,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) @@ -1064,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; @@ -1081,10 +958,9 @@ Session::auto_loop_changed (Location* location) } } - } + } last_loopend = location->end(); - } void @@ -1107,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; @@ -1116,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); } @@ -1138,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) { @@ -1151,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); } @@ -1196,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) { @@ -1207,7 +1100,7 @@ Session::handle_locations_changed (Locations::LocationList& locations) } set_dirty(); -} +} void Session::enable_record () @@ -1216,13 +1109,13 @@ Session::enable_record () if (g_atomic_int_get (&_record_status) != Recording) { g_atomic_int_set (&_record_status, Recording); _last_record_location = _transport_frame; - send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordStrobe); + deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location); if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - (*i)->monitor_input (true); + (*i)->monitor_input (true); } } } @@ -1238,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) { @@ -1246,18 +1139,22 @@ Session::disable_record (bool rt_context, bool force) } } - send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordExit); + // FIXME: timestamp correct? [DR] + // FIXME FIXME FIXME: rt_context? this must be called in the process thread. + // does this /need/ to be sent in all cases? + if (rt_context) + deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame); 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) { @@ -1269,15 +1166,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); + } } } } @@ -1297,9 +1197,9 @@ Session::maybe_enable_record () if (_transport_speed) { if (!Config->get_punch_in()) { enable_record (); - } + } } else { - send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordPause); + deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame); RecordStateChanged (); /* EMIT SIGNAL */ } @@ -1314,7 +1214,7 @@ Session::audible_frame () const nframes_t tf; /* 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". @@ -1327,7 +1227,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 @@ -1357,7 +1257,7 @@ Session::audible_frame () const /* MOVING */ /* take latency into account */ - + ret -= offset; } @@ -1368,7 +1268,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. @@ -1378,9 +1278,11 @@ 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()))); - // XXX we need some equivalent to this, somehow + clear_clicks (); + + // XXX we need some equivalent to this, somehow // SndFileSource::setup_standard_crossfades (frames_per_second); set_dirty(); @@ -1391,46 +1293,18 @@ 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. */ - { - vector::iterator i; - uint32_t np; - - current_block_size = nframes; - - for (np = 0, i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i, ++np) { - free (*i); - } - - for (vector::iterator i = _silent_buffers.begin(); i != _silent_buffers.end(); ++i) { - free (*i); - } - - _passthru_buffers.clear (); - _silent_buffers.clear (); - - ensure_passthru_buffers (np); - - for (vector::iterator i = _send_buffers.begin(); i != _send_buffers.end(); ++i) { - free(*i); + { - Sample *buf; -#ifdef NO_POSIX_MEMALIGN - buf = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&buf,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)); -#endif - *i = buf; + current_block_size = nframes; - memset (*i, 0, sizeof (Sample) * current_block_size); - } + ensure_buffers(_scratch_buffers->available()); - if (_gain_automation_buffer) { delete [] _gain_automation_buffer; } @@ -1443,7 +1317,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); @@ -1458,18 +1332,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; @@ -1484,7 +1358,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 @@ -1520,7 +1394,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 */ @@ -1584,51 +1458,204 @@ 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); - - /* don't leave dangling references to routes in Route::fed_by */ - - for (i = r->begin(); i != r->end(); ++i) { - (*i)->fed_by.clear (); - } #if 0 cerr << "finished route resort\n"; - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl; } cerr << endl; #endif - + +} + +list > +Session::new_midi_track (TrackMode mode, uint32_t how_many) +{ + char track_name[32]; + uint32_t track_id = 0; + uint32_t n = 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 */ + + { + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (dynamic_cast((*i).get()) != 0) { + if (!(*i)->is_hidden()) { + n++; + //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 Audio1, Audio2, delete Audio1, + save, close,restart,add new route - first named route is now + Audio2) + */ + + + do { + ++track_id; + + snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id); + + if (route_by_name (track_name) == 0) { + break; + } + + } while (track_id < (UINT_MAX-1)); + + shared_ptr track; + + try { + 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; + } + + /* + 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 (control_id); + + new_routes.push_back (track); + ret.push_back (track); + } + + catch (failed_constructor &err) { + error << _("Session: could not create new midi track.") << 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->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); + } + + return ret; } list > @@ -1650,9 +1677,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(); + channels_used += (*i)->n_inputs().n_audio(); } } } @@ -1660,11 +1687,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) { @@ -1674,7 +1700,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; @@ -1684,82 +1710,63 @@ 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 (input_channels, output_channels, false, this)) { + + 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) << endmsg; goto failed; } - if (nphysical_in) { - for (uint32_t x = 0; x < track->n_inputs() && 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(); ++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())->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 (); - - 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) - + channels_used += track->n_inputs ().n_audio(); + 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; @@ -1775,7 +1782,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()); @@ -1793,7 +1800,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()); @@ -1808,8 +1815,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; @@ -1823,14 +1829,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 } } } @@ -1842,6 +1848,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; @@ -1852,9 +1859,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(); } } } @@ -1863,8 +1873,8 @@ 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); control_id = ntracks() + nbusses() + 1; while (how_many) { @@ -1872,68 +1882,62 @@ 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 (input_channels, output_channels, false, this)) { + + 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(); ++x) { - + + for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().n_audio(); ++x) { + port = ""; if (Config->get_input_auto_connect() & AutoConnectPhysical) { port = physinputs[((n+x)%n_physical_inputs)]; - } - + } + if (port.length() && bus->connect_input (bus->input (x), port, this)) { break; } } - - for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs(); ++x) { - + + for (uint32_t x = 0; 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())->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(); - - 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; @@ -1951,8 +1955,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; @@ -1962,7 +1965,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()); @@ -1970,21 +1973,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(); @@ -2001,7 +2025,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); { @@ -2009,7 +2033,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 */ @@ -2022,10 +2046,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 @@ -2050,18 +2074,17 @@ Session::remove_route (shared_ptr route) } update_route_solo_state (); - + /* writer goes out of scope, forces route list update */ } - // FIXME: audio specific - AudioTrack* at; - boost::shared_ptr ds; - - if ((at = dynamic_cast(route.get())) != 0) { - ds = at->audio_diskstream(); + Track* t; + boost::shared_ptr ds; + + if ((t = dynamic_cast(route.get())) != 0) { + ds = t->diskstream(); } - + if (ds) { { @@ -2072,14 +2095,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) */ @@ -2090,12 +2114,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) @@ -2105,12 +2131,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 (); @@ -2121,46 +2147,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) { + + if (dynamic_cast((*i).get()) == 0) { continue; } - + } else { - + /* don't mess with tracks */ - - if (dynamic_cast((*i).get()) != 0) { + + 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; @@ -2168,7 +2194,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; @@ -2176,7 +2202,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->soloed()) { something_soloed = true; - if (dynamic_cast((*i).get())) { + if (dynamic_cast((*i).get())) { if (is_track) { same_thing_soloed = true; break; @@ -2190,12 +2216,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) { @@ -2219,13 +2245,13 @@ Session::update_route_solo_state () /* 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())) { + if (dynamic_cast((*i).get())) { is_track = true; } break; @@ -2244,7 +2270,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); } @@ -2265,12 +2291,12 @@ 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 (dynamic_cast((*i).get())) { if ((*i)->soloed()) { (*i)->set_solo_mute (!mute); } else { @@ -2282,7 +2308,7 @@ Session::modify_solo_mute (bool is_track, bool mute) /* only alter bus solo mute */ - if (!dynamic_cast((*i).get())) { + if (!dynamic_cast((*i).get())) { if ((*i)->soloed()) { @@ -2293,7 +2319,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); @@ -2303,7 +2329,7 @@ Session::modify_solo_mute (bool is_track, bool mute) } } -} +} void @@ -2315,8 +2341,8 @@ Session::catch_up_on_solo () has. */ update_route_solo_state(); -} - +} + shared_ptr Session::route_by_name (string name) { @@ -2379,11 +2405,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; @@ -2421,7 +2449,7 @@ Session::diskstream_by_id (const PBD::ID& id) return boost::shared_ptr((Diskstream*) 0); } -/* AudioRegion management */ +/* Region management */ string Session::new_region_name (string old) @@ -2432,7 +2460,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 += '.'; @@ -2447,7 +2475,7 @@ Session::new_region_name (string old) while (number < (UINT_MAX-1)) { - AudioRegionList::const_iterator i; + RegionList::const_iterator i; string sbuf; number++; @@ -2455,45 +2483,43 @@ Session::new_region_name (string old) snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number); sbuf = buf; - for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { + for (i = regions.begin(); i != regions.end(); ++i) { if (i->second->name() == sbuf) { break; } } - - if (i == audio_regions.end()) { + + if (i == regions.end()) { break; } } 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; + assert(base.find("/") == string::npos); + if (base == "") { - - Glib::Mutex::Lock lm (region_lock); - snprintf (buf, sizeof (buf), "%d", (int)audio_regions.size() + 1); + 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 { @@ -2507,100 +2533,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 (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) { - boost::shared_ptr ar; - boost::shared_ptr oar; + 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); - if ((ar = boost::dynamic_pointer_cast (region)) != 0) { + for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { - AudioRegionList::iterator x; + boost::shared_ptr region = *ii; - for (x = audio_regions.begin(); x != audio_regions.end(); ++x) { + 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 (x == audio_regions.end()) { + RegionList::iterator x; - pair entry; + for (x = regions.begin(); x != regions.end(); ++x) { - entry.first = region->id(); - entry.second = ar; + if (region->region_list_equivalent (x->second)) { + break; + } + } - pair x = audio_regions.insert (entry); + if (x == regions.end()) { - - if (!x.second) { - return; - } + pair entry; - added = true; - } + entry.first = region->id(); + entry.second = region; - } else { + pair x = regions.insert (entry); - fatal << _("programming error: ") - << X_("unknown region type passed to Session::add_region()") - << endmsg; - /*NOTREACHED*/ + 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))); - AudioRegionAdded (ar); /* 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); } } @@ -2617,36 +2680,30 @@ 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 Session::remove_region (boost::weak_ptr weak_region) { - AudioRegionList::iterator i; + RegionList::iterator i; boost::shared_ptr region (weak_region.lock ()); if (!region) { return; } - boost::shared_ptr ar; bool removed = false; - { + { Glib::Mutex::Lock lm (region_lock); - if ((ar = boost::dynamic_pointer_cast (region)) != 0) { - if ((i = audio_regions.find (region->id())) != audio_regions.end()) { - audio_regions.erase (i); - removed = true; - } - - } else { - - fatal << _("programming error: ") - << X_("unknown region type passed to Session::remove_region()") - << endmsg; - /*NOTREACHED*/ + if ((i = regions.find (region->id())) != regions.end()) { + regions.erase (i); + removed = true; } } @@ -2657,18 +2714,19 @@ Session::remove_region (boost::weak_ptr weak_region) set_dirty(); if (removed) { - AudioRegionRemoved (ar); /* EMIT SIGNAL */ + RegionRemoved(region); /* EMIT SIGNAL */ } } -boost::shared_ptr -Session::find_whole_file_parent (boost::shared_ptr child) +boost::shared_ptr +Session::find_whole_file_parent (boost::shared_ptr child) { - AudioRegionList::iterator i; - boost::shared_ptr region; + RegionList::iterator i; + boost::shared_ptr region; + Glib::Mutex::Lock lm (region_lock); - for (i = audio_regions.begin(); i != audio_regions.end(); ++i) { + for (i = regions.begin(); i != regions.end(); ++i) { region = i->second; @@ -2678,10 +2736,10 @@ Session::find_whole_file_parent (boost::shared_ptr child) return region; } } - } + } - return boost::shared_ptr (); -} + return boost::shared_ptr (); +} void Session::find_equivalent_playlist_regions (boost::shared_ptr region, vector >& result) @@ -2694,20 +2752,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)); } } @@ -2715,17 +2767,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; @@ -2744,12 +2789,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 (); @@ -2757,6 +2802,9 @@ Session::remove_last_capture () } destroy_regions (r); + + save_state (_current_snapshot_name); + return 0; } @@ -2768,57 +2816,58 @@ Session::remove_region_from_region_list (boost::shared_ptr r) } /* Source Management */ - void Session::add_source (boost::shared_ptr source) { - boost::shared_ptr afs; + pair entry; + pair result; - if ((afs = boost::dynamic_pointer_cast(source)) != 0) { + entry.first = source->id(); + entry.second = source; - pair entry; - pair result; + { + Glib::Mutex::Lock lm (source_lock); + result = sources.insert (entry); + } - entry.first = source->id(); - entry.second = afs; - - { - Glib::Mutex::Lock lm (audio_source_lock); - result = audio_sources.insert (entry); - } + if (result.second) { + source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); + set_dirty(); + } - if (result.second) { - 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 Session::remove_source (boost::weak_ptr src) { - AudioSourceList::iterator i; + SourceMap::iterator i; boost::shared_ptr source = src.lock(); if (!source) { return; - } + } - { - Glib::Mutex::Lock lm (audio_source_lock); - - if ((i = audio_sources.find (source->id())) != audio_sources.end()) { - audio_sources.erase (i); - } + { + Glib::Mutex::Lock lm (source_lock); + + 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); } } @@ -2826,16 +2875,14 @@ Session::remove_source (boost::weak_ptr src) boost::shared_ptr Session::source_by_id (const PBD::ID& id) { - Glib::Mutex::Lock lm (audio_source_lock); - AudioSourceList::iterator i; + Glib::Mutex::Lock lm (source_lock); + SourceMap::iterator i; boost::shared_ptr source; - if ((i = audio_sources.find (id)) != audio_sources.end()) { + if ((i = sources.find (id)) != sources.end()) { source = i->second; } - /* XXX search MIDI or other searches here */ - return source; } @@ -2843,34 +2890,254 @@ Session::source_by_id (const PBD::ID& id) boost::shared_ptr Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) { - Glib::Mutex::Lock lm (audio_source_lock); + Glib::Mutex::Lock lm (source_lock); - for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ++i) { + for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { cerr << "comparing " << path << " with " << i->second->name() << endl; boost::shared_ptr afs = boost::dynamic_pointer_cast(i->second); - if (afs && afs->path() == path && chn == afs->channel()) { - return afs; - } - + if (afs && afs->path() == path && chn == afs->channel()) { + return afs; + } + + } + return boost::shared_ptr(); +} + +Glib::ustring +Session::peak_path (Glib::ustring base) const +{ + sys::path peakfile_path(_session_dir->peak_path()); + peakfile_path /= basename_nosuffix (base) + peakfile_suffix; + return peakfile_path.to_string(); +} + +string +Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive) +{ + string look_for; + string old_basename = PBD::basename_nosuffix (oldname); + string new_legalized = legalize_for_path (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; + string prefix; + string::size_type slash; + string::size_type dash; + + if ((slash = path.find_last_of ('/')) == string::npos) { + return ""; + } + + dir = path.substr (0, slash+1); + + /* '-' is not a legal character for the NAME part of the path */ + + if ((dash = path.find_last_of ('-')) == string::npos) { + return ""; + } + + prefix = path.substr (slash+1, dash-(slash+1)); + + path = dir; + path += prefix; + 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; + string::size_type dash; + string::size_type postfix; + + /* find last slash */ + + if ((slash = path.find_last_of ('/')) == string::npos) { + return ""; + } + + dir = path.substr (0, slash+1); + + /* '-' is not a legal character for the NAME part of the path */ + + if ((dash = path.find_last_of ('-')) == string::npos) { + return ""; + } + + 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 '.' + + postfix = suffix.find_last_of ("%"); + if (postfix == string::npos) { + postfix = suffix.find_last_of ('.'); + } + + if (postfix != string::npos) { + suffix = suffix.substr (postfix); + } else { + error << "Logic error in Session::change_audio_path_by_name(), please report to the developers" << endl; + return ""; + } + + const uint32_t limit = 10000; + char buf[PATH_MAX+1]; + + for (uint32_t cnt = 1; cnt <= limit; ++cnt) { + + snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str()); + + if (access (buf, F_OK) != 0) { + path = buf; + break; + } + path = ""; + } + + if (path == "") { + error << "FATAL ERROR! Could not find a " << endl; + } + + } + + return path; +} + +string +Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive) +{ + string spath; + uint32_t cnt; + char buf[PATH_MAX+1]; + const uint32_t limit = 10000; + string legalized; + + buf[0] = '\0'; + legalized = legalize_for_path (name); + + /* find a "version" of the file name that doesn't exist in + any of the possible directories. + */ + + for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { + + vector::iterator i; + uint32_t existing = 0; + + for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { + + SessionDirectory sdir((*i).path); + + spath = sdir.sound_path().to_string(); + + if (destructive) { + if (nchan < 2) { + snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); + } else if (nchan == 2) { + if (chan == 0) { + snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav", spath.c_str(), cnt, legalized.c_str()); + } else { + snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", spath.c_str(), cnt, legalized.c_str()); + } + } else if (nchan < 26) { + snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav", spath.c_str(), cnt, legalized.c_str(), 'a' + chan); + } else { + snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); + } + + } else { + + spath += '/'; + spath += legalized; + + if (nchan < 2) { + snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + } else if (nchan == 2) { + if (chan == 0) { + snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt); + } else { + snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt); + } + } else if (nchan < 26) { + snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan); + } else { + snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + } + } + + if (sys::exists(buf)) { + existing++; + } + + } + + if (existing == 0) { + break; + } + + if (cnt > limit) { + error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg; + destroy (); + throw failed_constructor(); + } + } + + /* we now have a unique name for the file, but figure out where to + actually put it. + */ + + string foo = buf; + + 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 { + spath += foo.substr (pos + 1); } - return boost::shared_ptr(); + + return spath; } -string -Session::peak_path_from_audio_path (string audio_path) const +boost::shared_ptr +Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) { - string res; - - res = peak_dir (); - res += PBD::basename_nosuffix (audio_path); - res += ".peak"; - - return res; + 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())); } +// FIXME: _terrible_ code duplication string -Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive) +Session::change_midi_path_by_name (string path, string oldname, string newname, bool destructive) { string look_for; string old_basename = PBD::basename_nosuffix (oldname); @@ -2879,14 +3146,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; @@ -2912,17 +3179,17 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, path += prefix; path += '-'; path += new_legalized; - path += ".wav"; /* XXX gag me with a spoon */ - + 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; @@ -2944,7 +3211,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 '.' @@ -2956,7 +3223,7 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, if (postfix != string::npos) { suffix = suffix.substr (postfix); } else { - error << "Logic error in Session::change_audio_path_by_name(), please report to the developers" << endl; + error << "Logic error in Session::change_midi_path_by_name(), please report to the developers" << endl; return ""; } @@ -2984,7 +3251,7 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, } string -Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive) +Session::midi_path_from_name (string name) { string spath; uint32_t cnt; @@ -2999,56 +3266,26 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool any of the possible directories. */ - for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { + for (cnt = 1; cnt <= limit; ++cnt) { vector::iterator i; uint32_t existing = 0; for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - spath = (*i).path; - - spath += sound_dir (false); + SessionDirectory sdir((*i).path); - if (destructive) { - if (nchan < 2) { - snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); - } else if (nchan == 2) { - if (chan == 0) { - snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav", spath.c_str(), cnt, legalized.c_str()); - } else { - snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", spath.c_str(), cnt, legalized.c_str()); - } - } else if (nchan < 26) { - snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav", spath.c_str(), cnt, legalized.c_str(), 'a' + chan); - } else { - snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); - } + sys::path p = sdir.midi_path(); - } else { + p /= legalized; - spath += '/'; - spath += legalized; + spath = p.to_string(); - if (nchan < 2) { - snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); - } else if (nchan == 2) { - if (chan == 0) { - snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt); - } else { - snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt); - } - } else if (nchan < 26) { - snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan); - } else { - snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); - } - } + 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) { @@ -3057,7 +3294,6 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool if (cnt > limit) { error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg; - destroy (); throw failed_constructor(); } } @@ -3068,11 +3304,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.midi_path().to_string(); spath += '/'; string::size_type pos = foo.find_last_of ('/'); - + if (pos == string::npos) { spath += foo; } else { @@ -3082,13 +3320,15 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool return spath; } -boost::shared_ptr -Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) +boost::shared_ptr +Session::create_midi_source_for_session (MidiDiskstream& ds) { - string spath = audio_path_from_name (ds.name(), ds.n_channels(), chan, destructive); - return boost::dynamic_pointer_cast (SourceFactory::createWritable (*this, spath, destructive, frame_rate())); + string mpath = midi_path_from_name (ds.name()); + + return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, mpath, false, frame_rate())); } + /* Playlist management */ boost::shared_ptr @@ -3110,13 +3350,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); @@ -3125,6 +3381,10 @@ Session::add_playlist (boost::shared_ptr playlist) } } + if (unused) { + playlist->release(); + } + set_dirty(); PlaylistAdded (playlist); /* EMIT SIGNAL */ @@ -3133,7 +3393,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); @@ -3160,22 +3420,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); } @@ -3196,7 +3456,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) return; } - { + { Glib::Mutex::Lock lm (playlist_lock); PlaylistList::iterator i; @@ -3210,7 +3470,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) if (i != unused_playlists.end()) { unused_playlists.erase (i); } - + } set_dirty(); @@ -3218,7 +3478,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; @@ -3272,46 +3532,37 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: void Session::remove_empty_sounds () { - PathScanner scanner; + vector audio_filenames; - vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); - - Glib::Mutex::Lock lm (audio_source_lock); - - regex_t compiled_tape_track_pattern; - int err; + get_files_in_directory (_session_dir->sound_path(), audio_filenames); - if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) { + Glib::Mutex::Lock lm (source_lock); - 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; - } + TapeFileMatcher tape_file_matcher; - for (vector::iterator i = possible_audiofiles->begin(); i != possible_audiofiles->end(); ++i) { - - /* never remove files that appear to be a tape track */ + remove_if (audio_filenames.begin(), audio_filenames.end(), + sigc::mem_fun (tape_file_matcher, &TapeFileMatcher::matches)); - if (regexec (&compiled_tape_track_pattern, (*i)->c_str(), 0, 0, 0) == 0) { - delete *i; - continue; - } - - if (AudioFileSource::is_empty (*this, *(*i))) { + for (vector::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) { - unlink ((*i)->c_str()); - - string peak_path = peak_path_from_audio_path (**i); - unlink (peak_path.c_str()); - } + sys::path audio_file_path (_session_dir->sound_path()); - delete* i; - } + audio_file_path /= *i; - delete possible_audiofiles; + if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) { + + 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 @@ -3329,30 +3580,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 { @@ -3387,10 +3638,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) { @@ -3414,78 +3665,64 @@ void Session::record_enable_change_all (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - AudioTrack* at; + Track* at; - if ((at = dynamic_cast((*i).get())) != 0) { + if ((at = dynamic_cast((*i).get())) != 0) { 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*/ } @@ -3506,7 +3743,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")) @@ -3519,100 +3760,95 @@ 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 (); } +/** Ensures that all buffers (scratch, send, silent, etc) are allocated for + * the given count with the current block size. + */ void -Session::ensure_passthru_buffers (uint32_t howmany) +Session::ensure_buffers (ChanCount howmany) { - while (howmany > _passthru_buffers.size()) { - Sample *p; -#ifdef NO_POSIX_MEMALIGN - p = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)); -#endif - _passthru_buffers.push_back (p); + if (current_block_size == 0) + return; // too early? (is this ok?) - *p = 0; - -#ifdef NO_POSIX_MEMALIGN - p = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4); -#endif - memset (p, 0, sizeof (Sample) * current_block_size); - _silent_buffers.push_back (p); + // We need at least 2 MIDI scratch buffers to mix/merge + if (howmany.n_midi() < 2) + howmany.set_midi(2); - *p = 0; - -#ifdef NO_POSIX_MEMALIGN - p = (Sample *) malloc(current_block_size * sizeof(Sample)); -#else - 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); - - } - allocate_pan_automation_buffers (current_block_size, howmany, false); + // 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); + _mix_buffers->ensure_buffers(howmany, current_block_size); + _silent_buffers->ensure_buffers(howmany, current_block_size); + + allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false); } uint32_t @@ -3625,10 +3861,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); @@ -3645,10 +3881,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); @@ -3696,7 +3932,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); } @@ -3715,7 +3951,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); @@ -3747,13 +3983,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; } @@ -3781,7 +4017,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]; } @@ -3796,9 +4032,9 @@ Session::freeze (InterThreadInfo& itt) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - AudioTrack *at; + Track *at; - if ((at = dynamic_cast((*i).get())) != 0) { + if ((at = dynamic_cast((*i).get())) != 0) { /* XXX this is wrong because itt.progress will keep returning to zero at the start of every track. */ @@ -3809,29 +4045,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; - uint32_t nchans; + ChanCount nchans(track.audio_diskstream()->n_channels()); nframes_t position; nframes_t this_chunk; nframes_t to_do; - vector buffers; + 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; } @@ -3842,28 +4086,25 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le goto out; } - nchans = track.audio_diskstream()->n_channels(); - - dir = discover_best_sound_dir (); - - for (uint32_t chan_n=0; chan_n < nchans; ++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 (*this, buf, false, frame_rate())); + 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; @@ -3873,60 +4114,54 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le } /* XXX need to flush all redirects */ - + position = start; to_do = len; /* create a set of reasonably-sized buffers */ + buffers.ensure_buffers(nchans, chunk_size); + buffers.set_count(nchans); - for (vector::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) { - Sample* b; -#ifdef NO_POSIX_MEMALIGN - b = (Sample *) malloc(chunk_size * sizeof(Sample)); -#else - posix_memalign((void **)&b,4096,chunk_size * sizeof(Sample)); -#endif - buffers.push_back (b); + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + if (afs) + afs->prepare_for_peakfile_writes (); } - 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, start, this_chunk)) { goto out; } uint32_t n = 0; - for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) { - if (afs->write (buffers[n], this_chunk) != 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) { + + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); if (afs) { @@ -3934,61 +4169,78 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le 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) { - for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { + 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 src = srcs.begin(); src != srcs.end(); ++src) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - for (vector::iterator i = buffers.begin(); i != buffers.end(); ++i) { - free(*i); + if (afs) + afs->done_with_peakfile_writes (); + } } g_atomic_int_set (&processing_prohibited, 0); - itt.done = true; - - return ret; + return result; } -vector& -Session::get_silent_buffers (uint32_t howmany) +BufferSet& +Session::get_silent_buffers (ChanCount count) { - for (uint32_t i = 0; i < howmany; ++i) { - memset (_silent_buffers[i], 0, sizeof (Sample) * current_block_size); + assert(_silent_buffers->available() >= 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) { + _silent_buffers->get(*t, i).clear(); + } } - return _silent_buffers; + + return *_silent_buffers; +} + +BufferSet& +Session::get_scratch_buffers (ChanCount count) +{ + assert(_scratch_buffers->available() >= count); + _scratch_buffers->set_count(count); + return *_scratch_buffers; +} + +BufferSet& +Session::get_mix_buffers (ChanCount count) +{ + assert(_mix_buffers->available() >= count); + _mix_buffers->set_count(count); + return *_mix_buffers; } -uint32_t +uint32_t Session::ntracks () const { uint32_t n = 0; shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { - if (dynamic_cast ((*i).get())) { + if (dynamic_cast ((*i).get())) { ++n; } } @@ -3996,14 +4248,14 @@ Session::ntracks () const return n; } -uint32_t +uint32_t Session::nbusses () const { uint32_t n = 0; shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { - if (dynamic_cast ((*i).get()) == 0) { + if (dynamic_cast ((*i).get()) == 0) { ++n; } } @@ -4023,3 +4275,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); + } +} +