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:
parent
1686db8b0c
commit
bc38f8d424
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user