13
0

Improve reliability of MIDI event rec-box

Previously there was a race condition. DiskWriter::run()
cleared the gui_feed_buffer before writing new events.
If the GUI thread had not yet picked up the events by then
they were not displayed. Furthermore due to the try-lock,
some events may have been written to the buffer in the first
place.

This fixes missing events (notably stuck notes) in the red
record box while recording MIDI.
This commit is contained in:
Robin Gareus 2024-08-22 18:32:38 +02:00
parent 9df6d7c5fa
commit a1ba561cc5
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
2 changed files with 29 additions and 22 deletions

View File

@ -26,6 +26,7 @@
#include <boost/optional.hpp>
#include "ardour/disk_io.h"
#include "ardour/event_ring_buffer.h"
#include "ardour/midi_buffer.h"
namespace ARDOUR
@ -201,8 +202,8 @@ private:
/** A buffer that we use to put newly-arrived MIDI data in for
* the GUI to read (so that it can update itself).
*/
MidiBuffer _gui_feed_buffer;
mutable Glib::Threads::Mutex _gui_feed_buffer_mutex;
mutable EventRingBuffer<samplepos_t> _gui_feed_fifo;
mutable Glib::Threads::Mutex _gui_feed_reset_mutex;
};
} // namespace

View File

@ -60,7 +60,7 @@ DiskWriter::DiskWriter (Session& s, Track& t, string const & str, DiskIOProcesso
, _accumulated_capture_offset (0)
, _transport_looped (false)
, _transport_loop_sample (0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
, _gui_feed_fifo (AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
{
DiskIOProcessor::init ();
_xruns.reserve (128);
@ -684,25 +684,18 @@ DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
_samples_pending_write.fetch_add ((int) nframes);
if (buf.size() != 0) {
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
if (lm.locked ()) {
/* Copy this data into our GUI feed buffer and tell the GUI
that it can read it if it likes.
*/
_gui_feed_buffer.clear ();
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
/* This may fail if buf is larger than _gui_feed_buffer, but it's not really
* the end of the world if it does.
*/
samplepos_t mpos = (*i).time() + start_sample - _accumulated_capture_offset;
if (mpos >= _first_recordable_sample) {
_gui_feed_buffer.push_back (mpos, Evoral::MIDI_EVENT, (*i).size(), (*i).buffer());
}
/* Copy this data into our GUI feed buffer and tell the GUI
* that it can read it if it likes.
*/
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
/* This may fail if buf is larger than _gui_feed_fifo, but it's not really
* the end of the world if it does.
*/
samplepos_t mpos = (*i).time() + start_sample - _accumulated_capture_offset;
if (mpos >= _first_recordable_sample) {
_gui_feed_fifo.write (mpos, Evoral::MIDI_EVENT, (*i).size(), (*i).buffer());
}
}
}
if (cnt) {
@ -807,10 +800,18 @@ DiskWriter::finish_capture (std::shared_ptr<ChannelList const> c)
std::shared_ptr<MidiBuffer>
DiskWriter::get_gui_feed_buffer () const
{
Glib::Threads::Mutex::Lock lm (_gui_feed_reset_mutex);
std::shared_ptr<MidiBuffer> b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)));
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex);
b->copy (_gui_feed_buffer);
vector<MIDI::byte> buffer (_gui_feed_fifo.capacity());
samplepos_t time;
Evoral::EventType type;
uint32_t size;
while (_gui_feed_fifo.read (&time, &type, &size, &buffer[0])) {
b->push_back (time, type, size, &buffer[0]);
}
return b;
}
@ -1191,6 +1192,11 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
finish_capture (c);
{
Glib::Threads::Mutex::Lock lm (_gui_feed_reset_mutex);
_gui_feed_fifo.reset ();
}
/* butler is already stopped, but there may be work to do
to flush remaining data to disk.
*/