*/
+#define __STDC_FORMAT_MACROS 1
+#include <stdint.h>
+
#include <algorithm>
#include <fstream>
#include <string>
#include <ardour/audioengine.h>
#include <ardour/configuration.h>
#include <ardour/session.h>
+#include <ardour/buffer.h>
#include <ardour/audio_diskstream.h>
+#include <ardour/midi_diskstream.h>
#include <ardour/utils.h>
#include <ardour/audioplaylist.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/smf_source.h>
#include <ardour/audiofilesource.h>
#include <ardour/silentfilesource.h>
#include <ardour/sndfilesource.h>
+#include <ardour/midi_source.h>
#include <ardour/sndfile_helpers.h>
#include <ardour/auditioner.h>
#include <ardour/export.h>
#include <ardour/slave.h>
#include <ardour/tempo.h>
#include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
#include <ardour/cycle_timer.h>
#include <ardour/utils.h>
#include <ardour/named_selection.h>
#include <ardour/version.h>
#include <ardour/location.h>
#include <ardour/audioregion.h>
+#include <ardour/midi_region.h>
#include <ardour/crossfade.h>
#include <ardour/control_protocol_manager.h>
#include <ardour/region_factory.h>
#include <ardour/source_factory.h>
#include <ardour/playlist_factory.h>
-
+#include <ardour/filename_extensions.h>
+#include <ardour/directory_names.h>
#include <control_protocol/control_protocol.h>
#include "i18n.h"
first_file_data_format_reset = true;
first_file_header_format_reset = true;
butler_thread = (pthread_t) 0;
- midi_thread = (pthread_t) 0;
+ //midi_thread = (pthread_t) 0;
AudioDiskstream::allocate_working_buffers();
-
+
/* default short fade = 15ms */
Crossfade::set_short_xfade_length ((nframes_t) floor (Config->get_short_xfade_seconds() * frame_rate()));
PlaylistFactory::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist));
Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect));
NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection));
- AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list));
+ AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list));
Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable));
- IO::MoreOutputs.connect (mem_fun (*this, &Session::ensure_passthru_buffers));
+ IO::MoreChannels.connect (mem_fun (*this, &Session::ensure_buffers));
/* stop IO objects from doing stuff until we're ready for them */
return -1;
}
- if (start_midi_thread ()) {
+ /*if (start_midi_thread ()) {
return -1;
- }
+ }*/
// set_state() will call setup_raid_path(), but if it's a new session we need
// to call setup_raid_path() here.
return -1;
}
- send_full_time_code ();
+ //send_full_time_code ();
_engine.transport_locate (0);
deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
fspath += sound_dir (false);
AudioFileSource::set_search_path (fspath);
+ SMFSource::set_search_path (fspath); // FIXME: should be different
return;
}
/* set the AudioFileSource search path */
AudioFileSource::set_search_path (fspath);
+ SMFSource::set_search_path (fspath); // FIXME: should be different
/* reset the round-robin soundfile path thingie */
last_rr_session_dir = session_dirs.begin();
}
-int
-Session::create (bool& new_session, string* mix_template, nframes_t initial_length)
+void
+Session::initialize_start_and_end_locations (nframes_t start, nframes_t end)
{
- string dir;
-
- if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
- return -1;
- }
-
- dir = peak_dir ();
-
- if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
- return -1;
- }
-
- /* if this is is an existing session with an old "sounds" directory, just use it. see Session::sound_dir() for more details */
-
- if (!Glib::file_test (old_sound_dir(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) {
-
- dir = sound_dir ();
-
- 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 = dead_sound_dir ();
-
- if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
- return -1;
- }
-
- dir = automation_dir ();
-
- if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
- return -1;
- }
-
- dir = export_dir ();
-
- if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
- error << string_compose(_("Session: cannot create session export dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
- return -1;
- }
-
-
- /* check new_session so we don't overwrite an existing one */
-
- if (mix_template) {
- std::string in_path = *mix_template;
-
- ifstream in(in_path.c_str());
-
- if (in){
- string out_path = _path;
- out_path += _name;
- out_path += _statefile_suffix;
-
- ofstream out(out_path.c_str());
-
- if (out){
- out << in.rdbuf();
-
- // okay, session is set up. Treat like normal saved
- // session from now on.
-
- new_session = false;
- return 0;
-
- } else {
- error << string_compose (_("Could not open %1 for writing mix template"), out_path)
- << endmsg;
- return -1;
- }
-
- } else {
- error << string_compose (_("Could not open mix template %1 for reading"), in_path)
- << endmsg;
- return -1;
- }
-
- }
-
- /* set initial start + end point */
-
- start_location->set_end (0);
+ start_location->set_end (start);
_locations.add (start_location);
- end_location->set_end (initial_length);
+ end_location->set_end (end);
_locations.add (end_location);
+}
+bool
+Session::create_session_file ()
+{
_state_of_the_state = Clean;
if (save_state (_current_snapshot_name)) {
- return -1;
+ error << "Could not create new session file" << endmsg;
+ return false;
}
+ return true;
+}
- return 0;
+bool
+Session::create_session_file_from_template (const string& template_path)
+{
+ string out_path = _path + _name + statefile_suffix;
+
+ if(!copy_file (template_path, out_path)) {
+ error << string_compose (_("Could not use session template %1 to create new session."), template_path)
+ << endmsg;
+ return false;
+ }
+ return true;
}
int
clist = node.children();
for (citer = clist.begin(); citer != clist.end(); ++citer) {
-
try {
- boost::shared_ptr<AudioDiskstream> dstream (new AudioDiskstream (*this, **citer));
- add_diskstream (dstream);
+ /* diskstreams added automatically by DiskstreamCreated handler */
+ if ((*citer)->name() == "AudioDiskstream" || (*citer)->name() == "DiskStream") {
+ boost::shared_ptr<AudioDiskstream> dstream (new AudioDiskstream (*this, **citer));
+ add_diskstream (dstream);
+ } else if ((*citer)->name() == "MidiDiskstream") {
+ boost::shared_ptr<MidiDiskstream> dstream (new MidiDiskstream (*this, **citer));
+ add_diskstream (dstream);
+ } else {
+ error << _("Session: unknown diskstream type in XML") << endmsg;
+ }
}
catch (failed_constructor& err) {
- error << _("Session: could not load diskstream via XML state") << endmsg;
+ error << _("Session: could not load diskstream via XML state") << endmsg;
return -1;
}
}
return 0;
}
+void
+Session::maybe_write_autosave()
+{
+ if (dirty() && record_status() != Recording) {
+ save_state("", true);
+ }
+}
+
void
Session::remove_pending_capture_state ()
{
xml_path = _path;
xml_path += _current_snapshot_name;
- xml_path += _pending_suffix;
+ xml_path += pending_suffix;
unlink (xml_path.c_str());
}
+/** Rename a state file.
+ * @param snapshot_name Snapshot name.
+ */
+void
+Session::rename_state (string old_name, string new_name)
+{
+ if (old_name == _current_snapshot_name || old_name == _name) {
+ /* refuse to rename the current snapshot or the "main" one */
+ return;
+ }
+
+ const string old_xml_path = _path + old_name + statefile_suffix;
+ const string new_xml_path = _path + new_name + statefile_suffix;
+
+ if (rename (old_xml_path.c_str(), new_xml_path.c_str()) != 0) {
+ error << string_compose(_("could not rename snapshot %1 to %2"), old_name, new_name) << endmsg;
+ }
+}
+
+/** Remove a state file.
+ * @param snapshot_name Snapshot name.
+ */
+void
+Session::remove_state (string snapshot_name)
+{
+ if (snapshot_name == _current_snapshot_name || snapshot_name == _name) {
+ /* refuse to remove the current snapshot or the "main" one */
+ return;
+ }
+
+ const string xml_path = _path + snapshot_name + statefile_suffix;
+
+ /* make a backup copy of the state file */
+ const string bak_path = xml_path + ".bak";
+ if (g_file_test (xml_path.c_str(), G_FILE_TEST_EXISTS)) {
+ copy_file (xml_path, bak_path);
+ }
+
+ /* and delete it */
+ unlink (xml_path.c_str());
+}
+
int
Session::save_state (string snapshot_name, bool pending)
{
if (!pending) {
+ /* proper save: use statefile_suffix (.ardour in English) */
xml_path = _path;
xml_path += snapshot_name;
- xml_path += _statefile_suffix;
+ xml_path += statefile_suffix;
+ /* make a backup copy of the old file */
bak_path = xml_path;
bak_path += ".bak";
} else {
+ /* pending save: use pending_suffix (.pending in English) */
xml_path = _path;
xml_path += snapshot_name;
- xml_path += _pending_suffix;
+ xml_path += pending_suffix;
}
tmp_path += snapshot_name;
tmp_path += ".tmp";
- cerr << "actually writing state\n";
+ // cerr << "actually writing state to " << xml_path << endl;
if (!tree.write (tmp_path)) {
error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg;
xmlpath = _path;
xmlpath += snapshot_name;
- xmlpath += _pending_suffix;
+ xmlpath += pending_suffix;
- if (!access (xmlpath.c_str(), F_OK)) {
+ if (Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
/* there is pending state from a crashed capture attempt */
xmlpath = _path;
xmlpath += snapshot_name;
- xmlpath += _statefile_suffix;
+ xmlpath += statefile_suffix;
}
-
- if (access (xmlpath.c_str(), F_OK)) {
+
+ if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg;
return 1;
}
if (is_old) {
string backup_path;
- backup_path = xmlpath;
- backup_path += ".1";
+ backup_path = _path;
+ backup_path += snapshot_name;
+ backup_path += "-1";
+ backup_path += statefile_suffix;
info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"),
xmlpath, backup_path)
child = node->add_child ("Sources");
if (full_state) {
- Glib::Mutex::Lock sl (audio_source_lock);
+ Glib::Mutex::Lock sl (source_lock);
- for (AudioSourceList::iterator siter = audio_sources.begin(); siter != audio_sources.end(); ++siter) {
+ for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
/* Don't save information about AudioFileSources that are empty */
if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (siter->second)) != 0) {
- /* destructive file sources are OK if they are empty, because
- we will re-use them every time.
+ /* Don't save sources that are empty, unless they're destructive (which are OK
+ if they are empty, because we will re-use them every time.)
*/
if (!fs->destructive()) {
if (full_state) {
Glib::Mutex::Lock rl (region_lock);
- for (AudioRegionList::const_iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) {
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
/* only store regions not attached to playlists */
return boost::shared_ptr<Route> ((Route*) 0);
}
- if (node.property ("diskstream") != 0 || node.property ("diskstream-id") != 0) {
- boost::shared_ptr<Route> x (new AudioTrack (*this, node));
- return x;
+ bool has_diskstream = (node.property ("diskstream") != 0 || node.property ("diskstream-id") != 0);
+
+ DataType type = DataType::AUDIO;
+ const XMLProperty* prop = node.property("default-type");
+ if (prop)
+ type = DataType(prop->value());
+
+ assert(type != DataType::NIL);
+
+ if (has_diskstream) {
+ if (type == DataType::AUDIO) {
+ boost::shared_ptr<Route> ret (new AudioTrack (*this, node));
+ return ret;
+ } else {
+ boost::shared_ptr<Route> ret (new MidiTrack (*this, node));
+ return ret;
+ }
} else {
- boost::shared_ptr<Route> x (new Route (*this, node));
- return x;
+ boost::shared_ptr<Route> ret (new Route (*this, node));
+ return ret;
}
}
{
XMLNodeList nlist;
XMLNodeConstIterator niter;
- boost::shared_ptr<AudioRegion> region;
+ boost::shared_ptr<Region> region;
nlist = node.children();
return 0;
}
-boost::shared_ptr<AudioRegion>
+boost::shared_ptr<Region>
Session::XMLRegionFactory (const XMLNode& node, bool full)
+{
+ const XMLProperty* type = node.property("type");
+
+ try {
+
+ if ( !type || type->value() == "audio" ) {
+
+ return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
+
+ } else if (type->value() == "midi") {
+
+ return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
+
+ }
+
+ } catch (failed_constructor& err) {
+ return boost::shared_ptr<Region> ();
+ }
+
+ return boost::shared_ptr<Region> ();
+}
+
+boost::shared_ptr<AudioRegion>
+Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
{
const XMLProperty* prop;
boost::shared_ptr<Source> source;
}
}
+boost::shared_ptr<MidiRegion>
+Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
+{
+ const XMLProperty* prop;
+ boost::shared_ptr<Source> source;
+ boost::shared_ptr<MidiSource> ms;
+ MidiRegion::SourceList sources;
+ uint32_t nchans = 1;
+
+ if (node.name() != X_("Region")) {
+ return boost::shared_ptr<MidiRegion>();
+ }
+
+ if ((prop = node.property (X_("channels"))) != 0) {
+ nchans = atoi (prop->value().c_str());
+ }
+
+ // Multiple midi channels? that's just crazy talk
+ assert(nchans == 1);
+
+ if ((prop = node.property (X_("source-0"))) == 0) {
+ if ((prop = node.property ("source")) == 0) {
+ error << _("Session: XMLNode describing a MidiRegion is incomplete (no source)") << endmsg;
+ return boost::shared_ptr<MidiRegion>();
+ }
+ }
+
+ PBD::ID s_id (prop->value());
+
+ if ((source = source_by_id (s_id)) == 0) {
+ error << string_compose(_("Session: XMLNode describing a MidiRegion references an unknown source id =%1"), s_id) << endmsg;
+ return boost::shared_ptr<MidiRegion>();
+ }
+
+ ms = boost::dynamic_pointer_cast<MidiSource>(source);
+ if (!ms) {
+ error << string_compose(_("Session: XMLNode describing a MidiRegion references a non-midi source id =%1"), s_id) << endmsg;
+ return boost::shared_ptr<MidiRegion>();
+ }
+
+ sources.push_back (ms);
+
+ try {
+ boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (sources, node)));
+ return region;
+ }
+
+ catch (failed_constructor& err) {
+ return boost::shared_ptr<MidiRegion>();
+ }
+}
+
XMLNode&
Session::get_sources_as_xml ()
{
XMLNode* node = new XMLNode (X_("Sources"));
- Glib::Mutex::Lock lm (audio_source_lock);
+ Glib::Mutex::Lock lm (source_lock);
- for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ++i) {
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
node->add_child_nocopy (i->second->get_state());
}
snprintf (buf, sizeof(buf), "%s/%s-%" PRIu32 ".wav", dir.c_str(), name.c_str(), n);
}
- if (!g_file_test (buf, G_FILE_TEST_EXISTS)) {
+ if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) {
return buf;
}
}
xml_path = dir;
xml_path += template_name;
- xml_path += _template_suffix;
+ xml_path += template_suffix;
ifstream in(xml_path.c_str());
int
Session::rename_template (string old_name, string new_name)
{
- string old_path = template_dir() + old_name + _template_suffix;
- string new_path = template_dir() + new_name + _template_suffix;
+ string old_path = template_dir() + old_name + template_suffix;
+ string new_path = template_dir() + new_name + template_suffix;
return rename (old_path.c_str(), new_path.c_str());
}
{
string template_path = template_dir();
template_path += name;
- template_path += _template_suffix;
+ template_path += template_suffix;
return remove (template_path.c_str());
}
return 0;
}
-
boost::shared_ptr<Playlist>
Session::XMLPlaylistFactory (const XMLNode& node)
{
static bool
state_file_filter (const string &str, void *arg)
{
- return (str.length() > strlen(Session::statefile_suffix()) &&
- str.find (Session::statefile_suffix()) == (str.length() - strlen (Session::statefile_suffix())));
+ return (str.length() > strlen(statefile_suffix) &&
+ str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
}
struct string_cmp {
static bool
template_filter (const string &str, void *arg)
{
- return (str.length() > strlen(Session::template_suffix()) &&
- str.find (Session::template_suffix()) == (str.length() - strlen (Session::template_suffix())));
+ return (str.length() > strlen(template_suffix) &&
+ str.find (template_suffix) == (str.length() - strlen (template_suffix)));
}
void
this_snapshot_path = _path;
this_snapshot_path += _current_snapshot_name;
- this_snapshot_path += _statefile_suffix;
+ this_snapshot_path += statefile_suffix;
for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
rep.paths.clear ();
rep.space = 0;
- for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ) {
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
- AudioSourceList::iterator tmp;
+ SourceMap::iterator tmp;
tmp = i;
++tmp;
/* add our current source list
*/
- for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ++i) {
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
boost::shared_ptr<AudioFileSource> fs;
if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (i->second)) != 0) {
}
}
+ char tmppath1[PATH_MAX+1];
+ char tmppath2[PATH_MAX+1];
+
for (vector<string*>::iterator x = soundfiles->begin(); x != soundfiles->end(); ++x) {
used = false;
for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
- if (spath == *i) {
+ realpath(spath.c_str(), tmppath1);
+ realpath((*i).c_str(), tmppath2);
+
+ if (strcmp(tmppath1, tmppath2) == 0) {
used = true;
break;
}
} else if (PARAM_IS ("use-video-sync")) {
- if (transport_stopped()) {
- if (Config->get_use_video_sync()) {
- waiting_for_sync_offset = true;
- }
- }
+ waiting_for_sync_offset = Config->get_use_video_sync();
} else if (PARAM_IS ("mmc-control")) {
- poke_midi_thread ();
+ //poke_midi_thread ();
} else if (PARAM_IS ("mmc-device-id")) {
} else if (PARAM_IS ("midi-control")) {
- poke_midi_thread ();
+ //poke_midi_thread ();
} else if (PARAM_IS ("raid-path")) {