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:
parent
e4249b97ff
commit
b7f367ae26
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
@ -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") {
|
||||
|
Loading…
Reference in New Issue
Block a user