summaryrefslogtreecommitdiff
path: root/libs/ardour/session_state.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour/session_state.cc')
-rw-r--r--libs/ardour/session_state.cc380
1 files changed, 139 insertions, 241 deletions
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 9699a7c37f..64f8e387be 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 1999-2002 Paul Davis
+ Copyright (C) 1999-2013 Paul Davis
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
@@ -59,7 +59,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "evoral/SMF.hpp"
@@ -88,6 +87,7 @@
#include "ardour/control_protocol_manager.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.h"
+#include "ardour/graph.h"
#include "ardour/location.h"
#include "ardour/midi_model.h"
#include "ardour/midi_patch_manager.h"
@@ -126,112 +126,47 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-/** @param snapshot_name Snapshot name, without the .ardour prefix */
void
-Session::first_stage_init (string fullpath, string snapshot_name)
+Session::pre_engine_init (string fullpath)
{
- if (fullpath.length() == 0) {
+ if (fullpath.empty()) {
destroy ();
throw failed_constructor();
}
- _path = canonical_path (fullpath);
+ /* discover canonical fullpath */
- if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
- _path += G_DIR_SEPARATOR;
+ char buf[PATH_MAX+1];
+ if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
+ error << string_compose(_("Could not use path %1 (%2)"), buf, strerror(errno)) << endmsg;
+ destroy ();
+ throw failed_constructor();
}
- /* these two are just provisional settings. set_state()
- will likely override them.
- */
-
- _name = _current_snapshot_name = snapshot_name;
+ _path = string(buf);
+
+ /* we require _path to end with a dir separator */
- set_history_depth (Config->get_history_depth());
+ if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
+ _path += G_DIR_SEPARATOR;
+ }
- _current_frame_rate = _engine.frame_rate ();
- _nominal_frame_rate = _current_frame_rate;
- _base_frame_rate = _current_frame_rate;
+ /* is it new ? */
- _tempo_map = new TempoMap (_current_frame_rate);
- _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+ _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ /* finish initialization that can't be done in a normal C++ constructor
+ definition.
+ */
- _non_soloed_outs_muted = false;
- _listen_cnt = 0;
- _solo_isolated_cnt = 0;
+ timerclear (&last_mmc_step);
g_atomic_int_set (&processing_prohibited, 0);
- _transport_speed = 0;
- _default_transport_speed = 1.0;
- _last_transport_speed = 0;
- _target_transport_speed = 0;
- auto_play_legal = false;
- transport_sub_state = 0;
- _transport_frame = 0;
- _requested_return_frame = -1;
- _session_range_location = 0;
g_atomic_int_set (&_record_status, Disabled);
- loop_changing = false;
- play_loop = false;
- have_looped = false;
- _last_roll_location = 0;
- _last_roll_or_reversal_location = 0;
- _last_record_location = 0;
- pending_locate_frame = 0;
- pending_locate_roll = false;
- pending_locate_flush = false;
- state_was_pending = false;
- set_next_event ();
- outbound_mtc_timecode_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);
- _was_seamless = Config->get_seamless_loop ();
- _slave = 0;
- _send_qf_mtc = false;
- _pframes_since_last_mtc = 0;
g_atomic_int_set (&_playback_load, 100);
g_atomic_int_set (&_capture_load, 100);
- _play_range = false;
- _exporting = false;
- pending_abort = false;
- _adding_routes_in_progress = false;
- destructive_index = 0;
- first_file_data_format_reset = true;
- first_file_header_format_reset = true;
- post_export_sync = false;
- midi_control_ui = 0;
- _step_editors = 0;
- no_questions_about_missing_files = false;
- _speakers.reset (new Speakers);
- _clicks_cleared = 0;
- ignore_route_processor_changes = false;
- _pre_export_mmc_enabled = false;
-
- AudioDiskstream::allocate_working_buffers();
-
- /* default short fade = 15ms */
-
- SndFileSource::setup_standard_crossfades (*this, frame_rate());
-
- 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.
- */
-
- click_length = 0;
- click_emphasis_length = 0;
- _clicking = false;
-
- process_function = &Session::process_with_events;
+ set_next_event ();
+ _all_route_group->set_active (true, this);
+ interpolation.add_channel_to (0, 0);
if (config.get_use_video_sync()) {
waiting_for_sync_offset = true;
@@ -239,32 +174,26 @@ Session::first_stage_init (string fullpath, string snapshot_name)
waiting_for_sync_offset = false;
}
- last_timecode_when = 0;
- last_timecode_valid = false;
-
- sync_time_vars ();
-
last_rr_session_dir = session_dirs.begin();
- refresh_disk_space ();
+
+ set_history_depth (Config->get_history_depth());
+
+ if (how_many_dsp_threads () > 1) {
+ /* For now, only create the graph if we are using >1 DSP threads, as
+ it is a bit slower than the old code with 1 thread.
+ */
+ _process_graph.reset (new Graph (*this));
+ }
/* default: assume simple stereo speaker configuration */
_speakers->setup_default_speakers (2);
- /* slave stuff */
-
- average_slave_delta = 1800; // !!! why 1800 ????
- have_first_delta_accumulator = false;
- delta_accumulator_cnt = 0;
- _slave_state = Stopped;
-
_solo_cut_control.reset (new ProxyControllable (_("solo cut control (dB)"), PBD::Controllable::GainLike,
boost::bind (&RCConfiguration::set_solo_mute_gain, Config, _1),
boost::bind (&RCConfiguration::get_solo_mute_gain, Config)));
add_controllable (_solo_cut_control);
- _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
-
/* These are all static "per-class" signals */
SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
@@ -277,87 +206,101 @@ Session::first_stage_init (string fullpath, string snapshot_name)
Delivery::disable_panners ();
IO::disable_connecting ();
+
+ AudioFileSource::set_peak_dir (_session_dir->peak_path());
}
int
-Session::second_stage_init ()
+Session::post_engine_init ()
{
- AudioFileSource::set_peak_dir (_session_dir->peak_path());
+ BootMessage (_("Set block size and sample rate"));
- if (!_is_new) {
- if (load_state (_current_snapshot_name)) {
- return -1;
- }
- }
+ set_block_size (_engine.samples_per_cycle());
+ set_frame_rate (_engine.sample_rate());
+
+ n_physical_outputs = _engine.n_physical_outputs ();
+ n_physical_inputs = _engine.n_physical_inputs ();
+ BootMessage (_("Using configuration"));
+
+ _midi_ports = new MidiPortManager;
+ setup_midi_machine_control ();
+
if (_butler->start_thread()) {
return -1;
}
-
+
if (start_midi_thread ()) {
return -1;
}
+
+ setup_click_sounds (0);
+ setup_midi_control ();
- setup_midi_machine_control ();
-
- // 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(), Stateful::loading_state_version)) {
- return -1;
- }
- } else {
- setup_raid_path(_path);
- }
+ _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
+ _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
- /* 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.
+ try {
+ /* tempo map requires sample rate knowledge */
- 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.
- */
+ _tempo_map = new TempoMap (_current_frame_rate);
+ _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+
+ /* MidiClock requires a tempo map */
- _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
+ midi_clock = new MidiClockTicker ();
+ midi_clock->set_session (this);
- _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
- _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
- setup_click_sounds (0);
- setup_midi_control ();
+ /* crossfades require sample rate knowledge */
- /* Pay attention ... */
+ SndFileSource::setup_standard_crossfades (*this, frame_rate());
+ _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
+
+ AudioDiskstream::allocate_working_buffers();
+ refresh_disk_space ();
+
+ /* we're finally ready to call set_state() ... all objects have
+ * been created, the engine is running.
+ */
+
+ if (state_tree) {
+ if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
+ return -1;
+ }
+ } else {
+ // set_state() will call setup_raid_path(), but if it's a new session we need
+ // to call setup_raid_path() here.
+ setup_raid_path (_path);
+ }
- _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
- _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
+ /* ENGINE */
- midi_clock = new MidiClockTicker ();
- midi_clock->set_session (this);
+ boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
+ boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
+
+ Config->map_parameters (ff);
+ config.map_parameters (ft);
- try {
when_engine_running ();
- }
-
- /* handle this one in a different way than all others, so that its clear what happened */
- catch (AudioEngine::PortRegistrationFailure& err) {
+ _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
+ _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
+
+ } catch (AudioEngine::PortRegistrationFailure& err) {
+ /* handle this one in a different way than all others, so that its clear what happened */
error << err.what() << endmsg;
return -1;
- }
-
- catch (...) {
+ } catch (...) {
return -1;
}
BootMessage (_("Reset Remote Controls"));
- send_full_time_code (0);
+ // send_full_time_code (0);
_engine.transport_locate (0);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (Timecode::Time ()));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
+ _mmc->send (MIDI::MachineControlCommand (Timecode::Time ()));
MIDI::Name::MidiPatchManager::instance().set_session (this);
@@ -366,18 +309,16 @@ Session::second_stage_init ()
_state_of_the_state = Clean;
- Port::set_connecting_blocked (false);
-
DirtyChanged (); /* EMIT SIGNAL */
- if (state_was_pending) {
- save_state (_current_snapshot_name);
+ if (_is_new) {
+ save_state ("");
+ } else if (state_was_pending) {
+ save_state ("");
remove_pending_capture_state ();
state_was_pending = false;
}
- BootMessage (_("Session loading complete"));
-
return 0;
}
@@ -604,8 +545,6 @@ Session::create (const string& session_template, BusProfile* bus_profile)
add_monitor_section ();
}
- save_state ("");
-
return 0;
}
@@ -684,62 +623,6 @@ Session::remove_state (string snapshot_name)
}
}
-#ifdef HAVE_JACK_SESSION
-void
-Session::jack_session_event (jack_session_event_t * event)
-{
- char timebuf[128], *tmp;
- time_t n;
- struct tm local_time;
-
- time (&n);
- localtime_r (&n, &local_time);
- strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time);
-
- while ((tmp = strchr(timebuf, ':'))) { *tmp = '.'; }
-
- if (event->type == JackSessionSaveTemplate)
- {
- if (save_template( timebuf )) {
- event->flags = JackSessionSaveError;
- } else {
- string cmd ("ardour3 -P -U ");
- cmd += event->client_uuid;
- cmd += " -T ";
- cmd += timebuf;
-
- event->command_line = strdup (cmd.c_str());
- }
- }
- else
- {
- if (save_state (timebuf)) {
- event->flags = JackSessionSaveError;
- } else {
- std::string xml_path (_session_dir->root_path());
- std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix;
- xml_path = Glib::build_filename (xml_path, legalized_filename);
-
- string cmd ("ardour3 -P -U ");
- cmd += event->client_uuid;
- cmd += " \"";
- cmd += xml_path;
- cmd += '\"';
-
- event->command_line = strdup (cmd.c_str());
- }
- }
-
- jack_session_reply (_engine.jack(), event);
-
- if (event->type == JackSessionSaveAndQuit) {
- Quit (); /* EMIT SIGNAL */
- }
-
- jack_session_event_free( event );
-}
-#endif
-
/** @param snapshot_name Name to save under, without .ardour / .pending prefix */
int
Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
@@ -1034,6 +917,15 @@ Session::state (bool full_state)
/* various options */
+ list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
+ if (!midi_port_nodes.empty()) {
+ XMLNode* midi_port_stuff = new XMLNode ("MIDIPorts");
+ for (list<XMLNode*>::const_iterator n = midi_port_nodes.begin(); n != midi_port_nodes.end(); ++n) {
+ midi_port_stuff->add_child_nocopy (**n);
+ }
+ node->add_child_nocopy (*midi_port_stuff);
+ }
+
node->add_child_nocopy (config.get_variables ());
node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state());
@@ -1239,6 +1131,11 @@ Session::set_state (const XMLNode& node, int version)
Evoral::init_event_id_counter (atoi (prop->value()));
}
+
+ if ((child = find_named_node (node, "MIDIPorts")) != 0) {
+ _midi_ports->set_midi_port_states (child->children());
+ }
+
IO::disable_connecting ();
Stateful::save_extra_xml (node);
@@ -3453,11 +3350,11 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
- MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
+ _mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
} else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
- MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
+ _mmc->set_send_device_id (Config->get_mmc_send_device_id());
} else if (p == "midi-control") {
@@ -3520,7 +3417,7 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "send-mmc") {
- MIDI::Manager::instance()->mmc()->enable_send (Config->get_send_mmc ());
+ _mmc->enable_send (Config->get_send_mmc ());
} else if (p == "midi-feedback") {
@@ -3578,13 +3475,13 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "initial-program-change") {
- if (MIDI::Manager::instance()->mmc()->output_port() && Config->get_initial_program_change() >= 0) {
+ if (_mmc->output_port() && Config->get_initial_program_change() >= 0) {
MIDI::byte buf[2];
buf[0] = MIDI::program; // channel zero by default
buf[1] = (Config->get_initial_program_change() & 0x7f);
- MIDI::Manager::instance()->mmc()->output_port()->midimsg (buf, sizeof (buf), 0);
+ _mmc->output_port()->midimsg (buf, sizeof (buf), 0);
}
} else if (p == "solo-mute-override") {
// catch_up_on_solo_mute_override ();
@@ -3648,27 +3545,28 @@ Session::load_diskstreams_2X (XMLNode const & node, int)
void
Session::setup_midi_machine_control ()
{
- MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
-
- mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
- mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
- mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
- mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
- mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
- mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
- mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
- mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
- mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
- mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
- mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
+ _mmc = new MIDI::MachineControl;
+ _mmc->set_ports (_midi_ports->mmc_input_port(), _midi_ports->mmc_output_port());
+
+ _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
+ _mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
+ _mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
+ _mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
+ _mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
+ _mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
+ _mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
+ _mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
+ _mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
+ _mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
+ _mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
/* also handle MIDI SPP because its so common */
- mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
- mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
- mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
+ _mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
+ _mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
+ _mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
}
boost::shared_ptr<Controllable>