Fix various MIDI locking issues.

Attempt to make mistakes much less likely in the future by statically requiring
caller to pass scoped locks where necessary.
This commit is contained in:
David Robillard 2014-12-17 16:05:27 -05:00
parent 1fa9edd872
commit a706755710
24 changed files with 228 additions and 174 deletions

View file

@ -76,7 +76,8 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region
if (wait_for_data) { if (wait_for_data) {
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region); boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
if (mr) { if (mr) {
mr->midi_source()->load_model(); Source::Lock lock(mr->midi_source()->mutex());
mr->midi_source()->load_model(lock);
} }
} }

View file

@ -270,7 +270,8 @@ MidiRegionView::init (bool wfd)
gui_context()); gui_context());
if (wfd) { if (wfd) {
midi_region()->midi_source(0)->load_model(); Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
midi_region()->midi_source(0)->load_model(lm);
} }
_model = midi_region()->midi_source(0)->model(); _model = midi_region()->midi_source(0)->model();

View file

@ -193,7 +193,8 @@ MidiStreamView::display_region(MidiRegionView* region_view, bool load_model)
} }
if (load_model) { if (load_model) {
source->load_model(); Glib::Threads::Mutex::Lock lm(source->mutex());
source->load_model(lm);
} }
if (!source->model()) { if (!source->model()) {
@ -225,7 +226,8 @@ MidiStreamView::update_contents_metrics(boost::shared_ptr<Region> r)
{ {
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r); boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
if (mr) { if (mr) {
mr->midi_source(0)->load_model(); Glib::Threads::Mutex::Lock lm(mr->midi_source(0)->mutex());
mr->midi_source(0)->load_model(lm);
_range_dirty = update_data_note_range( _range_dirty = update_data_note_range(
mr->model()->lowest_note(), mr->model()->lowest_note(),
mr->model()->highest_note()); mr->model()->highest_note());

View file

@ -64,7 +64,7 @@ public:
virtual int update_header (framepos_t when, struct tm&, time_t) = 0; virtual int update_header (framepos_t when, struct tm&, time_t) = 0;
virtual int flush_header () = 0; virtual int flush_header () = 0;
void mark_streaming_write_completed (); void mark_streaming_write_completed (const Lock& lock);
int setup_peakfile (); int setup_peakfile ();

View file

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

View file

@ -238,10 +238,15 @@ public:
void apply_command (Session& session, Command* cmd); void apply_command (Session& session, Command* cmd);
void apply_command_as_subcommand (Session& session, Command* cmd); void apply_command_as_subcommand (Session& session, Command* cmd);
bool sync_to_source (); bool sync_to_source (const Glib::Threads::Mutex::Lock& source_lock);
bool write_to(boost::shared_ptr<MidiSource> source);
bool write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime, bool write_to(boost::shared_ptr<MidiSource> source,
Evoral::MusicalTime end = Evoral::MaxMusicalTime); const Glib::Threads::Mutex::Lock& source_lock);
bool write_section_to(boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock,
Evoral::MusicalTime begin = Evoral::MinMusicalTime,
Evoral::MusicalTime end = Evoral::MaxMusicalTime);
// MidiModel doesn't use the normal AutomationList serialisation code // MidiModel doesn't use the normal AutomationList serialisation code
// since controller data is stored in the .mid // since controller data is stored in the .mid

View file

@ -45,10 +45,10 @@ public:
XMLNode& get_state (); XMLNode& get_state ();
int set_state (const XMLNode&, int version); int set_state (const XMLNode&, int version);
void append_event_unlocked_beats(const Evoral::Event<Evoral::MusicalTime>& ev); void append_event_beats(const Glib::Threads::Mutex::Lock& lock, const Evoral::Event<Evoral::MusicalTime>& ev);
void append_event_unlocked_frames(const Evoral::Event<framepos_t>& ev, framepos_t source_start); void append_event_frames(const Glib::Threads::Mutex::Lock& lock, const Evoral::Event<framepos_t>& ev, framepos_t source_start);
void load_model(bool lock=true, bool force_reload=false); void load_model(const Glib::Threads::Mutex::Lock& lock, bool force_reload=false);
void destroy_model(); void destroy_model(const Glib::Threads::Mutex::Lock& lock);
protected: protected:
friend class SourceFactory; friend class SourceFactory;
@ -58,15 +58,17 @@ protected:
MidiPlaylistSource (Session&, const XMLNode&); MidiPlaylistSource (Session&, const XMLNode&);
void flush_midi(); void flush_midi(const Lock& lock);
framecnt_t read_unlocked (Evoral::EventSink<framepos_t>& dst, framecnt_t read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t position, framepos_t position,
framepos_t start, framepos_t start,
framecnt_t cnt, framecnt_t cnt,
MidiStateTracker* tracker) const; MidiStateTracker* tracker) const;
framecnt_t write_unlocked (MidiRingBuffer<framepos_t>& dst, framecnt_t write_unlocked (const Lock& lock,
MidiRingBuffer<framepos_t>& dst,
framepos_t position, framepos_t position,
framecnt_t cnt); framecnt_t cnt);

View file

@ -57,7 +57,8 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
* \param end time of latest event that can be written. * \param end time of latest event that can be written.
* \return zero on success, non-zero if the write failed for any reason. * \return zero on success, non-zero if the write failed for any reason.
*/ */
int write_to (boost::shared_ptr<MidiSource> newsrc, int write_to (const Lock& lock,
boost::shared_ptr<MidiSource> newsrc,
Evoral::MusicalTime begin = Evoral::MinMusicalTime, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
Evoral::MusicalTime end = Evoral::MaxMusicalTime); Evoral::MusicalTime end = Evoral::MaxMusicalTime);
@ -70,7 +71,8 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
* \param tracker an optional pointer to MidiStateTracker object, for note on/off tracking. * \param tracker an optional pointer to MidiStateTracker object, for note on/off tracking.
* \param filtered Parameters whose MIDI messages will not be returned. * \param filtered Parameters whose MIDI messages will not be returned.
*/ */
virtual framecnt_t midi_read (Evoral::EventSink<framepos_t>& dst, virtual framecnt_t midi_read (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t source_start, framepos_t source_start,
framepos_t start, framepos_t start,
framecnt_t cnt, framecnt_t cnt,
@ -82,22 +84,33 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
* @param source_start This source's start position in session frames. * @param source_start This source's start position in session frames.
* @param cnt The length of time to write. * @param cnt The length of time to write.
*/ */
virtual framecnt_t midi_write (MidiRingBuffer<framepos_t>& src, virtual framecnt_t midi_write (const Lock& lock,
MidiRingBuffer<framepos_t>& src,
framepos_t source_start, framepos_t source_start,
framecnt_t cnt); framecnt_t cnt);
virtual void append_event_unlocked_beats(const Evoral::Event<Evoral::MusicalTime>& ev) = 0; /** Append a single event with a timestamp in beats.
*
* Caller must ensure that the event is later than the last written event.
*/
virtual void append_event_beats(const Lock& lock,
const Evoral::Event<Evoral::MusicalTime>& ev) = 0;
virtual void append_event_unlocked_frames(const Evoral::Event<framepos_t>& ev, /** Append a single event with a timestamp in frames.
framepos_t source_start) = 0; *
* Caller must ensure that the event is later than the last written event.
*/
virtual void append_event_frames(const Lock& lock,
const Evoral::Event<framepos_t>& ev,
framepos_t source_start) = 0;
virtual bool empty () const; virtual bool empty () const;
virtual framecnt_t length (framepos_t pos) const; virtual framecnt_t length (framepos_t pos) const;
virtual void update_length (framecnt_t); virtual void update_length (framecnt_t);
virtual void mark_streaming_midi_write_started (NoteMode mode); virtual void mark_streaming_midi_write_started (const Lock& lock, NoteMode mode);
virtual void mark_streaming_write_started (); virtual void mark_streaming_write_started (const Lock& lock);
virtual void mark_streaming_write_completed (); virtual void mark_streaming_write_completed (const Lock& lock);
/** Mark write starting with the given time parameters. /** Mark write starting with the given time parameters.
* *
@ -119,6 +132,7 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
* etc. * etc.
*/ */
virtual void mark_midi_streaming_write_completed ( virtual void mark_midi_streaming_write_completed (
const Lock& lock,
Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_option, Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_option,
Evoral::MusicalTime when = Evoral::MusicalTime()); Evoral::MusicalTime when = Evoral::MusicalTime());
@ -137,19 +151,17 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
void set_length_beats(TimeType l) { _length_beats = l; } void set_length_beats(TimeType l) { _length_beats = l; }
TimeType length_beats() const { return _length_beats; } TimeType length_beats() const { return _length_beats; }
virtual void load_model(bool lock=true, bool force_reload=false) = 0; virtual void load_model(const Glib::Threads::Mutex::Lock& lock, bool force_reload=false) = 0;
virtual void destroy_model() = 0; virtual void destroy_model(const Glib::Threads::Mutex::Lock& lock) = 0;
/** This must be called with the source lock held whenever the /** Reset cached information (like iterators) when things have changed. */
* source/model contents have been changed (reset iterators/cache/etc). void invalidate(const Glib::Threads::Mutex::Lock& lock);
*/
void invalidate();
void set_note_mode(NoteMode mode); void set_note_mode(const Glib::Threads::Mutex::Lock& lock, NoteMode mode);
boost::shared_ptr<MidiModel> model() { return _model; } boost::shared_ptr<MidiModel> model() { return _model; }
void set_model (boost::shared_ptr<MidiModel>); void set_model(const Glib::Threads::Mutex::Lock& lock, boost::shared_ptr<MidiModel>);
void drop_model(); void drop_model(const Glib::Threads::Mutex::Lock& lock);
Evoral::ControlList::InterpolationStyle interpolation_of (Evoral::Parameter) const; Evoral::ControlList::InterpolationStyle interpolation_of (Evoral::Parameter) const;
void set_interpolation_of (Evoral::Parameter, Evoral::ControlList::InterpolationStyle); void set_interpolation_of (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
@ -169,9 +181,10 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
PBD::Signal2<void, Evoral::Parameter, AutoState> AutomationStateChanged; PBD::Signal2<void, Evoral::Parameter, AutoState> AutomationStateChanged;
protected: protected:
virtual void flush_midi() = 0; virtual void flush_midi(const Lock& lock) = 0;
virtual framecnt_t read_unlocked (Evoral::EventSink<framepos_t>& dst, virtual framecnt_t read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t position, framepos_t position,
framepos_t start, framepos_t start,
framecnt_t cnt, framecnt_t cnt,
@ -182,7 +195,8 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
* @param position This source's start position in session frames. * @param position This source's start position in session frames.
* @param cnt The duration of this block to write for. * @param cnt The duration of this block to write for.
*/ */
virtual framecnt_t write_unlocked (MidiRingBuffer<framepos_t>& source, virtual framecnt_t write_unlocked (const Lock& lock,
MidiRingBuffer<framepos_t>& source,
framepos_t position, framepos_t position,
framecnt_t cnt) = 0; framecnt_t cnt) = 0;

View file

@ -20,6 +20,8 @@
#ifndef __ardour_midi_state_tracker_h__ #ifndef __ardour_midi_state_tracker_h__
#define __ardour_midi_state_tracker_h__ #define __ardour_midi_state_tracker_h__
#include <glibmm/threads.h>
#include "ardour/midi_buffer.h" #include "ardour/midi_buffer.h"
namespace Evoral { namespace Evoral {
@ -44,7 +46,7 @@ public:
void remove (uint8_t note, uint8_t chn); void remove (uint8_t note, uint8_t chn);
void resolve_notes (MidiBuffer& buffer, framepos_t time); void resolve_notes (MidiBuffer& buffer, framepos_t time);
void resolve_notes (Evoral::EventSink<framepos_t>& buffer, framepos_t time); void resolve_notes (Evoral::EventSink<framepos_t>& buffer, framepos_t time);
void resolve_notes (MidiSource& src, Evoral::MusicalTime time); void resolve_notes (MidiSource& src, const Glib::Threads::Mutex::Lock& lock, Evoral::MusicalTime time);
void dump (std::ostream&); void dump (std::ostream&);
void reset (); void reset ();
bool empty() const { return _on == 0; } bool empty() const { return _on == 0; }

View file

@ -47,26 +47,24 @@ public:
virtual ~SMFSource (); virtual ~SMFSource ();
bool safe_file_extension (const std::string& path) const { bool safe_file_extension (const std::string& path) const {
return safe_midi_file_extension(path); return safe_midi_file_extension(path);
} }
void append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>& ev); void append_event_beats (const Lock& lock, const Evoral::Event<Evoral::MusicalTime>& ev);
void append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t source_start); void append_event_frames (const Lock& lock, const Evoral::Event<framepos_t>& ev, framepos_t source_start);
void mark_streaming_midi_write_started (NoteMode mode); void mark_streaming_midi_write_started (const Lock& lock, NoteMode mode);
void mark_streaming_write_completed (); void mark_streaming_write_completed (const Lock& lock);
void mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption, void mark_midi_streaming_write_completed (const Lock& lock,
Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption,
Evoral::MusicalTime when = Evoral::MusicalTime()); Evoral::MusicalTime when = Evoral::MusicalTime());
XMLNode& get_state (); XMLNode& get_state ();
int set_state (const XMLNode&, int version); int set_state (const XMLNode&, int version);
void load_model (bool lock=true, bool force_reload=false); void load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload=false);
void destroy_model (); void destroy_model (const Glib::Threads::Mutex::Lock& lock);
void flush_midi ();
void ensure_disk_file ();
static bool safe_midi_file_extension (const std::string& path); static bool safe_midi_file_extension (const std::string& path);
static bool valid_midi_file (const std::string& path); static bool valid_midi_file (const std::string& path);
@ -75,6 +73,7 @@ public:
protected: protected:
void set_path (const std::string& newpath); void set_path (const std::string& newpath);
void flush_midi (const Lock& lock);
private: private:
bool _open; bool _open;
@ -87,13 +86,17 @@ public:
int open_for_write (); int open_for_write ();
framecnt_t read_unlocked (Evoral::EventSink<framepos_t>& dst, void ensure_disk_file (const Lock& lock);
framecnt_t read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t position, framepos_t position,
framepos_t start, framepos_t start,
framecnt_t cnt, framecnt_t cnt,
MidiStateTracker* tracker) const; MidiStateTracker* tracker) const;
framecnt_t write_unlocked (MidiRingBuffer<framepos_t>& src, framecnt_t write_unlocked (const Lock& lock,
MidiRingBuffer<framepos_t>& src,
framepos_t position, framepos_t position,
framecnt_t cnt); framecnt_t cnt);

View file

@ -51,6 +51,8 @@ class LIBARDOUR_API Source : public SessionObject
Empty = 0x100, /* used for MIDI only */ Empty = 0x100, /* used for MIDI only */
}; };
typedef Glib::Threads::Mutex::Lock Lock;
Source (Session&, DataType type, const std::string& name, Flag flags=Flag(0)); Source (Session&, DataType type, const std::string& name, Flag flags=Flag(0));
Source (Session&, const XMLNode&); Source (Session&, const XMLNode&);
@ -69,8 +71,8 @@ class LIBARDOUR_API Source : public SessionObject
void mark_for_remove(); void mark_for_remove();
virtual void mark_streaming_write_started () {} virtual void mark_streaming_write_started (const Lock& lock) {}
virtual void mark_streaming_write_completed () = 0; virtual void mark_streaming_write_completed (const Lock& lock) = 0;
virtual void session_saved() {} virtual void session_saved() {}

View file

@ -1771,13 +1771,15 @@ AudioDiskstream::prep_record_enable ()
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
(*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling)); (*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling));
capturing_sources.push_back ((*chan)->write_source); capturing_sources.push_back ((*chan)->write_source);
(*chan)->write_source->mark_streaming_write_started (); (*chan)->write_source->mark_streaming_write_started (
Source::Lock((*chan)->write_source->mutex()));
} }
} else { } else {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
capturing_sources.push_back ((*chan)->write_source); capturing_sources.push_back ((*chan)->write_source);
(*chan)->write_source->mark_streaming_write_started (); (*chan)->write_source->mark_streaming_write_started (
Source::Lock((*chan)->write_source->mutex()));
} }
} }
@ -1963,7 +1965,8 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
if ((*chan)->write_source) { if ((*chan)->write_source) {
if (mark_write_complete) { if (mark_write_complete) {
(*chan)->write_source->mark_streaming_write_completed (); (*chan)->write_source->mark_streaming_write_completed (
Source::Lock((*chan)->write_source->mutex()));
(*chan)->write_source->done_with_peakfile_writes (); (*chan)->write_source->done_with_peakfile_writes ();
} }

View file

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

View file

@ -923,7 +923,7 @@ AudioSource::available_peaks (double zoom_factor) const
} }
void void
AudioSource::mark_streaming_write_completed () AudioSource::mark_streaming_write_completed (const Lock& lock)
{ {
Glib::Threads::Mutex::Lock lm (_peaks_ready_lock); Glib::Threads::Mutex::Lock lm (_peaks_ready_lock);

View file

@ -354,7 +354,9 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource> (*s); boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource> (*s);
smfs->drop_model (); Glib::Threads::Mutex::Lock source_lock(smfs->mutex());
smfs->drop_model (source_lock);
source->seek_to_track (i); source->seek_to_track (i);
uint64_t t = 0; uint64_t t = 0;
@ -384,11 +386,12 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
} }
if (first) { if (first) {
smfs->mark_streaming_write_started (); smfs->mark_streaming_write_started (source_lock);
first = false; first = false;
} }
smfs->append_event_unlocked_beats( smfs->append_event_beats(
source_lock,
Evoral::Event<Evoral::MusicalTime>( Evoral::Event<Evoral::MusicalTime>(
0, 0,
Evoral::MusicalTime::ticks_at_rate(t, source->ppqn()), Evoral::MusicalTime::ticks_at_rate(t, source->ppqn()),
@ -408,7 +411,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
const Evoral::MusicalTime length_beats = Evoral::MusicalTime::ticks_at_rate(t, source->ppqn()); const Evoral::MusicalTime length_beats = Evoral::MusicalTime::ticks_at_rate(t, source->ppqn());
BeatsFramesConverter converter(smfs->session().tempo_map(), pos); BeatsFramesConverter converter(smfs->session().tempo_map(), pos);
smfs->update_length(pos + converter.to(length_beats.round_up_to_beat())); smfs->update_length(pos + converter.to(length_beats.round_up_to_beat()));
smfs->mark_streaming_write_completed (); smfs->mark_streaming_write_completed (source_lock);
if (status.cancel) { if (status.cancel) {
break; break;

View file

@ -851,7 +851,8 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
} }
if (record_enabled() && ((total > disk_io_chunk_frames) || force_flush)) { if (record_enabled() && ((total > disk_io_chunk_frames) || force_flush)) {
if (_write_source->midi_write (*_capture_buf, get_capture_start_frame (0), to_write) != to_write) { Source::Lock lm(_write_source->mutex());
if (_write_source->midi_write (lm, *_capture_buf, get_capture_start_frame (0), to_write) != to_write) {
error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg; error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg;
return -1; return -1;
} }
@ -919,6 +920,8 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
/* phew, we have data */ /* phew, we have data */
Source::Lock source_lock(_write_source->mutex());
/* figure out the name for this take */ /* figure out the name for this take */
srcs.push_back (_write_source); srcs.push_back (_write_source);
@ -936,7 +939,7 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
where all the data is already on disk. where all the data is already on disk.
*/ */
_write_source->mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, total_capture_beats); _write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, total_capture_beats);
/* we will want to be able to keep (over)writing the source /* we will want to be able to keep (over)writing the source
but we don't want it to be removable. this also differs but we don't want it to be removable. this also differs
@ -1280,7 +1283,8 @@ MidiDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
} }
if (_write_source && mark_write_complete) { if (_write_source && mark_write_complete) {
_write_source->mark_streaming_write_completed (); Source::Lock lm(_write_source->mutex());
_write_source->mark_streaming_write_completed (lm);
} }
use_new_write_source (0); use_new_write_source (0);
} }

View file

@ -1426,25 +1426,23 @@ MidiModel::PatchChangeDiffCommand::get_state ()
* `Discrete' mode). * `Discrete' mode).
*/ */
bool bool
MidiModel::write_to (boost::shared_ptr<MidiSource> source) MidiModel::write_to (boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock)
{ {
ReadLock lock(read_lock()); ReadLock lock(read_lock());
const bool old_percussive = percussive(); const bool old_percussive = percussive();
set_percussive(false); set_percussive(false);
boost::shared_ptr<MidiSource> ms = _midi_source.lock (); source->drop_model(source_lock);
assert (ms); source->mark_streaming_midi_write_started (source_lock, note_mode());
source->drop_model();
source->mark_streaming_midi_write_started (note_mode());
for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) { for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
source->append_event_unlocked_beats(*i); source->append_event_beats(source_lock, *i);
} }
set_percussive(old_percussive); set_percussive(old_percussive);
source->mark_streaming_write_completed(); source->mark_streaming_write_completed(source_lock);
set_edited(false); set_edited(false);
@ -1457,7 +1455,7 @@ MidiModel::write_to (boost::shared_ptr<MidiSource> source)
of the model. of the model.
*/ */
bool bool
MidiModel::sync_to_source () MidiModel::sync_to_source (const Glib::Threads::Mutex::Lock& source_lock)
{ {
ReadLock lock(read_lock()); ReadLock lock(read_lock());
@ -1465,16 +1463,19 @@ MidiModel::sync_to_source ()
set_percussive(false); set_percussive(false);
boost::shared_ptr<MidiSource> ms = _midi_source.lock (); boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
assert (ms); if (!ms) {
error << "MIDI model has no source to sync to" << endmsg;
return false;
}
ms->mark_streaming_midi_write_started (note_mode()); ms->mark_streaming_midi_write_started (source_lock, note_mode());
for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) { for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
ms->append_event_unlocked_beats(*i); ms->append_event_beats(source_lock, *i);
} }
set_percussive (old_percussive); set_percussive (old_percussive);
ms->mark_streaming_write_completed (); ms->mark_streaming_write_completed (source_lock);
set_edited (false); set_edited (false);
@ -1489,7 +1490,10 @@ MidiModel::sync_to_source ()
* destroying the original note durations. * destroying the original note durations.
*/ */
bool bool
MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time) MidiModel::write_section_to (boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock,
Evoral::MusicalTime begin_time,
Evoral::MusicalTime end_time)
{ {
ReadLock lock(read_lock()); ReadLock lock(read_lock());
MidiStateTracker mst; MidiStateTracker mst;
@ -1497,11 +1501,8 @@ MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::Music
const bool old_percussive = percussive(); const bool old_percussive = percussive();
set_percussive(false); set_percussive(false);
boost::shared_ptr<MidiSource> ms = _midi_source.lock (); source->drop_model(source_lock);
assert (ms); source->mark_streaming_midi_write_started (source_lock, note_mode());
source->drop_model();
source->mark_streaming_midi_write_started (note_mode());
for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) { for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
const Evoral::Event<Evoral::MusicalTime>& ev (*i); const Evoral::Event<Evoral::MusicalTime>& ev (*i);
@ -1526,22 +1527,22 @@ MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::Music
continue; continue;
} }
source->append_event_unlocked_beats (*i); source->append_event_beats (source_lock, *i);
mst.remove (mev->note(), mev->channel()); mst.remove (mev->note(), mev->channel());
} else if (mev->is_note_on()) { } else if (mev->is_note_on()) {
mst.add (mev->note(), mev->channel()); mst.add (mev->note(), mev->channel());
source->append_event_unlocked_beats(*i); source->append_event_beats(source_lock, *i);
} else { } else {
source->append_event_unlocked_beats(*i); source->append_event_beats(source_lock, *i);
} }
} }
} }
mst.resolve_notes (*source, end_time); mst.resolve_notes (*source, source_lock, end_time);
set_percussive(old_percussive); set_percussive(old_percussive);
source->mark_streaming_write_completed(); source->mark_streaming_write_completed(source_lock);
set_edited(false); set_edited(false);
@ -1630,7 +1631,7 @@ MidiModel::edit_lock()
assert (ms); assert (ms);
Glib::Threads::Mutex::Lock* source_lock = new Glib::Threads::Mutex::Lock (ms->mutex()); Glib::Threads::Mutex::Lock* source_lock = new Glib::Threads::Mutex::Lock (ms->mutex());
ms->invalidate(); // Release cached iterator's read lock on model ms->invalidate(*source_lock); // Release cached iterator's read lock on model
return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock)); return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
} }
@ -1855,7 +1856,8 @@ MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
boost::shared_ptr<MidiSource> old = _midi_source.lock (); boost::shared_ptr<MidiSource> old = _midi_source.lock ();
if (old) { if (old) {
old->invalidate (); Source::Lock lm(old->mutex());
old->invalidate (lm);
} }
_midi_source_connections.drop_connections (); _midi_source_connections.drop_connections ();

View file

@ -122,7 +122,8 @@ MidiPlaylistSource::length (framepos_t) const
} }
framecnt_t framecnt_t
MidiPlaylistSource::read_unlocked (Evoral::EventSink<framepos_t>& dst, MidiPlaylistSource::read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t /*position*/, framepos_t /*position*/,
framepos_t start, framecnt_t cnt, framepos_t start, framecnt_t cnt,
MidiStateTracker*) const MidiStateTracker*) const
@ -137,7 +138,8 @@ MidiPlaylistSource::read_unlocked (Evoral::EventSink<framepos_t>& dst,
} }
framecnt_t framecnt_t
MidiPlaylistSource::write_unlocked (MidiRingBuffer<framepos_t>&, MidiPlaylistSource::write_unlocked (const Lock&,
MidiRingBuffer<framepos_t>&,
framepos_t, framepos_t,
framecnt_t) framecnt_t)
{ {
@ -147,33 +149,33 @@ MidiPlaylistSource::write_unlocked (MidiRingBuffer<framepos_t>&,
} }
void void
MidiPlaylistSource::append_event_unlocked_beats(const Evoral::Event<Evoral::MusicalTime>& /*ev*/) MidiPlaylistSource::append_event_beats(const Glib::Threads::Mutex::Lock& /*lock*/, const Evoral::Event<Evoral::MusicalTime>& /*ev*/)
{ {
fatal << string_compose (_("programming error: %1"), "MidiPlaylistSource::append_event_unlocked_beats() called - should be impossible") << endmsg; fatal << string_compose (_("programming error: %1"), "MidiPlaylistSource::append_event_beats() called - should be impossible") << endmsg;
abort(); /*NOTREACHED*/ abort(); /*NOTREACHED*/
} }
void void
MidiPlaylistSource::append_event_unlocked_frames(const Evoral::Event<framepos_t>& /* ev */, framepos_t /*source_start*/) MidiPlaylistSource::append_event_frames(const Glib::Threads::Mutex::Lock& /*lock*/, const Evoral::Event<framepos_t>& /* ev */, framepos_t /*source_start*/)
{ {
fatal << string_compose (_("programming error: %1"), "MidiPlaylistSource::append_event_unlocked_frames() called - should be impossible") << endmsg; fatal << string_compose (_("programming error: %1"), "MidiPlaylistSource::append_event_frames() called - should be impossible") << endmsg;
abort(); /*NOTREACHED*/ abort(); /*NOTREACHED*/
} }
void void
MidiPlaylistSource::load_model (bool, bool) MidiPlaylistSource::load_model (const Glib::Threads::Mutex::Lock&, bool)
{ {
/* nothing to do */ /* nothing to do */
} }
void void
MidiPlaylistSource::destroy_model () MidiPlaylistSource::destroy_model (const Glib::Threads::Mutex::Lock&)
{ {
/* nothing to do */ /* nothing to do */
} }
void void
MidiPlaylistSource::flush_midi () MidiPlaylistSource::flush_midi (const Lock& lock)
{ {
} }

View file

@ -150,8 +150,11 @@ MidiRegion::clone (boost::shared_ptr<MidiSource> newsrc) const
Evoral::MusicalTime const bbegin = bfc.from (_start); Evoral::MusicalTime const bbegin = bfc.from (_start);
Evoral::MusicalTime const bend = bfc.from (_start + _length); Evoral::MusicalTime const bend = bfc.from (_start + _length);
if (midi_source(0)->write_to (newsrc, bbegin, bend)) { {
return boost::shared_ptr<MidiRegion> (); Source::Lock lm(newsrc->mutex());
if (midi_source(0)->write_to (lm, newsrc, bbegin, bend)) {
return boost::shared_ptr<MidiRegion> ();
}
} }
PropertyList plist; PropertyList plist;
@ -272,7 +275,10 @@ MidiRegion::_read_at (const SourceList& /*srcs*/, Evoral::EventSink<framepos_t>&
} }
boost::shared_ptr<MidiSource> src = midi_source(chan_n); boost::shared_ptr<MidiSource> src = midi_source(chan_n);
src->set_note_mode(mode);
Glib::Threads::Mutex::Lock lm(src->mutex());
src->set_note_mode(lm, mode);
/* /*
cerr << "MR " << name () << " read @ " << position << " * " << to_read cerr << "MR " << name () << " read @ " << position << " * " << to_read
@ -285,6 +291,7 @@ MidiRegion::_read_at (const SourceList& /*srcs*/, Evoral::EventSink<framepos_t>&
/* This call reads events from a source and writes them to `dst' timed in session frames */ /* This call reads events from a source and writes them to `dst' timed in session frames */
if (src->midi_read ( if (src->midi_read (
lm, // source lock
dst, // destination buffer dst, // destination buffer
_position - _start, // start position of the source in session frames _position - _start, // start position of the source in session frames
_start + internal_offset, // where to start reading in the source _start + internal_offset, // where to start reading in the source
@ -429,7 +436,7 @@ MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
the iterator. the iterator.
*/ */
Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex()); Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex());
midi_source(0)->invalidate (); midi_source(0)->invalidate (lm);
} }
/** This is called when a trim drag has resulted in a -ve _start time for this region. /** This is called when a trim drag has resulted in a -ve _start time for this region.

View file

@ -177,22 +177,21 @@ MidiSource::update_length (framecnt_t)
} }
void void
MidiSource::invalidate () MidiSource::invalidate (const Lock& lock)
{ {
_model_iter_valid = false; _model_iter_valid = false;
_model_iter.invalidate(); _model_iter.invalidate();
} }
framecnt_t framecnt_t
MidiSource::midi_read (Evoral::EventSink<framepos_t>& dst, MidiSource::midi_read (const Lock& lm,
Evoral::EventSink<framepos_t>& dst,
framepos_t source_start, framepos_t source_start,
framepos_t start, framepos_t start,
framecnt_t cnt, framecnt_t cnt,
MidiStateTracker* tracker, MidiStateTracker* tracker,
const std::set<Evoral::Parameter>& filtered) const const std::set<Evoral::Parameter>& filtered) const
{ {
Glib::Threads::Mutex::Lock lm (_lock);
BeatsFramesConverter converter(_session.tempo_map(), source_start); BeatsFramesConverter converter(_session.tempo_map(), source_start);
DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_TRACE (DEBUG::MidiSourceIO,
@ -233,22 +232,21 @@ MidiSource::midi_read (Evoral::EventSink<framepos_t>& dst,
} }
return cnt; return cnt;
} else { } else {
return read_unlocked (dst, source_start, start, cnt, tracker); return read_unlocked (lm, dst, source_start, start, cnt, tracker);
} }
} }
framecnt_t framecnt_t
MidiSource::midi_write (MidiRingBuffer<framepos_t>& source, MidiSource::midi_write (const Lock& lm,
MidiRingBuffer<framepos_t>& source,
framepos_t source_start, framepos_t source_start,
framecnt_t cnt) framecnt_t cnt)
{ {
Glib::Threads::Mutex::Lock lm (_lock); const framecnt_t ret = write_unlocked (lm, source, source_start, cnt);
const framecnt_t ret = write_unlocked (source, source_start, cnt);
if (cnt == max_framecnt) { if (cnt == max_framecnt) {
_last_read_end = 0; _last_read_end = 0;
invalidate(); invalidate(lm);
} else { } else {
_capture_length += cnt; _capture_length += cnt;
} }
@ -257,7 +255,7 @@ MidiSource::midi_write (MidiRingBuffer<framepos_t>& source,
} }
void void
MidiSource::mark_streaming_midi_write_started (NoteMode mode) MidiSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
{ {
if (_model) { if (_model) {
_model->set_note_mode (mode); _model->set_note_mode (mode);
@ -292,14 +290,15 @@ MidiSource::mark_write_starting_now (framecnt_t position,
} }
void void
MidiSource::mark_streaming_write_started () MidiSource::mark_streaming_write_started (const Lock& lock)
{ {
NoteMode note_mode = _model ? _model->note_mode() : Sustained; NoteMode note_mode = _model ? _model->note_mode() : Sustained;
mark_streaming_midi_write_started (note_mode); mark_streaming_midi_write_started (lock, note_mode);
} }
void void
MidiSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption option, MidiSource::mark_midi_streaming_write_completed (const Lock& lock,
Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption option,
Evoral::MusicalTime end) Evoral::MusicalTime end)
{ {
if (_model) { if (_model) {
@ -318,37 +317,39 @@ MidiSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::Musica
} }
void void
MidiSource::mark_streaming_write_completed () MidiSource::mark_streaming_write_completed (const Lock& lock)
{ {
mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes); mark_midi_streaming_write_completed (lock, Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
} }
int int
MidiSource::write_to (boost::shared_ptr<MidiSource> newsrc, Evoral::MusicalTime begin, Evoral::MusicalTime end) MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Evoral::MusicalTime begin, Evoral::MusicalTime end)
{ {
Lock newsrc_lock (newsrc->mutex ());
newsrc->set_timeline_position (_timeline_position); newsrc->set_timeline_position (_timeline_position);
newsrc->copy_interpolation_from (this); newsrc->copy_interpolation_from (this);
newsrc->copy_automation_state_from (this); newsrc->copy_automation_state_from (this);
if (_model) { if (_model) {
if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) { if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
_model->write_to (newsrc); _model->write_to (newsrc, newsrc_lock);
} else { } else {
_model->write_section_to (newsrc, begin, end); _model->write_section_to (newsrc, newsrc_lock, begin, end);
} }
} else { } else {
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()")); error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
return -1; return -1;
} }
newsrc->flush_midi(); newsrc->flush_midi(newsrc_lock);
/* force a reload of the model if the range is partial */ /* force a reload of the model if the range is partial */
if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) { if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) {
newsrc->load_model (true, true); newsrc->load_model (newsrc_lock, true);
} else { } else {
newsrc->set_model (_model); newsrc->set_model (newsrc_lock, _model);
} }
/* this file is not removable (but since it is MIDI, it is mutable) */ /* this file is not removable (but since it is MIDI, it is mutable) */
@ -361,6 +362,8 @@ MidiSource::write_to (boost::shared_ptr<MidiSource> newsrc, Evoral::MusicalTime
void void
MidiSource::session_saved() MidiSource::session_saved()
{ {
Lock lm (_lock);
/* this writes a copy of the data to disk. /* this writes a copy of the data to disk.
XXX do we need to do this every time? XXX do we need to do this every time?
*/ */
@ -375,18 +378,18 @@ MidiSource::session_saved()
_model.reset (); _model.reset ();
/* Flush model contents to disk. */ /* Flush model contents to disk. */
mm->sync_to_source (); mm->sync_to_source (lm);
/* Reacquire model. */ /* Reacquire model. */
_model = mm; _model = mm;
} else { } else {
flush_midi(); flush_midi(lm);
} }
} }
void void
MidiSource::set_note_mode(NoteMode mode) MidiSource::set_note_mode(const Lock& lock, NoteMode mode)
{ {
if (_model) { if (_model) {
_model->set_note_mode(mode); _model->set_note_mode(mode);
@ -394,18 +397,18 @@ MidiSource::set_note_mode(NoteMode mode)
} }
void void
MidiSource::drop_model () MidiSource::drop_model (const Lock& lock)
{ {
_model.reset(); _model.reset();
invalidate(); invalidate(lock);
ModelChanged (); /* EMIT SIGNAL */ ModelChanged (); /* EMIT SIGNAL */
} }
void void
MidiSource::set_model (boost::shared_ptr<MidiModel> m) MidiSource::set_model (const Lock& lock, boost::shared_ptr<MidiModel> m)
{ {
_model = m; _model = m;
invalidate(); invalidate(lock);
ModelChanged (); /* EMIT SIGNAL */ ModelChanged (); /* EMIT SIGNAL */
} }

View file

@ -168,7 +168,7 @@ MidiStateTracker::resolve_notes (Evoral::EventSink<framepos_t> &dst, framepos_t
} }
void void
MidiStateTracker::resolve_notes (MidiSource& src, Evoral::MusicalTime time) MidiStateTracker::resolve_notes (MidiSource& src, const MidiSource::Lock& lock, Evoral::MusicalTime time)
{ {
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 MS-resolve notes @ %2 on = %3\n", this, time, _on)); DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 MS-resolve notes @ %2 on = %3\n", this, time, _on));
@ -186,7 +186,7 @@ MidiStateTracker::resolve_notes (MidiSource& src, Evoral::MusicalTime time)
ev.set_channel (channel); ev.set_channel (channel);
ev.set_note (note); ev.set_note (note);
ev.set_velocity (0); ev.set_velocity (0);
src.append_event_unlocked_beats (ev); src.append_event_beats (lock, ev);
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: MS-resolved note %2/%3 at %4\n", DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: MS-resolved note %2/%3 at %4\n",
this, (int) note, (int) channel, time)); this, (int) note, (int) channel, time));
_active_notes[note + 128 * channel]--; _active_notes[note + 128 * channel]--;

View file

@ -76,7 +76,7 @@ MidiStretch::run (boost::shared_ptr<Region> r, Progress*)
return -1; return -1;
boost::shared_ptr<MidiSource> src = region->midi_source(0); boost::shared_ptr<MidiSource> src = region->midi_source(0);
src->load_model(); src->load_model(Glib::Threads::Mutex::Lock(src->mutex()));
boost::shared_ptr<MidiModel> old_model = src->model(); boost::shared_ptr<MidiModel> old_model = src->model();
@ -88,7 +88,7 @@ MidiStretch::run (boost::shared_ptr<Region> r, Progress*)
Glib::Threads::Mutex::Lock sl (new_src->mutex ()); Glib::Threads::Mutex::Lock sl (new_src->mutex ());
new_src->load_model(false, true); new_src->load_model(sl, true);
boost::shared_ptr<MidiModel> new_model = new_src->model(); boost::shared_ptr<MidiModel> new_model = new_src->model();
new_model->start_write(); new_model->start_write();

View file

@ -203,7 +203,8 @@ SMFSource::open_for_write ()
/** All stamps in audio frames */ /** All stamps in audio frames */
framecnt_t framecnt_t
SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination, SMFSource::read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& destination,
framepos_t const source_start, framepos_t const source_start,
framepos_t start, framepos_t start,
framecnt_t duration, framecnt_t duration,
@ -303,12 +304,13 @@ SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
} }
framecnt_t framecnt_t
SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source, SMFSource::write_unlocked (const Lock& lock,
MidiRingBuffer<framepos_t>& source,
framepos_t position, framepos_t position,
framecnt_t cnt) framecnt_t cnt)
{ {
if (!_writing) { if (!_writing) {
mark_streaming_write_started (); mark_streaming_write_started (lock);
} }
framepos_t time; framepos_t time;
@ -372,7 +374,7 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
continue; continue;
} }
append_event_unlocked_frames(ev, position); append_event_frames(lock, ev, position);
} }
Evoral::SMF::flush (); Evoral::SMF::flush ();
@ -383,13 +385,14 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
/** Append an event with a timestamp in beats */ /** Append an event with a timestamp in beats */
void void
SMFSource::append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>& ev) SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock,
const Evoral::Event<Evoral::MusicalTime>& ev)
{ {
if (!_writing || ev.size() == 0) { if (!_writing || ev.size() == 0) {
return; return;
} }
/*printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ", /*printf("SMFSource: %s - append_event_beats ID = %d time = %lf, size = %u, data = ",
name().c_str(), ev.id(), ev.time(), ev.size()); name().c_str(), ev.id(), ev.time(), ev.size());
for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
@ -435,13 +438,15 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>
/** Append an event with a timestamp in frames (framepos_t) */ /** Append an event with a timestamp in frames (framepos_t) */
void void
SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t position) SMFSource::append_event_frames (const Glib::Threads::Mutex::Lock& lock,
const Evoral::Event<framepos_t>& ev,
framepos_t position)
{ {
if (!_writing || ev.size() == 0) { if (!_writing || ev.size() == 0) {
return; return;
} }
// printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ", // printf("SMFSource: %s - append_event_frames ID = %d time = %u, size = %u, data = ",
// name().c_str(), ev.id(), ev.time(), ev.size()); // name().c_str(), ev.id(), ev.time(), ev.size());
// for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n"); // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
@ -508,33 +513,30 @@ SMFSource::set_state (const XMLNode& node, int version)
} }
void void
SMFSource::mark_streaming_midi_write_started (NoteMode mode) SMFSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
{ {
/* CALLER MUST HOLD LOCK */
if (!_open && open_for_write()) { if (!_open && open_for_write()) {
error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg; error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
/* XXX should probably throw or return something */ /* XXX should probably throw or return something */
return; return;
} }
MidiSource::mark_streaming_midi_write_started (mode); MidiSource::mark_streaming_midi_write_started (lock, mode);
Evoral::SMF::begin_write (); Evoral::SMF::begin_write ();
_last_ev_time_beats = Evoral::MusicalTime(); _last_ev_time_beats = Evoral::MusicalTime();
_last_ev_time_frames = 0; _last_ev_time_frames = 0;
} }
void void
SMFSource::mark_streaming_write_completed () SMFSource::mark_streaming_write_completed (const Lock& lock)
{ {
mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes); mark_midi_streaming_write_completed (lock, Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
} }
void void
SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when) SMFSource::mark_midi_streaming_write_completed (const Lock& lm, Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
{ {
Glib::Threads::Mutex::Lock lm (_lock); MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
if (!writable()) { if (!writable()) {
warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg; warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
@ -596,16 +598,12 @@ static bool compare_eventlist (
} }
void void
SMFSource::load_model (bool lock, bool force_reload) SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
{ {
if (_writing) { if (_writing) {
return; return;
} }
boost::shared_ptr<Glib::Threads::Mutex::Lock> lm;
if (lock)
lm = boost::shared_ptr<Glib::Threads::Mutex::Lock>(new Glib::Threads::Mutex::Lock(_lock));
if (_model && !force_reload) { if (_model && !force_reload) {
return; return;
} }
@ -616,7 +614,7 @@ SMFSource::load_model (bool lock, bool force_reload)
_model->clear(); _model->clear();
} }
invalidate(); invalidate(lock);
if (writable() && !_open) { if (writable() && !_open) {
return; return;
@ -707,33 +705,33 @@ SMFSource::load_model (bool lock, bool force_reload)
_model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats); _model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
_model->set_edited (false); _model->set_edited (false);
invalidate(); invalidate(lock);
free(buf); free(buf);
} }
void void
SMFSource::destroy_model () SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
{ {
//cerr << _name << " destroying model " << _model.get() << endl; //cerr << _name << " destroying model " << _model.get() << endl;
_model.reset(); _model.reset();
invalidate(); invalidate(lock);
} }
void void
SMFSource::flush_midi () SMFSource::flush_midi (const Lock& lock)
{ {
if (!writable() || _length_beats == 0.0) { if (!writable() || _length_beats == 0.0) {
return; return;
} }
ensure_disk_file (); ensure_disk_file (lock);
Evoral::SMF::end_write (); Evoral::SMF::end_write ();
/* data in the file means its no longer removable */ /* data in the file means its no longer removable */
mark_nonremovable (); mark_nonremovable ();
invalidate(); invalidate(lock);
} }
void void
@ -745,7 +743,7 @@ SMFSource::set_path (const string& p)
/** Ensure that this source has some file on disk, even if it's just a SMF header */ /** Ensure that this source has some file on disk, even if it's just a SMF header */
void void
SMFSource::ensure_disk_file () SMFSource::ensure_disk_file (const Lock& lock)
{ {
if (!writable()) { if (!writable()) {
return; return;
@ -757,9 +755,9 @@ SMFSource::ensure_disk_file ()
*/ */
boost::shared_ptr<MidiModel> mm = _model; boost::shared_ptr<MidiModel> mm = _model;
_model.reset (); _model.reset ();
mm->sync_to_source (); mm->sync_to_source (lock);
_model = mm; _model = mm;
invalidate(); invalidate(lock);
} else { } else {
/* No model; if it's not already open, it's an empty source, so create /* No model; if it's not already open, it's an empty source, so create
and open it for writing. and open it for writing.

View file

@ -205,7 +205,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
} }
} else if (type == DataType::MIDI) { } else if (type == DataType::MIDI) {
boost::shared_ptr<SMFSource> src (new SMFSource (s, node)); boost::shared_ptr<SMFSource> src (new SMFSource (s, node));
src->load_model (true, true); src->load_model (Glib::Threads::Mutex::Lock(src->mutex()), true);
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
// boost_debug_shared_ptr_mark_interesting (src, "Source"); // boost_debug_shared_ptr_mark_interesting (src, "Source");
#endif #endif
@ -273,7 +273,7 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path,
} else if (type == DataType::MIDI) { } else if (type == DataType::MIDI) {
boost::shared_ptr<SMFSource> src (new SMFSource (s, path)); boost::shared_ptr<SMFSource> src (new SMFSource (s, path));
src->load_model (true, true); src->load_model (Glib::Threads::Mutex::Lock(src->mutex()), true);
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
// boost_debug_shared_ptr_mark_interesting (src, "Source"); // boost_debug_shared_ptr_mark_interesting (src, "Source");
#endif #endif
@ -324,7 +324,7 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat
boost::shared_ptr<SMFSource> src (new SMFSource (s, path, SndFileSource::default_writable_flags)); boost::shared_ptr<SMFSource> src (new SMFSource (s, path, SndFileSource::default_writable_flags));
assert (src->writable ()); assert (src->writable ());
src->load_model (true, true); src->load_model (Glib::Threads::Mutex::Lock(src->mutex()), true);
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
// boost_debug_shared_ptr_mark_interesting (src, "Source"); // boost_debug_shared_ptr_mark_interesting (src, "Source");
#endif #endif