#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/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)
{
_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();
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;
_writable = exists_and_writable (sys::path (_path));
- if (!mix_template.empty()) {
- std::string in_path = mix_template;
+ 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;
}
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 (r.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 (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);
- }
-
} else {
/* prohibit auto-connect to master, because there isn't one */
bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster);
Config->set_output_auto_connect (bus_profile->output_ac);
}
+ if (Config->get_use_monitor_bus() && bus_profile) {
+ add_monitor_section ();
+ }
+
save_state ("");
return 0;
/* 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);
}
}
+ 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;
}
#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;
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;
}
track->set_diskstream (*i);
#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;
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;
}
}
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;
-}
-
-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;
+ /* copy plugin state directory */
- if (sys::exists (new_path)) {
- warning << string_compose(_("Template \"%1\" already exists - template not renamed"),
- new_path.to_string()) << endmsg;
- 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);
- try {
- sys::rename (old_path, new_path);
- return 0;
- } catch (...) {
- return -1;
- }
-}
-
-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
_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 ();
}
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);
}
}
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;
}
}
//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());
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
+}