X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_state.cc;h=efd2d180f284232d59dced08d0225ff5e802dd5f;hb=4ebea3867e782fd34ef7f76a86e2665595c59020;hp=9fb20973e29370aebcb7332ae82a9379bdc87566;hpb=8b46567e0677eb25c965ed46b80da8808fa33b2b;p=ardour.git diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 9fb20973e2..efd2d180f2 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include @@ -40,8 +39,8 @@ #ifdef HAVE_SYS_VFS_H #include #else -#include #include +#include #endif #include @@ -54,6 +53,8 @@ #include #include #include +#include +#include #include #include @@ -62,7 +63,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -81,6 +83,11 @@ #include #include #include +#include +#include +#include + +#include #include "i18n.h" #include @@ -93,14 +100,17 @@ 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)) { + 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] != '/') { @@ -112,17 +122,16 @@ Session::first_stage_init (string fullpath, string snapshot_name) */ _name = _current_snapshot_name = snapshot_name; - setup_raid_path (_path); _current_frame_rate = _engine.frame_rate (); _tempo_map = new TempoMap (_current_frame_rate); _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed)); g_atomic_int_set (&processing_prohibited, 0); - send_cnt = 0; insert_cnt = 0; _transport_speed = 0; _last_transport_speed = 0; + auto_play_legal = false; transport_sub_state = 0; _transport_frame = 0; last_stop_frame = 0; @@ -130,16 +139,8 @@ Session::first_stage_init (string fullpath, string snapshot_name) start_location = new Location (0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart))); _end_location_is_free = true; g_atomic_int_set (&_record_status, Disabled); - auto_play = false; - punch_in = false; - punch_out = false; - auto_loop = false; - seamless_loop = false; loop_changing = false; - auto_input = true; - crossfades_active = false; - all_safe = false; - auto_return = false; + play_loop = false; _last_roll_location = 0; _last_record_location = 0; pending_locate_frame = 0; @@ -152,23 +153,19 @@ Session::first_stage_init (string fullpath, string snapshot_name) outbound_mtc_smpte_frame = 0; next_quarter_frame_to_send = -1; current_block_size = 0; - _solo_latched = true; - _solo_model = InverseMute; solo_update_disabled = false; currently_soloing = false; _have_captured = false; _worst_output_latency = 0; _worst_input_latency = 0; _worst_track_latency = 0; - _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading); + _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading|Deletion); _slave = 0; - _slave_type = None; butler_mixdown_buffer = 0; butler_gain_buffer = 0; - auditioner = 0; - mmc_control = false; - midi_control = true; mmc = 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 (&butler_active, 0); @@ -176,81 +173,64 @@ Session::first_stage_init (string fullpath, string snapshot_name) g_atomic_int_set (&_capture_load, 100); g_atomic_int_set (&_playback_load_min, 100); g_atomic_int_set (&_capture_load_min, 100); - pending_audition_region = 0; - _edit_mode = Slide; - pending_edit_mode = _edit_mode; _play_range = false; - _control_out = 0; - _master_out = 0; - input_auto_connect = AutoConnectOption (0); - output_auto_connect = AutoConnectOption (0); waiting_to_start = false; _exporting = false; _gain_automation_buffer = 0; _pan_automation_buffer = 0; _npan_buffers = 0; pending_abort = false; - layer_model = MoveAddHigher; - xfade_model = ShortCrossfade; destructive_index = 0; + current_trans = 0; + first_file_data_format_reset = true; + first_file_header_format_reset = true; + butler_thread = (pthread_t) 0; + midi_thread = (pthread_t) 0; - /* allocate conversion buffers */ - _conversion_buffers[ButlerContext] = new char[AudioDiskstream::disk_io_frames() * 4]; - _conversion_buffers[TransportContext] = new char[AudioDiskstream::disk_io_frames() * 4]; + AudioDiskstream::allocate_working_buffers(); /* default short fade = 15ms */ - Crossfade::set_short_xfade_length ((jack_nframes_t) floor ((15.0 * frame_rate()) / 1000.0)); - DestructiveFileSource::setup_standard_crossfades (frame_rate()); + Crossfade::set_short_xfade_length ((nframes_t) floor (Config->get_short_xfade_seconds() * frame_rate())); + SndFileSource::setup_standard_crossfades (frame_rate()); last_mmc_step.tv_sec = 0; last_mmc_step.tv_usec = 0; step_speed = 0.0; - preroll.type = AnyTime::Frames; - preroll.frames = 0; - postroll.type = AnyTime::Frames; - postroll.frames = 0; - /* click sounds are unset by default, which causes us to internal waveforms for clicks. */ - _click_io = 0; - _clicking = false; - click_requested = false; click_data = 0; click_emphasis_data = 0; click_length = 0; click_emphasis_length = 0; + _clicking = false; process_function = &Session::process_with_events; + if (Config->get_use_video_sync()) { + waiting_for_sync_offset = true; + } else { + waiting_for_sync_offset = false; + } + + _current_frame_rate = 48000; + _base_frame_rate = 48000; + 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 */ - /* default configuration */ - - do_not_record_plugins = false; - over_length_short = 2; - over_length_long = 10; - send_midi_timecode = false; - send_midi_machine_control = false; - shuttle_speed_factor = 1.0; - shuttle_speed_threshold = 5; - rf_speed = 2.0; - _meter_hold = 100; // XXX unknown units: number of calls to meter::set() - _meter_falloff = 1.5f; // XXX unknown units: refresh_rate - max_level = 0; - min_level = 0; - /* slave stuff */ average_slave_delta = 1800; @@ -258,23 +238,18 @@ Session::first_stage_init (string fullpath, string snapshot_name) delta_accumulator_cnt = 0; slave_state = Stopped; - /* default SMPTE type is 30 FPS, non-drop */ - - set_smpte_type (30.0, false); - _engine.GraphReordered.connect (mem_fun (*this, &Session::graph_reordered)); /* These are all static "per-class" signals */ - Region::CheckNewRegion.connect (mem_fun (*this, &Session::add_region)); - AudioSource::AudioSourceCreated.connect (mem_fun (*this, &Session::add_audio_source)); - Playlist::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); + 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)); Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect)); - AudioDiskstream::AudioDiskstreamCreated.connect (mem_fun (*this, &Session::add_diskstream)); NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection)); + AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list)); - Controllable::Created.connect (mem_fun (*this, &Session::add_controllable)); - Controllable::GoingAway.connect (mem_fun (*this, &Session::remove_controllable)); + Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable)); IO::MoreOutputs.connect (mem_fun (*this, &Session::ensure_passthru_buffers)); @@ -305,10 +280,14 @@ Session::second_stage_init (bool new_session) 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, @@ -334,10 +313,20 @@ Session::second_stage_init (bool new_session) _engine.Halted.connect (mem_fun (*this, &Session::engine_halted)); _engine.Xrun.connect (mem_fun (*this, &Session::xrun_recovery)); - if (_engine.running()) { + try { when_engine_running(); - } else { - first_time_running = _engine.Running.connect (mem_fun (*this, &Session::when_engine_running)); + } + + /* handle this one in a different way than all others, so that its clear what happened */ + + catch (AudioEngine::PortRegistrationFailure& err) { + error << _("Unable to create all required ports") + << endmsg; + return -1; + } + + catch (...) { + return -1; } send_full_time_code (); @@ -369,14 +358,6 @@ Session::raid_path () const return path.substr (0, path.length() - 1); // drop final colon } -void -Session::set_raid_path (string path) -{ - /* public-access to setup_raid_path() */ - - setup_raid_path (path); -} - void Session::setup_raid_path (string path) { @@ -417,16 +398,8 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; - fspath += ':'; - /* tape dir */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; + fspath += sound_dir (false); AudioFileSource::set_search_path (fspath); @@ -447,16 +420,7 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; - fspath += ':'; - - /* add tape dir to file search path */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; + fspath += sound_dir (false); fspath += ':'; remaining = remaining.substr (colon+1); @@ -472,15 +436,9 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; + fspath += sound_dir (false); fspath += ':'; - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; - session_dirs.push_back (sp); } @@ -494,125 +452,98 @@ Session::setup_raid_path (string path) } int -Session::create (bool& new_session, string* mix_template, jack_nframes_t initial_length) +Session::create (bool& new_session, string* mix_template, nframes_t initial_length) { string dir; - - if (mkdir (_path.c_str(), 0755) < 0) { - if (errno == EEXIST) { - new_session = false; - } else { - error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; - return -1; - } - } else { - new_session = true; + + if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; + return -1; } dir = peak_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - dir = sound_dir (); + /* if this is is an existing session with an old "sounds" directory, just use it. see Session::sound_dir() for more details */ - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } - } + if (!Glib::file_test (old_sound_dir(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) { - dir = tape_dir (); - - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session tape dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + dir = sound_dir (); + + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } } dir = dead_sound_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - dir = automation_dir (); + dir = export_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session export dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - + /* check new_session so we don't overwrite an existing one */ - + if (mix_template) { - if (new_session){ - std::string in_path = *mix_template; + std::string in_path = *mix_template; + + ifstream in(in_path.c_str()); + + if (in){ + string out_path = _path; + out_path += _name; + out_path += _statefile_suffix; + + ofstream out(out_path.c_str()); + + if (out){ + out << in.rdbuf(); + + // okay, session is set up. Treat like normal saved + // session from now on. + + new_session = false; + return 0; - ifstream in(in_path.c_str()); - - if (in){ - string out_path = _path; - out_path += _name; - out_path += _statefile_suffix; - - ofstream out(out_path.c_str()); - - if (out){ - out << in.rdbuf(); - - // okay, session is set up. Treat like normal saved - // session from now on. - - new_session = false; - return 0; - - } else { - error << string_compose (_("Could not open %1 for writing mix template"), out_path) - << endmsg; - return -1; - } - } else { - error << string_compose (_("Could not open mix template %1 for reading"), in_path) - << endmsg; + error << string_compose (_("Could not open %1 for writing mix template"), out_path) + << endmsg; return -1; } - - + } else { - warning << _("Session already exists. Not overwriting") << endmsg; + error << string_compose (_("Could not open mix template %1 for reading"), in_path) + << endmsg; return -1; } + } - if (new_session) { + /* set initial start + end point */ - /* set initial start + end point */ + start_location->set_end (0); + _locations.add (start_location); - start_location->set_end (0); - _locations.add (start_location); + end_location->set_end (initial_length); + _locations.add (end_location); - end_location->set_end (initial_length); - _locations.add (end_location); - - _state_of_the_state = Clean; + _state_of_the_state = Clean; - if (save_state (_current_snapshot_name)) { - return -1; - } + if (save_state (_current_snapshot_name)) { + return -1; } return 0; @@ -628,11 +559,10 @@ Session::load_diskstreams (const XMLNode& node) for (citer = clist.begin(); citer != clist.end(); ++citer) { - AudioDiskstream* dstream; try { - dstream = new AudioDiskstream (*this, **citer); - /* added automatically by AudioDiskstreamCreated handler */ + boost::shared_ptr dstream (new AudioDiskstream (*this, **citer)); + add_diskstream (dstream); } catch (failed_constructor& err) { @@ -644,6 +574,14 @@ Session::load_diskstreams (const XMLNode& node) return 0; } +void +Session::maybe_write_autosave() +{ + if (dirty() && record_status() != Recording) { + save_state("", true); + } +} + void Session::remove_pending_capture_state () { @@ -656,6 +594,48 @@ Session::remove_pending_capture_state () unlink (xml_path.c_str()); } +/** Rename a state file. + * @param snapshot_name Snapshot name. + */ +void +Session::rename_state (string old_name, string new_name) +{ + if (old_name == _current_snapshot_name || old_name == _name) { + /* refuse to rename the current snapshot or the "main" one */ + return; + } + + const string old_xml_path = _path + old_name + _statefile_suffix; + const string new_xml_path = _path + new_name + _statefile_suffix; + + if (rename (old_xml_path.c_str(), new_xml_path.c_str()) != 0) { + error << string_compose(_("could not rename snapshot %1 to %2"), old_name, new_name) << endmsg; + } +} + +/** Remove a state file. + * @param snapshot_name Snapshot name. + */ +void +Session::remove_state (string snapshot_name) +{ + if (snapshot_name == _current_snapshot_name || snapshot_name == _name) { + /* refuse to remove the current snapshot or the "main" one */ + return; + } + + const string xml_path = _path + snapshot_name + _statefile_suffix; + + /* make a backup copy of the state file */ + const string bak_path = xml_path + ".bak"; + if (g_file_test (xml_path.c_str(), G_FILE_TEST_EXISTS)) { + copy_file (xml_path, bak_path); + } + + /* and delete it */ + unlink (xml_path.c_str()); +} + int Session::save_state (string snapshot_name, bool pending) { @@ -667,6 +647,12 @@ Session::save_state (string snapshot_name, bool pending) 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; + } + tree.set_root (&get_state()); if (snapshot_name.empty()) { @@ -675,50 +661,54 @@ Session::save_state (string snapshot_name, bool pending) if (!pending) { + /* proper save: use _statefile_suffix (.ardour in English) */ xml_path = _path; xml_path += snapshot_name; xml_path += _statefile_suffix; + + /* make a backup copy of the old file */ bak_path = xml_path; bak_path += ".bak"; - // Make backup of state file - - if ((access (xml_path.c_str(), F_OK) == 0) && - (rename(xml_path.c_str(), bak_path.c_str()))) { - error << _("could not backup old state file, current state not saved.") << endmsg; - return -1; + if (g_file_test (xml_path.c_str(), G_FILE_TEST_EXISTS)) { + copy_file (xml_path, bak_path); } } else { + /* pending save: use _pending_suffix (.pending in English) */ xml_path = _path; xml_path += snapshot_name; xml_path += _pending_suffix; } - if (!tree.write (xml_path)) { - error << string_compose (_("state could not be saved to %1"), xml_path) << endmsg; + string tmp_path; - /* don't leave a corrupt file lying around if it is - possible to fix. - */ + tmp_path = _path; + tmp_path += snapshot_name; + tmp_path += ".tmp"; - if (unlink (xml_path.c_str())) { - error << string_compose (_("could not remove corrupt state file %1"), xml_path) << endmsg; - } else { - if (!pending) { - if (rename (bak_path.c_str(), xml_path.c_str())) { - error << string_compose (_("could not restore state file from backup %1"), bak_path) << endmsg; - } - } - } + cerr << "actually writing state to " << xml_path << endl; + if (!tree.write (tmp_path)) { + error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg; + unlink (tmp_path.c_str()); return -1; + + } else { + + if (rename (tmp_path.c_str(), xml_path.c_str()) != 0) { + error << string_compose (_("could not rename temporary session file %1 to %2"), tmp_path, xml_path) << endmsg; + unlink (tmp_path.c_str()); + return -1; + } } if (!pending) { + save_history (snapshot_name); + bool was_dirty = dirty(); _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); @@ -761,7 +751,7 @@ Session::load_state (string snapshot_name) xmlpath += snapshot_name; xmlpath += _pending_suffix; - if (!access (xmlpath.c_str(), F_OK)) { + if (Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { /* there is pending state from a crashed capture attempt */ @@ -776,8 +766,8 @@ Session::load_state (string snapshot_name) xmlpath += snapshot_name; xmlpath += _statefile_suffix; } - - if (access (xmlpath.c_str(), F_OK)) { + + if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg; return 1; } @@ -786,483 +776,95 @@ Session::load_state (string snapshot_name) set_dirty(); - if (state_tree->read (xmlpath)) { - return 0; - } else { + if (!state_tree->read (xmlpath)) { error << string_compose(_("Could not understand ardour file %1"), xmlpath) << endmsg; + delete state_tree; + state_tree = 0; + return -1; } - delete state_tree; - state_tree = 0; - return -1; -} - -int -Session::load_options (const XMLNode& node) -{ - XMLNode* child; - XMLProperty* prop; - bool have_fade_msecs = false; - bool have_fade_steepness = false; - float fade_msecs = 0; - float fade_steepness = 0; - SlaveSource slave_src = None; - int x; - LocaleGuard lg (X_("POSIX")); + XMLNode& root (*state_tree->root()); - if ((child = find_named_node (node, "input-auto-connect")) != 0) { - if ((prop = child->property ("val")) != 0) { - sscanf (prop->value().c_str(), "%x", &x); - input_auto_connect = AutoConnectOption (x); - } - } - - if ((child = find_named_node (node, "output-auto-connect")) != 0) { - if ((prop = child->property ("val")) != 0) { - sscanf (prop->value().c_str(), "%x", &x); - output_auto_connect = AutoConnectOption (x); - } - } - - if ((child = find_named_node (node, "slave")) != 0) { - if ((prop = child->property ("type")) != 0) { - if (prop->value() == "none") { - slave_src = None; - } else if (prop->value() == "mtc") { - slave_src = MTC; - } else if (prop->value() == "jack") { - slave_src = JACK; - } - set_slave_source (slave_src, 0); - } + if (root.name() != X_("Session")) { + error << string_compose (_("Session file %1 is not an Ardour session"), xmlpath) << endmsg; + delete state_tree; + state_tree = 0; + return -1; } - /* we cannot set edit mode if we are loading a session, - because it might destroy the playlist's positioning - */ - - if ((child = find_named_node (node, "edit-mode")) != 0) { - if ((prop = child->property ("val")) != 0) { - if (prop->value() == "slide") { - pending_edit_mode = Slide; - } else if (prop->value() == "splice") { - pending_edit_mode = Splice; - } - } - } - - if ((child = find_named_node (node, "send-midi-timecode")) != 0) { - if ((prop = child->property ("val")) != 0) { - bool x = (prop->value() == "yes"); - send_mtc = !x; /* force change in value */ - set_send_mtc (x); - } - } - if ((child = find_named_node (node, "send-midi-machine-control")) != 0) { - if ((prop = child->property ("val")) != 0) { - bool x = (prop->value() == "yes"); - send_mmc = !x; /* force change in value */ - set_send_mmc (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "max-level")) != 0) { - if ((prop = child->property ("val")) != 0) { - max_level = atoi (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "min-level")) != 0) { - if ((prop = child->property ("val")) != 0) { - min_level = atoi (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "meter-hold")) != 0) { - if ((prop = child->property ("val")) != 0) { - _meter_hold = atof (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "meter-falloff")) != 0) { - if ((prop = child->property ("val")) != 0) { - _meter_falloff = atof (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "long-over-length")) != 0) { - if ((prop = child->property ("val")) != 0) { - over_length_long = atoi (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "short-over-length")) != 0) { - if ((prop = child->property ("val")) != 0) { - over_length_short = atoi (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "shuttle-speed-factor")) != 0) { - if ((prop = child->property ("val")) != 0) { - shuttle_speed_factor = atof (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "shuttle-speed-threshold")) != 0) { - if ((prop = child->property ("val")) != 0) { - shuttle_speed_threshold = atof (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "rf-speed")) != 0) { - if ((prop = child->property ("val")) != 0) { - rf_speed = atof (prop->value().c_str()); - } - } - if ((child = find_named_node (node, "smpte-frames-per-second")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_smpte_type( atof (prop->value().c_str()), smpte_drop_frames ); - } - } - if ((child = find_named_node (node, "smpte-drop-frames")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_smpte_type( smpte_frames_per_second, (prop->value() == "yes") ); - } - } - if ((child = find_named_node (node, "smpte-offset")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_smpte_offset( atoi (prop->value().c_str()) ); - } - } - if ((child = find_named_node (node, "smpte-offset-negative")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_smpte_offset_negative( (prop->value() == "yes") ); - } - } - if ((child = find_named_node (node, "click-sound")) != 0) { - if ((prop = child->property ("val")) != 0) { - click_sound = prop->value(); - } - } - if ((child = find_named_node (node, "click-emphasis-sound")) != 0) { - if ((prop = child->property ("val")) != 0) { - click_emphasis_sound = prop->value(); - } - } + const XMLProperty* prop; + bool is_old = false; - if ((child = find_named_node (node, "solo-model")) != 0) { - if ((prop = child->property ("val")) != 0) { - if (prop->value() == "SoloBus") - _solo_model = SoloBus; - else - _solo_model = InverseMute; + 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()); // grab just the first number before the period + if (major_version < 2) { + is_old = true; } } - /* BOOLEAN OPTIONS */ + if (is_old) { + string backup_path; - if ((child = find_named_node (node, "auto-play")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_auto_play (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "auto-input")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_auto_input (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "seamless-loop")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_seamless_loop (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "punch-in")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_punch_in (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "punch-out")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_punch_out (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "auto-return")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_auto_return (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "send-mtc")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_send_mtc (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "mmc-control")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_mmc_control (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "midi-control")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_midi_control (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "midi-feedback")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_midi_feedback (prop->value() == "yes"); - } - } - // Legacy support for - if ((child = find_named_node (node, "recording-plugins")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_do_not_record_plugins (prop->value() == "no"); - } - } - if ((child = find_named_node (node, "do-not-record-plugins")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_do_not_record_plugins (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "crossfades-active")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_crossfades_active (prop->value() == "yes"); - } - } - if ((child = find_named_node (node, "audible-click")) != 0) { - if ((prop = child->property ("val")) != 0) { - set_clicking (prop->value() == "yes"); - } - } + backup_path = _path; + backup_path += snapshot_name; + backup_path += "-1"; + backup_path += _statefile_suffix; - if ((child = find_named_node (node, "end-marker-is-free")) != 0) { - if ((prop = child->property ("val")) != 0) { - _end_location_is_free = (prop->value() == "yes"); - } - } + info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"), + xmlpath, backup_path) + << endmsg; - if ((child = find_named_node (node, "layer-model")) != 0) { - if ((prop = child->property ("val")) != 0) { - if (prop->value() == X_("LaterHigher")) { - set_layer_model (LaterHigher); - } else if (prop->value() == X_("AddHigher")) { - set_layer_model (AddHigher); - } else { - set_layer_model (MoveAddHigher); - } - } - } + copy_file (xmlpath, backup_path); - if ((child = find_named_node (node, "xfade-model")) != 0) { - if ((prop = child->property ("val")) != 0) { - if (prop->value() == X_("Short")) { - set_xfade_model (ShortCrossfade); - } else { - set_xfade_model (FullCrossfade); - } - } + /* if it fails, don't worry. right? */ } - if ((child = find_named_node (node, "short-xfade-length")) != 0) { - if ((prop = child->property ("val")) != 0) { - /* value is stored as a fractional seconds */ - float secs = atof (prop->value().c_str()); - Crossfade::set_short_xfade_length ((jack_nframes_t) floor (secs * frame_rate())); - } - } + return 0; +} - if ((child = find_named_node (node, "full-xfades-unmuted")) != 0) { - if ((prop = child->property ("val")) != 0) { - crossfades_active = (prop->value() == "yes"); - } - } +int +Session::load_options (const XMLNode& node) +{ + XMLNode* child; + XMLProperty* prop; + LocaleGuard lg (X_("POSIX")); - /* TIED OPTIONS */ + Config->set_variables (node, ConfigVariableBase::Session); - if ((child = find_named_node (node, "default-fade-steepness")) != 0) { - if ((prop = child->property ("val")) != 0) { - fade_steepness = atof (prop->value().c_str()); - have_fade_steepness = true; - } - } - if ((child = find_named_node (node, "default-fade-msec")) != 0) { + if ((child = find_named_node (node, "end-marker-is-free")) != 0) { if ((prop = child->property ("val")) != 0) { - fade_msecs = atof (prop->value().c_str()); - have_fade_msecs = true; + _end_location_is_free = (prop->value() == "yes"); } } - if (have_fade_steepness || have_fade_msecs) { - // set_default_fade (fade_steepness, fade_msecs); - } - return 0; } +bool +Session::save_config_options_predicate (ConfigVariableBase::Owner owner) const +{ + const ConfigVariableBase::Owner modified_by_session_or_user = (ConfigVariableBase::Owner) + (ConfigVariableBase::Session|ConfigVariableBase::Interface); + + return owner & modified_by_session_or_user; +} + XMLNode& Session::get_options () const { - XMLNode* opthead; XMLNode* child; - char buf[32]; LocaleGuard lg (X_("POSIX")); - opthead = new XMLNode ("Options"); - - SlaveSource src = slave_source (); - string src_string; - switch (src) { - case None: - src_string = "none"; - break; - case MTC: - src_string = "mtc"; - break; - case JACK: - src_string = "jack"; - break; - } - child = opthead->add_child ("slave"); - child->add_property ("type", src_string); - - child = opthead->add_child ("send-midi-timecode"); - child->add_property ("val", send_midi_timecode?"yes":"no"); - - child = opthead->add_child ("send-midi-machine-control"); - child->add_property ("val", send_midi_machine_control?"yes":"no"); - - snprintf (buf, sizeof(buf)-1, "%x", (int) input_auto_connect); - child = opthead->add_child ("input-auto-connect"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%x", (int) output_auto_connect); - child = opthead->add_child ("output-auto-connect"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%d", max_level); - child = opthead->add_child ("max-level"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%d", min_level); - child = opthead->add_child ("min-level"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%f", _meter_hold); - child = opthead->add_child ("meter-hold"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%f", _meter_falloff); - child = opthead->add_child ("meter-falloff"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%u", over_length_long); - child = opthead->add_child ("long-over-length"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%u", over_length_short); - child = opthead->add_child ("short-over-length"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%f", shuttle_speed_factor); - child = opthead->add_child ("shuttle-speed-factor"); - child->add_property ("val", buf); + XMLNode& option_root = Config->get_variables (mem_fun (*this, &Session::save_config_options_predicate)); - snprintf (buf, sizeof(buf)-1, "%f", shuttle_speed_threshold); - child = opthead->add_child ("shuttle-speed-threshold"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%f", rf_speed); - child = opthead->add_child ("rf-speed"); - child->add_property ("val", buf); - - snprintf (buf, sizeof(buf)-1, "%.2f", smpte_frames_per_second); - child = opthead->add_child ("smpte-frames-per-second"); - child->add_property ("val", buf); - - child = opthead->add_child ("smpte-drop-frames"); - child->add_property ("val", smpte_drop_frames ? "yes" : "no"); - - snprintf (buf, sizeof(buf)-1, "%u", smpte_offset ()); - child = opthead->add_child ("smpte-offset"); - child->add_property ("val", buf); - - child = opthead->add_child ("smpte-offset-negative"); - child->add_property ("val", smpte_offset_negative () ? "yes" : "no"); - - child = opthead->add_child ("edit-mode"); - switch (_edit_mode) { - case Splice: - child->add_property ("val", "splice"); - break; - - case Slide: - child->add_property ("val", "slide"); - break; - } - - child = opthead->add_child ("auto-play"); - child->add_property ("val", get_auto_play () ? "yes" : "no"); - child = opthead->add_child ("auto-input"); - child->add_property ("val", get_auto_input () ? "yes" : "no"); - child = opthead->add_child ("seamless-loop"); - child->add_property ("val", get_seamless_loop () ? "yes" : "no"); - child = opthead->add_child ("punch-in"); - child->add_property ("val", get_punch_in () ? "yes" : "no"); - child = opthead->add_child ("punch-out"); - child->add_property ("val", get_punch_out () ? "yes" : "no"); - child = opthead->add_child ("all-safe"); - child->add_property ("val", get_all_safe () ? "yes" : "no"); - child = opthead->add_child ("auto-return"); - child->add_property ("val", get_auto_return () ? "yes" : "no"); - child = opthead->add_child ("mmc-control"); - child->add_property ("val", get_mmc_control () ? "yes" : "no"); - child = opthead->add_child ("midi-control"); - child->add_property ("val", get_midi_control () ? "yes" : "no"); - child = opthead->add_child ("midi-feedback"); - child->add_property ("val", get_midi_feedback () ? "yes" : "no"); - child = opthead->add_child ("do-not-record-plugins"); - child->add_property ("val", get_do_not_record_plugins () ? "yes" : "no"); - child = opthead->add_child ("auto-crossfade"); - child->add_property ("val", get_crossfades_active () ? "yes" : "no"); - child = opthead->add_child ("audible-click"); - child->add_property ("val", get_clicking () ? "yes" : "no"); - child = opthead->add_child ("end-marker-is-free"); + child = option_root.add_child ("end-marker-is-free"); child->add_property ("val", _end_location_is_free ? "yes" : "no"); - if (click_sound.length()) { - child = opthead->add_child ("click-sound"); - child->add_property ("val", click_sound); - } - - if (click_emphasis_sound.length()) { - child = opthead->add_child ("click-emphasis-sound"); - child->add_property ("val", click_emphasis_sound); - } - - child = opthead->add_child ("solo-model"); - child->add_property ("val", _solo_model == SoloBus ? "SoloBus" : "InverseMute"); - - child = opthead->add_child ("layer-model"); - switch (layer_model) { - case LaterHigher: - child->add_property ("val", X_("LaterHigher")); - break; - case MoveAddHigher: - child->add_property ("val", X_("MoveAddHigher")); - break; - case AddHigher: - child->add_property ("val", X_("AddHigher")); - break; - } - - child = opthead->add_child ("xfade-model"); - switch (xfade_model) { - case FullCrossfade: - child->add_property ("val", X_("Full")); - break; - case ShortCrossfade: - child->add_property ("val", X_("Short")); - } - - child = opthead->add_child ("short-xfade-length"); - /* store as fractions of a second */ - snprintf (buf, sizeof(buf)-1, "%f", - (float) Crossfade::short_xfade_length() / frame_rate()); - child->add_property ("val", buf); - - child = opthead->add_child ("full-xfades-unmuted"); - child->add_property ("val", crossfades_active ? "yes" : "no"); - - return *opthead; + return option_root; } XMLNode& @@ -1333,6 +935,13 @@ Session::state(bool full_state) } } + /* save the ID counter */ + + snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter()); + node->add_property ("id-counter", buf); + + /* various options */ + node->add_child_nocopy (get_options()); child = node->add_child ("Sources"); @@ -1344,16 +953,15 @@ Session::state(bool full_state) /* Don't save information about AudioFileSources that are empty */ - AudioFileSource* fs; + boost::shared_ptr fs; - if ((fs = dynamic_cast (siter->second)) != 0) { - DestructiveFileSource* dfs = dynamic_cast (fs); + if ((fs = boost::dynamic_pointer_cast (siter->second)) != 0) { /* destructive file sources are OK if they are empty, because we will re-use them every time. */ - if (!dfs) { + if (!fs->destructive()) { if (fs->length() == 0) { continue; } @@ -1382,15 +990,28 @@ Session::state(bool full_state) child = node->add_child ("DiskStreams"); { - Glib::RWLock::ReaderLock dl (diskstream_lock); - for (AudioDiskstreamList::iterator i = audio_diskstreams.begin(); i != audio_diskstreams.end(); ++i) { + boost::shared_ptr dsl = diskstreams.reader(); + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if (!(*i)->hidden()) { child->add_child_nocopy ((*i)->get_state()); } } } - node->add_child_nocopy (_locations.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()); + } child = node->add_child ("Connections"); { @@ -1404,10 +1025,10 @@ Session::state(bool full_state) child = node->add_child ("Routes"); { - Glib::RWLock::ReaderLock lm (route_lock); + boost::shared_ptr r = routes.reader (); RoutePublicOrderSorter cmp; - RouteList public_order(routes); + RouteList public_order (*r); public_order.sort (cmp); for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) { @@ -1475,6 +1096,8 @@ Session::state(bool full_state) 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); } @@ -1482,6 +1105,13 @@ Session::state(bool full_state) return *node; } +XMLNode& +Session::get_control_protocol_state () +{ + ControlProtocolManager& cpm (ControlProtocolManager::instance()); + return cpm.get_state(); +} + int Session::set_state (const XMLNode& node) { @@ -1492,16 +1122,31 @@ Session::set_state (const XMLNode& node) _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave); - if (node.name() != "Session"){ + if (node.name() != X_("Session")){ fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg; return -1; } - StateManager::prohibit_save (); - if ((prop = node.property ("name")) != 0) { _name = prop->value (); } + + setup_raid_path(_path); + + 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); + } + IO::disable_ports (); IO::disable_connecting (); @@ -1511,38 +1156,67 @@ Session::set_state (const XMLNode& node) MIDI Path extra - Options + Options/Config + Locations Sources AudioRegions AudioDiskstreams Connections - Locations Routes EditGroups MixGroups Click + ControlProtocols */ if (use_config_midi_ports ()) { } - if ((child = find_named_node (node, "Path")) != 0) { - /* XXX this XML content stuff horrible API design */ - string raid_path = _path + ':' + child->children().front()->content(); - setup_raid_path (raid_path); - } else { - /* the path is already set */ - } - if ((child = find_named_node (node, "extra")) != 0) { _extra_xml = new XMLNode (*child); } - if ((child = find_named_node (node, "Options")) == 0) { + 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; - } else if (load_options (*child)) { } + 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; @@ -1590,39 +1264,6 @@ Session::set_state (const XMLNode& node) goto out; } - 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; - } - - _locations.save_state (_("initial state")); - if ((child = find_named_node (node, "EditGroups")) == 0) { error << _("Session: XML state has no edit groups section") << endmsg; goto out; @@ -1657,9 +1298,9 @@ Session::set_state (const XMLNode& node) _click_io->set_state (*child); } - /* OK, now we can set edit mode */ - - set_edit_mode (pending_edit_mode); + if ((child = find_named_node (node, "ControlProtocols")) != 0) { + ControlProtocolManager::instance().set_protocol_states (*child); + } /* here beginneth the second phase ... */ @@ -1667,8 +1308,6 @@ Session::set_state (const XMLNode& node) _state_of_the_state = Clean; - StateManager::allow_save (_("initial state"), true); - if (state_was_pending) { save_state (_current_snapshot_name); remove_pending_capture_state (); @@ -1678,8 +1317,6 @@ Session::set_state (const XMLNode& node) return 0; out: - /* we failed, re-enable state saving but don't actually save internal state */ - StateManager::allow_save (X_("ignored"), false); return ret; } @@ -1688,7 +1325,7 @@ Session::load_routes (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - Route *route; + RouteList new_routes; nlist = node.children(); @@ -1696,28 +1333,34 @@ Session::load_routes (const XMLNode& node) for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((route = XMLRouteFactory (**niter)) == 0) { + boost::shared_ptr route (XMLRouteFactory (**niter)); + + if (route == 0) { error << _("Session: cannot create Route from XML description.") << endmsg; return -1; } - add_route (route); + new_routes.push_back (route); } + add_routes (new_routes); + return 0; } -Route * +boost::shared_ptr Session::XMLRouteFactory (const XMLNode& node) { if (node.name() != "Route") { - return 0; + return boost::shared_ptr ((Route*) 0); } if (node.property ("diskstream") != 0 || node.property ("diskstream-id") != 0) { - return new AudioTrack (*this, node); + boost::shared_ptr x (new AudioTrack (*this, node)); + return x; } else { - return new Route (*this, node); + boost::shared_ptr x (new Route (*this, node)); + return x; } } @@ -1726,7 +1369,7 @@ Session::load_regions (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - AudioRegion* region; + boost::shared_ptr region; nlist = node.children(); @@ -1737,32 +1380,38 @@ Session::load_regions (const XMLNode& node) error << _("Session: cannot create Region from XML description.") << endmsg; } } + return 0; } -AudioRegion * +boost::shared_ptr Session::XMLRegionFactory (const XMLNode& node, bool full) { const XMLProperty* prop; - Source* source; - AudioSource* as; - AudioRegion::SourceList sources; + boost::shared_ptr source; + boost::shared_ptr as; + SourceList sources; uint32_t nchans = 1; char buf[128]; if (node.name() != X_("Region")) { - return 0; + return boost::shared_ptr(); } 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 (); + } 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 0; + return boost::shared_ptr(); } } @@ -1770,13 +1419,13 @@ Session::XMLRegionFactory (const XMLNode& node, bool full) 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 0; + return boost::shared_ptr(); } - - as = dynamic_cast(source); + + as = boost::dynamic_pointer_cast(source); if (!as) { error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), s_id) << endmsg; - return 0; + return boost::shared_ptr(); } sources.push_back (as); @@ -1791,24 +1440,39 @@ Session::XMLRegionFactory (const XMLNode& node, bool full) if ((source = source_by_id (id2)) == 0) { error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg; - return 0; + return boost::shared_ptr(); } - as = dynamic_cast(source); + as = boost::dynamic_pointer_cast(source); if (!as) { error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg; - return 0; + return boost::shared_ptr(); } sources.push_back (as); } } try { - return new AudioRegion (sources, node); + boost::shared_ptr region (boost::dynamic_pointer_cast (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 sfp = boost::dynamic_pointer_cast (*sx); + if (sfp) { + sfp->set_length (region->length()); + } + } + } + + + return region; + } catch (failed_constructor& err) { - return 0; + return boost::shared_ptr(); } } @@ -1842,11 +1506,16 @@ Session::path_from_region_name (string name, string identifier) } else { snprintf (buf, sizeof(buf), "%s/%s-%" PRIu32 ".wav", dir.c_str(), name.c_str(), n); } - if (access (buf, F_OK) != 0) { + + if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { return buf; } } + 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 ""; } @@ -1856,7 +1525,7 @@ Session::load_sources (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - Source* source; + boost::shared_ptr source; nlist = node.children(); @@ -1864,33 +1533,36 @@ Session::load_sources (const XMLNode& node) for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((source = XMLSourceFactory (**niter)) == 0) { - error << _("Session: cannot create Source from XML description.") << endmsg; + try { + if ((source = XMLSourceFactory (**niter)) == 0) { + error << _("Session: cannot create Source from XML description.") << endmsg; + } + } + + catch (non_existent_source& err) { + 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; } -Source * +boost::shared_ptr Session::XMLSourceFactory (const XMLNode& node) { - Source *src = 0; - if (node.name() != "Source") { - return 0; + return boost::shared_ptr(); } try { - src = AudioFileSource::create (node); + return SourceFactory::create (*this, node); } - + catch (failed_constructor& err) { error << _("Found a sound file that cannot be used by Ardour. Talk to the progammers.") << endmsg; - return 0; + return boost::shared_ptr(); } - - return src; } int @@ -1909,7 +1581,7 @@ Session::save_template (string template_name) if ((dp = opendir (dir.c_str()))) { closedir (dp); } else { - if (mkdir (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)<0) { + if (g_mkdir_with_parents (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } @@ -1989,11 +1661,9 @@ Session::ensure_sound_dir (string path, string& result) /* Ensure that the parent directory exists */ - if (mkdir (path.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; - return -1; - } + if (g_mkdir_with_parents (path.c_str(), 0775)) { + error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; + return -1; } /* Ensure that the sounds directory exists */ @@ -2002,33 +1672,27 @@ Session::ensure_sound_dir (string path, string& result) result += '/'; result += sound_dir_name; - if (mkdir (result.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; - return -1; - } + if (g_mkdir_with_parents (result.c_str(), 0775)) { + error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; + return -1; } dead = path; dead += '/'; dead += dead_sound_dir_name; - if (mkdir (dead.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dead.c_str(), 0775)) { + error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; + return -1; } peak = path; peak += '/'; peak += peak_dir_name; - if (mkdir (peak.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; - return -1; - } + if (g_mkdir_with_parents (peak.c_str(), 0775)) { + error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; + return -1; } /* callers expect this to be terminated ... */ @@ -2043,12 +1707,6 @@ Session::discover_best_sound_dir (bool destructive) vector::iterator i; string result; - /* destructive files all go into the same place */ - - if (destructive) { - return tape_dir(); - } - /* handle common case without system calls */ if (session_dirs.size() == 1) { @@ -2156,7 +1814,7 @@ Session::load_playlists (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - Playlist *playlist; + boost::shared_ptr playlist; nlist = node.children(); @@ -2177,7 +1835,7 @@ Session::load_unused_playlists (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - Playlist *playlist; + boost::shared_ptr playlist; nlist = node.children(); @@ -2192,22 +1850,22 @@ Session::load_unused_playlists (const XMLNode& node) // now manually untrack it - track_playlist (playlist, false); + track_playlist (false, boost::weak_ptr (playlist)); } return 0; } -Playlist * +boost::shared_ptr Session::XMLPlaylistFactory (const XMLNode& node) { try { - return new AudioPlaylist (*this, node); + return PlaylistFactory::create (*this, node); } catch (failed_constructor& err) { - return 0; + return boost::shared_ptr(); } } @@ -2249,25 +1907,74 @@ Session::dead_sound_dir () const { string res = _path; res += dead_sound_dir_name; - res += '/'; + return res; } string -Session::sound_dir () const +Session::old_sound_dir (bool with_path) const { - string res = _path; - res += sound_dir_name; - res += '/'; + string res; + + if (with_path) { + res = _path; + } + + res += old_sound_dir_name; + return res; } string -Session::tape_dir () const +Session::sound_dir (bool with_path) const { - string res = _path; - res += tape_dir_name; + string res; + string full; + + if (with_path) { + res = _path; + } else { + full = _path; + } + + res += interchange_dir_name; + res += '/'; + res += legalize_for_path (_name); res += '/'; + res += sound_dir_name; + + if (with_path) { + full = res; + } else { + full += res; + } + + /* if this already exists, don't check for the old session sound directory */ + + if (Glib::file_test (full, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { + return res; + } + + /* possibly support old session structure */ + + string old_nopath; + string old_withpath; + + old_nopath += old_sound_dir_name; + old_nopath += '/'; + + old_withpath = _path; + old_withpath += old_sound_dir_name; + + if (Glib::file_test (old_withpath.c_str(), Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { + if (with_path) + return old_withpath; + + return old_nopath; + } + + /* ok, old "sounds" directory isn't there, return the new path */ + return res; } @@ -2297,6 +2004,15 @@ Session::template_dir () return path; } +string +Session::export_dir () const +{ + string res = _path; + res += export_dir_name; + res += '/'; + return res; +} + string Session::suffixed_search_path (string suffix, bool data) { @@ -2401,23 +2117,6 @@ Session::load_route_groups (const XMLNode& node, bool edit) return 0; } -void -Session::swap_configuration(Configuration** new_config) -{ - Glib::RWLock::WriterLock lm (route_lock); // jlc - WHY? - Configuration* tmp = *new_config; - *new_config = Config; - Config = tmp; - set_dirty(); -} - -void -Session::copy_configuration(Configuration* new_config) -{ - Glib::RWLock::WriterLock lm (route_lock); - new_config = new Configuration(*Config); -} - static bool state_file_filter (const string &str, void *arg) { @@ -2441,7 +2140,7 @@ remove_end(string* state) statename = statename.substr (start+1); } - if ((end = statename.rfind(".ardour")) < 0) { + if ((end = statename.rfind(".ardour")) == string::npos) { end = statename.length(); } @@ -2549,58 +2248,40 @@ Session::edit_group_by_name (string name) } void -Session::set_meter_hold (float val) -{ - _meter_hold = val; - MeterHoldChanged(); // emit -} - -void -Session::set_meter_falloff (float val) +Session::begin_reversible_command (string name) { - _meter_falloff = val; - MeterFalloffChanged(); // emit -} - - -void -Session::begin_reversible_command (string name, UndoAction* private_undo) -{ - current_cmd.clear (); - current_cmd.set_name (name); - - if (private_undo) { - current_cmd.add_undo (*private_undo); - } + current_trans = new UndoTransaction; + current_trans->set_name (name); } void -Session::commit_reversible_command (UndoAction* private_redo) +Session::commit_reversible_command (Command *cmd) { struct timeval now; - if (private_redo) { - current_cmd.add_redo_no_execute (*private_redo); + if (cmd) { + current_trans->add_command (cmd); } gettimeofday (&now, 0); - current_cmd.set_timestamp (now); + current_trans->set_timestamp (now); - history.add (current_cmd); + _history.add (current_trans); } Session::GlobalRouteBooleanState Session::get_global_route_boolean (bool (Route::*method)(void) const) { GlobalRouteBooleanState s; - Glib::RWLock::ReaderLock lm (route_lock); + boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->hidden()) { RouteBooleanState v; v.first =* i; - v.second = ((*i)->*method)(); + Route* r = (*i).get(); + v.second = (r->*method)(); s.push_back (v); } @@ -2613,9 +2294,9 @@ Session::GlobalRouteMeterState Session::get_global_route_metering () { GlobalRouteMeterState s; - Glib::RWLock::ReaderLock lm (route_lock); + boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->hidden()) { RouteMeterState v; @@ -2633,7 +2314,12 @@ void Session::set_global_route_metering (GlobalRouteMeterState s, void* arg) { for (GlobalRouteMeterState::iterator i = s.begin(); i != s.end(); ++i) { - i->first->set_meter_point (i->second, arg); + + boost::shared_ptr r = (i->first.lock()); + + if (r) { + r->set_meter_point (i->second, arg); + } } } @@ -2641,7 +2327,13 @@ 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) { - (i->first->*method) (i->second, arg); + + boost::shared_ptr r = (i->first.lock()); + + if (r) { + Route* rp = r.get(); + (rp->*method) (i->second, arg); + } } } @@ -2663,6 +2355,7 @@ Session::set_global_record_enable (GlobalRouteBooleanState s, void* src) set_global_route_boolean (s, &Route::set_record_enable, src); } +#if 0 UndoAction Session::global_mute_memento (void* src) { @@ -2686,6 +2379,7 @@ Session::global_record_enable_memento (void* src) { return sigc::bind (mem_fun (*this, &Session::set_global_record_enable), get_global_route_boolean (&Route::record_enabled), src); } +#endif static bool template_filter (const string &str, void *arg) @@ -2871,11 +2565,20 @@ Session::find_all_sources_across_snapshots (set& result, bool exclude_th return 0; } +struct RegionCounter { + typedef std::map > AudioSourceList; + AudioSourceList::iterator iter; + boost::shared_ptr region; + uint32_t count; + + RegionCounter() : count (0) {} +}; + int Session::cleanup_sources (Session::cleanup_report& rep) { - vector dead_sources; - vector playlists_tbd; + vector > dead_sources; + vector > playlists_tbd; PathScanner scanner; string sound_path; vector::iterator i; @@ -2914,78 +2617,36 @@ Session::cleanup_sources (Session::cleanup_report& rep) /* now delete any that were marked for deletion */ - for (vector::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) { - PlaylistList::iterator foo; - - if ((foo = unused_playlists.find (*x)) != unused_playlists.end()) { - unused_playlists.erase (foo); - } - delete *x; + for (vector >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) { + (*x)->drop_references (); } - /* step 2: clear the undo/redo history for all playlists */ - - for (PlaylistList::iterator x = playlists.begin(); x != playlists.end(); ++x) { - (*x)->drop_all_states (); - } + playlists_tbd.clear (); - /* step 3: find all un-referenced sources */ + /* step 2: find all un-used sources */ rep.paths.clear (); rep.space = 0; for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ) { - + AudioSourceList::iterator tmp; tmp = i; ++tmp; - /* only remove files that are not in use and have some size - to them. otherwise we remove the current "nascent" + /* do not bother with files that are zero size, otherwise we remove the current "nascent" capture files. */ - if (i->second->use_cnt() == 0 && i->second->length() > 0) { + if (!i->second->used() && i->second->length() > 0) { dead_sources.push_back (i->second); - - /* remove this source from our own list to avoid us - adding it to the list of all sources below - */ - - audio_sources.erase (i); - } + i->second->GoingAway(); + } i = tmp; } - /* Step 4: get rid of all regions in the region list that use any dead sources - in case the sources themselves don't go away (they might be referenced in - other snapshots). - */ - - for (vector::iterator i = dead_sources.begin(); i != dead_sources.end();++i) { - - for (AudioRegionList::iterator r = audio_regions.begin(); r != audio_regions.end(); ) { - AudioRegionList::iterator tmp; - AudioRegion* ar; - - tmp = r; - ++tmp; - - ar = r->second; - - for (uint32_t n = 0; n < ar->n_channels(); ++n) { - if (&ar->source (n) == (*i)) { - /* this region is dead */ - remove_region (ar); - } - } - - r = tmp; - } - } - /* build a list of all the possible sound directories for the session */ for (i = session_dirs.begin(); i != session_dirs.end(); ) { @@ -2994,7 +2655,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) ++nexti; sound_path += (*i).path; - sound_path += sound_dir_name; + sound_path += sound_dir (false); if (nexti != session_dirs.end()) { sound_path += ':'; @@ -3002,7 +2663,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) i = nexti; } - + /* now do the same thing for the files that ended up in the sounds dir(s) but are not referenced as sources in any snapshot. */ @@ -3024,13 +2685,16 @@ Session::cleanup_sources (Session::cleanup_report& rep) */ for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ++i) { - AudioFileSource* fs; + boost::shared_ptr fs; - if ((fs = dynamic_cast (i->second)) != 0) { + if ((fs = boost::dynamic_pointer_cast (i->second)) != 0) { all_sources.insert (fs->path()); } } + char tmppath1[PATH_MAX+1]; + char tmppath2[PATH_MAX+1]; + for (vector::iterator x = soundfiles->begin(); x != soundfiles->end(); ++x) { used = false; @@ -3038,11 +2702,13 @@ Session::cleanup_sources (Session::cleanup_report& rep) for (set::iterator i = all_sources.begin(); i != all_sources.end(); ++i) { - if (spath == *i) { + realpath(spath.c_str(), tmppath1); + realpath((*i).c_str(), tmppath2); + + if (strcmp(tmppath1, tmppath2) == 0) { used = true; break; } - } if (!used) { @@ -3067,11 +2733,31 @@ Session::cleanup_sources (Session::cleanup_report& rep) on whichever filesystem it was already on. */ - newpath = Glib::path_get_dirname (*x); - newpath = Glib::path_get_dirname (newpath); + if ((*x).find ("/sounds/") != string::npos) { + + /* old school, go up 1 level */ + + newpath = Glib::path_get_dirname (*x); // "sounds" + newpath = Glib::path_get_dirname (newpath); // "session-name" + + } else { + + /* new school, go up 4 levels */ + + newpath = Glib::path_get_dirname (*x); // "audiofiles" + 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 dir \"%1\" (%2)"), newpath, strerror (errno)) << endmsg; + return -1; + } + newpath += '/'; newpath += Glib::path_get_basename ((*x)); @@ -3111,7 +2797,6 @@ Session::cleanup_sources (Session::cleanup_report& rep) << endmsg; goto out; } - /* see if there an easy to find peakfile for this file, and remove it. */ @@ -3129,14 +2814,13 @@ Session::cleanup_sources (Session::cleanup_report& rep) goto out; } } - } ret = 0; /* dump the history list */ - history.clear (); + _history.clear (); /* save state so we don't end up a session file referring to non-existent sources. @@ -3235,18 +2919,39 @@ Session::set_clean () } } +void +Session::set_deletion_in_progress () +{ + _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion); +} + void Session::add_controllable (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.push_back (c); + controllables.insert (c); } void Session::remove_controllable (Controllable* c) { + if (_state_of_the_state | Deletion) { + return; + } + Glib::Mutex::Lock lm (controllables_lock); - controllables.remove (c); + + Controllables::iterator x = controllables.find (c); + + if (x != controllables.end()) { + controllables.erase (x); + } } Controllable* @@ -3262,3 +2967,325 @@ Session::controllable_by_id (const PBD::ID& id) return 0; } + +void +Session::add_instant_xml (XMLNode& node, const std::string& dir) +{ + Stateful::add_instant_xml (node, dir); + Config->add_instant_xml (node, get_user_ardour_path()); +} + + +int +Session::save_history (string snapshot_name) +{ + XMLTree tree; + string xml_path; + string bak_path; + + tree.set_root (&_history.get_state (Config->get_saved_history_depth())); + + if (snapshot_name.empty()) { + snapshot_name = _current_snapshot_name; + } + + xml_path = _path + snapshot_name + ".history"; + + bak_path = xml_path + ".bak"; + + if ((access (xml_path.c_str(), F_OK) == 0) && + (rename (xml_path.c_str(), bak_path.c_str()))) + { + error << _("could not backup old history file, current history not saved.") << endmsg; + return -1; + } + + if (!tree.write (xml_path)) + { + error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg; + + /* don't leave a corrupt file lying around if it is + * possible to fix. + */ + + if (unlink (xml_path.c_str())) { + error << string_compose (_("could not remove corrupt history file %1"), xml_path) << endmsg; + } else { + if (rename (bak_path.c_str(), xml_path.c_str())) + { + error << string_compose (_("could not restore history file from backup %1"), bak_path) << endmsg; + } + } + + return -1; + } + + return 0; +} + +int +Session::restore_history (string snapshot_name) +{ + XMLTree tree; + string xmlpath; + + if (snapshot_name.empty()) { + snapshot_name = _current_snapshot_name; + } + + /* read xml */ + xmlpath = _path + snapshot_name + ".history"; + cerr << string_compose(_("Loading history from '%1'."), xmlpath) << endmsg; + + if (access (xmlpath.c_str(), F_OK)) { + info << string_compose (_("%1: no history file \"%2\" for this session."), _name, xmlpath) << endmsg; + return 1; + } + + if (!tree.read (xmlpath)) { + error << string_compose (_("Could not understand session history file \"%1\""), xmlpath) << endmsg; + return -1; + } + + /* replace history */ + _history.clear(); + + 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(); + child_it != t->children().end(); + child_it++) + { + XMLNode *n = *child_it; + Command *c; + + if (n->name() == "MementoCommand" || + n->name() == "MementoUndoCommand" || + n->name() == "MementoRedoCommand") { + + 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 { + + 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; +} + +void +Session::config_changed (const char* parameter_name) +{ +#define PARAM_IS(x) (!strcmp (parameter_name, (x))) + + if (PARAM_IS ("seamless-loop")) { + + } else if (PARAM_IS ("rf-speed")) { + + } else if (PARAM_IS ("auto-loop")) { + + } else if (PARAM_IS ("auto-input")) { + + if (Config->get_monitoring_model() == HardwareMonitoring && transport_rolling()) { + /* auto-input only makes a difference if we're rolling */ + + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + if ((*i)->record_enabled ()) { + (*i)->monitor_input (!Config->get_auto_input()); + } + } + } + + } else if (PARAM_IS ("punch-in")) { + + Location* location; + + if ((location = _locations.auto_punch_location()) != 0) { + + if (Config->get_punch_in ()) { + replace_event (Event::PunchIn, location->start()); + } else { + remove_event (location->start(), Event::PunchIn); + } + } + + } else if (PARAM_IS ("punch-out")) { + + Location* location; + + if ((location = _locations.auto_punch_location()) != 0) { + + if (Config->get_punch_out()) { + replace_event (Event::PunchOut, location->end()); + } else { + clear_events (Event::PunchOut); + } + } + + } else if (PARAM_IS ("edit-mode")) { + + Glib::Mutex::Lock lm (playlist_lock); + + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + (*i)->set_edit_mode (Config->get_edit_mode ()); + } + + } else if (PARAM_IS ("use-video-sync")) { + + if (transport_stopped()) { + if (Config->get_use_video_sync()) { + waiting_for_sync_offset = true; + } + } + + } else if (PARAM_IS ("mmc-control")) { + + poke_midi_thread (); + + } else if (PARAM_IS ("mmc-device-id")) { + + if (mmc) { + mmc->set_device_id (Config->get_mmc_device_id()); + } + + } else if (PARAM_IS ("midi-control")) { + + poke_midi_thread (); + + } else if (PARAM_IS ("raid-path")) { + + setup_raid_path (Config->get_raid_path()); + + } else if (PARAM_IS ("smpte-format")) { + + sync_time_vars (); + + } else if (PARAM_IS ("video-pullup")) { + + sync_time_vars (); + + } else if (PARAM_IS ("seamless-loop")) { + + if (play_loop && transport_rolling()) { + // to reset diskstreams etc + request_play_loop (true); + } + + } else if (PARAM_IS ("rf-speed")) { + + cumulative_rf_motion = 0; + reset_rf_scale (0); + + } else if (PARAM_IS ("click-sound")) { + + setup_click_sounds (1); + + } else if (PARAM_IS ("click-emphasis-sound")) { + + setup_click_sounds (-1); + + } else if (PARAM_IS ("clicking")) { + + if (Config->get_clicking()) { + if (_click_io && click_data) { // don't require emphasis data + _clicking = true; + } + } else { + _clicking = false; + } + + } else if (PARAM_IS ("send-mtc")) { + + /* only set the internal flag if we have + a port. + */ + + 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; + } + + } else if (PARAM_IS ("send-mmc")) { + + /* only set the internal flag if we have + a port. + */ + + if (_mmc_port != 0) { + session_send_mmc = Config->get_send_mmc(); + } else { + mmc = 0; + session_send_mmc = false; + } + + } else if (PARAM_IS ("midi-feedback")) { + + /* only set the internal flag if we have + a port. + */ + + if (_mtc_port != 0) { + session_midi_feedback = Config->get_midi_feedback(); + } + + } else if (PARAM_IS ("jack-time-master")) { + + engine().reset_timebase (); + + } else if (PARAM_IS ("native-file-header-format")) { + + if (!first_file_header_format_reset) { + reset_native_file_format (); + } + + first_file_header_format_reset = false; + + } else if (PARAM_IS ("native-file-data-format")) { + + if (!first_file_data_format_reset) { + reset_native_file_format (); + } + + first_file_data_format_reset = false; + + } else if (PARAM_IS ("slave-source")) { + set_slave_source (Config->get_slave_source()); + } else if (PARAM_IS ("remote-model")) { + set_remote_control_ids (); + } + + set_dirty (); + +#undef PARAM_IS + +}