X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_state.cc;h=6a06863e9e16b1ca347cd261df2fe3e374d861ff;hb=152935e736eaf06f85bc7f5cb27337a62d95edd4;hp=f588080f25e23f710a5090a1e7501aeee005e65c;hpb=8cd71108c1d153f84f25c3b9e5153a110826c130;p=ardour.git diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index f588080f25..6a06863e9e 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" @@ -78,6 +77,7 @@ #include "pbd/localtime_r.h" #include "ardour/amp.h" +#include "ardour/async_midi_port.h" #include "ardour/audio_diskstream.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" @@ -88,10 +88,12 @@ #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" #include "ardour/midi_region.h" +#include "ardour/midi_scene_changer.h" #include "ardour/midi_source.h" #include "ardour/midi_track.h" #include "ardour/pannable.h" @@ -126,112 +128,40 @@ 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 */ + + _path = canonical_path(fullpath); + + /* we require _path to end with a dir separator */ if (_path[_path.length()-1] != G_DIR_SEPARATOR) { _path += G_DIR_SEPARATOR; } - /* 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()); + /* is it new ? */ - _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->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 +169,19 @@ 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()); + /* 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 +194,147 @@ 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()); + + BootMessage (_("Using configuration")); + + _midi_ports = new MidiPortManager; + + MIDISceneChanger* msc; + + _scene_changer = msc = new MIDISceneChanger (*this); + msc->set_input_port (scene_input_port()); + msc->set_output_port (scene_out()); + + boost::function timer_func (boost::bind (&Session::audible_frame, this)); + boost::dynamic_pointer_cast(scene_in())->set_timer (timer_func); + setup_midi_machine_control (); + if (_butler->start_thread()) { return -1; } - + if (start_midi_thread ()) { return -1; } - - 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); - } - - /* 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_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 (); - /* Pay attention ... */ - _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this)); _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this)); - midi_clock = new MidiClockTicker (); - midi_clock->set_session (this); - try { - when_engine_running (); - } + /* tempo map requires sample rate knowledge */ + + _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 */ + + midi_clock = new MidiClockTicker (); + midi_clock->set_session (this); + + /* crossfades require sample rate knowledge */ - /* handle this one in a different way than all others, so that its clear what happened */ + 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); + } - catch (AudioEngine::PortRegistrationFailure& err) { + /* ENGINE */ + + boost::function ff (boost::bind (&Session::config_changed, this, _1, false)); + boost::function ft (boost::bind (&Session::config_changed, this, _1, true)); + + Config->map_parameters (ff); + config.map_parameters (ft); + + /* Reset all panners */ + + Delivery::reset_panners (); + + /* this will cause the CPM to instantiate any protocols that are in use + * (or mandatory), which will pass it this Session, and then call + * set_state() on each instantiated protocol to match stored state. + */ + + ControlProtocolManager::instance().set_session (this); + + /* This must be done after the ControlProtocolManager set_session above, + as it will set states for ports which the ControlProtocolManager creates. + */ + + // XXX set state of MIDI::Port's + // MidiPortManager::instance()->set_port_states (Config->midi_port_states ()); + + /* And this must be done after the MIDI::Manager::set_port_states as + * it will try to make connections whose details are loaded by set_port_states. + */ + + hookup_io (); + + /* Let control protocols know that we are now all connected, so they + * could start talking to surfaces if they want to. + */ + + ControlProtocolManager::instance().midi_connectivity_established (); + + if (_is_new && !no_auto_connect()) { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock()); + auto_connect_master_bus (); + } + + _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); + + /* update latencies */ + + initialize_latencies (); + + _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); @@ -370,21 +347,21 @@ Session::second_stage_init () 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; } string Session::raid_path () const { - SearchPath raid_search_path; + Searchpath raid_search_path; for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { raid_search_path += (*i).path; @@ -405,11 +382,11 @@ Session::setup_raid_path (string path) session_dirs.clear (); - SearchPath search_path(path); - SearchPath sound_search_path; - SearchPath midi_search_path; + Searchpath search_path(path); + Searchpath sound_search_path; + Searchpath midi_search_path; - for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) { + for (Searchpath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) { sp.path = *i; sp.blocks = 0; // not needed session_dirs.push_back (sp); @@ -522,9 +499,10 @@ Session::create (const string& session_template, BusProfile* bus_profile) ifstream in(in_path.c_str()); if (in) { - string out_path = _path; - out_path += _name; - out_path += statefile_suffix; + /* no need to call legalize_for_path() since the string + * in session_template is already a legal path name + */ + string out_path = Glib::build_filename (_session_dir->root_path(), _name + statefile_suffix); ofstream out(out_path.c_str()); @@ -604,8 +582,6 @@ Session::create (const string& session_template, BusProfile* bus_profile) add_monitor_section (); } - save_state (""); - return 0; } @@ -684,62 +660,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) @@ -890,7 +810,7 @@ Session::load_state (string snapshot_name) set_dirty(); - _writable = exists_and_writable (xmlpath); + _writable = exists_and_writable (xmlpath) && exists_and_writable(Glib::path_get_dirname(xmlpath)); if (!state_tree->read (xmlpath)) { error << string_compose(_("Could not understand session file %1"), xmlpath) << endmsg; @@ -1008,7 +928,7 @@ Session::state (bool full_state) p += (*i).path; if (next != session_dirs.end()) { - p += ':'; + p += G_SEARCHPATH_SEPARATOR; } else { break; } @@ -1034,6 +954,15 @@ Session::state (bool full_state) /* various options */ + list midi_port_nodes = _midi_ports->get_midi_port_states(); + if (!midi_port_nodes.empty()) { + XMLNode* midi_port_stuff = new XMLNode ("MIDIPorts"); + for (list::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 +1168,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); @@ -1389,13 +1323,7 @@ Session::set_state (const XMLNode& node, int version) if ((child = find_named_node (node, "Click")) == 0) { warning << _("Session: XML state has no click section") << endmsg; } else if (_click_io) { - const XMLNodeList& children (child->children()); - XMLNodeList::const_iterator i = children.begin(); - _click_io->set_state (**i, version); - ++i; - if (i != children.end()) { - _click_gain->set_state (**i, version); - } + setup_click_state (&node); } if ((child = find_named_node (node, ControlProtocolManager::state_node_name)) != 0) { @@ -2353,7 +2281,7 @@ Session::auto_save() } static bool -state_file_filter (const string &str, void */*arg*/) +state_file_filter (const string &str, void* /*arg*/) { return (str.length() > strlen(statefile_suffix) && str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix))); @@ -2530,7 +2458,7 @@ Session::commit_reversible_command (Command *cmd) } static bool -accept_all_audio_files (const string& path, void */*arg*/) +accept_all_audio_files (const string& path, void* /*arg*/) { if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) { return false; @@ -2544,7 +2472,7 @@ accept_all_audio_files (const string& path, void */*arg*/) } static bool -accept_all_midi_files (const string& path, void */*arg*/) +accept_all_midi_files (const string& path, void* /*arg*/) { if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) { return false; @@ -2556,7 +2484,7 @@ accept_all_midi_files (const string& path, void */*arg*/) } static bool -accept_all_state_files (const string& path, void */*arg*/) +accept_all_state_files (const string& path, void* /*arg*/) { if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) { return false; @@ -2764,7 +2692,7 @@ Session::cleanup_sources (CleanupReport& rep) audio_path += sdir.sound_path(); if (nexti != session_dirs.end()) { - audio_path += ':'; + audio_path += G_SEARCHPATH_SEPARATOR; } i = nexti; @@ -2782,7 +2710,7 @@ Session::cleanup_sources (CleanupReport& rep) midi_path += sdir.midi_path(); if (nexti != session_dirs.end()) { - midi_path += ':'; + midi_path += G_SEARCHPATH_SEPARATOR; } i = nexti; @@ -2820,19 +2748,23 @@ Session::cleanup_sources (CleanupReport& rep) ++tmp; if ((fs = boost::dynamic_pointer_cast (i->second)) != 0) { - if (playlists->source_use_count (fs) != 0) { - all_sources.insert (fs->path()); - } else { - /* we might not remove this source from disk, because it may be used - by other snapshots, but its not being used in this version - so lets get rid of it now, along with any representative regions - in the region list. - */ + if (!fs->is_stub()) { - RegionFactory::remove_regions_using_source (i->second); - sources.erase (i); - } + if (playlists->source_use_count (fs) != 0) { + all_sources.insert (fs->path()); + } else { + + /* we might not remove this source from disk, because it may be used + by other snapshots, but its not being used in this version + so lets get rid of it now, along with any representative regions + in the region list. + */ + + RegionFactory::remove_regions_using_source (i->second); + sources.erase (i); + } + } } i = tmp; @@ -3453,11 +3385,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 +3452,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") { @@ -3560,31 +3492,15 @@ Session::config_changed (std::string p, bool ours) /* XXX DO SOMETHING HERE TO TELL THE GUI THAT WE NEED TO SET REMOTE ID'S */ - } else if (p == "sync-all-route-ordering") { - - /* sync to editor order unless mixer is used for remote IDs - */ - - switch (Config->get_remote_model()) { - case UserOrdered: - sync_order_keys (EditorSort); - break; - case EditorOrdered: - sync_order_keys (EditorSort); - break; - case MixerOrdered: - sync_order_keys (MixerSort); - } - } 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 +3564,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 @@ -3697,8 +3614,6 @@ Session::rename (const std::string& new_name) string const old_sources_root = _session_dir->sources_root(); -#define RENAME ::rename - /* Rename: * session directory @@ -3760,7 +3675,8 @@ Session::rename (const std::string& new_name) cerr << "Rename " << oldstr << " => " << newstr << endl; - if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) { + if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) { + error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg; return 1; } @@ -3787,7 +3703,8 @@ Session::rename (const std::string& new_name) cerr << "Rename " << oldstr << " => " << newstr << endl; - if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) { + if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) { + error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg; return 1; } } @@ -3799,7 +3716,8 @@ Session::rename (const std::string& new_name) cerr << "Rename " << oldstr << " => " << newstr << endl; - if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) { + if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) { + error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg; return 1; } @@ -3813,7 +3731,8 @@ Session::rename (const std::string& new_name) cerr << "Rename " << oldstr << " => " << newstr << endl; - if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) { + if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) { + error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg; return 1; } } @@ -3837,6 +3756,11 @@ Session::rename (const std::string& new_name) _current_snapshot_name = new_name; _name = new_name; + /* re-add directory separator - reverse hack to oldstr above */ + if (_path[_path.length()-1] != G_DIR_SEPARATOR) { + _path += G_DIR_SEPARATOR; + } + set_dirty (); /* save state again to get everything just right */ @@ -3849,6 +3773,69 @@ Session::rename (const std::string& new_name) store_recent_sessions (new_name, _path); return 0; +} + +int +Session::get_session_info_from_path (XMLTree& tree, const string& xmlpath) +{ + if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { + return -1; + } + + if (!tree.read (xmlpath)) { + return -1; + } + + return 0; +} + +int +Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFormat& data_format) +{ + XMLTree tree; + bool found_sr = false; + bool found_data_format = false; + + if (get_session_info_from_path (tree, xmlpath)) { + return -1; + } + + /* sample rate */ + + const XMLProperty* prop; + if ((prop = tree.root()->property (X_("sample-rate"))) != 0) { + sample_rate = atoi (prop->value()); + found_sr = true; + } + + const XMLNodeList& children (tree.root()->children()); + for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) { + const XMLNode* child = *c; + if (child->name() == "Config") { + const XMLNodeList& options (child->children()); + for (XMLNodeList::const_iterator oc = options.begin(); oc != options.end(); ++oc) { + const XMLNode* option = *oc; + const XMLProperty* name = option->property("name"); + + if (!name) { + continue; + } + + if (name->value() == "native-file-data-format") { + const XMLProperty* value = option->property ("value"); + if (value) { + SampleFormat fmt = (SampleFormat) string_2_enum (option->property ("value")->value(), fmt); + data_format = fmt; + found_data_format = true; + break; + } + } + } + } + if (found_data_format) { + break; + } + } -#undef RENAME + return !(found_sr && found_data_format); // zero if they are both found }