X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=4b02d8ed7e6b127b319c0ae5e459369ff75f594e;hb=c35e94a3c83028220f6eb494fdfe9c1960aaf751;hp=8333146d513164dc8a47bf611e3c5bf9d15748b3;hpb=73d98df3d279dc785bcce7e2d729b78cca60f382;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 8333146d51..4b02d8ed7e 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -164,8 +164,6 @@ Session::Session (AudioEngine &eng, , _worst_input_latency (0) , _worst_track_latency (0) , _have_captured (false) - , _meter_hold (0) - , _meter_falloff (0) , _non_soloed_outs_muted (false) , _listen_cnt (0) , _solo_isolated_cnt (0) @@ -480,6 +478,7 @@ Session::destroy () /* clear state tree so that no references to objects are held any more */ delete state_tree; + state_tree = 0; /* reset dynamic state version back to default */ @@ -489,9 +488,13 @@ Session::destroy () delete _butler; _butler = 0; - delete midi_control_ui; delete _all_route_group; + DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n"); + for (list::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) { + delete *i; + } + if (click_data != default_click) { delete [] click_data; } @@ -557,16 +560,11 @@ Session::destroy () sources.clear (); } - DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n"); - for (list::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) { - - delete *i; - } - /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ playlists.reset (); delete _scene_changer; _scene_changer = 0; + delete midi_control_ui; midi_control_ui = 0; delete _mmc; _mmc = 0; delete _midi_ports; _midi_ports = 0; @@ -881,7 +879,7 @@ Session::add_monitor_section () return; } - boost::shared_ptr r (new Route (*this, _("monitor"), Route::MonitorOut, DataType::AUDIO)); + boost::shared_ptr r (new Route (*this, _("Monitor"), Route::MonitorOut, DataType::AUDIO)); if (r->init ()) { return; @@ -1235,9 +1233,9 @@ Session::set_auto_punch_location (Location* location) punch_connections.drop_connections (); - location->start_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, _1)); - location->end_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, _1)); - location->changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_changed, this, _1)); + location->StartChanged.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, location)); + location->EndChanged.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, location)); + location->Changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_changed, this, location)); location->set_auto_punch (true, this); @@ -1246,6 +1244,25 @@ Session::set_auto_punch_location (Location* location) auto_punch_location_changed (location); } +void +Session::set_session_extents (framepos_t start, framepos_t end) +{ + Location* existing; + if ((existing = _locations->session_range_location()) == 0) { + //if there is no existing session, we need to make a new session location (should never happen) + existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange); + } + + if (end <= start) { + error << _("Session: you can't use that location for session start/end)") << endmsg; + return; + } + + existing->set( start, end ); + + set_dirty(); +} + void Session::set_auto_loop_location (Location* location) { @@ -1277,9 +1294,9 @@ Session::set_auto_loop_location (Location* location) loop_connections.drop_connections (); - location->start_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1)); - location->end_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1)); - location->changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1)); + location->StartChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); + location->EndChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); + location->Changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); location->set_auto_loop (true, this); @@ -1293,51 +1310,165 @@ Session::set_auto_loop_location (Location* location) } void -Session::locations_added (Location *) +Session::update_skips (Location* loc, bool consolidate) { - set_dirty (); + Locations::LocationList skips; + + if (consolidate) { + + skips = consolidate_skips (loc); + + } else { + Locations::LocationList all_locations = _locations->list (); + + for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ++l) { + if ((*l)->is_skip ()) { + skips.push_back (*l); + } + } + } + + sync_locations_to_skips (skips); +} + +Locations::LocationList +Session::consolidate_skips (Location* loc) +{ + Locations::LocationList all_locations = _locations->list (); + Locations::LocationList skips; + + for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) { + + if (!(*l)->is_skip ()) { + ++l; + continue; + } + + /* don't test against self */ + + if (*l == loc) { + ++l; + continue; + } + + switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) { + case Evoral::OverlapInternal: + case Evoral::OverlapExternal: + case Evoral::OverlapStart: + case Evoral::OverlapEnd: + /* adjust new location to cover existing one */ + loc->set_start (min (loc->start(), (*l)->start())); + loc->set_end (max (loc->end(), (*l)->end())); + /* we don't need this one any more */ + _locations->remove (*l); + /* the location has been deleted, so remove reference to it in our local list */ + l = all_locations.erase (l); + break; + + case Evoral::OverlapNone: + skips.push_back (*l); + ++l; + break; + } + } + + /* add the new one, which now covers the maximal appropriate range based on overlaps with existing skips */ + + skips.push_back (loc); + + return skips; } void -Session::locations_changed () +Session::sync_locations_to_skips (const Locations::LocationList& locations) { - _locations->apply (*this, &Session::handle_locations_changed); + clear_events (SessionEvent::Skip); + + for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { + + Location* location = *i; + + if (location->is_skipping()) { + SessionEvent* ev = new SessionEvent (SessionEvent::Skip, SessionEvent::Add, location->start(), location->end(), 1.0); + queue_event (ev); + } + } } void -Session::handle_locations_changed (Locations::LocationList& locations) +Session::location_added (Location *location) { - Locations::LocationList::iterator i; - Location* location; - bool set_loop = false; - bool set_punch = false; + if (location->is_auto_punch()) { + set_auto_punch_location (location); + } - for (i = locations.begin(); i != locations.end(); ++i) { + if (location->is_auto_loop()) { + set_auto_loop_location (location); + } + + if (location->is_session_range()) { + /* no need for any signal handling or event setting with the session range, + because we keep a direct reference to it and use its start/end directly. + */ + _session_range_location = location; + } - location =* i; + if (location->is_skip()) { + /* listen for per-location signals that require us to update skip-locate events */ - if (location->is_auto_punch()) { - set_auto_punch_location (location); - set_punch = true; - } - if (location->is_auto_loop()) { - set_auto_loop_location (location); - set_loop = true; - } + location->StartChanged.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, true)); + location->EndChanged.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, true)); + location->Changed.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, true)); + location->FlagsChanged.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, false)); - if (location->is_session_range()) { - _session_range_location = location; - } - } + update_skips (location, true); + } - if (!set_loop) { - set_auto_loop_location (0); - } - if (!set_punch) { - set_auto_punch_location (0); - } + set_dirty (); +} - set_dirty(); +void +Session::location_removed (Location *location) +{ + if (location->is_auto_loop()) { + set_auto_loop_location (0); + } + + if (location->is_auto_punch()) { + set_auto_punch_location (0); + } + + if (location->is_session_range()) { + /* this is never supposed to happen */ + error << _("programming error: session range removed!") << endl; + } + + if (location->is_skip()) { + + update_skips (location, false); + } + + set_dirty (); +} + +void +Session::locations_changed () +{ + _locations->apply (*this, &Session::_locations_changed); +} + +void +Session::_locations_changed (const Locations::LocationList& locations) +{ + /* There was some mass-change in the Locations object. + + We might be re-adding a location here but it doesn't actually matter + for all the locations that the Session takes an interest in. + */ + + for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { + location_added (*i); + } } void @@ -1358,7 +1489,7 @@ Session::enable_record () if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) { _last_record_location = _transport_frame; - _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe)); + send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe)); if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { set_track_monitor_input_status (true); @@ -1379,7 +1510,7 @@ Session::disable_record (bool rt_context, bool force) if ((!Config->get_latched_record_enable () && !play_loop) || force) { g_atomic_int_set (&_record_status, Disabled); - _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit)); + send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit)); } else { if (rs == Recording) { g_atomic_int_set (&_record_status, Enabled); @@ -1433,7 +1564,7 @@ Session::maybe_enable_record () enable_record (); } } else { - _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause)); + send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause)); RecordStateChanged (); /* EMIT SIGNAL */ } @@ -1447,30 +1578,10 @@ Session::audible_frame () const framepos_t tf; framecnt_t offset; - /* the first of these two possible settings for "offset" - mean that the audible frame is stationary until - audio emerges from the latency compensation - "pseudo-pipeline". - - the second means that the audible frame is stationary - until audio would emerge from a physical port - in the absence of any plugin latency compensation - */ - offset = worst_playback_latency (); - if (offset > current_block_size) { - offset -= current_block_size; - } else { - /* XXX is this correct? if we have no external - physical connections and everything is internal - then surely this is zero? still, how - likely is that anyway? - */ - offset = current_block_size; - } - if (synced_to_engine()) { + /* Note: this is basically just sync-to-JACK */ tf = _engine.transport_frame(); } else { tf = _transport_frame; @@ -2050,6 +2161,14 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing } } +void +Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool reconnect_inputs, bool reconnect_outputs) +{ + /* TRX does stuff here, ardour does not (but probably should). This is called after an engine reset (in particular). + */ +} + + /** Caller must not hold process lock * @param name_template string to use for the start of the name, or "" to use "Audio". */ @@ -2278,7 +2397,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template /* generate a new name by adding a number to the end of the template name */ if (!find_route_name (route_name.c_str(), ++number, name, sizeof(name), true)) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } } @@ -3509,7 +3628,6 @@ Session::audio_source_name_is_unique (const string& name, uint32_t chan) std::vector sdirs = source_search_path (DataType::AUDIO); vector::iterator i; uint32_t existing = 0; - string basename = PBD::basename_nosuffix (name); for (vector::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) { @@ -3521,7 +3639,7 @@ Session::audio_source_name_is_unique (const string& name, uint32_t chan) const string spath = *i; - if (matching_unsuffixed_filename_exists_in (spath, basename)) { + if (matching_unsuffixed_filename_exists_in (spath, name)) { existing++; break; } @@ -3543,7 +3661,7 @@ Session::audio_source_name_is_unique (const string& name, uint32_t chan) break; } } - + return (existing == 0); } @@ -3633,7 +3751,7 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha return s; } -/** Return a unique name based on \a owner_name for a new internal MIDI source */ +/** Return a unique name based on `base` for a new internal MIDI source */ string Session::new_midi_source_path (const string& base) { @@ -3762,7 +3880,11 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr track) return boost::shared_ptr(); } - const string path = new_midi_source_path (name); + /* MIDI files are small, just put them in the first location of the + session source search path. + */ + + const string path = Glib::build_filename (source_search_path (DataType::MIDI).front(), name); return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( @@ -3936,7 +4058,7 @@ Session::available_capture_duration () fatal << string_compose (_("programming error: %1"), X_("illegal native file data format")) << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } double scale = 4096.0 / sample_bytes_on_disk; @@ -4012,9 +4134,9 @@ Session::tempo_map_changed (const PropertyChange&) } void -Session::update_locations_after_tempo_map_change (Locations::LocationList& loc) +Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc) { - for (Locations::LocationList::iterator i = loc.begin(); i != loc.end(); ++i) { + for (Locations::LocationList::const_iterator i = loc.begin(); i != loc.end(); ++i) { (*i)->recompute_frames_from_bbt (); } } @@ -4262,7 +4384,7 @@ Session::freeze_all (InterThreadInfo& itt) } boost::shared_ptr -Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, +Session::write_one_track (Track& track, framepos_t start, framepos_t end, bool /*overwrite*/, vector >& srcs, InterThreadInfo& itt, boost::shared_ptr endpoint, bool include_endpoint, @@ -4270,7 +4392,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, { boost::shared_ptr result; boost::shared_ptr playlist; - boost::shared_ptr fsource; + boost::shared_ptr source; ChanCount diskstream_channels (track.n_channels()); framepos_t position; framecnt_t this_chunk; @@ -4292,8 +4414,8 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint, include_endpoint, for_export, for_freeze); - if (diskstream_channels.n_audio() < 1) { - error << _("Cannot write a range with no audio.") << endmsg; + if (diskstream_channels.n(track.data_type()) < 1) { + error << _("Cannot write a range with no data.") << endmsg; return result; } @@ -4319,26 +4441,27 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, legal_playlist_name = legalize_for_path (playlist->name()); - for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) { + for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(track.data_type()); ++chan_n) { string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n); - string path = new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true); + string path = ((track.data_type() == DataType::AUDIO) + ? new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true) + : new_midi_source_path (legal_playlist_name)); if (path.empty()) { goto out; } try { - fsource = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate())); + source = SourceFactory::createWritable (track.data_type(), *this, path, false, frame_rate()); } catch (failed_constructor& err) { - error << string_compose (_("cannot create new audio file \"%1\" for %2"), path, track.name()) << endmsg; + error << string_compose (_("cannot create new file \"%1\" for %2"), path, track.name()) << endmsg; goto out; } - srcs.push_back (fsource); + srcs.push_back (source); } /* tell redirects that care that we are about to use a much larger @@ -4388,11 +4511,20 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, uint32_t n = 0; for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + boost::shared_ptr ms; if (afs) { if (afs->write (buffers.get_audio(n).data(latency_skip), current_chunk) != current_chunk) { goto out; } + } else if ((ms = boost::dynamic_pointer_cast(*src))) { + Source::Lock lock(ms->mutex()); + ms->mark_streaming_write_started(lock); + + const MidiBuffer& buf = buffers.get_midi(0); + for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) { + ms->append_event_frames(lock, *i, ms->timeline_position()); + } } } latency_skip = 0; @@ -4429,10 +4561,14 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + boost::shared_ptr ms; if (afs) { afs->update_header (position, *xnow, now); afs->flush_header (); + } else if ((ms = boost::dynamic_pointer_cast(*src))) { + Source::Lock lock(ms->mutex()); + ms->mark_streaming_write_completed(lock); } } @@ -4451,12 +4587,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, out: 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)->mark_for_remove (); (*src)->drop_references (); } @@ -4643,7 +4774,9 @@ Session::get_tracks () const for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { if (boost::dynamic_pointer_cast (*r)) { - tl->push_back (*r); + if (!(*r)->is_auditioner()) { + tl->push_back (*r); + } } } return tl;