13
0

Allow multi-threaded AudioSource reading

This is needed for threaded peak-file creation. The same
nested source may be accessed multiple times concurrently
(in addition to the butler thread reading it).

This fixes Bitrunner's "filum2020" session randomly showing garbage
waveforms.
This commit is contained in:
Robin Gareus 2020-12-21 21:55:55 +01:00
parent e4249b97ff
commit b7f367ae26
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
4 changed files with 2 additions and 81 deletions

View File

@ -103,25 +103,10 @@ class LIBARDOUR_API AudioSource : virtual public Source,
/** @return true if the each source sample s must be clamped to -1 < s < 1 */
virtual bool clamped_at_unity () const = 0;
static void allocate_working_buffers (samplecnt_t framerate);
protected:
static bool _build_missing_peakfiles;
static bool _build_peakfiles;
/* these collections of working buffers for supporting
playlist's reading from potentially nested/recursive
sources assume SINGLE THREADED reads by the butler
thread, or a lock around calls that use them.
*/
static std::vector<boost::shared_array<Sample> > _mixdown_buffers;
static std::vector<boost::shared_array<gain_t> > _gain_buffers;
static Glib::Threads::Mutex _level_buffer_lock;
static void ensure_buffers_for_level (uint32_t, samplecnt_t);
static void ensure_buffers_for_level_locked (uint32_t, samplecnt_t);
samplecnt_t _length;
std::string _peakpath;

View File

@ -52,7 +52,6 @@ AudioPlaylistSource::AudioPlaylistSource (Session& s, const ID& orig, const std:
, _playlist_channel (chn)
{
AudioSource::_length = len;
ensure_buffers_for_level (_level, _session.sample_rate());
}
AudioPlaylistSource::AudioPlaylistSource (Session& s, const XMLNode& node)
@ -117,16 +116,12 @@ AudioPlaylistSource::set_state (const XMLNode& node, int version, bool with_desc
throw failed_constructor ();
}
ensure_buffers_for_level (_level, _session.sample_rate());
return 0;
}
samplecnt_t
AudioPlaylistSource::read_unlocked (Sample* dst, samplepos_t start, samplecnt_t cnt) const
{
boost::shared_array<Sample> sbuf;
boost::shared_array<gain_t> gbuf;
samplecnt_t to_read;
samplecnt_t to_zero;
@ -143,16 +138,8 @@ AudioPlaylistSource::read_unlocked (Sample* dst, samplepos_t start, samplecnt_t
to_zero = 0;
}
{
/* Don't need to hold the lock for the actual read, and
actually, we cannot, but we do want to interlock
with any changes to the list of buffers caused
by creating new nested playlists/sources
*/
Glib::Threads::Mutex::Lock lm (_level_buffer_lock);
sbuf = _mixdown_buffers[_level-1];
gbuf = _gain_buffers[_level-1];
}
boost::scoped_array<float> sbuf(new float[to_read]);
boost::scoped_array<gain_t> gbuf(new gain_t[to_read]);
boost::dynamic_pointer_cast<AudioPlaylist>(_playlist)->read (dst, sbuf.get(), gbuf.get(), start+_playlist_offset, to_read, _playlist_channel);

View File

@ -72,9 +72,6 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
Glib::Threads::Mutex AudioSource::_level_buffer_lock;
vector<boost::shared_array<Sample> > AudioSource::_mixdown_buffers;
vector<boost::shared_array<gain_t> > AudioSource::_gain_buffers;
bool AudioSource::_build_missing_peakfiles = false;
/** true if we want peakfiles (e.g. if we are displaying a GUI) */
@ -1138,49 +1135,3 @@ AudioSource::mark_streaming_write_completed (const Lock& lock)
PeaksReady (); /* EMIT SIGNAL */
}
}
void
AudioSource::allocate_working_buffers (samplecnt_t framerate)
{
Glib::Threads::Mutex::Lock lm (_level_buffer_lock);
/* Note: we don't need any buffers allocated until
a level 1 audiosource is created, at which
time we'll call ::ensure_buffers_for_level()
with the right value and do the right thing.
*/
if (!_mixdown_buffers.empty()) {
ensure_buffers_for_level_locked ( _mixdown_buffers.size(), framerate);
}
}
void
AudioSource::ensure_buffers_for_level (uint32_t level, samplecnt_t sample_rate)
{
Glib::Threads::Mutex::Lock lm (_level_buffer_lock);
ensure_buffers_for_level_locked (level, sample_rate);
}
void
AudioSource::ensure_buffers_for_level_locked (uint32_t level, samplecnt_t sample_rate)
{
samplecnt_t nframes = PlaybackBuffer<Sample>::power_of_two_size ((samplecnt_t) floor (Config->get_audio_playback_buffer_seconds() * sample_rate));
/* this may be called because either "level" or "sample_rate" have
* changed. and it may be called with "level" smaller than the current
* number of buffers, because a new compound region has been created at
* a more shallow level than the deepest one we currently have.
*/
uint32_t limit = max ((size_t) level, _mixdown_buffers.size());
_mixdown_buffers.clear ();
_gain_buffers.clear ();
for (uint32_t n = 0; n < limit; ++n) {
_mixdown_buffers.push_back (boost::shared_array<Sample> (new Sample[nframes]));
_gain_buffers.push_back (boost::shared_array<gain_t> (new gain_t[nframes]));
}
}

View File

@ -4191,8 +4191,6 @@ Session::config_changed (std::string p, bool ours)
_solo_cut_control->Changed (true, Controllable::NoGroup);
} else if (p == "timecode-offset" || p == "timecode-offset-negative") {
last_timecode_valid = false;
} else if (p == "playback-buffer-seconds") {
AudioSource::allocate_working_buffers (sample_rate());
} else if (p == "ltc-sink-port") {
reconnect_ltc_output ();
} else if (p == "timecode-generator-offset") {