#include <sys/mount.h>
#endif
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+
#include <glib.h>
#include <glibmm.h>
#include "midi++/port.h"
#include "midi++/manager.h"
+#include "evoral/SMF.hpp"
+
#include "pbd/boost_debug.h"
#include "pbd/basename.h"
#include "pbd/controllable_descriptor.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/audioplaylist.h"
#include "ardour/audioregion.h"
-#include "ardour/auditioner.h"
#include "ardour/automation_control.h"
-#include "ardour/buffer.h"
#include "ardour/butler.h"
-#include "ardour/configuration.h"
#include "ardour/control_protocol_manager.h"
-#include "ardour/crossfade.h"
-#include "ardour/cycle_timer.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.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/midi_source.h"
#include "ardour/midi_track.h"
#include "ardour/named_selection.h"
#include "ardour/pannable.h"
-#include "ardour/processor.h"
+#include "ardour/playlist_factory.h"
#include "ardour/port.h"
+#include "ardour/processor.h"
#include "ardour/proxy_controllable.h"
#include "ardour/recent_sessions.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/session_metadata.h"
-#include "ardour/session_state_utils.h"
#include "ardour/session_playlists.h"
+#include "ardour/session_state_utils.h"
#include "ardour/session_utils.h"
#include "ardour/silentfilesource.h"
-#include "ardour/slave.h"
-#include "ardour/smf_source.h"
-#include "ardour/sndfile_helpers.h"
#include "ardour/sndfilesource.h"
#include "ardour/source_factory.h"
#include "ardour/speakers.h"
#include "ardour/tempo.h"
#include "ardour/ticker.h"
#include "ardour/user_bundle.h"
-#include "ardour/utils.h"
-#include "ardour/utils.h"
-#include "ardour/version.h"
-#include "ardour/playlist_factory.h"
#include "control_protocol/control_protocol.h"
_solo_isolated_cnt = 0;
g_atomic_int_set (&processing_prohibited, 0);
_transport_speed = 0;
+ _default_transport_speed = 1.0;
_last_transport_speed = 0;
_target_transport_speed = 0;
auto_play_legal = false;
no_questions_about_missing_files = false;
_speakers.reset (new Speakers);
_clicks_cleared = 0;
+ ignore_route_processor_changes = false;
+ _pre_export_mmc_enabled = false;
AudioDiskstream::allocate_working_buffers();
/* default short fade = 15ms */
- Crossfade::set_short_xfade_length ((framecnt_t) floor (config.get_short_xfade_seconds() * frame_rate()));
SndFileSource::setup_standard_crossfades (*this, frame_rate());
last_mmc_step.tv_sec = 0;
int
Session::second_stage_init ()
{
- AudioFileSource::set_peak_dir (_session_dir->peak_path().to_string());
+ AudioFileSource::set_peak_dir (_session_dir->peak_path());
if (!_is_new) {
if (load_state (_current_snapshot_name)) {
_engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
_engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
+ midi_clock = new MidiClockTicker ();
+ midi_clock->set_session (this);
+
try {
when_engine_running ();
}
MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (Timecode::Time ()));
- MidiClockTicker::instance().set_session (this);
MIDI::Name::MidiPatchManager::instance().set_session (this);
/* initial program change will be delivered later; see ::config_changed() */
SearchPath raid_search_path;
for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
- raid_search_path += sys::path((*i).path);
+ raid_search_path += (*i).path;
}
return raid_search_path.to_string ();
SearchPath midi_search_path;
for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
- sp.path = (*i).to_string ();
+ sp.path = *i;
sp.blocks = 0; // not needed
session_dirs.push_back (sp);
Session::path_is_within_session (const std::string& path)
{
for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
- if (path.find ((*i).path) == 0) {
+ if (PBD::sys::path_is_within (i->path, path)) {
return true;
}
}
{
string dir;
- dir = session_directory().peak_path().to_string();
+ dir = session_directory().peak_path();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
- dir = session_directory().sound_path().to_string();
+ dir = session_directory().sound_path();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
- dir = session_directory().midi_path().to_string();
+ dir = session_directory().midi_path();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
- dir = session_directory().dead_path().to_string();
+ dir = session_directory().dead_path();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
- dir = session_directory().export_path().to_string();
+ dir = session_directory().export_path();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
+ dir = externals_dir ();
+
+ if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
+ error << string_compose(_("Session: cannot create session externals folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ return -1;
+ }
+
return 0;
}
}
- /* Instantiate metadata */
-
- _metadata = new SessionMetadata ();
-
/* set initial start + end point */
_state_of_the_state = Clean;
if (bus_profile) {
RouteList rl;
- int control_id = 1;
ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
if (bus_profile->master_out_channels) {
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);
}
if (!rl.empty()) {
- add_routes (rl, false, false);
+ add_routes (rl, false, false, false);
}
/* this allows the user to override settings with an environment variable.
Config->set_output_auto_connect (bus_profile->output_ac);
}
+ if (Config->get_use_monitor_bus() && bus_profile) {
+ add_monitor_section ();
+ }
+
save_state ("");
return 0;
const string old_xml_filename = legalize_for_path (old_name) + statefile_suffix;
const string new_xml_filename = legalize_for_path (new_name) + statefile_suffix;
- const sys::path old_xml_path = _session_dir->root_path() / old_xml_filename;
- const sys::path new_xml_path = _session_dir->root_path() / new_xml_filename;
+ const sys::path old_xml_path(Glib::build_filename (_session_dir->root_path(), old_xml_filename));
+ const sys::path new_xml_path(Glib::build_filename (_session_dir->root_path(), new_xml_filename));
try
{
void
Session::remove_state (string snapshot_name)
{
- if (snapshot_name == _current_snapshot_name || snapshot_name == _name) {
+ if (!_writable || snapshot_name == _current_snapshot_name || snapshot_name == _name) {
// refuse to remove the current snapshot or the "main" one
return;
}
- sys::path xml_path(_session_dir->root_path());
+ std::string xml_path(_session_dir->root_path());
- xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
+ xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + statefile_suffix);
if (!create_backup_file (xml_path)) {
// don't remove it if a backup can't be made
Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
{
XMLTree tree;
- sys::path xml_path(_session_dir->root_path());
+ std::string xml_path(_session_dir->root_path());
if (!_writable || (_state_of_the_state & CannotSave)) {
return 1;
/* 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());
/* proper save: use statefile_suffix (.ardour in English) */
- xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
+ xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + statefile_suffix);
/* make a backup copy of the old file */
} else {
/* pending save: use pending_suffix (.pending in English) */
- xml_path /= legalize_for_path (snapshot_name) + pending_suffix;
+ xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + pending_suffix);
}
sys::path tmp_path(_session_dir->root_path());
} 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.c_str()) != 0) {
error << string_compose (_("could not rename temporary session file %1 to %2"),
- tmp_path.to_string(), xml_path.to_string()) << endmsg;
+ tmp_path.to_string(), xml_path) << endmsg;
sys::remove (tmp_path);
return -1;
}
/* no version implies very old version of Ardour */
Stateful::loading_state_version = 1000;
} else {
- int major;
- int minor;
- int micro;
-
- sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ);
- Stateful::loading_state_version = (major * 1000) + minor;
+ if (prop->value().find ('.') != string::npos) {
+ /* old school version format */
+ if (prop->value()[0] == '2') {
+ Stateful::loading_state_version = 2000;
+ } else {
+ Stateful::loading_state_version = 3000;
+ }
+ } else {
+ Stateful::loading_state_version = atoi (prop->value());
+ }
}
- if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) {
+ if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION && _writable) {
sys::path backup_path(_session_dir->root_path());
- backup_path /= legalize_for_path (snapshot_name) + "-1" + statefile_suffix;
+ backup_path /= string_compose ("%1-%2%3", legalize_for_path (snapshot_name), Stateful::loading_state_version, statefile_suffix);
- // only create a backup once
- if (sys::exists (backup_path)) {
- return 0;
- }
-
- info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"),
- xmlpath.to_string(), backup_path.to_string(), PROGRAM_NAME)
- << endmsg;
+ // only create a backup for a given statefile version once
- try
- {
- sys::copy_file (xmlpath, backup_path);
- }
- catch(sys::filesystem_error& ex)
- {
- error << string_compose (_("Unable to make backup of state file %1 (%2)"),
- xmlpath.to_string(), ex.what())
- << endmsg;
- return -1;
+ if (!sys::exists (backup_path)) {
+
+ info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"),
+ xmlpath.to_string(), backup_path.to_string(), PROGRAM_NAME)
+ << endmsg;
+
+ try {
+ sys::copy_file (xmlpath, backup_path);
+
+ } catch (sys::filesystem_error& ex) {
+
+ error << string_compose (_("Unable to make backup of state file %1 (%2)"),
+ xmlpath.to_string(), ex.what())
+ << endmsg;
+ return -1;
+ }
}
}
}
XMLNode&
-Session::state(bool full_state)
+Session::state (bool full_state)
{
XMLNode* node = new XMLNode("Session");
XMLNode* child;
- // store libardour version, just in case
char buf[16];
- snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_micro_version);
- node->add_property("version", string(buf));
+ snprintf(buf, sizeof(buf), "%d", CURRENT_SESSION_FILE_VERSION);
+ node->add_property("version", buf);
/* store configuration settings */
node->add_child_nocopy (config.get_variables ());
- node->add_child_nocopy (_metadata->get_state());
+ node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state());
child = node->add_child ("Sources");
}
if (_click_io) {
- child = node->add_child ("Click");
- child->add_child_nocopy (_click_io->state (full_state));
+ XMLNode* gain_child = node->add_child ("Click");
+ gain_child->add_child_nocopy (_click_io->state (full_state));
+ gain_child->add_child_nocopy (_click_gain->state (full_state));
}
if (full_state) {
- child = node->add_child ("NamedSelections");
+ XMLNode* ns_child = node->add_child ("NamedSelections");
for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) {
if (full_state) {
- child->add_child_nocopy ((*i)->get_state());
+ ns_child->add_child_nocopy ((*i)->get_state());
}
}
}
return -1;
}
- if ((prop = node.property ("version")) != 0) {
- version = atoi (prop->value ()) * 1000;
- }
-
if ((prop = node.property ("name")) != 0) {
_name = prop->value ();
}
}
}
- setup_raid_path(_session_dir->root_path().to_string());
+ setup_raid_path(_session_dir->root_path());
if ((prop = node.property (X_("id-counter"))) != 0) {
uint64_t x;
if (version >= 3000) {
if ((child = find_named_node (node, "Metadata")) == 0) {
warning << _("Session: XML state has no metadata section") << endmsg;
- } else if (_metadata->set_state (*child, version)) {
+ } else if ( ARDOUR::SessionMetadata::Metadata()->set_state (*child, version) ) {
goto out;
}
}
if ((child = find_named_node (node, "Click")) == 0) {
warning << _("Session: XML state has no click section") << endmsg;
} else if (_click_io) {
- _click_io->set_state (*child, version);
+ const XMLNodeList& children (child->children());
+ XMLNodeList::const_iterator i = children.begin();
+ _click_io->set_state (**i, version);
+ ++i;
+ if (i != children.end()) {
+ _click_gain->set_state (**i, version);
+ }
}
if ((child = find_named_node (node, "ControlProtocols")) != 0) {
ControlProtocolManager::instance().set_protocol_states (*child);
}
+ update_have_rec_enabled_track ();
+
/* here beginneth the second phase ... */
StateReady (); /* EMIT SIGNAL */
new_routes.push_back (route);
}
- add_routes (new_routes, false, false);
+ add_routes (new_routes, false, false, false);
return 0;
}
void
Session::refresh_disk_space ()
{
-#if HAVE_SYS_VFS_H
- struct statfs statfsbuf;
- vector<space_and_path>::iterator i;
+#if HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H
+
Glib::Mutex::Lock lm (space_lock);
- double scale;
/* get freespace on every FS that is part of the session path */
_total_free_4k_blocks = 0;
+ _total_free_4k_blocks_uncertain = false;
- for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
- statfs ((*i).path.c_str(), &statfsbuf);
+ for (vector<space_and_path>::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+
+ struct statfs statfsbuf;
+ statfs (i->path.c_str(), &statfsbuf);
+
+ double const scale = statfsbuf.f_bsize / 4096.0;
- scale = statfsbuf.f_bsize/4096.0;
+ /* See if this filesystem is read-only */
+ struct statvfs statvfsbuf;
+ statvfs (i->path.c_str(), &statvfsbuf);
+
+ /* f_bavail can be 0 if it is undefined for whatever
+ filesystem we are looking at; Samba shares mounted
+ via GVFS are an example of this.
+ */
+ if (statfsbuf.f_bavail == 0) {
+ /* block count unknown */
+ i->blocks = 0;
+ i->blocks_unknown = true;
+ } else if (statvfsbuf.f_flag & ST_RDONLY) {
+ /* read-only filesystem */
+ i->blocks = 0;
+ i->blocks_unknown = false;
+ } else {
+ /* read/write filesystem with known space */
+ i->blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
+ i->blocks_unknown = false;
+ }
- (*i).blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
- _total_free_4k_blocks += (*i).blocks;
+ _total_free_4k_blocks += i->blocks;
+ if (i->blocks_unknown) {
+ _total_free_4k_blocks_uncertain = true;
+ }
}
#endif
}
Session::get_best_session_directory_for_new_source ()
{
vector<space_and_path>::iterator i;
- string result = _session_dir->root_path().to_string();
+ string result = _session_dir->root_path();
/* handle common case without system calls */
return Glib::build_filename (_path, "plugins");
}
+string
+Session::externals_dir () const
+{
+ return Glib::build_filename (_path, "externals");
+}
+
int
Session::load_bundles (XMLNode const & node)
{
++nexti;
SessionDirectory sdir ((*i).path);
- audio_path += sdir.sound_path().to_string();
+ audio_path += sdir.sound_path();
if (nexti != session_dirs.end()) {
audio_path += ':';
++nexti;
SessionDirectory sdir ((*i).path);
- midi_path += sdir.midi_path().to_string();
+ midi_path += sdir.midi_path();
if (nexti != session_dirs.end()) {
midi_path += ':';
void
Session::remove_controllable (Controllable* c)
{
- if (_state_of_the_state | Deletion) {
+ if (_state_of_the_state & Deletion) {
return;
}
const string history_filename = legalize_for_path (snapshot_name) + history_suffix;
const string backup_filename = history_filename + backup_suffix;
- const sys::path xml_path = _session_dir->root_path() / history_filename;
- const sys::path backup_path = _session_dir->root_path() / backup_filename;
+ const sys::path xml_path(Glib::build_filename (_session_dir->root_path(), history_filename));
+ const sys::path backup_path(Glib::build_filename (_session_dir->root_path(), backup_filename));
if (sys::exists (xml_path)) {
try
}
const string xml_filename = legalize_for_path (snapshot_name) + history_suffix;
- const sys::path xml_path = _session_dir->root_path() / xml_filename;
+ const sys::path xml_path(Glib::build_filename (_session_dir->root_path(), xml_filename));
info << "Loading history from " << xml_path.to_string() << endmsg;
_clicking = false;
}
+ } else if (p == "click-gain") {
+
+ if (_click_gain) {
+ _click_gain->set_gain (Config->get_click_gain(), this);
+ }
+
} else if (p == "send-mtc") {
if (Config->get_send_mtc ()) {
last_timecode_valid = false;
} else if (p == "playback-buffer-seconds") {
AudioSource::allocate_working_buffers (frame_rate());
+ } else if (p == "automation-thinning-factor") {
+ Evoral::ControlList::set_thinning_factor (Config->get_automation_thinning_factor());
}
set_dirty ();
/* also handle MIDI SPP because its so common */
- mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this, _1, _2));
- mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this, _1, _2));
- mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this, _1, _2));
+ mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
+ mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
+ mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
}
boost::shared_ptr<Controllable>
string newstr;
bool first = true;
- string const old_sources_root = _session_dir->sources_root().to_string ();
+ string const old_sources_root = _session_dir->sources_root();
#define RENAME ::rename
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 ());
+ boost::replace_all (p, old_sources_root, _session_dir->sources_root());
fs->set_path (p);
}
}