diff --git a/libs/ardour/ardour/disk_writer.h b/libs/ardour/ardour/disk_writer.h index 511b6bc4fc..025393b9e2 100644 --- a/libs/ardour/ardour/disk_writer.h +++ b/libs/ardour/ardour/disk_writer.h @@ -94,6 +94,7 @@ public: void set_record_enabled (bool yn); void set_record_safe (bool yn); + void mark_capture_xrun (); /** @return Start position of currently-running capture (in session samples) */ samplepos_t current_capture_start () const { return _capture_start_sample; } @@ -177,6 +178,8 @@ private: samplepos_t _capture_start_sample; samplecnt_t _capture_captured; bool _was_recording; + bool _xrun_flag; + XrunPositions _xruns; samplepos_t _first_recordable_sample; samplepos_t _last_recordable_sample; int _last_possibly_recording; diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index a695d9e5ed..2b20669305 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -337,6 +337,8 @@ public: */ void transients (AnalysisFeatureList&); + void captured_xruns (XrunPositions&, bool abs = false) const; + /** merges _onsets OR _transients with _user_transients into given list * if _onsets and _transients are unset, run analysis. * list is not thinned, duplicates remain in place. diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 6b09a4d51d..1381d5bfdc 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -106,6 +106,10 @@ public: std::string get_transients_path() const; int load_transients (const std::string&); + size_t n_captured_xruns () const { return _xruns.size (); } + XrunPositions const& captured_xruns () const { return _xruns; } + void set_captured_xruns (XrunPositions const& xruns) { _xruns = xruns; } + virtual samplepos_t natural_position() const { return _natural_position; } virtual void set_natural_position (samplepos_t pos); bool have_natural_position() const { return _have_natural_position; } @@ -144,6 +148,7 @@ public: uint32_t _level; /* how deeply nested is this source w.r.t a disk file */ std::string _ancestor_name; std::string _captured_for; + XrunPositions _xruns; private: void fix_writable_flags (); diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index b2ba38ef54..5df552256b 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -147,6 +147,7 @@ public: samplecnt_t get_captured_samples (uint32_t n = 0) const; void transport_looped (samplepos_t); void transport_stopped_wallclock (struct tm &, time_t, bool); + void mark_capture_xrun (); bool pending_overwrite () const; void set_slaved (bool); ChanCount n_channels (); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index dbb28cf0eb..6bc1dd93ef 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -621,6 +621,7 @@ enum SrcQuality { }; typedef std::list AnalysisFeatureList; +typedef std::vector XrunPositions; typedef std::list > RouteList; typedef std::list > StripableList; @@ -794,9 +795,10 @@ enum MidiTempoMapDisposition { }; struct CaptureInfo { - samplepos_t start; - samplecnt_t samples; - samplecnt_t loop_offset; + samplepos_t start; + samplecnt_t samples; + samplecnt_t loop_offset; + XrunPositions xruns; }; enum LoopFadeChoice { diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc index 7c136192b0..d41c30f64d 100644 --- a/libs/ardour/disk_writer.cc +++ b/libs/ardour/disk_writer.cc @@ -53,6 +53,7 @@ DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f) , _capture_start_sample (0) , _capture_captured (0) , _was_recording (false) + , _xrun_flag (false) , _first_recordable_sample (max_samplepos) , _last_recordable_sample (max_samplepos) , _last_possibly_recording (0) @@ -66,6 +67,7 @@ DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f) , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { DiskIOProcessor::init (); + _xruns.reserve (128); } DiskWriter::~DiskWriter () @@ -370,6 +372,7 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp double speed, pframes_t nframes, bool result_required) { if (!_active && !_pending_active) { + _xrun_flag = false; return; } _active = _pending_active; @@ -421,6 +424,7 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp check_record_status (start_sample, speed, can_record); if (nframes == 0) { + _xrun_flag = false; return; } @@ -444,6 +448,7 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp if (rec_nframes && !_was_recording) { _capture_captured = 0; + _xrun_flag = false; if (loop_loc) { /* Loop recording, so pretend the capture started at the loop @@ -520,6 +525,8 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n", DEBUG_THREAD_SELF, name(), rec_nframes, total)); Overrun (); + _xruns.push_back (_capture_captured); + _xrun_flag = false; return; } @@ -623,6 +630,12 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp } } + if (_xrun_flag) { + /* There still are `Port::resampler_quality () -1` samples in the resampler + * buffer from before the x-run. */ + _xruns.push_back (_capture_captured + Port::resampler_quality () - 1); + } + _capture_captured += rec_nframes; DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), _capture_captured, rec_nframes)); @@ -636,6 +649,9 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp } } + /* clear xrun flag */ + _xrun_flag = false; + /* AUDIO BUTLER REQUIRED CODE */ if (_playlists[DataType::AUDIO] && !c->empty()) { @@ -657,6 +673,7 @@ void DiskWriter::finish_capture (boost::shared_ptr c) { _was_recording = false; + _xrun_flag = false; _first_recordable_sample = max_samplepos; _last_recordable_sample = max_samplepos; @@ -664,10 +681,12 @@ DiskWriter::finish_capture (boost::shared_ptr c) return; } - CaptureInfo* ci = new CaptureInfo; + CaptureInfo* ci = new CaptureInfo (); ci->start = _capture_start_sample; ci->samples = _capture_captured; + ci->xruns = _xruns; + _xruns.clear (); if (_loop_location) { samplepos_t loop_start = 0; @@ -707,6 +726,12 @@ DiskWriter::get_gui_feed_buffer () const return b; } +void +DiskWriter::mark_capture_xrun () +{ + _xrun_flag = true; +} + void DiskWriter::set_record_enabled (bool yn) { @@ -1098,6 +1123,7 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo } if (abort_capture) { + _xruns.clear (); for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { @@ -1152,6 +1178,7 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo } (*chan)->write_source->stamp (twhen); + (*chan)->write_source->set_captured_xruns (capture_info.front()->xruns); /* "re-announce the source to the world */ Source::SourcePropertyChanged ((*chan)->write_source); diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 9bb00d407c..ef51c10fba 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -244,6 +244,7 @@ CLASSKEYS(std::vector); CLASSKEYS(std::vector); CLASSKEYS(std::vector); CLASSKEYS(std::list); +CLASSKEYS(std::vector); CLASSKEYS(std::list); @@ -1253,6 +1254,7 @@ LuaBindings::common (lua_State* L) .addFunction ("covers", &Region::covers) .addFunction ("at_natural_position", &Region::at_natural_position) .addFunction ("is_compound", &Region::is_compound) + .addFunction ("captured_xruns", &Region::captured_xruns) .addFunction ("has_transients", &Region::has_transients) .addFunction ("transients", (AnalysisFeatureList (Region::*)())&Region::transients) @@ -1330,6 +1332,7 @@ LuaBindings::common (lua_State* L) .addFunction ("use_count", &Source::use_count) .addFunction ("used", &Source::used) .addFunction ("ancestor_name", &Source::ancestor_name) + .addFunction ("captured_xruns", &Source::captured_xruns) .endClass () .deriveWSPtrClass ("FileSource") @@ -1764,6 +1767,10 @@ LuaBindings::common (lua_State* L) .beginStdVector > ("RegionVector") .endClass () + // typedef std::vector XrunPositions + .beginStdVector ("XrunPositions") + .endClass () + // typedef std::list > RegionList .beginConstStdList > ("RegionList") .endClass () diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 616b1221bc..ddf5918889 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -1891,6 +1891,26 @@ Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& } } +void +Region::captured_xruns (XrunPositions& xruns, bool abs) const +{ + bool was_empty = xruns.empty (); + for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) { + XrunPositions const& x = (*i)->captured_xruns (); + for (XrunPositions::const_iterator p = x.begin (); p != x.end (); ++p) { + if (abs) { + xruns.push_back (*p); + } else if (*p >= _start && *p < _start + _length) { + xruns.push_back (*p - _start); + } + } + } + if (_sources.size () > 1 || !was_empty) { + sort (xruns.begin (), xruns.end ()); + xruns.erase (unique (xruns.begin (), xruns.end ()), xruns.end ()); + } +} + void Region::drop_sources () { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 66ada49c51..c3b799b479 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1875,16 +1875,27 @@ Session::xrun_recovery () Xrun (_transport_sample); /* EMIT SIGNAL */ - if (Config->get_stop_recording_on_xrun() && actively_recording()) { + if (actively_recording ()) { + if (Config->get_stop_recording_on_xrun()) { - /* it didn't actually halt, but we need - * to handle things in the same way. - */ + /* it didn't actually halt, but we need + * to handle things in the same way. + */ - engine_halted(); + engine_halted(); - /* ..and start the FSM engine again */ - _transport_fsm->start (); + /* ..and start the FSM engine again */ + _transport_fsm->start (); + } else { + boost::shared_ptr rl = routes.reader(); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + tr->mark_capture_xrun (); + } + } + + } } } diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index ebee48d2f0..cf48501dc1 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -127,6 +127,18 @@ Source::get_state () node->set_property ("natural-position", _natural_position); } + if (!_xruns.empty ()) { + stringstream str; + for (XrunPositions::const_iterator xx = _xruns.begin(); xx != _xruns.end(); ++xx) { + str << PBD::to_string (*xx) << '\n'; + } + XMLNode* xnode = new XMLNode (X_("xruns")); + XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */ + content_node->set_content (str.str()); + xnode->add_child_nocopy (*content_node); + node->add_child_nocopy (*xnode); + } + return *node; } @@ -167,6 +179,29 @@ Source::set_state (const XMLNode& node, int version) _flags = Flag (0); } + _xruns.clear (); + XMLNodeList nlist = node.children(); + for (XMLNodeIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() != X_("xruns")) { + continue; + } + const XMLNode& xruns (*(*niter)); + if (xruns.children().empty()) { + break; + } + XMLNode* content_node = xruns.children().front(); + stringstream str (content_node->content()); + while (str) { + samplepos_t x; + std::string x_str; + str >> x_str; + if (!str || !PBD::string_to (x_str, x)) { + break; + } + _xruns.push_back (x); + } + } + /* Destructive is no longer valid */ if (_flags & Destructive) { diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index ddc6eb5855..7f7bba8d3c 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -562,6 +562,14 @@ Track::transport_stopped_wallclock (struct tm & n, time_t t, bool g) _disk_writer->transport_stopped_wallclock (n, t, g); } +void +Track::mark_capture_xrun () +{ + if (_disk_writer->record_enabled ()) { + _disk_writer->mark_capture_xrun (); + } +} + bool Track::pending_overwrite () const {