X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=ffa7d17761a5ee495fab1a07c0c5f29efafd5bd6;hb=22dc575e4cbc35a5d486d6f448332fb721865d57;hp=ff269cc931175bd77849db6099c41bef322f9e71;hpb=da45f489dd3d923bbba8f1cee39ab445c4bf1b64;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ff269cc931..ffa7d17761 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 1999-2004 Paul Davis + Copyright (C) 1999-2004 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 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -115,9 +116,9 @@ sigc::signal Session::SendFeedback; sigc::signal Session::SMPTEOffsetChanged; sigc::signal Session::StartTimeChanged; sigc::signal Session::EndTimeChanged; - sigc::signal Session::AutoBindingOn; sigc::signal Session::AutoBindingOff; +sigc::signal Session::Exported; Session::Session (AudioEngine &eng, const string& fullpath, @@ -131,28 +132,31 @@ Session::Session (AudioEngine &eng, _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), + _midi_clock_port (default_midi_clock_port), _session_dir (new SessionDirectory(fullpath)), pending_events (2048), post_transport_work((PostTransportWork)0), _send_smpte_update (false), - midi_requests (128), + midi_requests (128), diskstreams (new DiskstreamList), routes (new RouteList), auditioner ((Auditioner*) 0), + _total_free_4k_blocks (0), _bundle_xml_node (0), _click_io ((IO*) 0), - main_outs (0) + main_outs (0), + _metadata (new SessionMetadata()) { bool new_session; if (!eng.connected()) { throw failed_constructor(); } - + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; - n_physical_outputs = _engine.n_physical_outputs(); - n_physical_inputs = _engine.n_physical_inputs(); + n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO); + n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO); first_stage_init (fullpath, snapshot_name); @@ -164,14 +168,14 @@ Session::Session (AudioEngine &eng, throw failed_constructor (); } } - + if (second_stage_init (new_session)) { destroy (); throw failed_constructor (); } - + store_recent_sessions(_name, _path); - + bool was_dirty = dirty(); _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); @@ -201,6 +205,7 @@ Session::Session (AudioEngine &eng, _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), + _midi_clock_port (default_midi_clock_port), _session_dir ( new SessionDirectory(fullpath)), pending_events (2048), post_transport_work((PostTransportWork)0), @@ -208,6 +213,7 @@ Session::Session (AudioEngine &eng, midi_requests (16), diskstreams (new DiskstreamList), routes (new RouteList), + _total_free_4k_blocks (0), _bundle_xml_node (0), main_outs (0) @@ -220,8 +226,8 @@ Session::Session (AudioEngine &eng, cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; - n_physical_outputs = _engine.n_physical_outputs(); - n_physical_inputs = _engine.n_physical_inputs(); + n_physical_outputs = _engine.n_physical_outputs (DataType::AUDIO); + n_physical_inputs = _engine.n_physical_inputs (DataType::AUDIO); if (n_physical_inputs) { n_physical_inputs = max (requested_physical_in, n_physical_inputs); @@ -244,31 +250,31 @@ Session::Session (AudioEngine &eng, { /* set up Master Out and Control Out if necessary */ - + RouteList rl; int control_id = 1; - + if (control_out_channels) { shared_ptr r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); r->set_remote_control_id (control_id++); - + rl.push_back (r); } - + if (master_out_channels) { shared_ptr r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); r->set_remote_control_id (control_id); - + rl.push_back (r); } else { /* prohibit auto-connect to master, because there isn't one */ output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); } - + if (!rl.empty()) { add_routes (rl, false); } - + } Config->set_input_auto_connect (input_ac); @@ -278,9 +284,9 @@ Session::Session (AudioEngine &eng, destroy (); throw failed_constructor (); } - + store_recent_sessions (_name, _path); - + _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); @@ -305,7 +311,7 @@ Session::destroy () _engine.remove_session (); GoingAway (); /* EMIT SIGNAL */ - + /* do this */ notify_callbacks (); @@ -315,14 +321,14 @@ Session::destroy () _history.clear (); /* clear state tree so that no references to objects are held any more */ - + if (state_tree) { delete state_tree; } terminate_butler_thread (); //terminate_midi_thread (); - + if (click_data && click_data != default_click) { delete [] click_data; } @@ -338,9 +344,9 @@ Session::destroy () delete _mix_buffers; AudioDiskstream::free_working_buffers(); - + Route::SyncOrderKeys.clear(); - + #undef TRACK_DESTRUCTION #ifdef TRACK_DESTRUCTION cerr << "delete named selections\n"; @@ -365,10 +371,10 @@ Session::destroy () ++tmp; (*i)->drop_references (); - + i = tmp; } - + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { PlaylistList::iterator tmp; @@ -376,17 +382,17 @@ Session::destroy () ++tmp; (*i)->drop_references (); - + i = tmp; } - + playlists.clear (); unused_playlists.clear (); #ifdef TRACK_DESTRUCTION cerr << "delete regions\n"; #endif /* TRACK_DESTRUCTION */ - + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { RegionList::iterator tmp; @@ -436,13 +442,13 @@ Session::destroy () tmp = i; ++tmp; - + i->second->drop_references (); - + i = tmp; } sources.clear (); - + #ifdef TRACK_DESTRUCTION cerr << "delete mix groups\n"; #endif /* TRACK_DESTRUCTION */ @@ -462,7 +468,7 @@ Session::destroy () #endif /* TRACK_DESTRUCTION */ for (list::iterator i = edit_groups.begin(); i != edit_groups.end(); ) { list::iterator tmp; - + tmp = i; ++tmp; @@ -470,7 +476,7 @@ Session::destroy () i = tmp; } - + if (butler_mixdown_buffer) { delete [] butler_mixdown_buffer; } @@ -497,7 +503,7 @@ Session::set_worst_io_latencies () } boost::shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); @@ -536,15 +542,15 @@ Session::when_engine_running () try { XMLNode* child = 0; - + _click_io.reset (new ClickIO (*this, "click", 0, 0, -1, -1)); if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { /* existing state for Click */ - + if (_click_io->set_state (*child->children().front()) == 0) { - + _clicking = Config->get_clicking (); } else { @@ -554,7 +560,7 @@ Session::when_engine_running () } } else { - + /* default state for Click */ first_physical_output = _engine.get_nth_physical_output (DataType::AUDIO, 0); @@ -642,24 +648,24 @@ Session::when_engine_running () if (_master_out) { /* create master/control ports */ - + if (_master_out) { uint32_t n; /* force the master to ignore any later call to this */ - + if (_master_out->pending_state_node) { _master_out->ports_became_legal(); } /* no panner resets till we are through */ - + _master_out->defer_pan_reset (); - + while (_master_out->n_inputs().n_audio() < _master_out->input_maximum().n_audio()) { if (_master_out->add_input_port ("", this, DataType::AUDIO)) { - error << _("cannot setup master inputs") + error << _("cannot setup master inputs") << endmsg; break; } @@ -676,7 +682,7 @@ Session::when_engine_running () } _master_out->allow_pan_reset (); - + } shared_ptr c (new AutoBundle (_("Master Out"), true)); @@ -686,8 +692,8 @@ Session::when_engine_running () c->set_port (n, _master_out->input(n)->name()); } add_bundle (c); - } - + } + BootMessage (_("Setup signal flow and plugins")); hookup_io (); @@ -697,7 +703,7 @@ Session::when_engine_running () BootMessage (_("Catch up with send/insert state")); insert_cnt = 0; - + for (list::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { uint32_t id; @@ -712,7 +718,7 @@ Session::when_engine_running () for (list::iterator i = _sends.begin(); i != _sends.end(); ++i) { uint32_t id; - + if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { if (id > send_cnt) { send_cnt = id; @@ -720,7 +726,7 @@ Session::when_engine_running () } } - + _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); /* hook us up to the engine */ @@ -736,7 +742,7 @@ Session::when_engine_running () osc->set_session (*this); #endif - + } void @@ -750,16 +756,16 @@ Session::hookup_io () if (auditioner == 0) { - + /* we delay creating the auditioner till now because it makes its own connections to ports. the engine has to be running for this to work. */ - + try { auditioner.reset (new Auditioner (*this)); } - + catch (failed_constructor& err) { warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; } @@ -797,7 +803,7 @@ Session::hookup_io () cports.push_back (_control_out->input(n)->name()); } - boost::shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { (*x)->set_control_outs (cports); @@ -808,7 +814,7 @@ Session::hookup_io () if (_bundle_xml_node) { load_bundles (*_bundle_xml_node); delete _bundle_xml_node; - } + } /* Tell all IO objects to connect themselves together */ @@ -839,10 +845,10 @@ Session::hookup_io () void Session::playlist_length_changed () { - /* we can't just increase end_location->end() if pl->get_maximum_extent() + /* we can't just increase end_location->end() if pl->get_maximum_extent() if larger. if the playlist used to be the longest playlist, and its now shorter, we have to decrease end_location->end(). hence, - we have to iterate over all diskstreams and check the + we have to iterate over all diskstreams and check the playlists currently in use. */ find_current_end (); @@ -856,7 +862,7 @@ Session::diskstream_playlist_changed (boost::shared_ptr dstream) if ((playlist = dstream->playlist()) != 0) { playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); } - + /* see comment in playlist_length_changed () */ find_current_end (); } @@ -909,7 +915,7 @@ Session::auto_punch_start_changed (Location* location) /* capture start has been changed, so save new pending state */ save_state ("", true); } -} +} void Session::auto_punch_end_changed (Location* location) @@ -917,7 +923,7 @@ Session::auto_punch_end_changed (Location* location) nframes_t when_to_stop = location->end(); // when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (Event::PunchOut, when_to_stop); -} +} void Session::auto_punch_changed (Location* location) @@ -927,7 +933,7 @@ Session::auto_punch_changed (Location* location) replace_event (Event::PunchIn, location->start()); //when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (Event::PunchOut, when_to_stop); -} +} void Session::auto_loop_changed (Location* location) @@ -941,12 +947,12 @@ Session::auto_loop_changed (Location* location) if (_transport_frame > location->end()) { // relocate to beginning of loop clear_events (Event::LocateRoll); - + request_locate (location->start(), true); } else if (Config->get_seamless_loop() && !loop_changing) { - + // schedule a locate-roll to refill the diskstreams at the // previous loop end loop_changing = true; @@ -958,7 +964,7 @@ Session::auto_loop_changed (Location* location) } } - } + } last_loopend = location->end(); } @@ -983,7 +989,7 @@ Session::set_auto_punch_location (Location* location) if (location == 0) { return; } - + if (location->end() <= location->start()) { error << _("Session: you can't use that location for auto punch (start <= end)") << endmsg; return; @@ -992,7 +998,7 @@ Session::set_auto_punch_location (Location* location) auto_punch_start_changed_connection.disconnect(); auto_punch_end_changed_connection.disconnect(); auto_punch_changed_connection.disconnect(); - + auto_punch_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_punch_start_changed)); auto_punch_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_punch_end_changed)); auto_punch_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_punch_changed)); @@ -1018,7 +1024,7 @@ Session::set_auto_loop_location (Location* location) remove_event (existing->end(), Event::AutoLoop); auto_loop_location_changed (0); } - + set_dirty(); if (location == 0) { @@ -1031,11 +1037,11 @@ Session::set_auto_loop_location (Location* location) } last_loopend = location->end(); - + auto_loop_start_changed_connection.disconnect(); auto_loop_end_changed_connection.disconnect(); auto_loop_changed_connection.disconnect(); - + auto_loop_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_loop_changed)); auto_loop_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_loop_changed)); auto_loop_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_loop_changed)); @@ -1083,7 +1089,13 @@ Session::handle_locations_changed (Locations::LocationList& locations) set_auto_loop_location (location); set_loop = true; } - + + if (location->is_start()) { + start_location = location; + } + if (location->is_end()) { + end_location = location; + } } if (!set_loop) { @@ -1094,7 +1106,7 @@ Session::handle_locations_changed (Locations::LocationList& locations) } set_dirty(); -} +} void Session::enable_record () @@ -1109,7 +1121,7 @@ Session::enable_record () boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - (*i)->monitor_input (true); + (*i)->monitor_input (true); } } } @@ -1141,14 +1153,14 @@ Session::disable_record (bool rt_context, bool force) if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { boost::shared_ptr dsl = diskstreams.reader(); - + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - (*i)->monitor_input (false); + (*i)->monitor_input (false); } } } - + RecordStateChanged (); /* emit signal */ if (!rt_context) { @@ -1166,11 +1178,11 @@ Session::step_back_from_record () if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { boost::shared_ptr dsl = diskstreams.reader(); - + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (false); + (*i)->monitor_input (false); } } } @@ -1191,7 +1203,7 @@ Session::maybe_enable_record () if (_transport_speed) { if (!Config->get_punch_in()) { enable_record (); - } + } } else { deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame); RecordStateChanged (); /* EMIT SIGNAL */ @@ -1208,7 +1220,7 @@ Session::audible_frame () const nframes_t tf; /* the first of these two possible settings for "offset" - mean that the audible frame is stationary until + mean that the audible frame is stationary until audio emerges from the latency compensation "pseudo-pipeline". @@ -1221,7 +1233,7 @@ Session::audible_frame () const if (offset > current_block_size) { offset -= current_block_size; - } else { + } else { /* XXX is this correct? if we have no external physical connections and everything is internal then surely this is zero? still, how @@ -1251,7 +1263,7 @@ Session::audible_frame () const /* MOVING */ /* take latency into account */ - + ret -= offset; } @@ -1262,7 +1274,7 @@ void Session::set_frame_rate (nframes_t frames_per_second) { /** \fn void Session::set_frame_size(nframes_t) - the AudioEngine object that calls this guarantees + the AudioEngine object that calls this guarantees that it will not be called while we are also in ::process(). Its fine to do things that block here. @@ -1275,7 +1287,7 @@ Session::set_frame_rate (nframes_t frames_per_second) Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); clear_clicks (); - + // XXX we need some equivalent to this, somehow // SndFileSource::setup_standard_crossfades (frames_per_second); @@ -1287,14 +1299,14 @@ Session::set_frame_rate (nframes_t frames_per_second) void Session::set_block_size (nframes_t nframes) { - /* the AudioEngine guarantees + /* the AudioEngine guarantees that it will not be called while we are also in ::process(). It is therefore fine to do things that block here. */ - { - + { + current_block_size = nframes; ensure_buffers(_scratch_buffers->available()); @@ -1311,7 +1323,7 @@ Session::set_block_size (nframes_t nframes) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_block_size (nframes); } - + boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { (*i)->set_block_size (nframes); @@ -1326,18 +1338,18 @@ Session::set_default_fade (float steepness, float fade_msecs) { #if 0 nframes_t fade_frames; - + /* Don't allow fade of less 1 frame */ - + if (fade_msecs < (1000.0 * (1.0/_current_frame_rate))) { fade_msecs = 0; fade_frames = 0; } else { - + fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001); - + } default_fade_msecs = fade_msecs; @@ -1352,7 +1364,7 @@ Session::set_default_fade (float steepness, float fade_msecs) set_dirty(); /* XXX have to do this at some point */ - /* foreach region using default fade, reset, then + /* foreach region using default fade, reset, then refill_all_diskstream_buffers (); */ #endif @@ -1388,7 +1400,7 @@ trace_terminal (shared_ptr r1, shared_ptr rbase) if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) { info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg; return; - } + } /* make a copy of the existing list of routes that feed r1 */ @@ -1452,45 +1464,45 @@ void Session::resort_routes_using (shared_ptr r) { RouteList::iterator i, j; - + for (i = r->begin(); i != r->end(); ++i) { - + (*i)->fed_by.clear (); - + for (j = r->begin(); j != r->end(); ++j) { - + /* although routes can feed themselves, it will cause an endless recursive descent if we detect it. so don't bother checking for self-feeding. */ - + if (*j == *i) { continue; } - + if ((*j)->feeds (*i)) { (*i)->fed_by.insert (*j); - } + } } } - + for (i = r->begin(); i != r->end(); ++i) { trace_terminal (*i, *i); } - + RouteSorter cmp; r->sort (cmp); - + #if 0 cerr << "finished route resort\n"; - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl; } cerr << endl; #endif - + } list > @@ -1505,7 +1517,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) //uint32_t control_id; // FIXME: need physical I/O and autoconnect stuff for MIDI - + /* count existing midi tracks */ { @@ -1521,16 +1533,16 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) } } - /* +#if 0 vector physinputs; vector physoutputs; + + _engine.get_physical_outputs (DataType::MIDI, physoutputs); + _engine.get_physical_inputs (DataType::MIDI, physinputs); uint32_t nphysical_in; uint32_t nphysical_out; - - _engine.get_physical_outputs (physoutputs); - _engine.get_physical_inputs (physinputs); - control_id = ntracks() + nbusses() + 1; - */ + control_id = ntracks() + nbusses(); +#endif while (how_many) { @@ -1539,7 +1551,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) save, close,restart,add new route - first named route is now Audio2) */ - + do { ++track_id; @@ -1549,16 +1561,16 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) if (route_by_name (track_name) == 0) { break; } - + } while (track_id < (UINT_MAX-1)); /* - if (Config->get_input_auto_connect() & AutoConnectPhysical) { + if (Config->get_input_auto_connect() & AutoConnectPhysical) { nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size()); } else { nphysical_in = 0; } - + if (Config->get_output_auto_connect() & AutoConnectPhysical) { nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size()); } else { @@ -1567,10 +1579,10 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) */ shared_ptr track; - + try { track = boost::shared_ptr((new MidiTrack (*this, track_name, Route::Flag (0), mode))); - + if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) { error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; goto failed; @@ -1579,23 +1591,23 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) /* if (nphysical_in) { for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) { - + port = ""; - + if (Config->get_input_auto_connect() & AutoConnectPhysical) { port = physinputs[(channels_used+x)%nphysical_in]; - } - + } + if (port.length() && track->connect_input (track->input (x), port, this)) { break; } } } - + for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) { - + port = ""; - + if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { port = physoutputs[(channels_used+x)%nphysical_out]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { @@ -1603,18 +1615,18 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) port = _master_out->input (x%_master_out->n_inputs().n_midi())->name(); } } - + if (port.length() && track->connect_output (track->output (x), port, this)) { break; } } - + channels_used += track->n_inputs ().n_midi(); */ track->midi_diskstream()->non_realtime_input_change(); - + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); //track->set_remote_control_id (control_id); @@ -1629,7 +1641,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) /* we need to get rid of this, since the track failed to be created */ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ - { + { RCUWriter writer (diskstreams); boost::shared_ptr ds = writer.get_copy(); ds->remove (track->midi_diskstream()); @@ -1647,7 +1659,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) /* we need to get rid of this, since the track failed to be created */ /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */ - { + { RCUWriter writer (diskstreams); boost::shared_ptr ds = writer.get_copy(); ds->remove (track->midi_diskstream()); @@ -1701,8 +1713,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod uint32_t nphysical_in; uint32_t nphysical_out; - _engine.get_physical_outputs (physoutputs); - _engine.get_physical_inputs (physinputs); + _engine.get_physical_outputs (DataType::AUDIO, physoutputs); + _engine.get_physical_inputs (DataType::AUDIO, physinputs); control_id = ntracks() + nbusses() + 1; while (how_many) { @@ -1712,7 +1724,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod save, close,restart,add new route - first named route is now Audio2) */ - + do { ++track_id; @@ -1722,7 +1734,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod if (route_by_name (track_name) == 0) { break; } - + } while (track_id < (UINT_MAX-1)); if (Config->get_input_auto_connect() & AutoConnectPhysical) { @@ -1730,7 +1742,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod } else { nphysical_in = 0; } - + if (Config->get_output_auto_connect() & AutoConnectPhysical) { nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size()); } else { @@ -1738,10 +1750,10 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod } shared_ptr track; - + try { track = boost::shared_ptr((new AudioTrack (*this, track_name, Route::Flag (0), mode))); - + if (track->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) @@ -1751,23 +1763,23 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod if (nphysical_in) { for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) { - + port = ""; - + if (Config->get_input_auto_connect() & AutoConnectPhysical) { port = physinputs[(channels_used+x)%nphysical_in]; - } - + } + if (port.length() && track->connect_input (track->input (x), port, this)) { break; } } } - + for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) { - + port = ""; - + if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { port = physoutputs[(channels_used+x)%nphysical_out]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { @@ -1775,16 +1787,16 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); } } - + if (port.length() && track->connect_output (track->output (x), port, this)) { break; } } - + channels_used += track->n_inputs ().n_audio(); track->audio_diskstream()->non_realtime_input_change(); - + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); track->set_remote_control_id (control_id); ++control_id; @@ -1800,7 +1812,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod /* we need to get rid of this, since the track failed to be created */ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ - { + { RCUWriter writer (diskstreams); boost::shared_ptr ds = writer.get_copy(); ds->remove (track->audio_diskstream()); @@ -1818,7 +1830,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod /* we need to get rid of this, since the track failed to be created */ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ - { + { RCUWriter writer (diskstreams); boost::shared_ptr ds = writer.get_copy(); ds->remove (track->audio_diskstream()); @@ -1847,14 +1859,14 @@ Session::set_remote_control_ids () shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ( MixerOrdered == m) { + if ( MixerOrdered == m) { long order = (*i)->order_key(N_("signal")); (*i)->set_remote_control_id( order+1 ); } else if ( EditorOrdered == m) { long order = (*i)->order_key(N_("editor")); (*i)->set_remote_control_id( order+1 ); } else if ( UserOrdered == m) { - //do nothing ... only changes to remote id's are initiated by user + //do nothing ... only changes to remote id's are initiated by user } } } @@ -1887,8 +1899,8 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ vector physinputs; vector physoutputs; - _engine.get_physical_outputs (physoutputs); - _engine.get_physical_inputs (physinputs); + _engine.get_physical_outputs (DataType::AUDIO, physoutputs); + _engine.get_physical_inputs (DataType::AUDIO, physinputs); control_id = ntracks() + nbusses() + 1; while (how_many) { @@ -1906,31 +1918,31 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ try { shared_ptr bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); - + if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; goto failure; } - + for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().n_audio(); ++x) { - + port = ""; if (Config->get_input_auto_connect() & AutoConnectPhysical) { port = physinputs[((n+x)%n_physical_inputs)]; - } - + } + if (port.length() && bus->connect_input (bus->input (x), port, this)) { break; } } - + for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs().n_audio(); ++x) { - + port = ""; - + if (Config->get_output_auto_connect() & AutoConnectPhysical) { port = physoutputs[((n+x)%n_physical_outputs)]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { @@ -1938,18 +1950,18 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); } } - + if (port.length() && bus->connect_output (bus->output (x), port, this)) { break; } } - + bus->set_remote_control_id (control_id); ++control_id; ret.push_back (bus); } - + catch (failed_constructor &err) { error << _("Session: could not create new audio route.") << endmsg; @@ -1977,7 +1989,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ void Session::add_routes (RouteList& new_routes, bool save) { - { + { RCUWriter writer (routes); shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); @@ -1985,18 +1997,18 @@ Session::add_routes (RouteList& new_routes, bool save) } for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - + boost::weak_ptr wpr (*x); (*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr)); (*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed)); (*x)->output_changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); (*x)->processors_changed.connect (bind (mem_fun (*this, &Session::update_latency_compensation), false, false)); - + if ((*x)->is_master()) { _master_out = (*x); } - + if ((*x)->is_control()) { _control_out = (*x); } @@ -2017,7 +2029,7 @@ Session::add_routes (RouteList& new_routes, bool save) for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { (*x)->set_control_outs (cports); } - } + } set_dirty(); @@ -2033,7 +2045,7 @@ Session::add_diskstream (boost::shared_ptr dstream) { /* need to do this in case we're rolling at the time, to prevent false underruns */ dstream->do_refill_with_alloc (); - + dstream->set_block_size (current_block_size); { @@ -2041,7 +2053,7 @@ Session::add_diskstream (boost::shared_ptr dstream) boost::shared_ptr ds = writer.get_copy(); ds->push_back (dstream); /* writer goes out of scope, copies ds back to main */ - } + } dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream)); /* this will connect to future changes, and check the current length */ @@ -2054,10 +2066,10 @@ Session::add_diskstream (boost::shared_ptr dstream) void Session::remove_route (shared_ptr route) { - { + { RCUWriter writer (routes); shared_ptr rs = writer.get_copy (); - + rs->remove (route); /* deleting the master out seems like a dumb @@ -2082,17 +2094,17 @@ Session::remove_route (shared_ptr route) } update_route_solo_state (); - + /* writer goes out of scope, forces route list update */ } Track* t; boost::shared_ptr ds; - + if ((t = dynamic_cast(route.get())) != 0) { ds = t->diskstream(); } - + if (ds) { { @@ -2103,12 +2115,12 @@ Session::remove_route (shared_ptr route) } find_current_end (); - - // We need to disconnect the routes inputs and outputs + + // We need to disconnect the routes inputs and outputs route->disconnect_inputs (0); route->disconnect_outputs (0); - + update_latency_compensation (false, false); set_dirty(); @@ -2127,7 +2139,7 @@ Session::remove_route (shared_ptr route) if (save_state (_current_snapshot_name)) { save_history (_current_snapshot_name); } -} +} void Session::route_mute_changed (void* src) @@ -2137,12 +2149,12 @@ Session::route_mute_changed (void* src) void Session::route_solo_changed (void* src, boost::weak_ptr wpr) -{ +{ if (solo_update_disabled) { // We know already return; } - + bool is_track; boost::shared_ptr route = wpr.lock (); @@ -2153,46 +2165,46 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) } is_track = (boost::dynamic_pointer_cast(route) != 0); - + shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - + /* soloing a track mutes all other tracks, soloing a bus mutes all other busses */ - + if (is_track) { - + /* don't mess with busses */ - + if (dynamic_cast((*i).get()) == 0) { continue; } - + } else { - + /* don't mess with tracks */ - + if (dynamic_cast((*i).get()) != 0) { continue; } } - + if ((*i) != route && ((*i)->mix_group () == 0 || (*i)->mix_group () != route->mix_group () || !route->mix_group ()->is_active())) { - + if ((*i)->soloed()) { - + /* if its already soloed, and solo latching is enabled, then leave it as it is. */ - + if (Config->get_solo_latched()) { continue; - } + } } - + /* do it */ solo_update_disabled = true; @@ -2200,7 +2212,7 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) solo_update_disabled = false; } } - + bool something_soloed = false; bool same_thing_soloed = false; bool signal = false; @@ -2222,12 +2234,12 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) break; } } - + if (something_soloed != currently_soloing) { signal = true; currently_soloing = something_soloed; } - + modify_solo_mute (is_track, same_thing_soloed); if (signal) { @@ -2251,7 +2263,7 @@ Session::update_route_solo_state () /* this is where we actually implement solo by changing the solo mute setting of each track. */ - + shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -2276,7 +2288,7 @@ Session::update_route_solo_state () for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_solo_mute (false); } - + if (signal) { SoloActive (false); } @@ -2297,11 +2309,11 @@ Session::modify_solo_mute (bool is_track, bool mute) shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - + if (is_track) { - + /* only alter track solo mute */ - + if (dynamic_cast((*i).get())) { if ((*i)->soloed()) { (*i)->set_solo_mute (!mute); @@ -2325,7 +2337,7 @@ Session::modify_solo_mute (bool is_track, bool mute) /* don't mute master or control outs in response to another bus solo */ - + if ((*i) != _master_out && (*i) != _control_out) { (*i)->set_solo_mute (mute); @@ -2335,7 +2347,7 @@ Session::modify_solo_mute (bool is_track, bool mute) } } -} +} void @@ -2347,8 +2359,8 @@ Session::catch_up_on_solo () has. */ update_route_solo_state(); -} - +} + shared_ptr Session::route_by_name (string name) { @@ -2411,11 +2423,13 @@ nframes_t Session::get_maximum_extent () const { nframes_t max = 0; - nframes_t me; + nframes_t me; boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { + if ((*i)->destructive()) //ignore tape tracks when getting max extents + continue; boost::shared_ptr pl = (*i)->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; @@ -2464,7 +2478,7 @@ Session::new_region_name (string old) char buf[len]; if ((last_period = old.find_last_of ('.')) == string::npos) { - + /* no period present - add one explicitly */ old += '.'; @@ -2492,7 +2506,7 @@ Session::new_region_name (string old) break; } } - + if (i == regions.end()) { break; } @@ -2500,14 +2514,14 @@ Session::new_region_name (string old) if (number != (UINT_MAX-1)) { return buf; - } + } error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg; return old; } int -Session::region_name (string& result, string base, bool newlevel) const +Session::region_name (string& result, string base, bool newlevel) { char buf[16]; string subbase; @@ -2515,19 +2529,15 @@ Session::region_name (string& result, string base, bool newlevel) const assert(base.find("/") == string::npos); if (base == "") { - + Glib::Mutex::Lock lm (region_lock); snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1); - - result = "region."; result += buf; } else { - /* XXX this is going to be slow. optimize me later */ - if (newlevel) { subbase = base; } else { @@ -2541,39 +2551,27 @@ Session::region_name (string& result, string base, bool newlevel) const } - bool name_taken = true; - { Glib::Mutex::Lock lm (region_lock); - - for (int n = 1; n < 5000; ++n) { - - result = subbase; - snprintf (buf, sizeof (buf), ".%d", n); + + map::iterator x; + + result = subbase; + + if ((x = region_name_map.find (subbase)) == region_name_map.end()) { + result += ".1"; + region_name_map[subbase] = 1; + } else { + x->second++; + snprintf (buf, sizeof (buf), ".%d", x->second); + result += buf; - - name_taken = false; - - for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - if (i->second->name() == result) { - name_taken = true; - break; - } - } - - if (!name_taken) { - break; - } } } - - if (name_taken) { - fatal << string_compose(_("too many regions with names like %1"), base) << endmsg; - /*NOTREACHED*/ - } } + return 0; -} +} void Session::add_region (boost::shared_ptr region) @@ -2582,49 +2580,49 @@ Session::add_region (boost::shared_ptr region) v.push_back (region); add_regions (v); } - + void Session::add_regions (vector >& new_regions) { bool added = false; - { + { Glib::Mutex::Lock lm (region_lock); for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { - + boost::shared_ptr region = *ii; - + if (region == 0) { error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; } else { - + RegionList::iterator x; - + for (x = regions.begin(); x != regions.end(); ++x) { - + if (region->region_list_equivalent (x->second)) { break; } } - + if (x == regions.end()) { - + pair entry; - + entry.first = region->id(); entry.second = region; - + pair x = regions.insert (entry); - + if (!x.second) { return; } - + added = true; - } + } } } } @@ -2632,9 +2630,9 @@ Session::add_regions (vector >& new_regions) /* mark dirty because something has changed even if we didn't add the region to the region list. */ - - set_dirty(); - + + set_dirty (); + if (added) { vector > v; @@ -2658,14 +2656,35 @@ Session::add_regions (vector >& new_regions) region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr(region))); region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr(region))); + + update_region_name_map (region); } - + if (!v.empty()) { RegionsAdded (v); /* EMIT SIGNAL */ } } } +void +Session::update_region_name_map (boost::shared_ptr region) +{ + string::size_type last_period = region->name().find_last_of ('.'); + + if (last_period != string::npos && last_period < region->name().length() - 1) { + + string base = region->name().substr (0, last_period); + string number = region->name().substr (last_period+1); + map::iterator x; + + /* note that if there is no number, we get zero from atoi, + which is just fine + */ + + region_name_map[base] = atoi (number); + } +} + void Session::region_changed (Change what_changed, boost::weak_ptr weak_region) { @@ -2679,6 +2698,10 @@ Session::region_changed (Change what_changed, boost::weak_ptr weak_regio /* relay hidden changes */ RegionHiddenChange (region); } + + if (what_changed & NameChanged) { + update_region_name_map (region); + } } void @@ -2693,7 +2716,7 @@ Session::remove_region (boost::weak_ptr weak_region) bool removed = false; - { + { Glib::Mutex::Lock lm (region_lock); if ((i = regions.find (region->id())) != regions.end()) { @@ -2718,7 +2741,7 @@ Session::find_whole_file_parent (boost::shared_ptr child) { RegionList::iterator i; boost::shared_ptr region; - + Glib::Mutex::Lock lm (region_lock); for (i = regions.begin(); i != regions.end(); ++i) { @@ -2731,10 +2754,10 @@ Session::find_whole_file_parent (boost::shared_ptr child) return region; } } - } + } return boost::shared_ptr (); -} +} void Session::find_equivalent_playlist_regions (boost::shared_ptr region, vector >& result) @@ -2747,12 +2770,12 @@ int Session::destroy_region (boost::shared_ptr region) { vector > srcs; - + { if (region->playlist()) { region->playlist()->destroy_region (region); } - + for (uint32_t n = 0; n < region->n_channels(); ++n) { srcs.push_back (region->source (n)); } @@ -2764,7 +2787,7 @@ Session::destroy_region (boost::shared_ptr region) (*i)->mark_for_remove (); (*i)->drop_references (); - + cerr << "source was not used by any playlist\n"; } @@ -2784,12 +2807,12 @@ int Session::remove_last_capture () { list > r; - + boost::shared_ptr dsl = diskstreams.reader(); - + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { list >& l = (*i)->last_capture_regions(); - + if (!l.empty()) { r.insert (r.end(), l.begin(), l.end()); l.clear (); @@ -2819,7 +2842,7 @@ Session::add_source (boost::shared_ptr source) entry.first = source->id(); entry.second = source; - + { Glib::Mutex::Lock lm (source_lock); result = sources.insert (entry); @@ -2829,14 +2852,14 @@ Session::add_source (boost::shared_ptr source) source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); set_dirty(); } - + boost::shared_ptr afs; if ((afs = boost::dynamic_pointer_cast(source)) != 0) { if (Config->get_auto_analyse_audio()) { Analyser::queue_source_for_analysis (source, false); } - } + } } void @@ -2847,22 +2870,22 @@ Session::remove_source (boost::weak_ptr src) if (!source) { return; - } + } - { + { Glib::Mutex::Lock lm (source_lock); if ((i = sources.find (source->id())) != sources.end()) { sources.erase (i); - } + } } - + if (!_state_of_the_state & InCleanup) { - + /* save state so we don't end up with a session file referring to non-existent sources. */ - + save_state (_current_snapshot_name); } } @@ -2893,8 +2916,8 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) if (afs && afs->path() == path && chn == afs->channel()) { return afs; - } - + } + } return boost::shared_ptr(); } @@ -2917,14 +2940,14 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, /* note: we know (or assume) the old path is already valid */ if (destructive) { - + /* destructive file sources have a name of the form: /path/to/Tnnnn-NAME(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + /* find last slash */ string dir; @@ -2951,16 +2974,16 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, path += '-'; path += new_legalized; path += ".wav"; /* XXX gag me with a spoon */ - + } else { - + /* non-destructive file sources have a name of the form: /path/to/NAME-nnnnn(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + string dir; string suffix; string::size_type slash; @@ -2982,7 +3005,7 @@ Session::change_audio_path_by_name (string path, string oldname, string newname, } suffix = path.substr (dash+1); - + // Suffix is now everything after the dash. Now we need to eliminate // the nnnnn part, which is done by either finding a '%' or a '.' @@ -3085,7 +3108,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool if (sys::exists(buf)) { existing++; - } + } } @@ -3112,7 +3135,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool spath += '/'; string::size_type pos = foo.find_last_of ('/'); - + if (pos == string::npos) { spath += foo; } else { @@ -3141,14 +3164,14 @@ Session::change_midi_path_by_name (string path, string oldname, string newname, /* note: we know (or assume) the old path is already valid */ if (destructive) { - + /* destructive file sources have a name of the form: /path/to/Tnnnn-NAME(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + /* find last slash */ string dir; @@ -3175,16 +3198,16 @@ Session::change_midi_path_by_name (string path, string oldname, string newname, path += '-'; path += new_legalized; path += ".mid"; /* XXX gag me with a spoon */ - + } else { - + /* non-destructive file sources have a name of the form: /path/to/NAME-nnnnn(%[LR])?.wav - + the task here is to replace NAME with the new name. */ - + string dir; string suffix; string::size_type slash; @@ -3206,7 +3229,7 @@ Session::change_midi_path_by_name (string path, string oldname, string newname, } suffix = path.substr (dash+1); - + // Suffix is now everything after the dash. Now we need to eliminate // the nnnnn part, which is done by either finding a '%' or a '.' @@ -3269,7 +3292,7 @@ Session::midi_path_from_name (string name) for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { SessionDirectory sdir((*i).path); - + sys::path p = sdir.midi_path(); p /= legalized; @@ -3280,7 +3303,7 @@ Session::midi_path_from_name (string name) if (sys::exists (buf)) { existing++; - } + } } if (existing == 0) { @@ -3305,7 +3328,7 @@ Session::midi_path_from_name (string name) spath += '/'; string::size_type pos = foo.find_last_of ('/'); - + if (pos == string::npos) { spath += foo; } else { @@ -3314,12 +3337,12 @@ Session::midi_path_from_name (string name) return spath; } - + boost::shared_ptr Session::create_midi_source_for_session (MidiDiskstream& ds) { string mpath = midi_path_from_name (ds.name()); - + return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, mpath, false, frame_rate())); } @@ -3351,7 +3374,7 @@ Session::add_playlist (boost::shared_ptr playlist) return; } - { + { Glib::Mutex::Lock lm (playlist_lock); if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) { playlists.insert (playlists.begin(), playlist); @@ -3368,7 +3391,7 @@ Session::add_playlist (boost::shared_ptr playlist) void Session::get_playlists (vector >& s) { - { + { Glib::Mutex::Lock lm (playlist_lock); for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { s.push_back (*i); @@ -3395,22 +3418,22 @@ Session::track_playlist (bool inuse, boost::weak_ptr wpl) return; } - { + { Glib::Mutex::Lock lm (playlist_lock); if (!inuse) { unused_playlists.insert (pl); - + if ((x = playlists.find (pl)) != playlists.end()) { playlists.erase (x); } - + } else { playlists.insert (pl); - + if ((x = unused_playlists.find (pl)) != unused_playlists.end()) { unused_playlists.erase (x); } @@ -3431,7 +3454,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) return; } - { + { Glib::Mutex::Lock lm (playlist_lock); PlaylistList::iterator i; @@ -3445,7 +3468,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) if (i != unused_playlists.end()) { unused_playlists.erase (i); } - + } set_dirty(); @@ -3453,7 +3476,7 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) PlaylistRemoved (playlist); /* EMIT SIGNAL */ } -void +void Session::set_audition (boost::shared_ptr r) { pending_audition_region = r; @@ -3510,7 +3533,7 @@ Session::remove_empty_sounds () vector audio_filenames; get_files_in_directory (_session_dir->sound_path(), audio_filenames); - + Glib::Mutex::Lock lm (source_lock); TapeFileMatcher tape_file_matcher; @@ -3523,7 +3546,7 @@ Session::remove_empty_sounds () sys::path audio_file_path (_session_dir->sound_path()); audio_file_path /= *i; - + if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) { try @@ -3534,7 +3557,7 @@ Session::remove_empty_sounds () } catch (const sys::filesystem_error& err) { - error << err.what() << endmsg; + error << err.what() << endmsg; } } } @@ -3555,7 +3578,7 @@ void Session::set_all_solo (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->is_hidden()) { (*i)->set_solo (yn, this); @@ -3564,12 +3587,12 @@ Session::set_all_solo (bool yn) set_dirty(); } - + void Session::set_all_mute (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->is_hidden()) { (*i)->set_mute (yn, this); @@ -3578,7 +3601,7 @@ Session::set_all_mute (bool yn) set_dirty(); } - + uint32_t Session::n_diskstreams () const { @@ -3604,7 +3627,7 @@ Session::graph_reordered () if (_state_of_the_state & InitialConnecting) { return; } - + /* every track/bus asked for this to be handled but it was deferred because we were connecting. do it now. */ @@ -3613,10 +3636,10 @@ Session::graph_reordered () resort_routes (); - /* force all diskstreams to update their capture offset values to + /* force all diskstreams to update their capture offset values to reflect any changes in latencies within the graph. */ - + boost::shared_ptr dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { @@ -3640,7 +3663,7 @@ void Session::record_enable_change_all (bool yn) { shared_ptr r = routes.reader (); - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { Track* at; @@ -3648,7 +3671,7 @@ Session::record_enable_change_all (bool yn) at->set_record_enable (yn, this); } } - + /* since we don't keep rec-enable state, don't mark session dirty */ } @@ -3681,7 +3704,7 @@ Session::remove_processor (Processor* processor) Send* send; PortInsert* port_insert; PluginInsert* plugin_insert; - + if ((port_insert = dynamic_cast (processor)) != 0) { list::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert); if (x != _port_inserts.end()) { @@ -3722,7 +3745,7 @@ Session::available_capture_duration () sample_bytes_on_disk = 2.0; break; - default: + default: /* impossible, but keep some gcc versions happy */ fatal << string_compose (_("programming error: %1"), X_("illegal native file data format")) @@ -3735,7 +3758,7 @@ Session::available_capture_duration () if (_total_free_4k_blocks * scale > (double) max_frames) { return max_frames; } - + return (nframes_t) floor (_total_free_4k_blocks * scale); } @@ -3746,7 +3769,7 @@ Session::add_bundle (shared_ptr bundle) Glib::Mutex::Lock guard (bundle_lock); _bundles.push_back (bundle); } - + BundleAdded (bundle); /* EMIT SIGNAL */ set_dirty(); @@ -3760,7 +3783,7 @@ Session::remove_bundle (shared_ptr bundle) { Glib::Mutex::Lock guard (bundle_lock); BundleList::iterator i = find (_bundles.begin(), _bundles.end(), bundle); - + if (i != _bundles.end()) { _bundles.erase (i); removed = true; @@ -3792,7 +3815,7 @@ void Session::tempo_map_changed (Change ignored) { clear_clicks (); - + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { (*i)->update_after_tempo_map_change (); } @@ -3812,7 +3835,7 @@ Session::ensure_buffers (ChanCount howmany) { if (current_block_size == 0) return; // too early? (is this ok?) - + // We need at least 2 MIDI scratch buffers to mix/merge if (howmany.n_midi() < 2) howmany.set_midi(2); @@ -3822,7 +3845,7 @@ Session::ensure_buffers (ChanCount howmany) _scratch_buffers->ensure_buffers(howmany, current_block_size); _mix_buffers->ensure_buffers(howmany, current_block_size); _silent_buffers->ensure_buffers(howmany, current_block_size); - + allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false); } @@ -3836,10 +3859,10 @@ Session::next_insert_id () if (!insert_bitset[n]) { insert_bitset[n] = true; return n; - + } } - + /* none available, so resize and try again */ insert_bitset.resize (insert_bitset.size() + 16, false); @@ -3856,10 +3879,10 @@ Session::next_send_id () if (!send_bitset[n]) { send_bitset[n] = true; return n; - + } } - + /* none available, so resize and try again */ send_bitset.resize (send_bitset.size() + 16, false); @@ -3907,7 +3930,7 @@ Session::named_selection_by_name (string name) void Session::add_named_selection (NamedSelection* named_selection) { - { + { Glib::Mutex::Lock lm (named_selection_lock); named_selections.insert (named_selections.begin(), named_selection); } @@ -3926,7 +3949,7 @@ Session::remove_named_selection (NamedSelection* named_selection) { bool removed = false; - { + { Glib::Mutex::Lock lm (named_selection_lock); NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection); @@ -3958,13 +3981,13 @@ bool Session::route_name_unique (string n) const { shared_ptr r = routes.reader (); - + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == n) { return false; } } - + return true; } @@ -3992,7 +4015,7 @@ Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, b } _pan_automation_buffer = new pan_t*[howmany]; - + for (uint32_t i = 0; i < howmany; ++i) { _pan_automation_buffer[i] = new pan_t[nframes]; } @@ -4020,11 +4043,11 @@ Session::freeze (InterThreadInfo& itt) return 0; } -int -Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len, - bool overwrite, vector >& srcs, InterThreadInfo& itt) +boost::shared_ptr +Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, + bool overwrite, vector >& srcs, InterThreadInfo& itt) { - int ret = -1; + boost::shared_ptr result; boost::shared_ptr playlist; boost::shared_ptr fsource; uint32_t x; @@ -4036,14 +4059,21 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le BufferSet buffers; SessionDirectory sdir(get_best_session_directory_for_new_source ()); const string sound_dir = sdir.sound_path().to_string(); + nframes_t len = end - start; + + if (end <= start) { + error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"), + end, start) << endmsg; + return result; + } // any bigger than this seems to cause stack overflows in called functions const nframes_t chunk_size = (128 * 1024)/4; g_atomic_int_set (&processing_prohibited, 1); - + /* call tree *MUST* hold route_lock */ - + if ((playlist = track.diskstream()->playlist()) == 0) { goto out; } @@ -4062,17 +4092,17 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le break; } } - + if (x == 99999) { error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg; goto out; } - + try { fsource = boost::dynamic_pointer_cast ( SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } - + catch (failed_constructor& err) { error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg; goto out; @@ -4082,7 +4112,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le } /* XXX need to flush all redirects */ - + position = start; to_do = len; @@ -4095,11 +4125,11 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le if (afs) afs->prepare_for_peakfile_writes (); } - + while (to_do && !itt.cancel) { - + this_chunk = min (to_do, chunk_size); - + if (track.export_stuff (buffers, start, this_chunk)) { goto out; } @@ -4107,61 +4137,59 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le uint32_t n = 0; for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) { if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) { goto out; } } } - + start += this_chunk; to_do -= this_chunk; - + itt.progress = (float) (1.0 - ((double) to_do / len)); } if (!itt.cancel) { - + time_t now; struct tm* xnow; time (&now); xnow = localtime (&now); - + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) { afs->update_header (position, *xnow, now); afs->flush_header (); } } - - /* construct a region to represent the bounced material */ - boost::shared_ptr aregion = RegionFactory::create (srcs, 0, srcs.front()->length(), - region_name_from_path (srcs.front()->name(), true)); + /* construct a region to represent the bounced material */ - ret = 0; + result = RegionFactory::create (srcs, 0, srcs.front()->length(), + region_name_from_path (srcs.front()->name(), true)); } - + out: - if (ret) { + if (!result) { for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); if (afs) { afs->mark_for_remove (); } - + (*src)->drop_references (); } } else { for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - + if (afs) afs->done_with_peakfile_writes (); } @@ -4169,7 +4197,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le g_atomic_int_set (&processing_prohibited, 0); - return ret; + return result; } BufferSet& @@ -4183,7 +4211,7 @@ Session::get_silent_buffers (ChanCount count) _silent_buffers->get(*t, i).clear(); } } - + return *_silent_buffers; } @@ -4203,7 +4231,7 @@ Session::get_mix_buffers (ChanCount count) return *_mix_buffers; } -uint32_t +uint32_t Session::ntracks () const { uint32_t n = 0; @@ -4218,7 +4246,7 @@ Session::ntracks () const return n; } -uint32_t +uint32_t Session::nbusses () const { uint32_t n = 0;