2008-06-02 17:41:35 -04:00
|
|
|
/*
|
2009-10-14 12:10:01 -04:00
|
|
|
Copyright (C) 1999-2002 Paul Davis
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
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,
|
|
|
|
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.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
*/
|
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
|
|
|
|
#ifdef WAF_BUILD
|
|
|
|
#include "libardour-config.h"
|
|
|
|
#endif
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
#define __STDC_FORMAT_MACROS 1
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <fstream>
|
|
|
|
#include <string>
|
|
|
|
#include <cerrno>
|
|
|
|
|
|
|
|
#include <sigc++/bind.h>
|
|
|
|
|
|
|
|
#include <cstdio> /* snprintf(3) ... grrr */
|
|
|
|
#include <cmath>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <climits>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_VFS_H
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
#else
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <glibmm.h>
|
|
|
|
#include <glibmm/thread.h>
|
|
|
|
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "midi++/mmc.h"
|
|
|
|
#include "midi++/port.h"
|
|
|
|
|
|
|
|
#include "pbd/error.h"
|
|
|
|
#include "pbd/pathscanner.h"
|
|
|
|
#include "pbd/pthread_utils.h"
|
|
|
|
#include "pbd/search_path.h"
|
|
|
|
#include "pbd/stacktrace.h"
|
|
|
|
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/audio_diskstream.h"
|
|
|
|
#include "ardour/audio_track.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/audioengine.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/audiofilesource.h"
|
|
|
|
#include "ardour/audioplaylist.h"
|
|
|
|
#include "ardour/audioregion.h"
|
|
|
|
#include "ardour/auditioner.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/buffer.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#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"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/midi_diskstream.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/midi_patch_manager.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/midi_playlist.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/midi_region.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/midi_source.h"
|
|
|
|
#include "ardour/midi_track.h"
|
|
|
|
#include "ardour/named_selection.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/playlist_factory.h"
|
|
|
|
#include "ardour/processor.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/region_factory.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/route_group.h"
|
|
|
|
#include "ardour/send.h"
|
|
|
|
#include "ardour/session.h"
|
|
|
|
#include "ardour/session_directory.h"
|
|
|
|
#include "ardour/session_metadata.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"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/source_factory.h"
|
|
|
|
#include "ardour/template_utils.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/tempo.h"
|
2009-02-25 13:26:51 -05:00
|
|
|
#include "ardour/ticker.h"
|
2009-10-13 16:43:28 -04:00
|
|
|
#include "ardour/user_bundle.h"
|
|
|
|
#include "ardour/utils.h"
|
|
|
|
#include "ardour/utils.h"
|
|
|
|
#include "ardour/version.h"
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-02-25 13:39:39 -05:00
|
|
|
#include "control_protocol/control_protocol.h"
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
#include "i18n.h"
|
|
|
|
#include <locale.h>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace ARDOUR;
|
|
|
|
using namespace PBD;
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::first_stage_init (string fullpath, string snapshot_name)
|
|
|
|
{
|
|
|
|
if (fullpath.length() == 0) {
|
|
|
|
destroy ();
|
|
|
|
throw failed_constructor();
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[PATH_MAX+1];
|
|
|
|
if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
|
|
|
|
error << string_compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg;
|
|
|
|
destroy ();
|
|
|
|
throw failed_constructor();
|
|
|
|
}
|
|
|
|
|
|
|
|
_path = string(buf);
|
|
|
|
|
|
|
|
if (_path[_path.length()-1] != '/') {
|
|
|
|
_path += '/';
|
|
|
|
}
|
|
|
|
|
2009-10-13 16:43:28 -04:00
|
|
|
if (Glib::file_test (_path, Glib::FILE_TEST_EXISTS) && ::access (_path.c_str(), W_OK)) {
|
|
|
|
cerr << "Session non-writable based on " << _path << endl;
|
|
|
|
_writable = false;
|
|
|
|
} else {
|
|
|
|
cerr << "Session writable based on " << _path << endl;
|
2009-10-14 12:10:01 -04:00
|
|
|
_writable = true;
|
2009-10-13 16:43:28 -04:00
|
|
|
}
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* these two are just provisional settings. set_state()
|
|
|
|
will likely override them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
_name = _current_snapshot_name = snapshot_name;
|
|
|
|
|
|
|
|
set_history_depth (Config->get_history_depth());
|
|
|
|
|
|
|
|
_current_frame_rate = _engine.frame_rate ();
|
|
|
|
_nominal_frame_rate = _current_frame_rate;
|
|
|
|
_base_frame_rate = _current_frame_rate;
|
|
|
|
|
|
|
|
_tempo_map = new TempoMap (_current_frame_rate);
|
|
|
|
_tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed));
|
|
|
|
|
|
|
|
|
2009-06-09 16:21:19 -04:00
|
|
|
_non_soloed_outs_muted = false;
|
2009-07-01 09:36:50 -04:00
|
|
|
_listen_cnt = 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
g_atomic_int_set (&processing_prohibited, 0);
|
|
|
|
_transport_speed = 0;
|
|
|
|
_last_transport_speed = 0;
|
2009-06-09 20:03:47 -04:00
|
|
|
_target_transport_speed = 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
auto_play_legal = false;
|
|
|
|
transport_sub_state = 0;
|
|
|
|
_transport_frame = 0;
|
|
|
|
end_location = new Location (0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd)));
|
|
|
|
start_location = new Location (0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart)));
|
|
|
|
g_atomic_int_set (&_record_status, Disabled);
|
|
|
|
loop_changing = false;
|
|
|
|
play_loop = false;
|
2008-12-12 09:43:24 -05:00
|
|
|
have_looped = false;
|
2008-06-02 17:41:35 -04:00
|
|
|
_last_roll_location = 0;
|
|
|
|
_last_record_location = 0;
|
|
|
|
pending_locate_frame = 0;
|
|
|
|
pending_locate_roll = false;
|
|
|
|
pending_locate_flush = false;
|
|
|
|
audio_dstream_buffer_size = 0;
|
|
|
|
midi_dstream_buffer_size = 0;
|
|
|
|
state_was_pending = false;
|
|
|
|
set_next_event ();
|
|
|
|
outbound_mtc_smpte_frame = 0;
|
|
|
|
next_quarter_frame_to_send = -1;
|
|
|
|
current_block_size = 0;
|
|
|
|
solo_update_disabled = false;
|
|
|
|
_have_captured = false;
|
|
|
|
_worst_output_latency = 0;
|
|
|
|
_worst_input_latency = 0;
|
|
|
|
_worst_track_latency = 0;
|
|
|
|
_state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
|
|
|
|
|
|
|
|
_slave = 0;
|
|
|
|
session_send_mmc = false;
|
|
|
|
session_send_mtc = false;
|
|
|
|
post_transport_work = PostTransportWork (0);
|
|
|
|
g_atomic_int_set (&butler_should_do_transport_work, 0);
|
|
|
|
g_atomic_int_set (&_playback_load, 100);
|
|
|
|
g_atomic_int_set (&_capture_load, 100);
|
|
|
|
g_atomic_int_set (&_playback_load_min, 100);
|
|
|
|
g_atomic_int_set (&_capture_load_min, 100);
|
|
|
|
_play_range = false;
|
|
|
|
_exporting = false;
|
2008-09-17 04:44:51 -04:00
|
|
|
_exporting_realtime = false;
|
2008-06-02 17:41:35 -04:00
|
|
|
_gain_automation_buffer = 0;
|
|
|
|
_pan_automation_buffer = 0;
|
|
|
|
_npan_buffers = 0;
|
|
|
|
pending_abort = false;
|
|
|
|
destructive_index = 0;
|
|
|
|
first_file_data_format_reset = true;
|
|
|
|
first_file_header_format_reset = true;
|
|
|
|
butler_thread = (pthread_t) 0;
|
|
|
|
//midi_thread = (pthread_t) 0;
|
|
|
|
|
|
|
|
AudioDiskstream::allocate_working_buffers();
|
|
|
|
|
|
|
|
/* default short fade = 15ms */
|
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
Crossfade::set_short_xfade_length ((nframes_t) floor (config.get_short_xfade_seconds() * frame_rate()));
|
|
|
|
SndFileSource::setup_standard_crossfades (*this, frame_rate());
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
last_mmc_step.tv_sec = 0;
|
|
|
|
last_mmc_step.tv_usec = 0;
|
|
|
|
step_speed = 0.0;
|
|
|
|
|
|
|
|
/* click sounds are unset by default, which causes us to internal
|
|
|
|
waveforms for clicks.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
click_length = 0;
|
|
|
|
click_emphasis_length = 0;
|
|
|
|
_clicking = false;
|
|
|
|
|
|
|
|
process_function = &Session::process_with_events;
|
|
|
|
|
2009-05-15 21:53:43 -04:00
|
|
|
if (config.get_use_video_sync()) {
|
2008-06-02 17:41:35 -04:00
|
|
|
waiting_for_sync_offset = true;
|
|
|
|
} else {
|
|
|
|
waiting_for_sync_offset = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
last_smpte_when = 0;
|
|
|
|
_smpte_offset = 0;
|
|
|
|
_smpte_offset_negative = true;
|
|
|
|
last_smpte_valid = false;
|
|
|
|
|
|
|
|
sync_time_vars ();
|
|
|
|
|
|
|
|
last_rr_session_dir = session_dirs.begin();
|
|
|
|
refresh_disk_space ();
|
|
|
|
|
|
|
|
// set_default_fade (0.2, 5.0); /* steepness, millisecs */
|
|
|
|
|
|
|
|
/* slave stuff */
|
|
|
|
|
2008-12-08 13:16:12 -05:00
|
|
|
average_slave_delta = 1800; // !!! why 1800 ????
|
2008-06-02 17:41:35 -04:00
|
|
|
have_first_delta_accumulator = false;
|
|
|
|
delta_accumulator_cnt = 0;
|
|
|
|
slave_state = Stopped;
|
|
|
|
|
|
|
|
_engine.GraphReordered.connect (mem_fun (*this, &Session::graph_reordered));
|
|
|
|
|
|
|
|
/* These are all static "per-class" signals */
|
|
|
|
|
|
|
|
RegionFactory::CheckNewRegion.connect (mem_fun (*this, &Session::add_region));
|
|
|
|
SourceFactory::SourceCreated.connect (mem_fun (*this, &Session::add_source));
|
|
|
|
PlaylistFactory::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist));
|
|
|
|
Processor::ProcessorCreated.connect (mem_fun (*this, &Session::add_processor));
|
|
|
|
NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection));
|
|
|
|
AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list));
|
|
|
|
|
|
|
|
Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable));
|
|
|
|
|
|
|
|
IO::PortCountChanged.connect (mem_fun (*this, &Session::ensure_buffers));
|
|
|
|
|
|
|
|
/* stop IO objects from doing stuff until we're ready for them */
|
|
|
|
|
2009-06-09 16:21:19 -04:00
|
|
|
Delivery::disable_panners ();
|
2008-06-02 17:41:35 -04:00
|
|
|
IO::disable_connecting ();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::second_stage_init (bool new_session)
|
|
|
|
{
|
|
|
|
AudioFileSource::set_peak_dir (_session_dir->peak_path().to_string());
|
|
|
|
|
|
|
|
if (!new_session) {
|
|
|
|
if (load_state (_current_snapshot_name)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
remove_empty_sounds ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start_butler_thread()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
if (state_tree) {
|
|
|
|
if (set_state (*state_tree->root())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setup_raid_path(_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we can't save till after ::when_engine_running() is called,
|
|
|
|
because otherwise we save state with no connections made.
|
|
|
|
therefore, we reset _state_of_the_state because ::set_state()
|
|
|
|
will have cleared it.
|
|
|
|
|
|
|
|
we also have to include Loading so that any events that get
|
|
|
|
generated between here and the end of ::when_engine_running()
|
|
|
|
will be processed directly rather than queued.
|
|
|
|
*/
|
|
|
|
|
|
|
|
_state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
|
|
|
|
|
|
|
|
|
|
|
|
_locations.changed.connect (mem_fun (this, &Session::locations_changed));
|
|
|
|
_locations.added.connect (mem_fun (this, &Session::locations_added));
|
|
|
|
setup_click_sounds (0);
|
|
|
|
setup_midi_control ();
|
|
|
|
|
|
|
|
/* Pay attention ... */
|
|
|
|
|
|
|
|
_engine.Halted.connect (mem_fun (*this, &Session::engine_halted));
|
|
|
|
_engine.Xrun.connect (mem_fun (*this, &Session::xrun_recovery));
|
|
|
|
|
|
|
|
try {
|
|
|
|
when_engine_running();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle this one in a different way than all others, so that its clear what happened */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
catch (AudioEngine::PortRegistrationFailure& err) {
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 02:30:50 -04:00
|
|
|
error << err.what() << endmsg;
|
2008-06-02 17:41:35 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (...) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
BootMessage (_("Reset Remote Controls"));
|
|
|
|
|
|
|
|
send_full_time_code (0);
|
|
|
|
_engine.transport_locate (0);
|
|
|
|
deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
|
|
|
|
deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-12-04 04:09:50 -05:00
|
|
|
MidiClockTicker::instance().set_session(*this);
|
2008-12-09 03:35:02 -05:00
|
|
|
MIDI::Name::MidiPatchManager::instance().set_session(*this);
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2008-12-12 09:43:24 -05:00
|
|
|
/* initial program change will be delivered later; see ::config_changed() */
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
BootMessage (_("Reset Control Protocols"));
|
|
|
|
|
|
|
|
ControlProtocolManager::instance().set_session (*this);
|
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
config.set_end_marker_is_free (new_session);
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
_state_of_the_state = Clean;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
DirtyChanged (); /* EMIT SIGNAL */
|
|
|
|
|
|
|
|
if (state_was_pending) {
|
|
|
|
save_state (_current_snapshot_name);
|
|
|
|
remove_pending_capture_state ();
|
|
|
|
state_was_pending = false;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
BootMessage (_("Session loading complete"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
string
|
|
|
|
Session::raid_path () const
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
return raid_search_path.to_string ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::setup_raid_path (string path)
|
|
|
|
{
|
|
|
|
if (path.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
space_and_path sp;
|
|
|
|
string fspath;
|
|
|
|
|
|
|
|
session_dirs.clear ();
|
|
|
|
|
|
|
|
SearchPath search_path(path);
|
|
|
|
SearchPath sound_search_path;
|
|
|
|
SearchPath midi_search_path;
|
|
|
|
|
2009-02-16 21:11:49 -05:00
|
|
|
for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
|
2008-06-02 17:41:35 -04:00
|
|
|
sp.path = (*i).to_string ();
|
|
|
|
sp.blocks = 0; // not needed
|
|
|
|
session_dirs.push_back (sp);
|
|
|
|
|
|
|
|
SessionDirectory sdir(sp.path);
|
|
|
|
|
|
|
|
sound_search_path += sdir.sound_path ();
|
|
|
|
midi_search_path += sdir.midi_path ();
|
|
|
|
}
|
|
|
|
|
2009-02-16 21:11:49 -05:00
|
|
|
// set the search path for each data type
|
|
|
|
FileSource::set_search_path (DataType::AUDIO, sound_search_path.to_string ());
|
|
|
|
SMFSource::set_search_path (DataType::MIDI, midi_search_path.to_string ());
|
2008-09-10 11:03:30 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
// reset the round-robin soundfile path thingie
|
|
|
|
last_rr_session_dir = session_dirs.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::ensure_subdirs ()
|
|
|
|
{
|
|
|
|
string dir;
|
|
|
|
|
|
|
|
dir = session_directory().peak_path().to_string();
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
dir = session_directory().midi_path().to_string();
|
|
|
|
|
|
|
|
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_sound_path().to_string();
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
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 = analysis_dir ();
|
|
|
|
|
|
|
|
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
|
|
|
|
error << string_compose(_("Session: cannot create session analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::create (bool& new_session, const string& mix_template, nframes_t initial_length)
|
|
|
|
{
|
|
|
|
|
|
|
|
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 (ensure_subdirs ()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check new_session so we don't overwrite an existing one */
|
|
|
|
|
|
|
|
if (!mix_template.empty()) {
|
|
|
|
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 {
|
2009-10-14 12:10:01 -04:00
|
|
|
error << string_compose (_("Could not open %1 for writing mix template"), out_path)
|
2008-06-02 17:41:35 -04:00
|
|
|
<< endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2009-10-14 12:10:01 -04:00
|
|
|
error << string_compose (_("Could not open mix template %1 for reading"), in_path)
|
2008-06-02 17:41:35 -04:00
|
|
|
<< endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-09-17 04:44:51 -04:00
|
|
|
/* Instantiate metadata */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-09-17 04:44:51 -04:00
|
|
|
_metadata = new SessionMetadata ();
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
/* set initial start + end point */
|
|
|
|
|
|
|
|
start_location->set_end (0);
|
|
|
|
_locations.add (start_location);
|
|
|
|
|
|
|
|
end_location->set_end (initial_length);
|
|
|
|
_locations.add (end_location);
|
|
|
|
|
|
|
|
_state_of_the_state = Clean;
|
|
|
|
|
|
|
|
save_state ("");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_diskstreams (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList clist;
|
|
|
|
XMLNodeConstIterator citer;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
clist = node.children();
|
|
|
|
|
|
|
|
for (citer = clist.begin(); citer != clist.end(); ++citer) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
/* 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;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
catch (failed_constructor& err) {
|
|
|
|
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 ()
|
|
|
|
{
|
|
|
|
sys::path pending_state_file_path(_session_dir->root_path());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
pending_state_file_path /= legalize_for_path (_current_snapshot_name) + pending_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
sys::remove (pending_state_file_path);
|
|
|
|
}
|
|
|
|
catch(sys::filesystem_error& ex)
|
|
|
|
{
|
|
|
|
error << string_compose(_("Could remove pending capture state at path \"%1\" (%2)"),
|
|
|
|
pending_state_file_path.to_string(), ex.what()) << endmsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 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;
|
|
|
|
}
|
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
const string old_xml_filename = legalize_for_path (old_name) + statefile_suffix;
|
|
|
|
const string new_xml_filename = legalize_for_path (new_name) + statefile_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
sys::rename (old_xml_path, new_xml_path);
|
|
|
|
}
|
|
|
|
catch (const sys::filesystem_error& err)
|
|
|
|
{
|
|
|
|
error << string_compose(_("could not rename snapshot %1 to %2 (%3)"),
|
|
|
|
old_name, new_name, err.what()) << 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
sys::path xml_path(_session_dir->root_path());
|
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!create_backup_file (xml_path)) {
|
|
|
|
// don't remove it if a backup can't be made
|
|
|
|
// create_backup_file will log the error.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// and delete it
|
|
|
|
sys::remove (xml_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::save_state (string snapshot_name, bool pending)
|
|
|
|
{
|
|
|
|
XMLTree tree;
|
|
|
|
sys::path xml_path(_session_dir->root_path());
|
|
|
|
|
2009-10-13 16:43:28 -04:00
|
|
|
if (!_writable || (_state_of_the_state & CannotSave)) {
|
2008-06-02 17:41:35 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_engine.connected ()) {
|
|
|
|
error << _("Ardour's audio engine is not connected and state saving would lose all I/O connections. Session not saved")
|
|
|
|
<< endmsg;
|
|
|
|
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();
|
|
|
|
|
|
|
|
tree.set_root (&get_state());
|
|
|
|
|
|
|
|
if (snapshot_name.empty()) {
|
|
|
|
snapshot_name = _current_snapshot_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pending) {
|
|
|
|
|
|
|
|
/* proper save: use statefile_suffix (.ardour in English) */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
/* make a backup copy of the old file */
|
|
|
|
|
|
|
|
if (sys::exists(xml_path) && !create_backup_file (xml_path)) {
|
|
|
|
// create_backup_file will log the error
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* pending save: use pending_suffix (.pending in English) */
|
2009-07-12 20:26:28 -04:00
|
|
|
xml_path /= legalize_for_path (snapshot_name) + pending_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
sys::path tmp_path(_session_dir->root_path());
|
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
tmp_path /= legalize_for_path (snapshot_name) + temp_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
// cerr << "actually writing state to " << xml_path.to_string() << endl;
|
|
|
|
|
|
|
|
if (!tree.write (tmp_path.to_string())) {
|
|
|
|
error << string_compose (_("state could not be saved to %1"), tmp_path.to_string()) << endmsg;
|
|
|
|
sys::remove (tmp_path);
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
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);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pending) {
|
|
|
|
|
|
|
|
save_history (snapshot_name);
|
|
|
|
|
|
|
|
bool was_dirty = dirty();
|
|
|
|
|
|
|
|
_state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (was_dirty) {
|
|
|
|
DirtyChanged (); /* EMIT SIGNAL */
|
|
|
|
}
|
|
|
|
|
|
|
|
StateSaved (snapshot_name); /* EMIT SIGNAL */
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::restore_state (string snapshot_name)
|
|
|
|
{
|
|
|
|
if (load_state (snapshot_name) == 0) {
|
|
|
|
set_state (*state_tree->root());
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_state (string snapshot_name)
|
|
|
|
{
|
2008-12-18 14:31:00 -05:00
|
|
|
delete state_tree;
|
|
|
|
state_tree = 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
state_was_pending = false;
|
|
|
|
|
|
|
|
/* check for leftover pending state from a crashed capture attempt */
|
|
|
|
|
|
|
|
sys::path xmlpath(_session_dir->root_path());
|
2009-07-12 20:26:28 -04:00
|
|
|
xmlpath /= legalize_for_path (snapshot_name) + pending_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (sys::exists (xmlpath)) {
|
|
|
|
|
|
|
|
/* there is pending state from a crashed capture attempt */
|
|
|
|
|
|
|
|
if (AskAboutPendingState()) {
|
|
|
|
state_was_pending = true;
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!state_was_pending) {
|
|
|
|
xmlpath = _session_dir->root_path();
|
2009-07-12 20:26:28 -04:00
|
|
|
xmlpath /= legalize_for_path (snapshot_name) + statefile_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (!sys::exists (xmlpath)) {
|
|
|
|
error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath.to_string()) << endmsg;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
state_tree = new XMLTree;
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
2009-10-13 16:43:28 -04:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (!state_tree->read (xmlpath.to_string())) {
|
|
|
|
error << string_compose(_("Could not understand ardour file %1"), xmlpath.to_string()) << endmsg;
|
|
|
|
delete state_tree;
|
|
|
|
state_tree = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNode& root (*state_tree->root());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (root.name() != X_("Session")) {
|
|
|
|
error << string_compose (_("Session file %1 is not an Ardour session"), xmlpath.to_string()) << endmsg;
|
|
|
|
delete state_tree;
|
|
|
|
state_tree = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const XMLProperty* prop;
|
2008-10-05 19:14:48 -04:00
|
|
|
bool is_old = false; // session is _very_ old (pre-2.0)
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if ((prop = root.property ("version")) == 0) {
|
|
|
|
/* no version implies very old version of Ardour */
|
|
|
|
is_old = true;
|
|
|
|
} else {
|
|
|
|
int major_version;
|
|
|
|
major_version = atoi (prop->value().c_str()); // grab just the first number before the period
|
|
|
|
if (major_version < 2) {
|
|
|
|
is_old = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_old) {
|
|
|
|
|
|
|
|
sys::path backup_path(_session_dir->root_path());
|
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
backup_path /= legalize_for_path (snapshot_name) + "-1" + statefile_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
// 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 Ardour versions before 2.0 from now on"),
|
2009-10-14 12:10:01 -04:00
|
|
|
xmlpath.to_string(), backup_path.to_string())
|
2008-06-02 17:41:35 -04:00
|
|
|
<< 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_options (const XMLNode& node)
|
|
|
|
{
|
|
|
|
LocaleGuard lg (X_("POSIX"));
|
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
config.set_variables (node);
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
/* now reset MIDI ports because the session can have its own
|
2008-10-09 10:15:45 -04:00
|
|
|
MIDI configuration.
|
|
|
|
*/
|
|
|
|
|
|
|
|
setup_midi ();
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNode&
|
|
|
|
Session::get_state()
|
|
|
|
{
|
|
|
|
return state(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNode&
|
|
|
|
Session::get_template()
|
|
|
|
{
|
|
|
|
/* if we don't disable rec-enable, diskstreams
|
|
|
|
will believe they need to store their capture
|
2009-10-14 12:10:01 -04:00
|
|
|
sources in their state node.
|
2008-06-02 17:41:35 -04:00
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
disable_record (false);
|
|
|
|
|
|
|
|
return state(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNode&
|
|
|
|
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));
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* store configuration settings */
|
|
|
|
|
|
|
|
if (full_state) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
node->add_property ("name", _name);
|
|
|
|
snprintf (buf, sizeof (buf), "%" PRId32, _nominal_frame_rate);
|
|
|
|
node->add_property ("sample-rate", buf);
|
|
|
|
|
|
|
|
if (session_dirs.size() > 1) {
|
|
|
|
|
|
|
|
string p;
|
|
|
|
|
|
|
|
vector<space_and_path>::iterator i = session_dirs.begin();
|
|
|
|
vector<space_and_path>::iterator next;
|
|
|
|
|
|
|
|
++i; /* skip the first one */
|
|
|
|
next = i;
|
|
|
|
++next;
|
|
|
|
|
|
|
|
while (i != session_dirs.end()) {
|
|
|
|
|
|
|
|
p += (*i).path;
|
|
|
|
|
|
|
|
if (next != session_dirs.end()) {
|
|
|
|
p += ':';
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++next;
|
|
|
|
++i;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
child = node->add_child ("Path");
|
|
|
|
child->add_content (p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save the ID counter */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter());
|
|
|
|
node->add_property ("id-counter", buf);
|
|
|
|
|
|
|
|
/* various options */
|
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
node->add_child_nocopy (config.get_variables ());
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2008-09-17 04:44:51 -04:00
|
|
|
node->add_child_nocopy (_metadata->get_state());
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
child = node->add_child ("Sources");
|
|
|
|
|
|
|
|
if (full_state) {
|
|
|
|
Glib::Mutex::Lock sl (source_lock);
|
|
|
|
|
|
|
|
for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-02-16 21:11:49 -05:00
|
|
|
/* Don't save information about non-destructive file sources that are empty */
|
|
|
|
/* FIXME: MIDI breaks if this is made FileSource like it should be... */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
boost::shared_ptr<AudioFileSource> fs;
|
|
|
|
if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (siter->second)) != 0) {
|
|
|
|
if (!fs->destructive()) {
|
2009-02-19 19:30:42 -05:00
|
|
|
if (fs->length(fs->timeline_position()) == 0) {
|
2008-06-02 17:41:35 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
child->add_child_nocopy (siter->second->get_state());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
child = node->add_child ("Regions");
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
if (full_state) {
|
2008-06-02 17:41:35 -04:00
|
|
|
Glib::Mutex::Lock rl (region_lock);
|
|
|
|
|
|
|
|
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* only store regions not attached to playlists */
|
|
|
|
|
|
|
|
if (i->second->playlist() == 0) {
|
|
|
|
child->add_child_nocopy (i->second->state (true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
child = node->add_child ("DiskStreams");
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
{
|
2008-06-02 17:41:35 -04:00
|
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
|
|
|
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
|
|
|
if (!(*i)->hidden()) {
|
|
|
|
child->add_child_nocopy ((*i)->get_state());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (full_state) {
|
|
|
|
node->add_child_nocopy (_locations.get_state());
|
|
|
|
} else {
|
|
|
|
// for a template, just create a new Locations, populate it
|
|
|
|
// with the default start and end, and get the state for that.
|
|
|
|
Locations loc;
|
|
|
|
Location* start = new Location(0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart)));
|
|
|
|
Location* end = new Location(0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd)));
|
|
|
|
start->set_end(0);
|
|
|
|
loc.add (start);
|
|
|
|
end->set_end(compute_initial_length());
|
|
|
|
loc.add (end);
|
|
|
|
node->add_child_nocopy (loc.get_state());
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
child = node->add_child ("Bundles");
|
|
|
|
{
|
2009-01-25 01:47:11 -05:00
|
|
|
boost::shared_ptr<BundleList> bundles = _bundles.reader ();
|
|
|
|
for (BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
|
2008-06-02 17:41:35 -04:00
|
|
|
boost::shared_ptr<UserBundle> b = boost::dynamic_pointer_cast<UserBundle> (*i);
|
|
|
|
if (b) {
|
|
|
|
child->add_child_nocopy (b->get_state());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
child = node->add_child ("Routes");
|
|
|
|
{
|
|
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
RoutePublicOrderSorter cmp;
|
|
|
|
RouteList public_order (*r);
|
|
|
|
public_order.sort (cmp);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) {
|
|
|
|
if (!(*i)->is_hidden()) {
|
|
|
|
if (full_state) {
|
|
|
|
child->add_child_nocopy ((*i)->get_state());
|
|
|
|
} else {
|
|
|
|
child->add_child_nocopy ((*i)->get_template());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-21 15:59:56 -04:00
|
|
|
child = node->add_child ("RouteGroups");
|
|
|
|
for (list<RouteGroup *>::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) {
|
2008-06-02 17:41:35 -04:00
|
|
|
child->add_child_nocopy ((*i)->get_state());
|
|
|
|
}
|
|
|
|
|
|
|
|
child = node->add_child ("Playlists");
|
|
|
|
for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
|
|
if (!(*i)->hidden()) {
|
|
|
|
if (!(*i)->empty()) {
|
|
|
|
if (full_state) {
|
|
|
|
child->add_child_nocopy ((*i)->get_state());
|
|
|
|
} else {
|
|
|
|
child->add_child_nocopy ((*i)->get_template());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
child = node->add_child ("UnusedPlaylists");
|
|
|
|
for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
|
|
if (!(*i)->hidden()) {
|
|
|
|
if (!(*i)->empty()) {
|
|
|
|
if (full_state) {
|
|
|
|
child->add_child_nocopy ((*i)->get_state());
|
|
|
|
} else {
|
|
|
|
child->add_child_nocopy ((*i)->get_template());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (_click_io) {
|
|
|
|
child = node->add_child ("Click");
|
|
|
|
child->add_child_nocopy (_click_io->state (full_state));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (full_state) {
|
|
|
|
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());
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node->add_child_nocopy (_tempo_map->get_state());
|
|
|
|
|
|
|
|
node->add_child_nocopy (get_control_protocol_state());
|
|
|
|
|
|
|
|
if (_extra_xml) {
|
|
|
|
node->add_child_copy (*_extra_xml);
|
|
|
|
}
|
|
|
|
|
|
|
|
return *node;
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNode&
|
|
|
|
Session::get_control_protocol_state ()
|
|
|
|
{
|
|
|
|
ControlProtocolManager& cpm (ControlProtocolManager::instance());
|
|
|
|
return cpm.get_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::set_state (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNode* child;
|
|
|
|
const XMLProperty* prop;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
_state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (node.name() != X_("Session")){
|
|
|
|
fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((prop = node.property ("name")) != 0) {
|
|
|
|
_name = prop->value ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((prop = node.property (X_("sample-rate"))) != 0) {
|
|
|
|
|
|
|
|
_nominal_frame_rate = atoi (prop->value());
|
|
|
|
|
|
|
|
if (_nominal_frame_rate != _current_frame_rate) {
|
|
|
|
if (AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setup_raid_path(_session_dir->root_path().to_string());
|
|
|
|
|
|
|
|
if ((prop = node.property (X_("id-counter"))) != 0) {
|
|
|
|
uint64_t x;
|
|
|
|
sscanf (prop->value().c_str(), "%" PRIu64, &x);
|
|
|
|
ID::init_counter (x);
|
|
|
|
} else {
|
|
|
|
/* old sessions used a timebased counter, so fake
|
|
|
|
the startup ID counter based on a standard
|
|
|
|
timestamp.
|
|
|
|
*/
|
|
|
|
time_t now;
|
|
|
|
time (&now);
|
|
|
|
ID::init_counter (now);
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
IO::disable_connecting ();
|
|
|
|
|
|
|
|
/* Object loading order:
|
|
|
|
|
|
|
|
Path
|
2009-02-14 22:21:30 -05:00
|
|
|
Extra
|
2008-06-02 17:41:35 -04:00
|
|
|
Options/Config
|
2008-10-09 10:15:45 -04:00
|
|
|
MIDI Control // relies on data from Options/Config
|
2008-09-17 04:44:51 -04:00
|
|
|
Metadata
|
2008-06-02 17:41:35 -04:00
|
|
|
Locations
|
|
|
|
Sources
|
|
|
|
AudioRegions
|
|
|
|
AudioDiskstreams
|
|
|
|
Connections
|
|
|
|
Routes
|
2009-06-21 15:59:56 -04:00
|
|
|
RouteGroups
|
2008-06-02 17:41:35 -04:00
|
|
|
MixGroups
|
|
|
|
Click
|
|
|
|
ControlProtocols
|
|
|
|
*/
|
|
|
|
|
2009-02-14 22:21:30 -05:00
|
|
|
if ((child = find_named_node (node, "Extra")) != 0) {
|
2008-06-02 17:41:35 -04:00
|
|
|
_extra_xml = new XMLNode (*child);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
|
|
|
|
load_options (*child);
|
|
|
|
} else if ((child = find_named_node (node, "Config")) != 0) { /* new style */
|
|
|
|
load_options (*child);
|
|
|
|
} else {
|
|
|
|
error << _("Session: XML state has no options section") << endmsg;
|
|
|
|
}
|
|
|
|
|
2008-10-09 10:15:45 -04:00
|
|
|
if (use_config_midi_ports ()) {
|
|
|
|
}
|
|
|
|
|
2008-09-17 04:44:51 -04:00
|
|
|
if ((child = find_named_node (node, "Metadata")) == 0) {
|
|
|
|
warning << _("Session: XML state has no metadata section (2.0 session?)") << endmsg;
|
|
|
|
} else if (_metadata->set_state (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((child = find_named_node (node, "Locations")) == 0) {
|
|
|
|
error << _("Session: XML state has no locations section") << endmsg;
|
|
|
|
goto out;
|
|
|
|
} else if (_locations.set_state (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
Location* location;
|
|
|
|
|
|
|
|
if ((location = _locations.auto_loop_location()) != 0) {
|
|
|
|
set_auto_loop_location (location);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((location = _locations.auto_punch_location()) != 0) {
|
|
|
|
set_auto_punch_location (location);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((location = _locations.end_location()) == 0) {
|
|
|
|
_locations.add (end_location);
|
|
|
|
} else {
|
|
|
|
delete end_location;
|
|
|
|
end_location = location;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((location = _locations.start_location()) == 0) {
|
|
|
|
_locations.add (start_location);
|
|
|
|
} else {
|
|
|
|
delete start_location;
|
|
|
|
start_location = location;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioFileSource::set_header_position_offset (start_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, "Regions")) == 0) {
|
|
|
|
error << _("Session: XML state has no Regions section") << endmsg;
|
|
|
|
goto out;
|
|
|
|
} else if (load_regions (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = find_named_node (node, "Playlists")) == 0) {
|
|
|
|
error << _("Session: XML state has no playlists section") << endmsg;
|
|
|
|
goto out;
|
|
|
|
} else if (load_playlists (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = find_named_node (node, "UnusedPlaylists")) == 0) {
|
|
|
|
// this is OK
|
|
|
|
} else if (load_unused_playlists (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((child = find_named_node (node, "NamedSelections")) != 0) {
|
|
|
|
if (load_named_selections (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = find_named_node (node, "DiskStreams")) == 0) {
|
|
|
|
error << _("Session: XML state has no diskstreams section") << endmsg;
|
|
|
|
goto out;
|
|
|
|
} else if (load_diskstreams (*child)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = find_named_node (node, "Bundles")) == 0) {
|
|
|
|
warning << _("Session: XML state has no bundles section (2.0 session?)") << endmsg;
|
|
|
|
//goto out;
|
|
|
|
} else {
|
|
|
|
/* We can't load Bundles yet as they need to be able
|
|
|
|
to convert from port names to Port objects, which can't happen until
|
|
|
|
later */
|
|
|
|
_bundle_xml_node = new XMLNode (*child);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-21 15:59:56 -04:00
|
|
|
if ((child = find_named_node (node, "RouteGroups")) == 0) {
|
|
|
|
error << _("Session: XML state has no route groups section") << endmsg;
|
2008-06-02 17:41:35 -04:00
|
|
|
goto out;
|
2009-06-21 15:59:56 -04:00
|
|
|
} else if (load_route_groups (*child)) {
|
2008-06-02 17:41:35 -04:00
|
|
|
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)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = find_named_node (node, "Routes")) == 0) {
|
|
|
|
error << _("Session: XML state has no routes section") << endmsg;
|
|
|
|
goto out;
|
|
|
|
} else if (load_routes (*child)) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = find_named_node (node, "ControlProtocols")) != 0) {
|
|
|
|
ControlProtocolManager::instance().set_protocol_states (*child);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* here beginneth the second phase ... */
|
|
|
|
|
|
|
|
StateReady (); /* EMIT SIGNAL */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_routes (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
RouteList new_routes;
|
|
|
|
|
|
|
|
nlist = node.children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
|
|
|
|
boost::shared_ptr<Route> route (XMLRouteFactory (**niter));
|
|
|
|
|
|
|
|
if (route == 0) {
|
2009-01-03 23:43:12 -05:00
|
|
|
error << _("Session: cannot create Route from XML description.") << endmsg;
|
2008-06-02 17:41:35 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
BootMessage (string_compose (_("Loaded track/bus %1"), route->name()));
|
|
|
|
|
|
|
|
new_routes.push_back (route);
|
|
|
|
}
|
|
|
|
|
|
|
|
add_routes (new_routes, false);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<Route>
|
|
|
|
Session::XMLRouteFactory (const XMLNode& node)
|
|
|
|
{
|
|
|
|
if (node.name() != "Route") {
|
|
|
|
return boost::shared_ptr<Route> ((Route*) 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_diskstream = (node.property ("diskstream") != 0 || node.property ("diskstream-id") != 0);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
DataType type = DataType::AUDIO;
|
|
|
|
const XMLProperty* prop = node.property("default-type");
|
2009-03-05 12:27:05 -05:00
|
|
|
|
|
|
|
if (prop) {
|
2008-06-02 17:41:35 -04:00
|
|
|
type = DataType(prop->value());
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
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> ret (new Route (*this, node));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_regions (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
boost::shared_ptr<Region> region;
|
|
|
|
|
|
|
|
nlist = node.children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
|
|
|
|
}
|
|
|
|
|
|
|
|
error << endmsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<Region>
|
|
|
|
Session::XMLRegionFactory (const XMLNode& node, bool full)
|
|
|
|
{
|
|
|
|
const XMLProperty* type = node.property("type");
|
|
|
|
|
|
|
|
try {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ( !type || type->value() == "audio" ) {
|
|
|
|
|
|
|
|
return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
} else if (type->value() == "midi") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
|
|
|
|
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
} catch (failed_constructor& err) {
|
|
|
|
return boost::shared_ptr<Region> ();
|
|
|
|
}
|
|
|
|
|
|
|
|
return boost::shared_ptr<Region> ();
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<AudioRegion>
|
2009-07-21 11:55:17 -04:00
|
|
|
Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
const XMLProperty* prop;
|
|
|
|
boost::shared_ptr<Source> source;
|
|
|
|
boost::shared_ptr<AudioSource> as;
|
|
|
|
SourceList sources;
|
|
|
|
SourceList master_sources;
|
|
|
|
uint32_t nchans = 1;
|
|
|
|
char buf[128];
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (node.name() != X_("Region")) {
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((prop = node.property (X_("channels"))) != 0) {
|
|
|
|
nchans = atoi (prop->value().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((prop = node.property ("name")) == 0) {
|
|
|
|
cerr << "no name for this region\n";
|
|
|
|
abort ();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((prop = node.property (X_("source-0"))) == 0) {
|
|
|
|
if ((prop = node.property ("source")) == 0) {
|
|
|
|
error << _("Session: XMLNode describing a AudioRegion is incomplete (no source)") << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PBD::ID s_id (prop->value());
|
|
|
|
|
|
|
|
if ((source = source_by_id (s_id)) == 0) {
|
|
|
|
error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
as = boost::dynamic_pointer_cast<AudioSource>(source);
|
|
|
|
if (!as) {
|
|
|
|
error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), s_id) << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
|
|
|
|
|
|
|
sources.push_back (as);
|
|
|
|
|
|
|
|
/* pickup other channels */
|
|
|
|
|
|
|
|
for (uint32_t n=1; n < nchans; ++n) {
|
|
|
|
snprintf (buf, sizeof(buf), X_("source-%d"), n);
|
|
|
|
if ((prop = node.property (buf)) != 0) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
PBD::ID id2 (prop->value());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((source = source_by_id (id2)) == 0) {
|
|
|
|
error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
as = boost::dynamic_pointer_cast<AudioSource>(source);
|
|
|
|
if (!as) {
|
|
|
|
error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
|
|
|
sources.push_back (as);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-14 12:54:40 -04:00
|
|
|
for (uint32_t n = 0; n < nchans; ++n) {
|
2008-06-02 17:41:35 -04:00
|
|
|
snprintf (buf, sizeof(buf), X_("master-source-%d"), n);
|
|
|
|
if ((prop = node.property (buf)) != 0) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
PBD::ID id2 (prop->value());
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((source = source_by_id (id2)) == 0) {
|
|
|
|
error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
as = boost::dynamic_pointer_cast<AudioSource>(source);
|
|
|
|
if (!as) {
|
|
|
|
error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
|
|
|
master_sources.push_back (as);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
boost::shared_ptr<AudioRegion> region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, node)));
|
|
|
|
|
|
|
|
/* a final detail: this is the one and only place that we know how long missing files are */
|
|
|
|
|
|
|
|
if (region->whole_file()) {
|
|
|
|
for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
|
|
|
|
boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
|
|
|
|
if (sfp) {
|
|
|
|
sfp->set_length (region->length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!master_sources.empty()) {
|
2009-06-14 12:54:40 -04:00
|
|
|
if (master_sources.size() != nchans) {
|
2008-06-02 17:41:35 -04:00
|
|
|
error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg;
|
|
|
|
} else {
|
|
|
|
region->set_master_sources (master_sources);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return region;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
catch (failed_constructor& err) {
|
|
|
|
return boost::shared_ptr<AudioRegion>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<MidiRegion>
|
2009-07-21 11:55:17 -04:00
|
|
|
Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
const XMLProperty* prop;
|
|
|
|
boost::shared_ptr<Source> source;
|
|
|
|
boost::shared_ptr<MidiSource> ms;
|
|
|
|
SourceList sources;
|
|
|
|
uint32_t nchans = 1;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (node.name() != X_("Region")) {
|
|
|
|
return boost::shared_ptr<MidiRegion>();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((prop = node.property (X_("channels"))) != 0) {
|
|
|
|
nchans = atoi (prop->value().c_str());
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((prop = node.property ("name")) == 0) {
|
|
|
|
cerr << "no name for this region\n";
|
|
|
|
abort ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)));
|
|
|
|
/* a final detail: this is the one and only place that we know how long missing files are */
|
|
|
|
|
|
|
|
if (region->whole_file()) {
|
|
|
|
for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
|
|
|
|
boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
|
|
|
|
if (sfp) {
|
|
|
|
sfp->set_length (region->length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (source_lock);
|
|
|
|
|
|
|
|
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
|
|
|
|
node->add_child_nocopy (i->second->get_state());
|
|
|
|
}
|
|
|
|
|
|
|
|
return *node;
|
|
|
|
}
|
|
|
|
|
|
|
|
string
|
|
|
|
Session::path_from_region_name (DataType type, string name, string identifier)
|
|
|
|
{
|
|
|
|
char buf[PATH_MAX+1];
|
|
|
|
uint32_t n;
|
|
|
|
SessionDirectory sdir(get_best_session_directory_for_new_source());
|
|
|
|
sys::path source_dir = ((type == DataType::AUDIO)
|
|
|
|
? sdir.sound_path() : sdir.midi_path());
|
|
|
|
|
|
|
|
string ext = ((type == DataType::AUDIO) ? ".wav" : ".mid");
|
|
|
|
|
|
|
|
for (n = 0; n < 999999; ++n) {
|
|
|
|
if (identifier.length()) {
|
2009-10-14 12:10:01 -04:00
|
|
|
snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
|
2008-06-02 17:41:35 -04:00
|
|
|
identifier.c_str(), n, ext.c_str());
|
|
|
|
} else {
|
|
|
|
snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
|
|
|
|
n, ext.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
sys::path source_path = source_dir / buf;
|
|
|
|
|
|
|
|
if (!sys::exists (source_path)) {
|
|
|
|
return source_path.to_string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
|
|
|
|
name, identifier)
|
|
|
|
<< endmsg;
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_sources (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
boost::shared_ptr<Source> source;
|
|
|
|
|
|
|
|
nlist = node.children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
try {
|
|
|
|
if ((source = XMLSourceFactory (**niter)) == 0) {
|
|
|
|
error << _("Session: cannot create Source from XML description.") << endmsg;
|
|
|
|
}
|
2009-02-16 21:11:49 -05:00
|
|
|
} catch (MissingSource& err) {
|
2008-06-02 17:41:35 -04:00
|
|
|
warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
|
|
|
|
source = SourceFactory::createSilent (*this, **niter, max_frames, _current_frame_rate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<Source>
|
|
|
|
Session::XMLSourceFactory (const XMLNode& node)
|
|
|
|
{
|
|
|
|
if (node.name() != "Source") {
|
|
|
|
return boost::shared_ptr<Source>();
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
/* note: do peak building in another thread when loading session state */
|
|
|
|
return SourceFactory::create (*this, node, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (failed_constructor& err) {
|
|
|
|
error << _("Found a sound file that cannot be used by Ardour. Talk to the progammers.") << endmsg;
|
|
|
|
return boost::shared_ptr<Source>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::save_template (string template_name)
|
|
|
|
{
|
|
|
|
XMLTree tree;
|
|
|
|
|
|
|
|
if (_state_of_the_state & CannotSave) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sys::path user_template_dir(user_template_directory());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
sys::create_directories (user_template_dir);
|
|
|
|
}
|
|
|
|
catch(sys::filesystem_error& ex)
|
|
|
|
{
|
|
|
|
error << string_compose(_("Could not create mix 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))
|
|
|
|
{
|
|
|
|
warning << string_compose(_("Template \"%1\" already exists - new version not created"),
|
|
|
|
template_file_path.to_string()) << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tree.write (template_file_path.to_string())) {
|
|
|
|
error << _("mix template not saved") << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-10 11:03:30 -04:00
|
|
|
int
|
2009-10-14 12:10:01 -04:00
|
|
|
Session::rename_template (string old_name, string new_name)
|
2008-09-10 11:03:30 -04:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-10-14 12:10:01 -04:00
|
|
|
Session::delete_template (string name)
|
2008-09-10 11:03:30 -04:00
|
|
|
{
|
|
|
|
sys::path path = user_template_directory();
|
|
|
|
path /= name + template_suffix;
|
|
|
|
|
|
|
|
try {
|
|
|
|
sys::remove (path);
|
|
|
|
return 0;
|
|
|
|
} catch (...) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
void
|
|
|
|
Session::refresh_disk_space ()
|
|
|
|
{
|
|
|
|
#if HAVE_SYS_VFS_H
|
|
|
|
struct statfs statfsbuf;
|
|
|
|
vector<space_and_path>::iterator i;
|
|
|
|
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;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
|
|
|
|
statfs ((*i).path.c_str(), &statfsbuf);
|
|
|
|
|
|
|
|
scale = statfsbuf.f_bsize/4096.0;
|
|
|
|
|
|
|
|
(*i).blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
|
|
|
|
_total_free_4k_blocks += (*i).blocks;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
string
|
|
|
|
Session::get_best_session_directory_for_new_source ()
|
|
|
|
{
|
|
|
|
vector<space_and_path>::iterator i;
|
|
|
|
string result = _session_dir->root_path().to_string();
|
|
|
|
|
|
|
|
/* handle common case without system calls */
|
|
|
|
|
|
|
|
if (session_dirs.size() == 1) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, here's the algorithm we're following here:
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
We want to select which directory to use for
|
2008-06-02 17:41:35 -04:00
|
|
|
the next file source to be created. Ideally,
|
|
|
|
we'd like to use a round-robin process so as to
|
|
|
|
get maximum performance benefits from splitting
|
|
|
|
the files across multiple disks.
|
|
|
|
|
|
|
|
However, in situations without much diskspace, an
|
|
|
|
RR approach may end up filling up a filesystem
|
|
|
|
with new files while others still have space.
|
|
|
|
Its therefore important to pay some attention to
|
|
|
|
the freespace in the filesystem holding each
|
|
|
|
directory as well. However, if we did that by
|
|
|
|
itself, we'd keep creating new files in the file
|
|
|
|
system with the most space until it was as full
|
|
|
|
as all others, thus negating any performance
|
|
|
|
benefits of this RAID-1 like approach.
|
|
|
|
|
|
|
|
So, we use a user-configurable space threshold. If
|
|
|
|
there are at least 2 filesystems with more than this
|
2009-10-14 12:10:01 -04:00
|
|
|
much space available, we use RR selection between them.
|
2008-06-02 17:41:35 -04:00
|
|
|
If not, then we pick the filesystem with the most space.
|
|
|
|
|
|
|
|
This gets a good balance between the two
|
2009-10-14 12:10:01 -04:00
|
|
|
approaches.
|
2008-06-02 17:41:35 -04:00
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
refresh_disk_space ();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
int free_enough = 0;
|
|
|
|
|
|
|
|
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
|
|
|
|
if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
|
|
|
|
free_enough++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_enough >= 2) {
|
|
|
|
/* use RR selection process, ensuring that the one
|
|
|
|
picked works OK.
|
|
|
|
*/
|
|
|
|
|
|
|
|
i = last_rr_session_dir;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (++i == session_dirs.end()) {
|
|
|
|
i = session_dirs.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
|
|
|
|
if (create_session_directory ((*i).path)) {
|
|
|
|
result = (*i).path;
|
|
|
|
last_rr_session_dir = i;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (i != last_rr_session_dir);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* pick FS with the most freespace (and that
|
|
|
|
seems to actually work ...)
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
vector<space_and_path> sorted;
|
|
|
|
space_and_path_ascending_cmp cmp;
|
|
|
|
|
|
|
|
sorted = session_dirs;
|
|
|
|
sort (sorted.begin(), sorted.end(), cmp);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (i = sorted.begin(); i != sorted.end(); ++i) {
|
|
|
|
if (create_session_directory ((*i).path)) {
|
|
|
|
result = (*i).path;
|
|
|
|
last_rr_session_dir = i;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_playlists (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
boost::shared_ptr<Playlist> playlist;
|
|
|
|
|
|
|
|
nlist = node.children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((playlist = XMLPlaylistFactory (**niter)) == 0) {
|
|
|
|
error << _("Session: cannot create Playlist from XML description.") << endmsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_unused_playlists (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
boost::shared_ptr<Playlist> playlist;
|
|
|
|
|
|
|
|
nlist = node.children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((playlist = XMLPlaylistFactory (**niter)) == 0) {
|
|
|
|
error << _("Session: cannot create Playlist from XML description.") << endmsg;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now manually untrack it
|
|
|
|
|
|
|
|
track_playlist (false, boost::weak_ptr<Playlist> (playlist));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<Playlist>
|
|
|
|
Session::XMLPlaylistFactory (const XMLNode& node)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
return PlaylistFactory::create (*this, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (failed_constructor& err) {
|
|
|
|
return boost::shared_ptr<Playlist>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_named_selections (const XMLNode& node)
|
|
|
|
{
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
NamedSelection *ns;
|
|
|
|
|
|
|
|
nlist = node.children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((ns = XMLNamedSelectionFactory (**niter)) == 0) {
|
|
|
|
error << _("Session: cannot create Named Selection from XML description.") << endmsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
NamedSelection *
|
|
|
|
Session::XMLNamedSelectionFactory (const XMLNode& node)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
return new NamedSelection (*this, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (failed_constructor& err) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string
|
|
|
|
Session::automation_dir () const
|
|
|
|
{
|
2008-09-10 11:03:30 -04:00
|
|
|
return Glib::build_filename (_path, "automation");
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
string
|
|
|
|
Session::analysis_dir () const
|
|
|
|
{
|
2008-09-10 11:03:30 -04:00
|
|
|
return Glib::build_filename (_path, "analysis");
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::load_bundles (XMLNode const & node)
|
|
|
|
{
|
2009-10-14 12:10:01 -04:00
|
|
|
XMLNodeList nlist = node.children();
|
|
|
|
XMLNodeConstIterator niter;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
set_dirty();
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
if ((*niter)->name() == "InputBundle") {
|
2008-06-02 17:41:35 -04:00
|
|
|
add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, true)));
|
2009-10-14 12:10:01 -04:00
|
|
|
} else if ((*niter)->name() == "OutputBundle") {
|
|
|
|
add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false)));
|
|
|
|
} else {
|
|
|
|
error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
int
|
2009-06-21 15:59:56 -04:00
|
|
|
Session::load_route_groups (const XMLNode& node)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
XMLNodeList nlist = node.children();
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
|
2009-06-21 15:59:56 -04:00
|
|
|
set_dirty ();
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
if ((*niter)->name() == "RouteGroup") {
|
2009-06-20 11:40:26 -04:00
|
|
|
RouteGroup* rg = new RouteGroup (*this, "");
|
2009-06-21 15:59:56 -04:00
|
|
|
add_route_group (rg);
|
|
|
|
rg->set_state (**niter);
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
return 0;
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
Session::auto_save()
|
|
|
|
{
|
|
|
|
save_state (_current_snapshot_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2009-07-21 11:55:17 -04:00
|
|
|
state_file_filter (const string &str, void */*arg*/)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
return (str.length() > strlen(statefile_suffix) &&
|
|
|
|
str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct string_cmp {
|
|
|
|
bool operator()(const string* a, const string* b) {
|
|
|
|
return *a < *b;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static string*
|
|
|
|
remove_end(string* state)
|
|
|
|
{
|
|
|
|
string statename(*state);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
string::size_type start,end;
|
|
|
|
if ((start = statename.find_last_of ('/')) != string::npos) {
|
|
|
|
statename = statename.substr (start+1);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((end = statename.rfind(".ardour")) == string::npos) {
|
|
|
|
end = statename.length();
|
|
|
|
}
|
|
|
|
|
|
|
|
return new string(statename.substr (0, end));
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string *> *
|
2009-10-14 12:10:01 -04:00
|
|
|
Session::possible_states (string path)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
PathScanner scanner;
|
|
|
|
vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
transform(states->begin(), states->end(), states->begin(), remove_end);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
string_cmp cmp;
|
|
|
|
sort (states->begin(), states->end(), cmp);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
return states;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string *> *
|
|
|
|
Session::possible_states () const
|
|
|
|
{
|
|
|
|
return possible_states(_path);
|
|
|
|
}
|
|
|
|
|
2009-06-20 11:40:26 -04:00
|
|
|
void
|
2009-06-21 15:59:56 -04:00
|
|
|
Session::add_route_group (RouteGroup* g)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-06-21 15:59:56 -04:00
|
|
|
_route_groups.push_back (g);
|
|
|
|
route_group_added (g); /* EMIT SIGNAL */
|
2009-06-20 11:40:26 -04:00
|
|
|
set_dirty ();
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-06-20 11:40:26 -04:00
|
|
|
void
|
2009-06-21 15:59:56 -04:00
|
|
|
Session::remove_route_group (RouteGroup& rg)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
list<RouteGroup*>::iterator i;
|
|
|
|
|
2009-06-21 15:59:56 -04:00
|
|
|
if ((i = find (_route_groups.begin(), _route_groups.end(), &rg)) != _route_groups.end()) {
|
|
|
|
(*i)->apply (&Route::drop_route_group, this);
|
|
|
|
_route_groups.erase (i);
|
|
|
|
route_group_removed (); /* EMIT SIGNAL */
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
delete &rg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RouteGroup *
|
2009-06-21 15:59:56 -04:00
|
|
|
Session::route_group_by_name (string name)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
list<RouteGroup *>::iterator i;
|
|
|
|
|
2009-06-21 15:59:56 -04:00
|
|
|
for (i = _route_groups.begin(); i != _route_groups.end(); ++i) {
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((*i)->name() == name) {
|
|
|
|
return* i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-05-04 21:53:30 -04:00
|
|
|
Session::begin_reversible_command(const string& name)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-05-04 21:53:30 -04:00
|
|
|
UndoTransaction* trans = new UndoTransaction();
|
|
|
|
trans->set_name(name);
|
2009-08-17 11:58:47 -04:00
|
|
|
|
2009-05-04 21:53:30 -04:00
|
|
|
if (!_current_trans.empty()) {
|
2009-08-17 11:58:47 -04:00
|
|
|
_current_trans.top()->add_command (trans);
|
|
|
|
} else {
|
|
|
|
_current_trans.push(trans);
|
2009-05-04 21:53:30 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-05-04 21:53:30 -04:00
|
|
|
Session::commit_reversible_command(Command *cmd)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-05-04 21:53:30 -04:00
|
|
|
assert(!_current_trans.empty());
|
2008-06-02 17:41:35 -04:00
|
|
|
struct timeval now;
|
|
|
|
|
|
|
|
if (cmd) {
|
2009-05-04 21:53:30 -04:00
|
|
|
_current_trans.top()->add_command(cmd);
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-05-04 21:53:30 -04:00
|
|
|
if (_current_trans.top()->empty()) {
|
|
|
|
_current_trans.pop();
|
2008-06-02 17:41:35 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-05-04 21:53:30 -04:00
|
|
|
gettimeofday(&now, 0);
|
|
|
|
_current_trans.top()->set_timestamp(now);
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-05-04 21:53:30 -04:00
|
|
|
_history.add(_current_trans.top());
|
|
|
|
_current_trans.pop();
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
Session::GlobalRouteBooleanState
|
2008-06-02 17:41:35 -04:00
|
|
|
Session::get_global_route_boolean (bool (Route::*method)(void) const)
|
|
|
|
{
|
|
|
|
GlobalRouteBooleanState s;
|
|
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
|
|
|
|
|
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
if (!(*i)->is_hidden()) {
|
|
|
|
RouteBooleanState v;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
v.first =* i;
|
|
|
|
Route* r = (*i).get();
|
|
|
|
v.second = (r->*method)();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
s.push_back (v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
Session::GlobalRouteMeterState
|
|
|
|
Session::get_global_route_metering ()
|
|
|
|
{
|
|
|
|
GlobalRouteMeterState s;
|
|
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
|
|
|
|
|
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
if (!(*i)->is_hidden()) {
|
|
|
|
RouteMeterState v;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
v.first =* i;
|
|
|
|
v.second = (*i)->meter_point();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
s.push_back (v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-10-14 12:10:01 -04:00
|
|
|
Session::set_global_route_metering (GlobalRouteMeterState s, void* arg)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
for (GlobalRouteMeterState::iterator i = s.begin(); i != s.end(); ++i) {
|
|
|
|
|
|
|
|
boost::shared_ptr<Route> r = (i->first.lock());
|
|
|
|
|
|
|
|
if (r) {
|
|
|
|
r->set_meter_point (i->second, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_global_route_boolean (GlobalRouteBooleanState s, void (Route::*method)(bool, void*), void* arg)
|
|
|
|
{
|
|
|
|
for (GlobalRouteBooleanState::iterator i = s.begin(); i != s.end(); ++i) {
|
|
|
|
|
|
|
|
boost::shared_ptr<Route> r = (i->first.lock());
|
|
|
|
|
|
|
|
if (r) {
|
|
|
|
Route* rp = r.get();
|
|
|
|
(rp->*method) (i->second, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_global_mute (GlobalRouteBooleanState s, void* src)
|
|
|
|
{
|
|
|
|
set_global_route_boolean (s, &Route::set_mute, src);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_global_solo (GlobalRouteBooleanState s, void* src)
|
|
|
|
{
|
|
|
|
set_global_route_boolean (s, &Route::set_solo, src);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_global_record_enable (GlobalRouteBooleanState s, void* src)
|
|
|
|
{
|
|
|
|
set_global_route_boolean (s, &Route::set_record_enable, src);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2009-07-21 11:55:17 -04:00
|
|
|
accept_all_non_peak_files (const string& path, void */*arg*/)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
return (path.length() > 5 && path.find (peakfile_suffix) != (path.length() - 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2009-07-21 11:55:17 -04:00
|
|
|
accept_all_state_files (const string& path, void */*arg*/)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
|
|
|
return (path.length() > 7 && path.find (".ardour") == (path.length() - 7));
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
int
|
2008-06-02 17:41:35 -04:00
|
|
|
Session::find_all_sources (string path, set<string>& result)
|
|
|
|
{
|
|
|
|
XMLTree tree;
|
|
|
|
XMLNode* node;
|
|
|
|
|
|
|
|
if (!tree.read (path)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((node = find_named_node (*tree.root(), "Sources")) == 0) {
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNodeList nlist;
|
|
|
|
XMLNodeConstIterator niter;
|
|
|
|
|
|
|
|
nlist = node->children();
|
|
|
|
|
|
|
|
set_dirty();
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
XMLProperty* prop;
|
|
|
|
|
2009-06-25 16:46:39 -04:00
|
|
|
if ((prop = (*niter)->property (X_("type"))) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataType type (prop->value());
|
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((prop = (*niter)->property (X_("name"))) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop->value()[0] == '/') {
|
|
|
|
/* external file, ignore */
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-06-25 16:46:39 -04:00
|
|
|
Glib::ustring found_path;
|
|
|
|
bool is_new;
|
|
|
|
uint16_t chan;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-06-25 16:46:39 -04:00
|
|
|
if (FileSource::find (type, prop->value(), true, is_new, chan, found_path)) {
|
|
|
|
result.insert (found_path);
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
|
|
|
|
{
|
|
|
|
PathScanner scanner;
|
|
|
|
vector<string*>* state_files;
|
|
|
|
string ripped;
|
|
|
|
string this_snapshot_path;
|
|
|
|
|
|
|
|
result.clear ();
|
|
|
|
|
|
|
|
ripped = _path;
|
|
|
|
|
|
|
|
if (ripped[ripped.length()-1] == '/') {
|
|
|
|
ripped = ripped.substr (0, ripped.length() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
state_files = scanner (ripped, accept_all_state_files, (void *) 0, false, true);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (state_files == 0) {
|
|
|
|
/* impossible! */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this_snapshot_path = _path;
|
2009-07-12 20:26:28 -04:00
|
|
|
this_snapshot_path += legalize_for_path (_current_snapshot_name);
|
2008-06-02 17:41:35 -04:00
|
|
|
this_snapshot_path += statefile_suffix;
|
|
|
|
|
|
|
|
for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
|
|
|
|
|
|
|
|
if (exclude_this_snapshot && **i == this_snapshot_path) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (find_all_sources (**i, result) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct RegionCounter {
|
|
|
|
typedef std::map<PBD::ID,boost::shared_ptr<AudioSource> > AudioSourceList;
|
|
|
|
AudioSourceList::iterator iter;
|
|
|
|
boost::shared_ptr<Region> region;
|
|
|
|
uint32_t count;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
RegionCounter() : count (0) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::cleanup_sources (Session::cleanup_report& rep)
|
|
|
|
{
|
|
|
|
// FIXME: needs adaptation to midi
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
vector<boost::shared_ptr<Source> > dead_sources;
|
|
|
|
vector<boost::shared_ptr<Playlist> > playlists_tbd;
|
|
|
|
PathScanner scanner;
|
|
|
|
string sound_path;
|
|
|
|
vector<space_and_path>::iterator i;
|
|
|
|
vector<space_and_path>::iterator nexti;
|
|
|
|
vector<string*>* soundfiles;
|
|
|
|
vector<string> unused;
|
|
|
|
set<string> all_sources;
|
|
|
|
bool used;
|
|
|
|
string spath;
|
|
|
|
int ret = -1;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
_state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* step 1: consider deleting all unused playlists */
|
|
|
|
|
|
|
|
for (PlaylistList::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = AskAboutPlaylistDeletion (*x);
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case -1:
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
playlists_tbd.push_back (*x);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* leave it alone */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now delete any that were marked for deletion */
|
|
|
|
|
|
|
|
for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
|
|
|
|
(*x)->drop_references ();
|
|
|
|
}
|
|
|
|
|
|
|
|
playlists_tbd.clear ();
|
|
|
|
|
|
|
|
/* step 2: find all un-used sources */
|
|
|
|
|
|
|
|
rep.paths.clear ();
|
|
|
|
rep.space = 0;
|
|
|
|
|
|
|
|
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
SourceMap::iterator tmp;
|
|
|
|
|
|
|
|
tmp = i;
|
|
|
|
++tmp;
|
|
|
|
|
|
|
|
/* do not bother with files that are zero size, otherwise we remove the current "nascent"
|
|
|
|
capture files.
|
|
|
|
*/
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
if (!i->second->used() && i->second->length(i->second->timeline_position()) > 0) {
|
2008-06-02 17:41:35 -04:00
|
|
|
dead_sources.push_back (i->second);
|
|
|
|
i->second->GoingAway();
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
i = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* build a list of all the possible sound directories for the session */
|
|
|
|
|
|
|
|
for (i = session_dirs.begin(); i != session_dirs.end(); ) {
|
|
|
|
|
|
|
|
nexti = i;
|
|
|
|
++nexti;
|
|
|
|
|
|
|
|
SessionDirectory sdir ((*i).path);
|
|
|
|
sound_path += sdir.sound_path().to_string();
|
|
|
|
|
|
|
|
if (nexti != session_dirs.end()) {
|
|
|
|
sound_path += ':';
|
|
|
|
}
|
|
|
|
|
|
|
|
i = nexti;
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
/* now do the same thing for the files that ended up in the sounds dir(s)
|
2008-06-02 17:41:35 -04:00
|
|
|
but are not referenced as sources in any snapshot.
|
|
|
|
*/
|
|
|
|
|
|
|
|
soundfiles = scanner (sound_path, accept_all_non_peak_files, (void *) 0, false, true);
|
|
|
|
|
|
|
|
if (soundfiles == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find all sources, but don't use this snapshot because the
|
|
|
|
state file on disk still references sources we may have already
|
|
|
|
dropped.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
find_all_sources_across_snapshots (all_sources, true);
|
|
|
|
|
|
|
|
/* add our current source list
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
|
2009-02-16 21:11:49 -05:00
|
|
|
boost::shared_ptr<FileSource> fs;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-02-16 21:11:49 -05:00
|
|
|
if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
|
2008-06-02 17:41:35 -04:00
|
|
|
all_sources.insert (fs->path());
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
char tmppath1[PATH_MAX+1];
|
|
|
|
char tmppath2[PATH_MAX+1];
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (vector<string*>::iterator x = soundfiles->begin(); x != soundfiles->end(); ++x) {
|
|
|
|
|
|
|
|
used = false;
|
|
|
|
spath = **x;
|
|
|
|
|
|
|
|
for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
|
|
|
|
|
|
|
|
realpath(spath.c_str(), tmppath1);
|
|
|
|
realpath((*i).c_str(), tmppath2);
|
|
|
|
|
|
|
|
if (strcmp(tmppath1, tmppath2) == 0) {
|
|
|
|
used = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!used) {
|
|
|
|
unused.push_back (spath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now try to move all unused files into the "dead_sounds" directory(ies) */
|
|
|
|
|
|
|
|
for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
|
|
|
|
struct stat statbuf;
|
|
|
|
|
|
|
|
rep.paths.push_back (*x);
|
|
|
|
if (stat ((*x).c_str(), &statbuf) == 0) {
|
|
|
|
rep.space += statbuf.st_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
string newpath;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* don't move the file across filesystems, just
|
|
|
|
stick it in the `dead_sound_dir_name' directory
|
|
|
|
on whichever filesystem it was already on.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((*x).find ("/sounds/") != string::npos) {
|
|
|
|
|
|
|
|
/* old school, go up 1 level */
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
newpath = Glib::path_get_dirname (*x); // "sounds"
|
2008-06-02 17:41:35 -04:00
|
|
|
newpath = Glib::path_get_dirname (newpath); // "session-name"
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* new school, go up 4 levels */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
newpath = Glib::path_get_dirname (*x); // "audiofiles"
|
2008-06-02 17:41:35 -04:00
|
|
|
newpath = Glib::path_get_dirname (newpath); // "session-name"
|
|
|
|
newpath = Glib::path_get_dirname (newpath); // "interchange"
|
|
|
|
newpath = Glib::path_get_dirname (newpath); // "session-dir"
|
|
|
|
}
|
|
|
|
|
|
|
|
newpath += '/';
|
|
|
|
newpath += dead_sound_dir_name;
|
|
|
|
|
|
|
|
if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
|
|
|
|
error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
newpath += '/';
|
|
|
|
newpath += Glib::path_get_basename ((*x));
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (access (newpath.c_str(), F_OK) == 0) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* the new path already exists, try versioning */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
char buf[PATH_MAX+1];
|
|
|
|
int version = 1;
|
|
|
|
string newpath_v;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
|
|
|
|
newpath_v = buf;
|
|
|
|
|
|
|
|
while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
|
|
|
|
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
|
|
|
|
newpath_v = buf;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (version == 999) {
|
|
|
|
error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
|
|
|
|
newpath)
|
|
|
|
<< endmsg;
|
|
|
|
} else {
|
|
|
|
newpath = newpath_v;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
} else {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* it doesn't exist, or we can't read it or something */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
|
|
|
|
error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
|
|
|
|
(*x), newpath, strerror (errno))
|
|
|
|
<< endmsg;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* see if there an easy to find peakfile for this file, and remove it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
string peakpath = (*x).substr (0, (*x).find_last_of ('.'));
|
|
|
|
peakpath += peakfile_suffix;
|
|
|
|
|
|
|
|
if (access (peakpath.c_str(), W_OK) == 0) {
|
|
|
|
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());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
/* dump the history list */
|
|
|
|
|
|
|
|
_history.clear ();
|
|
|
|
|
|
|
|
/* save state so we don't end up a session file
|
|
|
|
referring to non-existent sources.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
save_state ("");
|
|
|
|
|
|
|
|
out:
|
|
|
|
_state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::cleanup_trash_sources (Session::cleanup_report& rep)
|
|
|
|
{
|
|
|
|
// FIXME: needs adaptation for MIDI
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
vector<space_and_path>::iterator i;
|
|
|
|
string dead_sound_dir;
|
|
|
|
struct dirent* dentry;
|
|
|
|
struct stat statbuf;
|
|
|
|
DIR* dead;
|
|
|
|
|
|
|
|
rep.paths.clear ();
|
|
|
|
rep.space = 0;
|
|
|
|
|
|
|
|
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
dead_sound_dir = (*i).path;
|
|
|
|
dead_sound_dir += dead_sound_dir_name;
|
|
|
|
|
|
|
|
if ((dead = opendir (dead_sound_dir.c_str())) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((dentry = readdir (dead)) != 0) {
|
|
|
|
|
|
|
|
/* avoid '.' and '..' */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
|
2008-06-02 17:41:35 -04:00
|
|
|
(dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
string fullpath;
|
|
|
|
|
|
|
|
fullpath = dead_sound_dir;
|
|
|
|
fullpath += '/';
|
|
|
|
fullpath += dentry->d_name;
|
|
|
|
|
|
|
|
if (stat (fullpath.c_str(), &statbuf)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISREG (statbuf.st_mode)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlink (fullpath.c_str())) {
|
|
|
|
error << string_compose (_("cannot remove dead sound file %1 (%2)"),
|
|
|
|
fullpath, strerror (errno))
|
|
|
|
<< endmsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
rep.paths.push_back (dentry->d_name);
|
|
|
|
rep.space += statbuf.st_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir (dead);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_dirty ()
|
|
|
|
{
|
|
|
|
bool was_dirty = dirty();
|
|
|
|
|
|
|
|
_state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
|
|
|
|
|
|
|
|
|
|
|
|
if (!was_dirty) {
|
|
|
|
DirtyChanged(); /* EMIT SIGNAL */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_clean ()
|
|
|
|
{
|
|
|
|
bool was_dirty = dirty();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
_state_of_the_state = Clean;
|
|
|
|
|
|
|
|
|
|
|
|
if (was_dirty) {
|
|
|
|
DirtyChanged(); /* EMIT SIGNAL */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_deletion_in_progress ()
|
|
|
|
{
|
|
|
|
_state_of_the_state = StateOfTheState (_state_of_the_state | Deletion);
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 02:30:50 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 02:30:50 -04:00
|
|
|
void
|
|
|
|
Session::clear_deletion_in_progress ()
|
|
|
|
{
|
|
|
|
_state_of_the_state = StateOfTheState (_state_of_the_state & (~Deletion));
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::add_controllable (boost::shared_ptr<Controllable> c)
|
|
|
|
{
|
|
|
|
/* this adds a controllable to the list managed by the Session.
|
|
|
|
this is a subset of those managed by the Controllable class
|
|
|
|
itself, and represents the only ones whose state will be saved
|
|
|
|
as part of the session.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Glib::Mutex::Lock lm (controllables_lock);
|
|
|
|
controllables.insert (c);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
struct null_deleter { void operator()(void const *) const {} };
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::remove_controllable (Controllable* c)
|
|
|
|
{
|
|
|
|
if (_state_of_the_state | Deletion) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Glib::Mutex::Lock lm (controllables_lock);
|
|
|
|
|
|
|
|
Controllables::iterator x = controllables.find(
|
|
|
|
boost::shared_ptr<Controllable>(c, null_deleter()));
|
|
|
|
|
|
|
|
if (x != controllables.end()) {
|
|
|
|
controllables.erase (x);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
boost::shared_ptr<Controllable>
|
|
|
|
Session::controllable_by_id (const PBD::ID& id)
|
|
|
|
{
|
|
|
|
Glib::Mutex::Lock lm (controllables_lock);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
|
|
|
|
if ((*i)->id() == id) {
|
|
|
|
return *i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return boost::shared_ptr<Controllable>();
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
void
|
2008-09-17 04:44:51 -04:00
|
|
|
Session::add_instant_xml (XMLNode& node, bool write_to_config)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-10-13 16:43:28 -04:00
|
|
|
if (_writable) {
|
|
|
|
Stateful::add_instant_xml (node, _path);
|
|
|
|
}
|
|
|
|
|
2008-09-17 04:44:51 -04:00
|
|
|
if (write_to_config) {
|
|
|
|
Config->add_instant_xml (node);
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
XMLNode*
|
|
|
|
Session::instant_xml (const string& node_name)
|
|
|
|
{
|
|
|
|
return Stateful::instant_xml (node_name, _path);
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
int
|
2008-06-02 17:41:35 -04:00
|
|
|
Session::save_history (string snapshot_name)
|
|
|
|
{
|
|
|
|
XMLTree tree;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-10-13 16:43:28 -04:00
|
|
|
if (!_writable) {
|
|
|
|
return 0;
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2009-10-13 16:43:28 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
if (snapshot_name.empty()) {
|
2008-06-02 17:41:35 -04:00
|
|
|
snapshot_name = _current_snapshot_name;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
|
|
|
const string history_filename = legalize_for_path (snapshot_name) + history_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
const string backup_filename = history_filename + backup_suffix;
|
2009-10-14 12:10:01 -04:00
|
|
|
const sys::path xml_path = _session_dir->root_path() / history_filename;
|
2008-06-02 17:41:35 -04:00
|
|
|
const sys::path backup_path = _session_dir->root_path() / backup_filename;
|
|
|
|
|
|
|
|
if (sys::exists (xml_path)) {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
sys::rename (xml_path, backup_path);
|
|
|
|
}
|
|
|
|
catch (const sys::filesystem_error& err)
|
|
|
|
{
|
|
|
|
error << _("could not backup old history file, current history not saved") << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!tree.write (xml_path.to_string()))
|
|
|
|
{
|
|
|
|
error << string_compose (_("history could not be saved to %1"), xml_path.to_string()) << endmsg;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
sys::remove (xml_path);
|
|
|
|
sys::rename (backup_path, xml_path);
|
|
|
|
}
|
|
|
|
catch (const sys::filesystem_error& err)
|
|
|
|
{
|
|
|
|
error << string_compose (_("could not restore history file from backup %1 (%2)"),
|
|
|
|
backup_path.to_string(), err.what()) << endmsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Session::restore_history (string snapshot_name)
|
|
|
|
{
|
|
|
|
XMLTree tree;
|
|
|
|
|
|
|
|
if (snapshot_name.empty()) {
|
|
|
|
snapshot_name = _current_snapshot_name;
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
const string xml_filename = legalize_for_path (snapshot_name) + history_suffix;
|
2008-06-02 17:41:35 -04:00
|
|
|
const sys::path xml_path = _session_dir->root_path() / xml_filename;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-07-12 20:26:28 -04:00
|
|
|
cerr << "Loading history from " << xml_path.to_string() << endmsg;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!sys::exists (xml_path)) {
|
|
|
|
info << string_compose (_("%1: no history file \"%2\" for this session."),
|
|
|
|
_name, xml_path.to_string()) << endmsg;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tree.read (xml_path.to_string())) {
|
|
|
|
error << string_compose (_("Could not understand session history file \"%1\""),
|
|
|
|
xml_path.to_string()) << endmsg;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace history
|
|
|
|
_history.clear();
|
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); it++) {
|
|
|
|
|
|
|
|
XMLNode *t = *it;
|
|
|
|
UndoTransaction* ut = new UndoTransaction ();
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
ut->set_name(t->property("name")->value());
|
|
|
|
stringstream ss(t->property("tv-sec")->value());
|
|
|
|
ss >> tv.tv_sec;
|
|
|
|
ss.str(t->property("tv-usec")->value());
|
|
|
|
ss >> tv.tv_usec;
|
|
|
|
ut->set_timestamp(tv);
|
|
|
|
|
|
|
|
for (XMLNodeConstIterator child_it = t->children().begin();
|
2008-10-05 19:14:48 -04:00
|
|
|
child_it != t->children().end(); child_it++)
|
2009-10-14 12:10:01 -04:00
|
|
|
{
|
|
|
|
XMLNode *n = *child_it;
|
|
|
|
Command *c;
|
|
|
|
|
|
|
|
if (n->name() == "MementoCommand" ||
|
2008-10-05 19:14:48 -04:00
|
|
|
n->name() == "MementoUndoCommand" ||
|
|
|
|
n->name() == "MementoRedoCommand") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-10-14 12:10:01 -04:00
|
|
|
if ((c = memento_command_factory(n))) {
|
|
|
|
ut->add_command(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (n->name() == X_("GlobalRouteStateCommand")) {
|
|
|
|
|
|
|
|
if ((c = global_state_command_factory (*n))) {
|
|
|
|
ut->add_command (c);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (n->name() == "DeltaCommand") {
|
|
|
|
PBD::ID id(n->property("midi-source")->value());
|
|
|
|
boost::shared_ptr<MidiSource> midi_source =
|
|
|
|
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
|
|
|
|
if(midi_source) {
|
|
|
|
ut->add_command(new MidiModel::DeltaCommand(midi_source->model(), *n));
|
|
|
|
} else {
|
|
|
|
error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg;
|
|
|
|
}
|
|
|
|
} else if (n->name() == "DiffCommand") {
|
|
|
|
PBD::ID id(n->property("midi-source")->value());
|
|
|
|
boost::shared_ptr<MidiSource> midi_source =
|
|
|
|
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
|
|
|
|
if(midi_source) {
|
|
|
|
ut->add_command(new MidiModel::DiffCommand(midi_source->model(), *n));
|
|
|
|
} else {
|
|
|
|
error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_history.add (ut);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-05-15 21:22:43 -04:00
|
|
|
Session::config_changed (std::string p, bool ours)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-05-15 21:22:43 -04:00
|
|
|
if (ours) {
|
|
|
|
set_dirty ();
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
if (p == "seamless-loop") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "rf-speed") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "auto-loop") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "auto-input") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (Config->get_monitoring_model() == HardwareMonitoring && transport_rolling()) {
|
|
|
|
/* auto-input only makes a difference if we're rolling */
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
|
|
|
if ((*i)->record_enabled ()) {
|
2009-05-13 20:13:27 -04:00
|
|
|
(*i)->monitor_input (!config.get_auto_input());
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "punch-in") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
Location* location;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((location = _locations.auto_punch_location()) != 0) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
if (config.get_punch_in ()) {
|
2008-06-02 17:41:35 -04:00
|
|
|
replace_event (Event::PunchIn, location->start());
|
|
|
|
} else {
|
|
|
|
remove_event (location->start(), Event::PunchIn);
|
|
|
|
}
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "punch-out") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
Location* location;
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if ((location = _locations.auto_punch_location()) != 0) {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
if (config.get_punch_out()) {
|
2008-06-02 17:41:35 -04:00
|
|
|
replace_event (Event::PunchOut, location->end());
|
|
|
|
} else {
|
|
|
|
clear_events (Event::PunchOut);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "edit-mode") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
Glib::Mutex::Lock lm (playlist_lock);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
|
|
(*i)->set_edit_mode (Config->get_edit_mode ());
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "use-video-sync") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-05-15 21:53:43 -04:00
|
|
|
waiting_for_sync_offset = config.get_use_video_sync();
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "mmc-control") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
//poke_midi_thread ();
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "mmc-device-id" || p == "mmc-receive-id") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (mmc) {
|
|
|
|
mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "mmc-send-id") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (mmc) {
|
|
|
|
mmc->set_send_device_id (Config->get_mmc_send_device_id());
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "midi-control") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
//poke_midi_thread ();
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "raid-path") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-05-13 20:13:27 -04:00
|
|
|
setup_raid_path (config.get_raid_path());
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "smpte-format") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
sync_time_vars ();
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "video-pullup") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
sync_time_vars ();
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "seamless-loop") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (play_loop && transport_rolling()) {
|
|
|
|
// to reset diskstreams etc
|
|
|
|
request_play_loop (true);
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "rf-speed") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
cumulative_rf_motion = 0;
|
|
|
|
reset_rf_scale (0);
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "click-sound") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
setup_click_sounds (1);
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "click-emphasis-sound") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
setup_click_sounds (-1);
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "clicking") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (Config->get_clicking()) {
|
|
|
|
if (_click_io && click_data) { // don't require emphasis data
|
|
|
|
_clicking = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_clicking = false;
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "send-mtc") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* only set the internal flag if we have
|
|
|
|
a port.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (_mtc_port != 0) {
|
|
|
|
session_send_mtc = Config->get_send_mtc();
|
|
|
|
if (session_send_mtc) {
|
|
|
|
/* mark us ready to send */
|
|
|
|
next_quarter_frame_to_send = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
session_send_mtc = false;
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "send-mmc") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* only set the internal flag if we have
|
|
|
|
a port.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (_mmc_port != 0) {
|
|
|
|
session_send_mmc = Config->get_send_mmc();
|
|
|
|
} else {
|
|
|
|
mmc = 0;
|
2009-10-14 12:10:01 -04:00
|
|
|
session_send_mmc = false;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "midi-feedback") {
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
/* only set the internal flag if we have
|
|
|
|
a port.
|
|
|
|
*/
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
if (_mtc_port != 0) {
|
|
|
|
session_midi_feedback = Config->get_midi_feedback();
|
|
|
|
}
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "jack-time-master") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
engine().reset_timebase ();
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "native-file-header-format") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!first_file_header_format_reset) {
|
|
|
|
reset_native_file_format ();
|
|
|
|
}
|
|
|
|
|
|
|
|
first_file_header_format_reset = false;
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "native-file-data-format") {
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
if (!first_file_data_format_reset) {
|
|
|
|
reset_native_file_format ();
|
|
|
|
}
|
|
|
|
|
|
|
|
first_file_data_format_reset = false;
|
|
|
|
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "slave-source") {
|
|
|
|
set_slave_source (Config->get_slave_source());
|
|
|
|
} else if (p == "remote-model") {
|
2008-06-02 17:41:35 -04:00
|
|
|
set_remote_control_ids ();
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "denormal-model") {
|
2008-06-02 17:41:35 -04:00
|
|
|
setup_fpu ();
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "history-depth") {
|
2008-06-02 17:41:35 -04:00
|
|
|
set_history_depth (Config->get_history_depth());
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "sync-all-route-ordering") {
|
2008-12-08 13:16:12 -05:00
|
|
|
sync_order_keys ("session");
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "initial-program-change") {
|
2008-10-09 10:15:45 -04:00
|
|
|
|
|
|
|
if (_mmc_port && Config->get_initial_program_change() >= 0) {
|
|
|
|
MIDI::byte buf[2];
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-10-09 10:15:45 -04:00
|
|
|
buf[0] = MIDI::program; // channel zero by default
|
|
|
|
buf[1] = (Config->get_initial_program_change() & 0x7f);
|
|
|
|
|
|
|
|
_mmc_port->midimsg (buf, sizeof (buf), 0);
|
|
|
|
}
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "initial-program-change") {
|
2008-12-12 09:43:24 -05:00
|
|
|
|
|
|
|
if (_mmc_port && Config->get_initial_program_change() >= 0) {
|
|
|
|
MIDI::byte* buf = new MIDI::byte[2];
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2008-12-12 09:43:24 -05:00
|
|
|
buf[0] = MIDI::program; // channel zero by default
|
|
|
|
buf[1] = (Config->get_initial_program_change() & 0x7f);
|
|
|
|
// deliver_midi (_mmc_port, buf, 2);
|
|
|
|
}
|
2009-05-15 21:22:43 -04:00
|
|
|
} else if (p == "solo-mute-override") {
|
2009-06-09 16:21:19 -04:00
|
|
|
// catch_up_on_solo_mute_override ();
|
2009-07-01 09:36:50 -04:00
|
|
|
} else if (p == "listen-position") {
|
|
|
|
listen_position_changed ();
|
|
|
|
} else if (p == "solo-control-is-listen-control") {
|
|
|
|
solo_control_mode_changed ();
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-07-01 09:36:50 -04:00
|
|
|
|
2008-06-02 17:41:35 -04:00
|
|
|
set_dirty ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Session::set_history_depth (uint32_t d)
|
|
|
|
{
|
|
|
|
_history.set_depth (d);
|
|
|
|
}
|