#include <stdint.h>
#include <algorithm>
-#include <fstream>
#include <string>
#include <cerrno>
#include <cstdio> /* snprintf(3) ... grrr */
#include <cmath>
+
#include <unistd.h>
#include <climits>
#include <signal.h>
#include <sys/vfs.h>
#endif
-#ifdef __APPLE__
+#if defined(__APPLE__) || defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/mount.h>
#endif
#endif
#include <glib.h>
-#include <pbd/gstdio_compat.h>
+#include "pbd/gstdio_compat.h"
#include <glibmm.h>
#include <glibmm/threads.h>
#include "evoral/SMF.hpp"
-#include "pbd/boost_debug.h"
#include "pbd/basename.h"
-#include "pbd/controllable_descriptor.h"
#include "pbd/debug.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "pbd/stacktrace.h"
#include "pbd/convert.h"
#include "pbd/localtime_r.h"
+#include "pbd/unwind.h"
#include "ardour/amp.h"
#include "ardour/async_midi_port.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
#include "ardour/audioregion.h"
+#include "ardour/auditioner.h"
#include "ardour/automation_control.h"
+#include "ardour/boost_debug.h"
#include "ardour/butler.h"
+#include "ardour/controllable_descriptor.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.h"
#include "ardour/proxy_controllable.h"
#include "ardour/recent_sessions.h"
#include "ardour/region_factory.h"
+#include "ardour/revision.h"
#include "ardour/route_group.h"
#include "ardour/send.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
#include "ardour/ticker.h"
#include "ardour/user_bundle.h"
+#include "ardour/vca.h"
+#include "ardour/vca_manager.h"
#include "control_protocol/control_protocol.h"
-#include "i18n.h"
+#include "LuaBridge/LuaBridge.h"
+
+#include "pbd/i18n.h"
#include <locale.h>
using namespace std;
MIDISceneChanger* msc;
_scene_changer = msc = new MIDISceneChanger (*this);
- msc->set_input_port (scene_input_port());
- msc->set_output_port (scene_out());
+ msc->set_input_port (boost::dynamic_pointer_cast<MidiPort>(scene_input_port()));
+ msc->set_output_port (boost::dynamic_pointer_cast<MidiPort>(scene_output_port()));
boost::function<framecnt_t(void)> timer_func (boost::bind (&Session::audible_frame, this));
- boost::dynamic_pointer_cast<AsyncMIDIPort>(scene_in())->set_timer (timer_func);
+ boost::dynamic_pointer_cast<AsyncMIDIPort>(scene_input_port())->set_timer (timer_func);
setup_midi_machine_control ();
delete _tempo_map;
_tempo_map = new TempoMap (_current_frame_rate);
_tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+ _tempo_map->MetricPositionChanged.connect_same_thread (*this, boost::bind (&Session::gui_tempo_map_changed, this));
/* MidiClock requires a tempo map */
+ delete midi_clock;
midi_clock = new MidiClockTicker ();
midi_clock->set_session (this);
send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
send_immediate_mmc (MIDI::MachineControlCommand (Timecode::Time ()));
- MIDI::Name::MidiPatchManager::instance().set_session (this);
+ MIDI::Name::MidiPatchManager::instance().add_search_path (session_directory().midi_patch_path() );
ltc_tx_initialize();
/* initial program change will be delivered later; see ::config_changed() */
_state_of_the_state = Clean;
- /* set up Master Out and Control Out if necessary */
+ /* set up Master Out and Monitor Out if necessary */
if (bus_profile) {
// Waves Tracks: always create master bus for Tracks
if (ARDOUR::Profile->get_trx() || bus_profile->master_out_channels) {
- boost::shared_ptr<Route> r (new Route (*this, _("Master"), Route::MasterOut, DataType::AUDIO));
+ boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
if (r->init ()) {
return -1;
}
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
- {
+
+ BOOST_MARK_ROUTE(r);
+
+ {
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
r->input()->ensure_io (count, false, this);
r->output()->ensure_io (count, false, this);
}
if (!rl.empty()) {
- add_routes (rl, false, false, false);
+ add_routes (rl, false, false, false, PresentationInfo::max_order);
}
// Waves Tracks: Skip this. Always use autoconnection for Tracks
int
Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot, bool template_only)
{
+ DEBUG_TRACE (DEBUG::Locale, string_compose ("Session::save_state locale '%1'\n", setlocale (LC_NUMERIC, NULL)));
+
XMLTree tree;
std::string xml_path(_session_dir->root_path());
return 1;
}
+#ifndef NDEBUG
+ const int64_t save_start_time = g_get_monotonic_time();
+#endif
+
/* 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) {
SessionSaveUnderway (); /* EMIT SIGNAL */
+ bool mark_as_clean = true;
+
+ if (!snapshot_name.empty() && !switch_to_snapshot) {
+ mark_as_clean = false;
+ }
+
if (template_only) {
+ mark_as_clean = false;
tree.set_root (&get_template());
} else {
tree.set_root (&get_state());
if (snapshot_name.empty()) {
snapshot_name = _current_snapshot_name;
} else if (switch_to_snapshot) {
- _current_snapshot_name = snapshot_name;
- }
+ set_snapshot_name (snapshot_name);
+ }
+
+ assert (!snapshot_name.empty());
if (!pending) {
save_history (snapshot_name);
- bool was_dirty = dirty();
+ if (mark_as_clean) {
+ bool was_dirty = dirty();
- _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
- if (was_dirty) {
- DirtyChanged (); /* EMIT SIGNAL */
+ if (was_dirty) {
+ DirtyChanged (); /* EMIT SIGNAL */
+ }
}
StateSaved (snapshot_name); /* EMIT SIGNAL */
}
+#ifndef NDEBUG
+ const int64_t elapsed_time_us = g_get_monotonic_time() - save_start_time;
+ cerr << "saved state in " << fixed << setprecision (1) << elapsed_time_us / 1000. << " ms\n";
+#endif
return 0;
}
return -1;
}
- XMLNode& root (*state_tree->root());
+ XMLNode const & root (*state_tree->root());
if (root.name() != X_("Session")) {
error << string_compose (_("Session file %1 is not a session"), xmlpath) << endmsg;
return -1;
}
- const XMLProperty* prop;
+ XMLProperty const * prop;
if ((prop = root.property ("version")) == 0) {
/* no version implies very old version of Ardour */
}
}
+ save_snapshot_name (snapshot_name);
+
return 0;
}
int
Session::load_options (const XMLNode& node)
{
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
config.set_variables (node);
return 0;
}
XMLNode&
Session::state (bool full_state)
{
+ LocaleGuard lg;
XMLNode* node = new XMLNode("Session");
XMLNode* child;
snprintf(buf, sizeof(buf), "%d", CURRENT_SESSION_FILE_VERSION);
node->add_property("version", buf);
+ child = node->add_child ("ProgramVersion");
+ child->add_property("created-with", created_with);
+
+ std::string modified_with = string_compose ("%1 %2", PROGRAM_NAME, revision);
+ child->add_property("modified-with", modified_with);
+
/* store configuration settings */
if (full_state) {
node->add_property ("name", _name);
- snprintf (buf, sizeof (buf), "%" PRId64, _nominal_frame_rate);
+ snprintf (buf, sizeof (buf), "%" PRId64, _base_frame_rate);
node->add_property ("sample-rate", buf);
if (session_dirs.size() > 1) {
}
}
+ node->add_property ("end-is-free", _session_range_end_is_free ? X_("yes") : X_("no"));
+
/* save the ID counter */
snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter());
node->add_property ("id-counter", buf);
+ snprintf (buf, sizeof (buf), "%u", name_id_counter ());
+ node->add_property ("name-counter", buf);
+
/* save the event ID counter */
snprintf (buf, sizeof (buf), "%d", Evoral::event_id_counter());
node->add_property ("event-counter", buf);
+ /* save the VCA counter */
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, VCA::get_next_vca_number());
+ node->add_property ("vca-counter", buf);
+
/* various options */
list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
}
}
+ node->add_child_nocopy (_vca_manager->get_state());
+
child = node->add_child ("Routes");
{
boost::shared_ptr<RouteList> r = routes.reader ();
RouteList public_order (*r);
public_order.sort (cmp);
- /* the sort should have put control outs first */
+ /* the sort should have put the monitor out first */
if (_monitor_out) {
assert (_monitor_out == public_order.front());
node->add_child_copy (*_extra_xml);
}
+ {
+ Glib::Threads::Mutex::Lock lm (lua_lock);
+ std::string saved;
+ {
+ luabridge::LuaRef savedstate ((*_lua_save)());
+ saved = savedstate.cast<std::string>();
+ }
+ lua.collect_garbage ();
+ lm.release ();
+
+ gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
+ std::string b64s (b64);
+ g_free (b64);
+
+ XMLNode* script_node = new XMLNode (X_("Script"));
+ script_node->add_property (X_("lua"), LUA_VERSION);
+ script_node->add_content (b64s);
+ node->add_child_nocopy (*script_node);
+ }
+
return *node;
}
int
Session::set_state (const XMLNode& node, int version)
{
+ LocaleGuard lg;
XMLNodeList nlist;
XMLNode* child;
- const XMLProperty* prop;
+ XMLProperty const * prop;
int ret = -1;
_state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
if ((prop = node.property (X_("sample-rate"))) != 0) {
- _nominal_frame_rate = atoi (prop->value());
+ _base_frame_rate = atoi (prop->value());
+ _nominal_frame_rate = _base_frame_rate;
- if (_nominal_frame_rate != _current_frame_rate) {
- boost::optional<int> r = AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate);
+ assert (AudioEngine::instance()->running ());
+ if (_base_frame_rate != AudioEngine::instance()->sample_rate ()) {
+ boost::optional<int> r = AskAboutSampleRateMismatch (_base_frame_rate, _current_frame_rate);
if (r.get_value_or (0)) {
goto out;
}
}
}
+ created_with = "unknown";
+ if ((child = find_named_node (node, "ProgramVersion")) != 0) {
+ if ((prop = child->property (X_("created-with"))) != 0) {
+ created_with = prop->value ();
+ }
+ }
+
setup_raid_path(_session_dir->root_path());
+ if ((prop = node.property (X_("end-is-free"))) != 0) {
+ _session_range_end_is_free = string_is_affirmative (prop->value());
+ }
+
if ((prop = node.property (X_("id-counter"))) != 0) {
uint64_t x;
sscanf (prop->value().c_str(), "%" PRIu64, &x);
ID::init_counter (now);
}
- if ((prop = node.property (X_("event-counter"))) != 0) {
- Evoral::init_event_id_counter (atoi (prop->value()));
- }
+ if ((prop = node.property (X_("name-counter"))) != 0) {
+ init_name_id_counter (atoi (prop->value()));
+ }
+ if ((prop = node.property (X_("event-counter"))) != 0) {
+ Evoral::init_event_id_counter (atoi (prop->value()));
+ }
+
+ if ((prop = node.property (X_("vca-counter"))) != 0) {
+ uint32_t x;
+ sscanf (prop->value().c_str(), "%" PRIu32, &x);
+ VCA::set_next_vca_number (x);
+ } else {
+ VCA::set_next_vca_number (1);
+ }
if ((child = find_named_node (node, "MIDIPorts")) != 0) {
_midi_ports->set_midi_port_states (child->children());
}
}
+ if ((child = find_named_node (node, VCAManager::xml_node_name)) != 0) {
+ _vca_manager->set_state (*child, version);
+ }
+
if ((child = find_named_node (node, "Routes")) == 0) {
error << _("Session: XML state has no routes section") << endmsg;
goto out;
goto out;
}
+ /* Now that we have Routes and masters loaded, connect them if appropriate */
+
+ Slavable::Assign (_vca_manager); /* EMIT SIGNAL */
+
/* our diskstreams list is no longer needed as they are now all owned by their Route */
_diskstreams_2X.clear ();
ControlProtocolManager::instance().set_state (*child, version);
}
+ if ((child = find_named_node (node, "Script"))) {
+ for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
+ if (!(*n)->is_content ()) { continue; }
+ gsize size;
+ guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
+ try {
+ Glib::Threads::Mutex::Lock lm (lua_lock);
+ (*_lua_load)(std::string ((const char*)buf, size));
+ } catch (luabridge::LuaException const& e) {
+ cerr << "LuaException:" << e.what () << endl;
+ }
+ g_free (buf);
+ }
+ }
+
update_route_record_state ();
/* here beginneth the second phase ... */
+ set_snapshot_name (_current_snapshot_name);
StateReady (); /* EMIT SIGNAL */
BootMessage (_("Tracks/busses loaded; Adding to Session"));
- add_routes (new_routes, false, false, false);
+ add_routes (new_routes, false, false, false, PresentationInfo::max_order);
BootMessage (_("Finished adding tracks/busses"));
XMLNode* ds_child = find_named_node (node, X_("Diskstream"));
DataType type = DataType::AUDIO;
- const XMLProperty* prop = node.property("default-type");
+ XMLProperty const * prop = node.property("default-type");
if (prop) {
type = DataType (prop->value());
return ret;
}
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+ BOOST_MARK_TRACK (track);
ret = track;
} else {
- enum Route::Flag flags = Route::Flag(0);
- const XMLProperty* prop = node.property("flags");
- if (prop) {
- flags = Route::Flag (string_2_enum (prop->value(), flags));
- }
-
+ PresentationInfo::Flag flags = PresentationInfo::get_flags (node);
boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
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");
-#endif
+ BOOST_MARK_ROUTE (r);
ret = r;
}
}
}
DataType type = DataType::AUDIO;
- const XMLProperty* prop = node.property("default-type");
+ XMLProperty const * prop = node.property("default-type");
if (prop) {
type = DataType (prop->value());
track->set_diskstream (*i);
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
- // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+ BOOST_MARK_TRACK (track);
ret = track;
} else {
- enum Route::Flag flags = Route::Flag(0);
- const XMLProperty* prop = node.property("flags");
- if (prop) {
- flags = Route::Flag (string_2_enum (prop->value(), flags));
- }
-
+ PresentationInfo::Flag flags = PresentationInfo::get_flags (node);
boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
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");
-#endif
+ BOOST_MARK_ROUTE (r);
ret = r;
}
}
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((region = XMLRegionFactory (**niter, false)) == 0) {
error << _("Session: cannot create Region from XML description.");
- const XMLProperty *name = (**niter).property("name");
+ XMLProperty const * name = (**niter).property("name");
if (name) {
error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
{
XMLNodeList calist = node.children();
XMLNodeConstIterator caiter;
- XMLProperty *caprop;
+ XMLProperty const * caprop;
for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
XMLNode* ca = *caiter;
/* it may already exist, so don't recreate it unnecessarily
*/
- XMLProperty* prop = (*niter)->property (X_("id"));
+ XMLProperty const * prop = (*niter)->property (X_("id"));
if (!prop) {
error << _("Nested source has no ID info in session file! (ignored)") << endmsg;
continue;
boost::shared_ptr<Region>
Session::XMLRegionFactory (const XMLNode& node, bool full)
{
- const XMLProperty* type = node.property("type");
+ XMLProperty const * type = node.property("type");
try {
boost::shared_ptr<AudioRegion>
Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
{
- const XMLProperty* prop;
+ XMLProperty const * prop;
boost::shared_ptr<Source> source;
boost::shared_ptr<AudioSource> as;
SourceList sources;
boost::shared_ptr<MidiRegion>
Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
{
- const XMLProperty* prop;
+ XMLProperty const * prop;
boost::shared_ptr<Source> source;
boost::shared_ptr<MidiSource> ms;
SourceList sources;
}
int
-Session::save_template (string template_name)
+Session::save_template (string template_name, bool replace_existing)
{
if ((_state_of_the_state & CannotSave) || template_name.empty ()) {
return -1;
}
if (!ARDOUR::Profile->get_trx()) {
- if (Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
+ if (!replace_existing && Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
warning << string_compose(_("Template \"%1\" already exists - new version not created"),
template_dir_path) << endmsg;
- return -1;
+ return -2;
}
if (g_mkdir_with_parents (template_dir_path.c_str(), 0755) != 0) {
XMLTree tree;
- tree.set_root (&get_template());
+ {
+ PBD::Unwinder<std::string> uw (_template_state_dir, template_dir_path);
+ tree.set_root (&get_template());
+ }
+
if (!tree.write (template_file_path)) {
error << _("template not saved") << endmsg;
return -1;
}
- if (!ARDOUR::Profile->get_trx()) {
- /* copy plugin state directory */
-
- std::string template_plugin_state_path (Glib::build_filename (template_dir_path, X_("plugins")));
-
- if (g_mkdir_with_parents (template_plugin_state_path.c_str(), 0755) != 0) {
- error << string_compose(_("Could not create directory for Session template plugin state\"%1\" (%2)"),
- template_plugin_state_path, g_strerror (errno)) << endmsg;
- return -1;
- }
- copy_files (plugins_dir(), template_plugin_state_path);
- }
-
store_recent_templates (template_file_path);
return 0;
void
Session::refresh_disk_space ()
{
-#if __APPLE__ || (HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H)
+#if __APPLE__ || __FreeBSD__ || (HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H)
Glib::Threads::Mutex::Lock lm (space_lock);
cmd->name ()));
_current_trans->add_command (cmd);
}
+
+PBD::StatefulDiffCommand*
+Session::add_stateful_diff_command (boost::shared_ptr<PBD::StatefulDestructible> sfd)
+{
+ PBD::StatefulDiffCommand* cmd = new PBD::StatefulDiffCommand (sfd);
+ add_command (cmd);
+ return cmd;
+}
+
void
Session::begin_reversible_command (const string& name)
{
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- XMLProperty* prop;
+ XMLProperty const * prop;
if ((prop = (*niter)->property (X_("type"))) == 0) {
continue;
return 0;
}
- this_snapshot_path = _path;
- this_snapshot_path += legalize_for_path (_current_snapshot_name);
+ this_snapshot_path = Glib::build_filename (_path, legalize_for_path (_current_snapshot_name));
this_snapshot_path += statefile_suffix;
for (vector<string>::iterator i = state_files.begin(); i != state_files.end(); ++i) {
+ cerr << "Looking at snapshot " << (*i) << " ( with this = [" << this_snapshot_path << "])\n";
+
if (exclude_this_snapshot && *i == this_snapshot_path) {
+ cerr << "\texcluded\n";
continue;
+
}
if (find_all_sources (*i, result) < 0) {
return 0;
}
+static void
+merge_all_sources (boost::shared_ptr<const Playlist> pl, std::set<boost::shared_ptr<Source> >* all_sources)
+{
+ pl->deep_sources (*all_sources);
+}
+
int
Session::cleanup_sources (CleanupReport& rep)
{
string midi_path;
vector<string> candidates;
vector<string> unused;
- set<string> all_sources;
- bool used;
+ set<string> sources_used_by_all_snapshots;
string spath;
int ret = -1;
string tmppath1;
string tmppath2;
Searchpath asp;
Searchpath msp;
+ set<boost::shared_ptr<Source> > sources_used_by_this_snapshot;
_state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
find_files_matching_filter (candidates, audio_path, accept_all_audio_files, (void *) 0, true, true);
find_files_matching_filter (candidates, midi_path, accept_all_midi_files, (void *) 0, true, true);
- /* find all sources, but don't use this snapshot because the
- state file on disk still references sources we may have already
- dropped.
+ /* add sources from all other snapshots as "used", but don't use this
+ snapshot because the state file on disk still references sources we
+ may have already dropped.
*/
- find_all_sources_across_snapshots (all_sources, true);
+ find_all_sources_across_snapshots (sources_used_by_all_snapshots, true);
+
+ /* Although the region factory has a list of all regions ever created
+ * for this session, we're only interested in regions actually in
+ * playlists right now. So merge all playlist regions lists together.
+ *
+ * This will include the playlists used within compound regions.
+ */
+
+ playlists->foreach (boost::bind (merge_all_sources, _1, &sources_used_by_this_snapshot));
/* add our current source list
*/
SourceMap::iterator tmp = i;
++tmp;
- if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
+ if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) == 0) {
+ /* not a file */
+ i = tmp;
+ continue;
+ }
- /* this is mostly for windows which doesn't allow file
- * renaming if the file is in use. But we don't special
- * case it because we need to know if this causes
- * problems, and the easiest way to notice that is to
- * keep it in place for all platforms.
- */
+ /* this is mostly for windows which doesn't allow file
+ * renaming if the file is in use. But we do not special
+ * case it because we need to know if this causes
+ * problems, and the easiest way to notice that is to
+ * keep it in place for all platforms.
+ */
- fs->close ();
+ fs->close ();
- if (!fs->is_stub()) {
+ if (!fs->is_stub()) {
- if (playlists->source_use_count (fs) != 0) {
- all_sources.insert (fs->path());
- } else {
+ /* Note that we're checking a list of all
+ * sources across all snapshots with the list
+ * of sources used by this snapshot.
+ */
- /* 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.
- */
+ if (sources_used_by_this_snapshot.find (i->second) != sources_used_by_this_snapshot.end()) {
+ /* this source is in use by this snapshot */
+ sources_used_by_all_snapshots.insert (fs->path());
+ cerr << "Source from source list found in used_by_this_snapshot (" << fs->path() << ")\n";
+ } else {
+ cerr << "Source from source list NOT found in used_by_this_snapshot (" << fs->path() << ")\n";
+ /* this source is NOT in use by this snapshot
+ */
- RegionFactory::remove_regions_using_source (i->second);
+ /* remove all related regions from RegionFactory master list
+ */
- // also remove source from all_sources
+ RegionFactory::remove_regions_using_source (i->second);
- for (set<string>::iterator j = all_sources.begin(); j != all_sources.end(); ++j) {
- spath = Glib::path_get_basename (*j);
- if (spath == i->second->name()) {
- all_sources.erase (j);
- break;
- }
- }
+ /* remove from our current source list
+ * also. We may not remove it from
+ * disk, because it may be used by
+ * other snapshots, but it isn't used inside this
+ * snapshot anymore, so we don't need a
+ * reference to it.
+ */
- sources.erase (i);
- }
+ sources.erase (i);
}
}
i = tmp;
}
+ /* now check each candidate source to see if it exists in the list of
+ sources_used_by_all_snapshots. If it doesn't, put it into "unused".
+ */
+
+ cerr << "Candidates: " << candidates.size() << endl;
+ cerr << "Used by others: " << sources_used_by_all_snapshots.size() << endl;
+
for (vector<string>::iterator x = candidates.begin(); x != candidates.end(); ++x) {
- used = false;
+ bool used = false;
spath = *x;
- for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
+ for (set<string>::iterator i = sources_used_by_all_snapshots.begin(); i != sources_used_by_all_snapshots.end(); ++i) {
tmppath1 = canonical_path (spath);
tmppath2 = canonical_path ((*i));
+ cerr << "\t => " << tmppath2 << endl;
+
if (tmppath1 == tmppath2) {
used = true;
break;
}
}
+ cerr << "Actually unused: " << unused.size() << endl;
+
+ if (unused.empty()) {
+ /* Nothing to do */
+ ret = 0;
+ goto out;
+ }
+
/* now try to move all unused files into the "dead" directory(ies) */
for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
newpath = newpath_v;
}
- } else {
-
- /* it doesn't exist, or we can't read it or something */
-
}
g_stat ((*x).c_str(), &statbuf);
if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
- error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"),
- (*x), newpath, strerror (errno))
- << endmsg;
- goto out;
+ error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"), (*x), newpath, strerror (errno)) << endmsg;
+ continue;
}
/* see if there an easy to find peakfile for this file, and remove it.
Session::controllable_by_descriptor (const ControllableDescriptor& desc)
{
boost::shared_ptr<Controllable> c;
+ boost::shared_ptr<Stripable> s;
boost::shared_ptr<Route> r;
switch (desc.top_level_type()) {
case ControllableDescriptor::NamedRoute:
{
std::string str = desc.top_level_name();
+
if (str == "Master" || str == "master") {
- r = _master_out;
- } else if (str == "control" || str == "listen") {
- r = _monitor_out;
+ s = _master_out;
+ } else if (str == "control" || str == "listen" || str == "monitor" || str == "Monitor") {
+ s = _monitor_out;
+ } else if (str == "auditioner") {
+ s = auditioner;
} else {
- r = route_by_name (desc.top_level_name());
+ s = route_by_name (desc.top_level_name());
}
+
break;
}
- case ControllableDescriptor::RemoteControlID:
- r = route_by_remote_id (desc.rid());
+ case ControllableDescriptor::PresentationOrderRoute:
+ s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::Route);
+ break;
+
+ case ControllableDescriptor::PresentationOrderTrack:
+ s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::Track);
+ break;
+
+ case ControllableDescriptor::PresentationOrderBus:
+ s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::Bus);
+ break;
+
+ case ControllableDescriptor::PresentationOrderVCA:
+ s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::VCA);
+ break;
+
+ case ControllableDescriptor::SelectionCount:
+ s = route_by_selected_count (desc.selection_id());
break;
}
- if (!r) {
+ if (!s) {
return c;
}
+ r = boost::dynamic_pointer_cast<Route> (s);
+
switch (desc.subtype()) {
case ControllableDescriptor::Gain:
- c = r->gain_control ();
+ c = s->gain_control ();
break;
case ControllableDescriptor::Trim:
- c = r->trim()->gain_control ();
+ c = s->trim_control ();
break;
case ControllableDescriptor::Solo:
- c = r->solo_control();
+ c = s->solo_control();
break;
case ControllableDescriptor::Mute:
- c = r->mute_control();
+ c = s->mute_control();
break;
case ControllableDescriptor::Recenable:
- {
- boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
-
- if (t) {
- c = t->rec_enable_control ();
- }
+ c = s->rec_enable_control ();
break;
- }
case ControllableDescriptor::PanDirection:
- {
- c = r->pannable()->pan_azimuth_control;
+ c = s->pan_azimuth_control();
break;
- }
case ControllableDescriptor::PanWidth:
- {
- c = r->pannable()->pan_width_control;
+ c = s->pan_width_control();
break;
- }
case ControllableDescriptor::PanElevation:
- {
- c = r->pannable()->pan_elevation_control;
+ c = s->pan_elevation_control();
break;
- }
case ControllableDescriptor::Balance:
/* XXX simple pan control */
--parameter_index;
}
+ if (!r) {
+ return c;
+ }
+
boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
if (p) {
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();
-
- if (a) {
- c = s->amp()->gain_control();
- }
+ if (!r) {
+ return c;
}
+ c = r->send_level_controllable (send);
break;
}
} else if (p == "click-gain") {
if (_click_gain) {
- _click_gain->set_gain (Config->get_click_gain(), this);
+ _click_gain->gain_control()->set_value (Config->get_click_gain(), Controllable::NoGroup);
}
} else if (p == "send-mtc") {
} else if (p == "solo-control-is-listen-control") {
solo_control_mode_changed ();
} else if (p == "solo-mute-gain") {
- _solo_cut_control->Changed();
+ _solo_cut_control->Changed (true, Controllable::NoGroup);
} else if (p == "timecode-offset" || p == "timecode-offset-negative") {
last_timecode_valid = false;
} else if (p == "playback-buffer-seconds") {
Session::setup_midi_machine_control ()
{
_mmc = new MIDI::MachineControl;
- _mmc->set_ports (_midi_ports->mmc_input_port(), _midi_ports->mmc_output_port());
+
+ boost::shared_ptr<AsyncMIDIPort> async_in = boost::dynamic_pointer_cast<AsyncMIDIPort> (_midi_ports->mmc_input_port());
+ boost::shared_ptr<AsyncMIDIPort> async_out = boost::dynamic_pointer_cast<AsyncMIDIPort> (_midi_ports->mmc_output_port());
+
+ if (!async_out || !async_out) {
+ return;
+ }
+
+ /* XXXX argh, passing raw pointers back into libmidi++ */
+
+ MIDI::Port* mmc_in = async_in.get();
+ MIDI::Port* mmc_out = async_out.get();
+
+ _mmc->set_ports (mmc_in, mmc_out);
_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));
return _solo_cut_control;
}
+void
+Session::save_snapshot_name (const std::string & n)
+{
+ /* assure Stateful::_instant_xml is loaded
+ * add_instant_xml() only adds to existing data and defaults
+ * to use an empty Tree otherwise
+ */
+ instant_xml ("LastUsedSnapshot");
+
+ XMLNode* last_used_snapshot = new XMLNode ("LastUsedSnapshot");
+ last_used_snapshot->add_property ("name", string(n));
+ add_instant_xml (*last_used_snapshot, false);
+}
+
+void
+Session::set_snapshot_name (const std::string & n)
+{
+ _current_snapshot_name = n;
+ save_snapshot_name (n);
+}
+
int
Session::rename (const std::string& new_name)
{
}
}
- _current_snapshot_name = new_name;
+ set_snapshot_name (new_name);
_name = new_name;
set_dirty ();
/* sample rate */
- const XMLProperty* prop;
- if ((prop = tree.root()->property (X_("sample-rate"))) != 0) {
+ XMLProperty const * prop;
+ XMLNode const * root (tree.root());
+
+ if ((prop = root->property (X_("sample-rate"))) != 0) {
sample_rate = atoi (prop->value());
found_sr = true;
}
- const XMLNodeList& children (tree.root()->children());
+ const XMLNodeList& children (root->children());
for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
const XMLNode* child = *c;
if (child->name() == "Config") {
const XMLNodeList& options (child->children());
for (XMLNodeList::const_iterator oc = options.begin(); oc != options.end(); ++oc) {
- const XMLNode* option = *oc;
- const XMLProperty* name = option->property("name");
+ XMLNode const * option = *oc;
+ XMLProperty const * name = option->property("name");
if (!name) {
continue;
}
if (name->value() == "native-file-data-format") {
- const XMLProperty* value = option->property ("value");
+ XMLProperty const * value = option->property ("value");
if (value) {
SampleFormat fmt = (SampleFormat) string_2_enum (option->property ("value")->value(), fmt);
data_format = fmt;
return !(found_sr && found_data_format); // zero if they are both found
}
+std::string
+Session::get_snapshot_from_instant (const std::string& session_dir)
+{
+ std::string instant_xml_path = Glib::build_filename (session_dir, "instant.xml");
+
+ if (!Glib::file_test (instant_xml_path, Glib::FILE_TEST_EXISTS)) {
+ return "";
+ }
+
+ XMLTree tree;
+ if (!tree.read (instant_xml_path)) {
+ return "";
+ }
+
+ XMLProperty const * prop;
+ XMLNode *last_used_snapshot = tree.root()->child("LastUsedSnapshot");
+ if (last_used_snapshot && (prop = last_used_snapshot->property ("name")) != 0) {
+ return prop->value();
+ }
+
+ return "";
+}
+
typedef std::vector<boost::shared_ptr<FileSource> > SeveralFileSources;
typedef std::map<std::string,SeveralFileSources> SourcePathMap;
_path = to_dir;
- _current_snapshot_name = saveas.new_name;
+ set_snapshot_name (saveas.new_name);
_name = saveas.new_name;
if (saveas.include_media && !saveas.copy_media) {
_path = old_path;
_name = old_name;
- _current_snapshot_name = old_snapshot;
+ set_snapshot_name (old_snapshot);
(*_session_dir) = old_sd;