Keep track of xruns per source when recording
This commit is contained in:
parent
d04713c0ab
commit
8486a938ed
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 ();
|
||||
|
@ -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 ();
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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 ()
|
||||
|
@ -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 ()
|
||||
{
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user