MidiSource: change thread mutual exclusion object from Mutex to a RWLock

This allows two reader threads to proceed without blocking each other, as can
happen when the butler renders a MIDI track into an RT-safe buffer while the
GUI reads the same MidiModel/Source for visual display.
This commit is contained in:
Paul Davis 2022-03-30 12:56:04 -06:00
parent 1686db8b0c
commit bc38f8d424
23 changed files with 129 additions and 122 deletions

View File

@ -219,10 +219,10 @@ void
MidiStreamView::update_contents_metrics(boost::shared_ptr<Region> r)
{
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
if (mr) {
_range_dirty = update_data_note_range(
mr->model()->lowest_note(),
mr->model()->highest_note());
Source::ReaderLock lm (mr->midi_source(0)->mutex());
_range_dirty = update_data_note_range (mr->model()->lowest_note(), mr->model()->highest_note());
}
}

View File

@ -61,7 +61,7 @@ public:
virtual int update_header (samplepos_t when, struct tm&, time_t) = 0;
virtual int flush_header () = 0;
void mark_streaming_write_completed (const Lock& lock);
void mark_streaming_write_completed (const WriterLock& lock);
int setup_peakfile ();
void set_gain (float g, bool temporarily = false);

View File

@ -60,7 +60,7 @@ class LIBARDOUR_API AudioSource : virtual public Source, public ARDOUR::AudioRea
virtual float sample_rate () const = 0;
virtual void mark_streaming_write_completed (const Lock& lock);
virtual void mark_streaming_write_completed (const WriterLock& lock);
virtual bool can_truncate_peaks() const { return true; }

View File

@ -36,6 +36,7 @@
#include "ardour/automatable_sequence.h"
#include "ardour/libardour_visibility.h"
#include "ardour/libardour_visibility.h"
#include "ardour/source.h"
#include "ardour/types.h"
#include "ardour/types.h"
#include "ardour/variant.h"
@ -280,13 +281,13 @@ public:
*/
void apply_command_as_subcommand (Session& session, Command* cmd);
bool sync_to_source (const Glib::Threads::Mutex::Lock& source_lock);
bool sync_to_source (const Source::WriterLock& source_lock);
bool write_to(boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock);
const Source::WriterLock& source_lock);
bool write_section_to(boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock,
const Source::WriterLock& source_lock,
Temporal::Beats begin = Temporal::Beats(),
Temporal::Beats end = std::numeric_limits<Temporal::Beats>::max(),
bool offset_events = false);
@ -331,7 +332,7 @@ public:
~WriteLockImpl() {
delete source_lock;
}
Glib::Threads::Mutex::Lock* source_lock;
Source::WriterLock* source_lock;
};
public:

View File

@ -67,7 +67,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
* @param end time of latest event that can be written.
* @return zero on success, non-zero if the write failed for any reason.
*/
int write_to (const Lock& lock,
int write_to (const ReaderLock& lock,
boost::shared_ptr<MidiSource> newsrc,
Temporal::Beats begin = Temporal::Beats(),
Temporal::Beats end = std::numeric_limits<Temporal::Beats>::max());
@ -81,7 +81,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
* @param end time of latest event that can be written.
* @return zero on success, non-zero if the write failed for any reason.
*/
int export_write_to (const Lock& lock,
int export_write_to (const ReaderLock& lock,
boost::shared_ptr<MidiSource> newsrc,
Temporal::Beats begin,
Temporal::Beats end);
@ -99,7 +99,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
* @param tracker an optional pointer to MidiNoteTracker object, for note on/off tracking.
* @param filtered Parameters whose MIDI messages will not be returned.
*/
virtual timecnt_t midi_read (const Lock& lock,
virtual timecnt_t midi_read (const ReaderLock& lock,
Evoral::EventSink<samplepos_t>& dst,
timepos_t const & source_start,
timepos_t const & start,
@ -116,7 +116,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
* @param source_start This source's start position in session samples.
* @param cnt The length of time to write.
*/
virtual timecnt_t midi_write (const Lock& lock,
virtual timecnt_t midi_write (const WriterLock& lock,
MidiRingBuffer<samplepos_t>& source,
timepos_t const & source_start,
timecnt_t const & cnt);
@ -125,20 +125,20 @@ class LIBARDOUR_API MidiSource : virtual public Source
*
* Caller must ensure that the event is later than the last written event.
*/
virtual void append_event_beats(const Lock& lock,
virtual void append_event_beats(const WriterLock& lock,
const Evoral::Event<Temporal::Beats>& ev) = 0;
/** Append a single event with a timestamp in samples.
*
* Caller must ensure that the event is later than the last written event.
*/
virtual void append_event_samples(const Lock& lock,
virtual void append_event_samples(const WriterLock& lock,
const Evoral::Event<samplepos_t>& ev,
samplepos_t source_start) = 0;
virtual void mark_streaming_midi_write_started (const Lock& lock, NoteMode mode);
virtual void mark_streaming_write_started (const Lock& lock);
virtual void mark_streaming_write_completed (const Lock& lock);
virtual void mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode);
virtual void mark_streaming_write_started (const WriterLock& lock);
virtual void mark_streaming_write_completed (const WriterLock& lock);
/** Mark write starting with the given time parameters.
*
@ -158,7 +158,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
* etc.
*/
virtual void mark_midi_streaming_write_completed (
const Lock& lock,
const WriterLock& lock,
Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_option,
Temporal::Beats when = Temporal::Beats());
@ -169,22 +169,22 @@ class LIBARDOUR_API MidiSource : virtual public Source
bool length_mutable() const { return true; }
virtual void load_model(const Glib::Threads::Mutex::Lock& lock, bool force_reload=false) = 0;
virtual void destroy_model(const Glib::Threads::Mutex::Lock& lock) = 0;
virtual void load_model(const WriterLock& lock, bool force_reload=false) = 0;
virtual void destroy_model(const WriterLock& lock) = 0;
/** Reset cached information (like iterators) when things have changed.
* @param lock Source lock, which must be held by caller.
*/
void invalidate(const Glib::Threads::Mutex::Lock& lock);
void invalidate(const WriterLock& lock);
/** Thou shalt not emit this directly, use invalidate() instead. */
mutable PBD::Signal1<void, bool> Invalidated;
void set_note_mode(const Glib::Threads::Mutex::Lock& lock, NoteMode mode);
void set_note_mode(const WriterLock& lock, NoteMode mode);
boost::shared_ptr<MidiModel> model() { return _model; }
void set_model(const Glib::Threads::Mutex::Lock& lock, boost::shared_ptr<MidiModel>);
void drop_model(const Glib::Threads::Mutex::Lock& lock);
void set_model(const WriterLock& lock, boost::shared_ptr<MidiModel>);
void drop_model(const WriterLock& lock);
Evoral::ControlList::InterpolationStyle interpolation_of (Evoral::Parameter) const;
void set_interpolation_of (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
@ -204,9 +204,9 @@ class LIBARDOUR_API MidiSource : virtual public Source
PBD::Signal2<void, Evoral::Parameter, AutoState> AutomationStateChanged;
protected:
virtual void flush_midi(const Lock& lock) = 0;
virtual void flush_midi(const WriterLock& lock) = 0;
virtual timecnt_t read_unlocked (const Lock& lock,
virtual timecnt_t read_unlocked (const ReaderLock& lock,
Evoral::EventSink<samplepos_t>& dst,
timepos_t const & position,
timepos_t const & start,
@ -221,7 +221,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
* @param position This source's start position in session samples.
* @param cnt The duration of this block to write for.
*/
virtual timecnt_t write_unlocked (const Lock& lock,
virtual timecnt_t write_unlocked (const WriterLock& lock,
MidiRingBuffer<samplepos_t>& source,
timepos_t const & position,
timecnt_t const & cnt) = 0;

View File

@ -25,6 +25,7 @@
#include "temporal/beats.h"
#include "ardour/midi_buffer.h"
#include "ardour/source.h"
namespace Evoral {
template <typename T> class EventSink;
@ -52,7 +53,7 @@ public:
void remove (uint8_t note, uint8_t chn);
void resolve_notes (MidiBuffer& buffer, samplepos_t time, bool reset = true);
void resolve_notes (Evoral::EventSink<samplepos_t>& buffer, samplepos_t time);
void resolve_notes (MidiSource& src, const Glib::Threads::Mutex::Lock& lock, Temporal::Beats time);
void resolve_notes (MidiSource& src, const Source::WriterLock& lock, Temporal::Beats time);
void flush_notes (MidiBuffer& buffer, samplepos_t time, bool reset = true);

View File

@ -53,14 +53,14 @@ public:
return safe_midi_file_extension(path);
}
void append_event_beats (const Lock& lock, const Evoral::Event<Temporal::Beats>& ev);
void append_event_samples (const Lock& lock, const Evoral::Event<samplepos_t>& ev, samplepos_t source_start);
void append_event_beats (const WriterLock& lock, const Evoral::Event<Temporal::Beats>& ev);
void append_event_samples (const WriterLock& lock, const Evoral::Event<samplepos_t>& ev, samplepos_t source_start);
void update_length (timepos_t const & dur);
void mark_streaming_midi_write_started (const Lock& lock, NoteMode mode);
void mark_streaming_write_completed (const Lock& lock);
void mark_midi_streaming_write_completed (const Lock& lock,
void mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode);
void mark_streaming_write_completed (const WriterLock& lock);
void mark_midi_streaming_write_completed (const WriterLock& lock,
Evoral::Sequence<Temporal::Beats>::StuckNoteOption,
Temporal::Beats when = Temporal::Beats());
@ -81,7 +81,7 @@ public:
protected:
void close ();
void flush_midi (const Lock& lock);
void flush_midi (const WriterLock& lock);
private:
bool _open;
@ -94,9 +94,9 @@ public:
int open_for_write ();
void ensure_disk_file (const Lock& lock);
void ensure_disk_file (const WriterLock& lock);
timecnt_t read_unlocked (const Lock& lock,
timecnt_t read_unlocked (const ReaderLock& lock,
Evoral::EventSink<samplepos_t>& dst,
timepos_t const & position,
timepos_t const & start,
@ -105,7 +105,7 @@ public:
MidiNoteTracker* tracker,
MidiChannelFilter* filter) const;
timecnt_t write_unlocked (const Lock& lock,
timecnt_t write_unlocked (const WriterLock& lock,
MidiRingBuffer<samplepos_t>& src,
timepos_t const & position,
timecnt_t const & cnt);

View File

@ -63,7 +63,8 @@ public:
Missing = 0x400, /* used for MIDI only */
};
typedef Glib::Threads::Mutex::Lock Lock;
typedef Glib::Threads::RWLock::ReaderLock ReaderLock;
typedef Glib::Threads::RWLock::WriterLock WriterLock;
Source (Session&, DataType type, const std::string& name, Flag flags=Flag(0));
Source (Session&, const XMLNode&);
@ -85,8 +86,8 @@ public:
void mark_for_remove();
virtual void mark_streaming_write_started (const Lock& lock) {}
virtual void mark_streaming_write_completed (const Lock& lock) = 0;
virtual void mark_streaming_write_started (const WriterLock& lock) {}
virtual void mark_streaming_write_completed (const WriterLock& lock) = 0;
virtual void session_saved() {}
@ -129,7 +130,7 @@ public:
void set_allow_remove_if_empty (bool yn);
Glib::Threads::Mutex& mutex() { return _lock; }
Glib::Threads::RWLock& mutex() { return _lock; }
Flag flags() const { return _flags; }
virtual void inc_use_count ();
@ -167,7 +168,7 @@ public:
typedef std::vector<SegmentDescriptor> SegmentDescriptors;
SegmentDescriptors segment_descriptors;
mutable Glib::Threads::Mutex _lock;
mutable Glib::Threads::RWLock _lock;
mutable Glib::Threads::Mutex _analysis_lock;
private:

View File

@ -249,7 +249,7 @@ AudioFileSource::set_state (const XMLNode& node, int version)
}
void
AudioFileSource::mark_streaming_write_completed (const Lock& lock)
AudioFileSource::mark_streaming_write_completed (const WriterLock& lock)
{
if (!writable()) {
return;

View File

@ -308,14 +308,14 @@ AudioSource::read (Sample *dst, samplepos_t start, samplecnt_t cnt, int /*channe
{
assert (cnt >= 0);
Glib::Threads::Mutex::Lock lm (_lock);
ReaderLock lm (_lock);
return read_unlocked (dst, start, cnt);
}
samplecnt_t
AudioSource::write (Sample *dst, samplecnt_t cnt)
{
Glib::Threads::Mutex::Lock lm (_lock);
WriterLock lm (_lock);
/* any write makes the file not removable */
_flags = Flag (_flags & ~Removable);
return write_unlocked (dst, cnt);
@ -335,7 +335,7 @@ int
AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos_t start, samplecnt_t cnt,
double samples_per_visual_peak, samplecnt_t samples_per_file_peak) const
{
Glib::Threads::Mutex::Lock lm (_lock);
ReaderLock lm (_lock);
#if 0 // DEBUG ONLY
/* Bypass peak-file cache, compute peaks using raw data from source */
@ -762,7 +762,7 @@ AudioSource::build_peaks_from_scratch ()
{
/* hold lock while building peaks */
Glib::Threads::Mutex::Lock lp (_lock);
ReaderLock lp (_lock);
if (prepare_for_peakfile_writes ()) {
goto out;
@ -827,7 +827,7 @@ AudioSource::build_peaks_from_scratch ()
int
AudioSource::close_peakfile ()
{
Glib::Threads::Mutex::Lock lp (_lock);
WriterLock lp (_lock);
if (_peakfile_fd >= 0) {
close (_peakfile_fd);
_peakfile_fd = -1;
@ -1117,7 +1117,7 @@ AudioSource::available_peaks (double zoom_factor) const
}
void
AudioSource::mark_streaming_write_completed (const Lock& lock)
AudioSource::mark_streaming_write_completed (const WriterLock& lock)
{
Glib::Threads::Mutex::Lock lm (_peaks_ready_lock);

View File

@ -860,7 +860,7 @@ DiskWriter::prep_record_enable ()
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
capturing_sources.push_back ((*chan)->write_source);
Source::Lock lock((*chan)->write_source->mutex());
Source::WriterLock lock ((*chan)->write_source->mutex());
(*chan)->write_source->mark_streaming_write_started (lock);
}
@ -1034,7 +1034,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
}
if ((total > _chunk_samples) || force_flush) {
Source::Lock lm(_midi_write_source->mutex());
Source::WriterLock lm(_midi_write_source->mutex());
if (_midi_write_source->midi_write (lm, *_midi_buf, timepos_t (get_capture_start_sample (0)), timecnt_t (to_write)) != to_write) {
error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg;
return -1;
@ -1066,7 +1066,7 @@ DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/)
if ((*chan)->write_source) {
if (mark_write_complete) {
Source::Lock lock((*chan)->write_source->mutex());
Source::WriterLock lock((*chan)->write_source->mutex());
(*chan)->write_source->mark_streaming_write_completed (lock);
(*chan)->write_source->done_with_peakfile_writes ();
}
@ -1088,7 +1088,7 @@ DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/)
if (_midi_write_source) {
if (mark_write_complete) {
Source::Lock lm(_midi_write_source->mutex());
Source::WriterLock lm(_midi_write_source->mutex());
_midi_write_source->mark_streaming_write_completed (lm);
}
}
@ -1267,7 +1267,7 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
/* phew, we have data */
Source::Lock source_lock(_midi_write_source->mutex());
Source::WriterLock source_lock(_midi_write_source->mutex());
/* figure out the name for this take */

View File

@ -584,7 +584,7 @@ FileSource::is_stub () const
int
FileSource::rename (const string& newpath)
{
Glib::Threads::Mutex::Lock lm (_lock);
WriterLock lm (_lock);
string oldpath = _path;
// Test whether newpath exists, if yes notify the user but continue.

View File

@ -394,7 +394,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
continue; //should never happen. The calling code should provide exactly the number of tracks&channels we need
}
Glib::Threads::Mutex::Lock source_lock(smfs->mutex());
Source::WriterLock source_lock(smfs->mutex());
smfs->drop_model (source_lock);
if (type0) {

View File

@ -1224,10 +1224,9 @@ MidiModel::PatchChangeDiffCommand::get_state ()
*/
bool
MidiModel::write_to (boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock)
const Source::WriterLock& source_lock)
{
ReadLock lock(read_lock());
ReadLock lock (read_lock()); /* Sequence read-lock */
source->drop_model(source_lock);
/* as of March 2022 or long before , the note mode argument does nothing */
@ -1250,7 +1249,7 @@ MidiModel::write_to (boost::shared_ptr<MidiSource> source,
of the model.
*/
bool
MidiModel::sync_to_source (const Glib::Threads::Mutex::Lock& source_lock)
MidiModel::sync_to_source (const Source::WriterLock& source_lock)
{
ReadLock lock(read_lock());
@ -1281,7 +1280,7 @@ MidiModel::sync_to_source (const Glib::Threads::Mutex::Lock& source_lock)
*/
bool
MidiModel::write_section_to (boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock,
const Source::WriterLock& source_lock,
TimeType begin_time,
TimeType end_time,
bool offset_events)
@ -1414,17 +1413,15 @@ MidiModel::find_sysex (Evoral::event_id_t sysex_id)
MidiModel::WriteLock
MidiModel::edit_lock()
{
Glib::Threads::Mutex::Lock* source_lock = 0;
Source::WriterLock* source_lock = 0;
if (ms) {
/* Take source lock and invalidate iterator to release its lock on model.
Add currently active notes to _active_notes so we can restore them
if playback resumes at the same point after the edit. */
source_lock = new Glib::Threads::Mutex::Lock(ms->mutex());
ms->invalidate(*source_lock);
}
return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
/* Take source lock and invalidate iterator to release its lock on model.
* Add currently active notes to _active_notes so we can restore them
* if playback resumes at the same point after the edit.
*/
source_lock = new Source::WriterLock (_midi_source.mutex());
_midi_source.invalidate(*source_lock);
return WriteLock (new WriteLockImpl (source_lock, _lock, _control_lock));
}
int

View File

@ -109,8 +109,9 @@ MidiRegion::do_export (string const& path) const
{
/* Lock our source since we'll be reading from it. write_to() will
take a lock on newsrc. */
Source::Lock lm (midi_source(0)->mutex());
* take a lock on newsrc.
*/
Source::ReaderLock lm (midi_source(0)->mutex());
if (midi_source(0)->export_write_to (lm, newsrc, _start.val().beats(), _start.val().beats() + _length.val().beats())) {
return false;
}
@ -235,8 +236,7 @@ MidiRegion::_read_at (const SourceList& /*srcs*/,
boost::shared_ptr<MidiSource> src = midi_source(chan_n);
Glib::Threads::Mutex::Lock lm(src->mutex());
Source::ReaderLock lm (src->mutex());
#if 0
cerr << "MR " << name () << " read @ " << pos << " + " << to_read
@ -298,8 +298,6 @@ MidiRegion::render_range (Evoral::EventSink<samplepos_t>& dst,
boost::shared_ptr<MidiSource> src = midi_source(chan_n);
Glib::Threads::Mutex::Lock lm(src->mutex());
#if 0
cerr << "MR " << name () << " render "
@ -317,6 +315,8 @@ MidiRegion::render_range (Evoral::EventSink<samplepos_t>& dst,
/* This call reads events from a source and writes them to `dst' timed in session samples */
Source::ReaderLock lm (src->mutex());
src->midi_read (
lm, // source lock
dst, // destination buffer
@ -505,7 +505,7 @@ MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
for a given set of filtered_parameters, so now that we've changed that list we must invalidate
the iterator.
*/
Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex(), Glib::Threads::TRY_LOCK);
Source::WriterLock lm (midi_source(0)->mutex(), Glib::Threads::TRY_LOCK);
if (lm.locked()) {
/* TODO: This is too aggressive, we need more fine-grained invalidation. */
midi_source(0)->invalidate (lm);

View File

@ -167,23 +167,26 @@ MidiSource::set_state (const XMLNode& node, int /*version*/)
}
void
MidiSource::invalidate (const Lock& lock)
MidiSource::invalidate (const WriterLock& lock)
{
Invalidated(_session.transport_rolling());
}
timecnt_t
MidiSource::midi_read (const Lock& lm,
MidiSource::midi_read (const ReaderLock& lm,
Evoral::EventSink<samplepos_t>& dst,
timepos_t const & source_start,
timepos_t const & start,
timecnt_t const & cnt,
Temporal::Range* loop_range,
MidiCursor& cursor,
MidiNoteTracker* tracker,
MidiNoteTracker* tracker,
MidiChannelFilter* filter,
const std::set<Evoral::Parameter>& filtered)
{
Timing t;
uint32_t ecnt = 0;
DEBUG_TRACE (DEBUG::MidiSourceIO,
string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
source_start, start, cnt, tracker, name()));
@ -289,12 +292,14 @@ MidiSource::midi_read (const Lock& lm,
}
}
}
t.update ();
std::cout << "MIDI read added " << ecnt << " events and took " << t.elapsed_msecs() << std::endl;
return cnt;
}
timecnt_t
MidiSource::midi_write (const Lock& lm,
MidiSource::midi_write (const WriterLock& lm,
MidiRingBuffer<samplepos_t>& source,
timepos_t const & source_start,
timecnt_t const & cnt)
@ -311,7 +316,7 @@ MidiSource::midi_write (const Lock& lm,
}
void
MidiSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
MidiSource::mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode)
{
if (_model) {
/* XXX do something with note mode? */
@ -348,14 +353,14 @@ MidiSource::mark_write_starting_now (timepos_t const & position, samplecnt_t cap
}
void
MidiSource::mark_streaming_write_started (const Lock& lock)
MidiSource::mark_streaming_write_started (const WriterLock& lock)
{
/* as of March 2022 or long before , the note mode argument does nothing */
mark_streaming_midi_write_started (lock, Sustained);
}
void
MidiSource::mark_midi_streaming_write_completed (const Lock& lock,
MidiSource::mark_midi_streaming_write_completed (const WriterLock& lock,
Evoral::Sequence<Temporal::Beats>::StuckNoteOption option,
Temporal::Beats end)
{
@ -376,15 +381,15 @@ MidiSource::mark_midi_streaming_write_completed (const Lock&
}
void
MidiSource::mark_streaming_write_completed (const Lock& lock)
MidiSource::mark_streaming_write_completed (const WriterLock& lock)
{
mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
}
int
MidiSource::export_write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
MidiSource::export_write_to (const ReaderLock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
{
Lock newsrc_lock (newsrc->mutex ());
WriterLock newsrc_lock (newsrc->mutex ());
if (!_model) {
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during export"));
@ -399,9 +404,9 @@ MidiSource::export_write_to (const Lock& lock, boost::shared_ptr<MidiSource> new
}
int
MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
MidiSource::write_to (const ReaderLock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
{
Lock newsrc_lock (newsrc->mutex ());
WriterLock newsrc_lock (newsrc->mutex ());
newsrc->set_natural_position (_natural_position);
newsrc->copy_interpolation_from (this);
@ -440,7 +445,7 @@ MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Te
void
MidiSource::session_saved()
{
Lock lm (_lock);
WriterLock lm (_lock);
/* this writes a copy of the data to disk.
XXX do we need to do this every time?
@ -467,7 +472,7 @@ MidiSource::session_saved()
}
void
MidiSource::drop_model (const Lock& lock)
MidiSource::drop_model (const WriterLock& lock)
{
_model.reset();
invalidate(lock);
@ -475,7 +480,7 @@ MidiSource::drop_model (const Lock& lock)
}
void
MidiSource::set_model (const Lock& lock, boost::shared_ptr<MidiModel> m)
MidiSource::set_model (const WriterLock& lock, boost::shared_ptr<MidiModel> m)
{
_model = m;
invalidate(lock);

View File

@ -181,7 +181,7 @@ MidiNoteTracker::resolve_notes (Evoral::EventSink<samplepos_t> &dst, samplepos_t
}
void
MidiNoteTracker::resolve_notes (MidiSource& src, const MidiSource::Lock& lock, Temporal::Beats time)
MidiNoteTracker::resolve_notes (MidiSource& src, const MidiSource::WriterLock& lock, Temporal::Beats time)
{
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 MS-resolve notes @ %2 on = %3\n", this, time, _on));

View File

@ -70,26 +70,24 @@ MidiStretch::run (boost::shared_ptr<Region> r, Progress*)
/* create new sources */
if (make_new_sources (region, nsrcs, suffix))
if (make_new_sources (region, nsrcs, suffix)) {
return -1;
boost::shared_ptr<MidiSource> src = region->midi_source(0);
{
Source::Lock lock(src->mutex());
src->load_model(lock);
}
boost::shared_ptr<MidiModel> old_model = src->model();
boost::shared_ptr<MidiSource> src = region->midi_source(0);
Source::ReaderLock lock (src->mutex());
boost::shared_ptr<MidiModel> old_model = src->model();
boost::shared_ptr<MidiSource> new_src = boost::dynamic_pointer_cast<MidiSource>(nsrcs[0]);
if (!new_src) {
error << _("MIDI stretch created non-MIDI source") << endmsg;
return -1;
}
Glib::Threads::Mutex::Lock sl (new_src->mutex ());
Source::WriterLock sl (new_src->mutex ());
new_src->load_model(sl, true);
new_src->load_model (sl, true);
boost::shared_ptr<MidiModel> new_model = new_src->model();
new_model->start_write();

View File

@ -5717,7 +5717,7 @@ Session::freeze_all (InterThreadInfo& itt)
struct MidiSourceLockMap
{
boost::shared_ptr<MidiSource> src;
Source::Lock lock;
Source::WriterLock lock;
MidiSourceLockMap (boost::shared_ptr<MidiSource> midi_source) : src (midi_source), lock (src->mutex()) {}
};
@ -6013,7 +6013,7 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
afs->flush_header ();
plist.add (Properties::start, timepos_t (0));
} else if ((ms = boost::dynamic_pointer_cast<MidiSource>(*src))) {
Source::Lock lock(ms->mutex());
Source::WriterLock lock (ms->mutex());
ms->mark_streaming_write_completed(lock);
plist.add (Properties::start, timepos_t (Beats()));
}

View File

@ -1339,11 +1339,14 @@ Session::state (bool save_template, snapshot_t snapshot_type, bool only_used_ass
/* use SMF-API to clone data (use the midi_model, not data on disk) */
boost::shared_ptr<SMFSource> newsrc (new SMFSource (*this, path, ms->flags()));
Source::Lock lm (ms->mutex());
{
Source::WriterLock lm (ms->mutex());
if (!ms->model()) {
ms->load_model (lm);
if (!ms->model()) {
ms->load_model (lm);
}
}
Source::ReaderLock lm (ms->mutex());
/* write_to() calls newsrc->flush_midi () to write the file to disk */
if (ms->write_to (lm, newsrc, Temporal::Beats(), std::numeric_limits<Temporal::Beats>::max())) {
error << string_compose (_("Session-Save: Failed to copy MIDI Source '%1' for snapshot"), ancestor_name) << endmsg;

View File

@ -218,13 +218,13 @@ SMFSource::close ()
extern PBD::Timing minsert;
timecnt_t
SMFSource::read_unlocked (const Lock& lock,
SMFSource::read_unlocked (const ReaderLock& lock,
Evoral::EventSink<samplepos_t>& destination,
timepos_t const & source_start,
timepos_t const & start,
timecnt_t const & duration,
Temporal::Range* loop_range,
MidiNoteTracker* tracker,
MidiNoteTracker* tracker,
MidiChannelFilter* filter) const
{
int ret = 0;
@ -320,7 +320,7 @@ SMFSource::read_unlocked (const Lock& lock,
}
timecnt_t
SMFSource::write_unlocked (const Lock& lock,
SMFSource::write_unlocked (const WriterLock& lock,
MidiRingBuffer<samplepos_t>& source,
timepos_t const & position,
timecnt_t const & cnt)
@ -411,7 +411,7 @@ SMFSource::update_length (timepos_t const & dur)
/** Append an event with a timestamp in beats */
void
SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock,
SMFSource::append_event_beats (const WriterLock& lock,
const Evoral::Event<Temporal::Beats>& ev)
{
if (!_writing || ev.size() == 0) {
@ -468,7 +468,7 @@ SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock,
/** Append an event with a timestamp in samples (samplepos_t) */
void
SMFSource::append_event_samples (const Glib::Threads::Mutex::Lock& lock,
SMFSource::append_event_samples (const WriterLock& lock,
const Evoral::Event<samplepos_t>& ev,
samplepos_t position)
{
@ -551,7 +551,7 @@ SMFSource::set_state (const XMLNode& node, int version)
}
void
SMFSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
SMFSource::mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode)
{
if (!_open && open_for_write()) {
error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
@ -566,13 +566,13 @@ SMFSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
}
void
SMFSource::mark_streaming_write_completed (const Lock& lock)
SMFSource::mark_streaming_write_completed (const WriterLock& lock)
{
mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
}
void
SMFSource::mark_midi_streaming_write_completed (const Lock& lm, Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option, Temporal::Beats when)
SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option, Temporal::Beats when)
{
MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
@ -640,7 +640,7 @@ static bool compare_eventlist (
}
void
SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
SMFSource::load_model (const WriterLock& lock, bool force_reload)
{
invalidate (lock);
load_model_unlocked (force_reload);
@ -787,7 +787,7 @@ SMFSource::used_midi_channels()
}
void
SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
SMFSource::destroy_model (const WriterLock& lock)
{
//cerr << _name << " destroying model " << _model.get() << endl;
_model.reset();
@ -795,7 +795,7 @@ SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
}
void
SMFSource::flush_midi (const Lock& lock)
SMFSource::flush_midi (const WriterLock& lock)
{
if (!writable() || _length.is_zero()) {
return;
@ -818,7 +818,7 @@ SMFSource::set_path (const string& p)
/** Ensure that this source has some file on disk, even if it's just a SMF header */
void
SMFSource::ensure_disk_file (const Lock& lock)
SMFSource::ensure_disk_file (const WriterLock& lock)
{
if (!writable()) {
return;

View File

@ -306,6 +306,7 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path,
} else if (type == DataType::MIDI) {
try {
boost::shared_ptr<SMFSource> src (new SMFSource (s, path));
Source::WriterLock lock (src->mutex ());
BOOST_MARK_SOURCE (src);
if (announce) {

View File

@ -887,7 +887,7 @@ StepSequencer::fill_midi_source (boost::shared_ptr<SMFSource> src) const
{
Temporal::Beats smf_beats;
Source::Lock lck (src->mutex());
Source::WriterLock lck (src->mutex());
/* first pass: run through the sequence one time to get all events, and
* then sort them. We have no idea what order they are in when we pull