13
0

Keep track of xruns per source when recording

This commit is contained in:
Robin Gareus 2021-02-13 00:20:30 +01:00
parent d04713c0ab
commit 8486a938ed
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
11 changed files with 132 additions and 11 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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 ();

View File

@ -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 ();

View File

@ -621,6 +621,7 @@ enum SrcQuality {
};
typedef std::list<samplepos_t> AnalysisFeatureList;
typedef std::vector<samplepos_t> XrunPositions;
typedef std::list<boost::shared_ptr<Route> > RouteList;
typedef std::list<boost::shared_ptr<Stripable> > 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 {

View File

@ -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<ChannelList> 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<ChannelList> 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);

View File

@ -244,6 +244,7 @@ CLASSKEYS(std::vector<float>);
CLASSKEYS(std::vector<float*>);
CLASSKEYS(std::vector<double>);
CLASSKEYS(std::list<int64_t>);
CLASSKEYS(std::vector<samplepos_t>);
CLASSKEYS(std::list<Evoral::ControlEvent*>);
@ -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, Source> ("FileSource")
@ -1764,6 +1767,10 @@ LuaBindings::common (lua_State* L)
.beginStdVector <boost::shared_ptr<Region> > ("RegionVector")
.endClass ()
// typedef std::vector<samplepos_t> XrunPositions
.beginStdVector <samplepos_t> ("XrunPositions")
.endClass ()
// typedef std::list<boost::shared_ptr<Region> > RegionList
.beginConstStdList <boost::shared_ptr<Region> > ("RegionList")
.endClass ()

View File

@ -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 ()
{

View File

@ -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<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->mark_capture_xrun ();
}
}
}
}
}

View File

@ -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<samplepos_t> (x_str, x)) {
break;
}
_xruns.push_back (x);
}
}
/* Destructive is no longer valid */
if (_flags & Destructive) {

View File

@ -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
{