the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
- This program is distributed in the hope that it will be useful,
+ This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
#include <fstream>
#include <string>
#include <cerrno>
-
-
#include <cstdio> /* snprintf(3) ... grrr */
#include <cmath>
#include <unistd.h>
#include <sys/mount.h>
#endif
+#include <glib.h>
+
#include <glibmm.h>
#include <glibmm/thread.h>
+#include <boost/algorithm/string.hpp>
+
#include "midi++/mmc.h"
#include "midi++/port.h"
#include "midi++/manager.h"
#include "ardour/amp.h"
#include "ardour/audio_diskstream.h"
+#include "ardour/audio_playlist_source.h"
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
#include "ardour/io_processor.h"
#include "ardour/location.h"
#include "ardour/midi_diskstream.h"
+#include "ardour/midi_model.h"
#include "ardour/midi_patch_manager.h"
#include "ardour/midi_playlist.h"
#include "ardour/midi_region.h"
#include "ardour/processor.h"
#include "ardour/port.h"
#include "ardour/proxy_controllable.h"
+#include "ardour/recent_sessions.h"
#include "ardour/region_factory.h"
#include "ardour/route_group.h"
#include "ardour/send.h"
#include "ardour/sndfile_helpers.h"
#include "ardour/sndfilesource.h"
#include "ardour/source_factory.h"
+#include "ardour/speakers.h"
#include "ardour/template_utils.h"
#include "ardour/tempo.h"
#include "ardour/ticker.h"
using namespace ARDOUR;
using namespace PBD;
+/** @param snapshot_name Snapshot name, without the .ardour prefix */
void
Session::first_stage_init (string fullpath, string snapshot_name)
{
_path += G_DIR_SEPARATOR;
}
- if (Glib::file_test (_path, Glib::FILE_TEST_EXISTS) && ::access (_path.c_str(), W_OK)) {
- _writable = false;
- } else {
- _writable = true;
- }
-
/* these two are just provisional settings. set_state()
will likely override them.
*/
_step_editors = 0;
no_questions_about_missing_files = false;
_speakers.reset (new Speakers);
+ _clicks_cleared = 0;
+ ignore_route_processor_changes = false;
AudioDiskstream::allocate_working_buffers();
/* initial program change will be delivered later; see ::config_changed() */
- BootMessage (_("Reset Control Protocols"));
-
- ControlProtocolManager::instance().set_session (this);
-
_state_of_the_state = Clean;
Port::set_connecting_blocked (false);
return 0;
}
-/** Caller must not hold process lock */
+/** @param session_template directory containing session template, or empty.
+ * Caller must not hold process lock.
+ */
int
-Session::create (const string& mix_template, BusProfile* bus_profile)
+Session::create (const string& session_template, BusProfile* bus_profile)
{
if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
return -1;
}
- if (!mix_template.empty()) {
- std::string in_path = mix_template;
+ _writable = exists_and_writable (sys::path (_path));
+
+ if (!session_template.empty()) {
+ std::string in_path = session_template_dir_to_file (session_template);
ifstream in(in_path.c_str());
if (out) {
out << in.rdbuf();
_is_new = false;
+
+ /* Copy plugin state files from template to new session */
+ sys::path template_plugins = session_template;
+ template_plugins /= X_("plugins");
+ sys::copy_files (template_plugins, plugins_dir ());
+
return 0;
} else {
- error << string_compose (_("Could not open %1 for writing mix template"), out_path)
+ error << string_compose (_("Could not open %1 for writing session template"), out_path)
<< endmsg;
return -1;
}
} else {
- error << string_compose (_("Could not open mix template %1 for reading"), in_path)
+ error << string_compose (_("Could not open session template %1 for reading"), in_path)
<< endmsg;
return -1;
}
/* set initial start + end point */
_state_of_the_state = Clean;
-
+
/* set up Master Out and Control Out if necessary */
if (bus_profile) {
RouteList rl;
- int control_id = 1;
ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
if (bus_profile->master_out_channels) {
return -1;
}
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_shared_ptr_mark_interesting (rt.get(), "Route");
+ // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
#endif
{
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
r->input()->ensure_io (count, false, this);
r->output()->ensure_io (count, false, this);
}
- r->set_remote_control_id (control_id++);
rl.push_back (r);
- if (Config->get_use_monitor_bus()) {
- boost::shared_ptr<Route> r (new Route (*this, _("monitor"), Route::MonitorOut, DataType::AUDIO));
- if (r->init ()) {
- return -1;
- }
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_shared_ptr_mark_interesting (rt, "Route");
-#endif
- {
- Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- r->input()->ensure_io (count, false, this);
- r->output()->ensure_io (count, false, this);
- }
- r->set_remote_control_id (control_id);
-
- rl.push_back (r);
- }
-
} else {
/* prohibit auto-connect to master, because there isn't one */
bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster);
}
if (!rl.empty()) {
- add_routes (rl, false);
+ add_routes (rl, false, false);
}
/* this allows the user to override settings with an environment variable.
bus_profile->input_ac = AutoConnectOption (0);
bus_profile->output_ac = AutoConnectOption (0);
}
-
+
Config->set_input_auto_connect (bus_profile->input_ac);
Config->set_output_auto_connect (bus_profile->output_ac);
}
+ if (Config->get_use_monitor_bus() && bus_profile) {
+ add_monitor_section ();
+ }
+
save_state ("");
return 0;
}
/** Rename a state file.
- * @param snapshot_name Snapshot name.
+ * @param old_name Old snapshot name.
+ * @param new_name New snapshot name.
*/
void
Session::rename_state (string old_name, string new_name)
}
/** Remove a state file.
- * @param snapshot_name Snapshot name.
+ * @param snapshot_name Snapshot name.
*/
void
Session::remove_state (string snapshot_name)
if (event->type == JackSessionSaveTemplate)
{
if (save_template( timebuf )) {
- event->flags = JackSessionSaveError;
+ event->flags = JackSessionSaveError;
} else {
string cmd ("ardour3 -P -U ");
cmd += event->client_uuid;
else
{
if (save_state (timebuf)) {
- event->flags = JackSessionSaveError;
+ event->flags = JackSessionSaveError;
} else {
sys::path xml_path (_session_dir->root_path());
xml_path /= legalize_for_path (timebuf) + statefile_suffix;
}
#endif
+/** @param snapshot_name Name to save under, without .ardour / .pending prefix */
int
Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
{
/* tell sources we're saving first, in case they write out to a new file
* which should be saved with the state rather than the old one */
for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
- i->second->session_saved();
+ try {
+ i->second->session_saved();
+ } catch (Evoral::SMF::FileError& e) {
+ error << string_compose ("Could not write to MIDI file %1; MIDI data not saved.", e.file_name ()) << endmsg;
+ }
}
tree.set_root (&get_state());
} else {
- if (rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
+ if (::rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
error << string_compose (_("could not rename temporary session file %1 to %2"),
tmp_path.to_string(), xml_path.to_string()) << endmsg;
sys::remove (tmp_path);
set_dirty();
- /* writable() really reflects the whole folder, but if for any
- reason the session state file can't be written to, still
- make us unwritable.
- */
-
- if (::access (xmlpath.to_string().c_str(), W_OK) != 0) {
- _writable = false;
- }
+ _writable = exists_and_writable (xmlpath);
if (!state_tree->read (xmlpath.to_string())) {
error << string_compose(_("Could not understand ardour file %1"), xmlpath.to_string()) << endmsg;
sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ);
Stateful::loading_state_version = (major * 1000) + minor;
}
-
+
if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) {
sys::path backup_path(_session_dir->root_path());
for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
- /* Don't save information about non-destructive file sources that are empty
- and unused by any regions.
+ /* Don't save information about non-file Sources, or
+ * about non-destructive file sources that are empty
+ * and unused by any regions.
*/
boost::shared_ptr<FileSource> fs;
+
if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) != 0) {
+
if (!fs->destructive()) {
if (fs->empty() && !fs->used()) {
continue;
}
}
- }
- child->add_child_nocopy (siter->second->get_state());
+ child->add_child_nocopy (siter->second->get_state());
+ }
}
}
child->add_child_nocopy (r->state ());
}
}
+
+ RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
+
+ if (!cassocs.empty()) {
+ XMLNode* ca = node->add_child (X_("CompoundAssociations"));
+
+ for (RegionFactory::CompoundAssociations::iterator i = cassocs.begin(); i != cassocs.end(); ++i) {
+ char buf[64];
+ XMLNode* can = new XMLNode (X_("CompoundAssociation"));
+ i->first->id().print (buf, sizeof (buf));
+ can->add_property (X_("copy"), buf);
+ i->second->id().print (buf, sizeof (buf));
+ can->add_property (X_("original"), buf);
+ ca->add_child_nocopy (*can);
+ }
+ }
}
if (full_state) {
IO::disable_connecting ();
- if ((child = find_named_node (node, "Extra")) != 0) {
- _extra_xml = new XMLNode (*child);
- }
+ Stateful::save_extra_xml (node);
if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
load_options (*child);
}
}
+ if ((child = find_named_node (node, X_("Speakers"))) != 0) {
+ _speakers->set_state (*child, version);
+ }
+
+ if ((child = find_named_node (node, "Sources")) == 0) {
+ error << _("Session: XML state has no sources section") << endmsg;
+ goto out;
+ } else if (load_sources (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "TempoMap")) == 0) {
+ error << _("Session: XML state has no Tempo Map section") << endmsg;
+ goto out;
+ } else if (_tempo_map->set_state (*child, version)) {
+ goto out;
+ }
+
if ((child = find_named_node (node, "Locations")) == 0) {
error << _("Session: XML state has no locations section") << endmsg;
goto out;
goto out;
}
- if ((child = find_named_node (node, X_("Speakers"))) != 0) {
- _speakers->set_state (*child, version);
- }
-
Location* location;
if ((location = _locations->auto_loop_location()) != 0) {
AudioFileSource::set_header_position_offset (_session_range_location->start());
}
- if ((child = find_named_node (node, "Sources")) == 0) {
- error << _("Session: XML state has no sources section") << endmsg;
- goto out;
- } else if (load_sources (*child)) {
- goto out;
- }
-
- if ((child = find_named_node (node, "TempoMap")) == 0) {
- error << _("Session: XML state has no Tempo Map section") << endmsg;
- goto out;
- } else if (_tempo_map->set_state (*child, version)) {
- goto out;
- }
-
if ((child = find_named_node (node, "Regions")) == 0) {
error << _("Session: XML state has no Regions section") << endmsg;
goto out;
} else if (playlists->load_unused (*this, *child)) {
goto out;
}
-
+
+ if ((child = find_named_node (node, "CompoundAssociations")) != 0) {
+ if (load_compounds (*child)) {
+ goto out;
+ }
+ }
+
if ((child = find_named_node (node, "NamedSelections")) != 0) {
if (load_named_selections (*child)) {
goto out;
_bundle_xml_node = new XMLNode (*child);
}
}
-
+
if (version < 3000) {
if ((child = find_named_node (node, X_("DiskStreams"))) == 0) {
error << _("Session: XML state has no diskstreams section") << endmsg;
_diskstreams_2X.clear ();
if (version >= 3000) {
-
+
if ((child = find_named_node (node, "RouteGroups")) == 0) {
error << _("Session: XML state has no route groups section") << endmsg;
goto out;
} else if (load_route_groups (*child, version)) {
goto out;
}
-
+
} else if (version < 3000) {
-
+
if ((child = find_named_node (node, "EditGroups")) == 0) {
error << _("Session: XML state has no edit groups section") << endmsg;
goto out;
} else {
route = XMLRouteFactory (**niter, version);
}
-
+
if (route == 0) {
error << _("Session: cannot create Route from XML description.") << endmsg;
return -1;
new_routes.push_back (route);
}
- add_routes (new_routes, false);
+ add_routes (new_routes, false, false);
return 0;
}
if (ds_child) {
boost::shared_ptr<Track> track;
-
+
if (type == DataType::AUDIO) {
track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
} else {
track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
}
-
+
if (track->init()) {
return ret;
}
-
+
if (track->set_state (node, version)) {
return ret;
}
-
+
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+ // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
#endif
ret = track;
-
+
} else {
boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
if (r->init () == 0 && r->set_state (node, version) == 0) {
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
+ // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
#endif
ret = r;
}
} else {
track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
}
-
+
if (track->init()) {
return ret;
}
-
+
if (track->set_state (node, version)) {
return ret;
}
track->set_diskstream (*i);
-
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+ // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
#endif
ret = track;
-
+
} else {
boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
if (r->init () == 0 && r->set_state (node, version) == 0) {
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- boost_debug_shared_ptr_mark_interesting (rt, "Route");
+ // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
#endif
ret = r;
}
return 0;
}
+int
+Session::load_compounds (const XMLNode& node)
+{
+ XMLNodeList calist = node.children();
+ XMLNodeConstIterator caiter;
+ XMLProperty *caprop;
+
+ for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
+ XMLNode* ca = *caiter;
+ ID orig_id;
+ ID copy_id;
+
+ if ((caprop = ca->property (X_("original"))) == 0) {
+ continue;
+ }
+ orig_id = caprop->value();
+
+ if ((caprop = ca->property (X_("copy"))) == 0) {
+ continue;
+ }
+ copy_id = caprop->value();
+
+ boost::shared_ptr<Region> orig = RegionFactory::region_by_id (orig_id);
+ boost::shared_ptr<Region> copy = RegionFactory::region_by_id (copy_id);
+
+ if (!orig || !copy) {
+ warning << string_compose (_("Regions in compound description not found (ID's %1 and %2): ignored"),
+ orig_id, copy_id)
+ << endmsg;
+ continue;
+ }
+
+ RegionFactory::add_compound_association (orig, copy);
+ }
+
+ return 0;
+}
+
+void
+Session::load_nested_sources (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "Source") {
+
+ /* it may already exist, so don't recreate it unnecessarily
+ */
+
+ XMLProperty* prop = (*niter)->property (X_("id"));
+ if (!prop) {
+ error << _("Nested source has no ID info in session state file! (ignored)") << endmsg;
+ continue;
+ }
+
+ ID source_id (prop->value());
+
+ if (!source_by_id (source_id)) {
+
+ try {
+ SourceFactory::create (*this, **niter, true);
+ }
+ catch (failed_constructor& err) {
+ error << string_compose (_("Cannot reconstruct nested source for region %1"), name()) << endmsg;
+ }
+ }
+ }
+ }
+}
+
boost::shared_ptr<Region>
Session::XMLRegionFactory (const XMLNode& node, bool full)
{
try {
+ const XMLNodeList& nlist = node.children();
+
+ for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ XMLNode *child = (*niter);
+ if (child->name() == "NestedSource") {
+ load_nested_sources (*child);
+ }
+ }
+
if (!type || type->value() == "audio") {
return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
} else if (type->value() == "midi") {
case 0:
/* user added a new search location, so try again */
goto retry;
-
-
+
+
case 1:
/* user asked to quit the entire session load
*/
}
catch(sys::filesystem_error& ex)
{
- error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"),
+ error << string_compose(_("Could not create templates directory \"%1\" (%2)"),
user_template_dir.to_string(), ex.what()) << endmsg;
return -1;
}
tree.set_root (&get_template());
- sys::path template_file_path(user_template_dir);
- template_file_path /= template_name + template_suffix;
-
- if (sys::exists (template_file_path))
+ sys::path template_dir_path(user_template_dir);
+
+ /* directory to put the template in */
+ template_dir_path /= template_name;
+ if (sys::exists (template_dir_path))
{
warning << string_compose(_("Template \"%1\" already exists - new version not created"),
- template_file_path.to_string()) << endmsg;
+ template_dir_path.to_string()) << endmsg;
return -1;
}
+
+ sys::create_directories (template_dir_path);
+
+ /* file to write */
+ sys::path template_file_path = template_dir_path;
+ template_file_path /= template_name + template_suffix;
if (!tree.write (template_file_path.to_string())) {
error << _("template not saved") << endmsg;
return -1;
}
- return 0;
-}
+ /* copy plugin state directory */
-int
-Session::rename_template (string old_name, string new_name)
-{
- sys::path old_path (user_template_directory());
- old_path /= old_name + template_suffix;
-
- sys::path new_path(user_template_directory());
- new_path /= new_name + template_suffix;
-
- if (sys::exists (new_path)) {
- warning << string_compose(_("Template \"%1\" already exists - template not renamed"),
- new_path.to_string()) << endmsg;
- return -1;
- }
-
- try {
- sys::rename (old_path, new_path);
- return 0;
- } catch (...) {
- return -1;
- }
-}
+ sys::path template_plugin_state_path = template_dir_path;
+ template_plugin_state_path /= X_("plugins");
+ sys::create_directories (template_plugin_state_path);
+ sys::copy_files (plugins_dir(), template_plugin_state_path);
-int
-Session::delete_template (string name)
-{
- sys::path path = user_template_directory();
- path /= name + template_suffix;
-
- try {
- sys::remove (path);
- return 0;
- } catch (...) {
- return -1;
- }
+ return 0;
}
void
set_dirty ();
if (version >= 3000) {
-
+
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() == "RouteGroup") {
RouteGroup* rg = new RouteGroup (*this, "");
_route_groups.push_back (g);
route_group_added (g); /* EMIT SIGNAL */
- g->MembershipChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
- g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
-
+ g->RouteAdded.connect_same_thread (*this, boost::bind (&Session::route_added_to_route_group, this, _1, _2));
+ g->RouteRemoved.connect_same_thread (*this, boost::bind (&Session::route_removed_from_route_group, this, _1, _2));
+ g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_property_changed, this, g));
+
set_dirty ();
}
route_group_removed (); /* EMIT SIGNAL */
}
+}
+
+/** Set a new order for our route groups, without adding or removing any.
+ * @param groups Route group list in the new order.
+ */
+void
+Session::reorder_route_groups (list<RouteGroup*> groups)
+{
+ _route_groups = groups;
+ route_groups_reordered (); /* EMIT SIGNAL */
+ set_dirty ();
}
+
RouteGroup *
Session::route_group_by_name (string name)
{
to hold all the commands that are committed. This keeps the order of
commands correct in the history.
*/
-
+
if (_current_trans == 0) {
/* start a new transaction */
assert (_current_trans_quarks.empty ());
_current_trans = new UndoTransaction();
_current_trans->set_name (g_quark_to_string (q));
}
-
+
_current_trans_quarks.push_front (q);
}
{
assert (_current_trans);
assert (!_current_trans_quarks.empty ());
-
+
struct timeval now;
if (cmd) {
static bool
accept_all_audio_files (const string& path, void */*arg*/)
-{
+{
if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
return false;
}
for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
- boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>( i->second);
-
- if (!audio_region) {
- continue;
- }
-
- uint32_t used = playlists->region_use_count (audio_region);
+ uint32_t used = playlists->region_use_count (i->second);
- if (used == 0 && !audio_region->automatic()) {
- RegionFactory::map_remove(i->second);
+ if (used == 0 && !i->second->automatic ()) {
+ RegionFactory::map_remove (i->second);
}
}
_state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
/* consider deleting all unused playlists */
-
+
if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
ret = 0;
goto out;
if (playlists->source_use_count (fs) != 0) {
all_sources.insert (fs->path());
} else {
-
+
/* we might not remove this source from disk, because it may be used
by other snapshots, but its not being used in this version
so lets get rid of it now, along with any representative regions
in the region list.
*/
-
+
RegionFactory::remove_regions_using_source (i->second);
sources.erase (i);
}
if (candidates) {
for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
-
+
used = false;
spath = **x;
-
+
for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-
+
if (realpath(spath.c_str(), tmppath1) == 0) {
error << string_compose (_("Cannot expand path %1 (%2)"),
spath, strerror (errno)) << endmsg;
continue;
}
-
+
if (realpath((*i).c_str(), tmppath2) == 0) {
error << string_compose (_("Cannot expand path %1 (%2)"),
(*i), strerror (errno)) << endmsg;
break;
}
}
-
+
if (!used) {
unused.push_back (spath);
}
}
newpath = Glib::build_filename (newpath, Glib::path_get_basename ((*x)));
-
+
if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
/* the new path already exists, try versioning */
*/
string base = basename_nosuffix (*x);
- base += "%A"; /* this is what we add for the channel suffix of all native files,
+ base += "%A"; /* this is what we add for the channel suffix of all native files,
or for the first channel of embedded files. it will miss
some peakfiles for other channels
*/
string peakpath = peak_path (base);
-
+
if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
if (::unlink (peakpath.c_str()) != 0) {
error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
peakpath, _path, strerror (errno))
<< endmsg;
/* try to back out */
- rename (newpath.c_str(), _path.c_str());
+ ::rename (newpath.c_str(), _path.c_str());
goto out;
}
}
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
- dead_dir = (*i).path;
- dead_dir += dead_dir_name;
+ dead_dir = Glib::build_filename ((*i).path, dead_dir_name);
clear_directory (dead_dir, &rep.space, &rep.paths);
}
r = route_by_remote_id (desc.rid());
break;
}
-
+
if (!r) {
return c;
}
case ControllableDescriptor::Recenable:
{
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
-
+
if (t) {
c = t->rec_enable_control ();
}
uint32_t parameter_index = desc.target (1);
/* revert to zero based counting */
-
+
if (plugin > 0) {
--plugin;
}
-
+
if (parameter_index > 0) {
--parameter_index;
}
boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
-
+
if (p) {
c = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
p->control(Evoral::Parameter(PluginAutomation, 0, parameter_index)));
break;
}
- case ControllableDescriptor::SendGain:
+ case ControllableDescriptor::SendGain:
{
uint32_t send = desc.target (0);
/* revert to zero-based counting */
-
+
if (send > 0) {
--send;
}
-
+
boost::shared_ptr<Processor> p = r->nth_send (send);
-
+
if (p) {
boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(p);
boost::shared_ptr<Amp> a = s->amp();
} else {
error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
}
-
+
} else if (n->name() == "PatchChangeDiffCommand") {
PBD::ID id (n->property("midi-source")->value());
if (Config->get_monitoring_model() == HardwareMonitoring && transport_rolling()) {
/* auto-input only makes a difference if we're rolling */
-
- boost::shared_ptr<RouteList> rl = routes.reader ();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && tr->record_enabled ()) {
- tr->monitor_input (!config.get_auto_input());
- }
- }
+ set_track_monitor_input_status (!config.get_auto_input());
}
} else if (p == "punch-in") {
//poke_midi_thread ();
- } else if (p == "mmc-device-id" || p == "mmc-receive-id") {
+ } else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
- } else if (p == "mmc-send-id") {
+ } else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
solo_control_mode_changed ();
} else if (p == "timecode-offset" || p == "timecode-offset-negative") {
last_timecode_valid = false;
+ } else if (p == "playback-buffer-seconds") {
+ AudioSource::allocate_working_buffers (frame_rate());
}
set_dirty ();
Session::setup_midi_machine_control ()
{
MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
-
+
mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
it up as a Controllable. Changes to the Controllable will just map back to the RCConfiguration
parameter.
*/
-
+
return _solo_cut_control;
}
+
+int
+Session::rename (const std::string& new_name)
+{
+ string legal_name = legalize_for_path (new_name);
+ string newpath;
+ string oldstr;
+ string newstr;
+ bool first = true;
+
+ string const old_sources_root = _session_dir->sources_root().to_string ();
+
+#define RENAME ::rename
+
+ /* Rename:
+
+ * session directory
+ * interchange subdirectory
+ * session file
+ * session history
+
+ * Backup files are left unchanged and not renamed.
+ */
+
+ /* pass one: not 100% safe check that the new directory names don't
+ * already exist ...
+ */
+
+ for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+ vector<string> v;
+
+ oldstr = (*i).path;
+
+ /* this is a stupid hack because Glib::path_get_dirname() is
+ * lexical-only, and so passing it /a/b/c/ gives a different
+ * result than passing it /a/b/c ...
+ */
+
+ if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
+ oldstr = oldstr.substr (0, oldstr.length() - 1);
+ }
+
+ string base = Glib::path_get_dirname (oldstr);
+ string p = Glib::path_get_basename (oldstr);
+
+ newstr = Glib::build_filename (base, legal_name);
+
+ if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
+ return -1;
+ }
+ }
+
+ /* Session dirs */
+
+ for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+ vector<string> v;
+
+ oldstr = (*i).path;
+
+ /* this is a stupid hack because Glib::path_get_dirname() is
+ * lexical-only, and so passing it /a/b/c/ gives a different
+ * result than passing it /a/b/c ...
+ */
+
+ if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
+ oldstr = oldstr.substr (0, oldstr.length() - 1);
+ }
+
+ string base = Glib::path_get_dirname (oldstr);
+ string p = Glib::path_get_basename (oldstr);
+
+ newstr = Glib::build_filename (base, legal_name);
+
+ cerr << "Rename " << oldstr << " => " << newstr << endl;
+
+ if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+ return 1;
+ }
+
+ if (first) {
+ (*_session_dir) = newstr;
+ newpath = newstr;
+ first = 1;
+ }
+
+ /* directory below interchange */
+
+ v.push_back (newstr);
+ v.push_back (interchange_dir_name);
+ v.push_back (p);
+
+ oldstr = Glib::build_filename (v);
+
+ v.clear ();
+ v.push_back (newstr);
+ v.push_back (interchange_dir_name);
+ v.push_back (legal_name);
+
+ newstr = Glib::build_filename (v);
+
+ cerr << "Rename " << oldstr << " => " << newstr << endl;
+
+ if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+ return 1;
+ }
+ }
+
+ /* state file */
+
+ oldstr = Glib::build_filename (newpath, _current_snapshot_name) + statefile_suffix;
+ newstr= Glib::build_filename (newpath, legal_name) + statefile_suffix;
+
+ cerr << "Rename " << oldstr << " => " << newstr << endl;
+
+ if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+ return 1;
+ }
+
+ /* history file */
+
+
+ oldstr = Glib::build_filename (newpath, _current_snapshot_name) + history_suffix;
+
+ if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS)) {
+ newstr = Glib::build_filename (newpath, legal_name) + history_suffix;
+
+ cerr << "Rename " << oldstr << " => " << newstr << endl;
+
+ if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+ return 1;
+ }
+ }
+
+ /* update file source paths */
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+ boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
+ if (fs) {
+ string p = fs->path ();
+ boost::replace_all (p, old_sources_root, _session_dir->sources_root().to_string ());
+ fs->set_path (p);
+ }
+ }
+
+ /* remove old name from recent sessions */
+
+ remove_recent_sessions (_path);
+
+ _path = newpath;
+ _current_snapshot_name = new_name;
+ _name = new_name;
+
+ set_dirty ();
+
+ /* save state again to get everything just right */
+
+ save_state (_current_snapshot_name);
+
+
+ /* add to recent sessions */
+
+ store_recent_sessions (new_name, _path);
+
+ return 0;
+
+#undef RENAME
+}