try to fix data loss at end of a capture pass for MIDI - add a new virtual method to MidiSource that specifies what should be done with stuck notes, remove duplicate(i hope) _last_flush_frame from SMFSource that mirrored, more or less, MidiSource::_last_write_end; use new virtual method when stopping after capture.
git-svn-id: svn://localhost/ardour2/branches/3.0@9910 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
8c7fda11cf
commit
c99738d88e
|
@ -186,7 +186,6 @@ class MidiDiskstream : public Diskstream
|
|||
MidiRingBuffer<framepos_t>* _capture_buf;
|
||||
MidiPort* _source_port;
|
||||
boost::shared_ptr<SMFSource> _write_source;
|
||||
framepos_t _last_flush_frame;
|
||||
NoteMode _note_mode;
|
||||
volatile gint _frames_written_to_ringbuffer;
|
||||
volatile gint _frames_read_from_ringbuffer;
|
||||
|
|
|
@ -83,6 +83,13 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this<
|
|||
virtual void mark_streaming_write_completed ();
|
||||
void mark_write_starting_now ();
|
||||
|
||||
/* like ::mark_streaming_write_completed() but with more arguments to
|
||||
* allow control over MIDI-specific behaviour. Expected to be used only
|
||||
* when recording actual MIDI input, rather then when importing files
|
||||
* etc.
|
||||
*/
|
||||
virtual void mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption, Evoral::MusicalTime when = 0);
|
||||
|
||||
virtual void session_saved();
|
||||
|
||||
std::string captured_for() const { return _captured_for; }
|
||||
|
@ -91,6 +98,9 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this<
|
|||
uint32_t read_data_count() const { return _read_data_count; }
|
||||
uint32_t write_data_count() const { return _write_data_count; }
|
||||
|
||||
framepos_t last_write_end() const { return _last_write_end; }
|
||||
void set_last_write_end (framepos_t pos) { _last_write_end = pos; }
|
||||
|
||||
static PBD::Signal1<void,MidiSource*> MidiSourceCreated;
|
||||
|
||||
XMLNode& get_state ();
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
|
||||
void mark_streaming_midi_write_started (NoteMode mode);
|
||||
void mark_streaming_write_completed ();
|
||||
void mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption, Evoral::MusicalTime when = 0);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
|
|
@ -74,7 +74,6 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
|
|||
, _playback_buf(0)
|
||||
, _capture_buf(0)
|
||||
, _source_port(0)
|
||||
, _last_flush_frame(0)
|
||||
, _note_mode(Sustained)
|
||||
, _frames_written_to_ringbuffer(0)
|
||||
, _frames_read_from_ringbuffer(0)
|
||||
|
@ -95,7 +94,6 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
|
|||
, _playback_buf(0)
|
||||
, _capture_buf(0)
|
||||
, _source_port(0)
|
||||
, _last_flush_frame(0)
|
||||
, _note_mode(Sustained)
|
||||
, _frames_written_to_ringbuffer(0)
|
||||
, _frames_read_from_ringbuffer(0)
|
||||
|
@ -203,7 +201,9 @@ MidiDiskstream::non_realtime_input_change ()
|
|||
seek (_session.transport_frame());
|
||||
}
|
||||
|
||||
_last_flush_frame = _session.transport_frame();
|
||||
if (_write_source) {
|
||||
_write_source->set_last_write_end (_session.transport_frame());
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -861,20 +861,27 @@ MidiDiskstream::do_refill ()
|
|||
int
|
||||
MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
|
||||
{
|
||||
uint32_t to_write;
|
||||
int32_t ret = 0;
|
||||
framecnt_t to_write;
|
||||
framecnt_t total;
|
||||
int32_t ret = 0;
|
||||
|
||||
if (!_write_source) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert (!destructive());
|
||||
|
||||
cerr << name() << " flushing to disk, bufspace = " << _capture_buf->read_space()
|
||||
<< " transport @ " << _session.transport_frame() << " last flush @ " << _last_flush_frame
|
||||
<< " transport @ " << _session.transport_frame()
|
||||
<< endl;
|
||||
|
||||
_write_data_count = 0;
|
||||
|
||||
total = _session.transport_frame() - _last_flush_frame;
|
||||
total = _session.transport_frame() - _write_source->last_write_end();
|
||||
|
||||
if (total == 0 || _capture_buf->read_space() == 0
|
||||
|| (!force_flush && (total < disk_io_chunk_frames && was_recording))) {
|
||||
if (total == 0 ||
|
||||
_capture_buf->read_space() == 0 ||
|
||||
(!force_flush && (total < disk_io_chunk_frames) && was_recording)) {
|
||||
cerr << "\tFlush shortcut because total = " << total
|
||||
<< " capture read space = " << _capture_buf->read_space()
|
||||
<< " force flush = " << force_flush
|
||||
|
@ -898,35 +905,25 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
|
|||
ret = 1;
|
||||
}
|
||||
|
||||
to_write = disk_io_chunk_frames;
|
||||
if (force_flush) {
|
||||
/* push out everything we have, right now */
|
||||
to_write = max_framecnt;
|
||||
} else {
|
||||
to_write = disk_io_chunk_frames;
|
||||
}
|
||||
|
||||
assert(!destructive());
|
||||
|
||||
if (record_enabled() &&
|
||||
((_session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) ||
|
||||
force_flush)) {
|
||||
if ((!_write_source) || _write_source->midi_write (*_capture_buf, get_capture_start_frame (0), to_write) != to_write) {
|
||||
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) {
|
||||
error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
|
||||
return -1;
|
||||
} else {
|
||||
cerr << "didn't write, _write_source = " << _write_source << endl;
|
||||
_last_flush_frame = _session.transport_frame();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cerr << "\tdidn't write to disk because recenabled = " << record_enabled()
|
||||
<< " last flush @ " << _last_flush_frame << " disk io " << disk_io_chunk_frames << " TF @ " << _session.transport_frame()
|
||||
<< " total = " << total << " TF @ " << _session.transport_frame()
|
||||
<< " force = " << force_flush << endl;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
if (ret == 0) {
|
||||
if (_last_flush_frame > _session.transport_frame() || _last_flush_frame < capture_start_frame) {
|
||||
_last_flush_frame = _session.transport_frame();
|
||||
cerr << name() << " set last flush frame to " << _last_flush_frame << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -996,17 +993,17 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
|
|||
_write_source->set_timeline_position (capture_info.front()->start);
|
||||
_write_source->set_captured_for (_name);
|
||||
|
||||
/* set length in beats to entire capture length */
|
||||
|
||||
BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start);
|
||||
const double total_capture_beats = converter.from (total_capture);
|
||||
_write_source->set_length_beats (total_capture_beats);
|
||||
|
||||
/* flush to disk: this step differs from the audio path,
|
||||
where all the data is already on disk.
|
||||
*/
|
||||
|
||||
_write_source->mark_streaming_write_completed ();
|
||||
|
||||
/* set length in beats to entire capture length */
|
||||
|
||||
BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start);
|
||||
const double total_capture_beats = converter.from(total_capture);
|
||||
_write_source->set_length_beats(total_capture_beats);
|
||||
_write_source->mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, total_capture_beats);
|
||||
|
||||
/* we will want to be able to keep (over)writing the source
|
||||
but we don't want it to be removable. this also differs
|
||||
|
|
|
@ -267,7 +267,13 @@ MidiSource::midi_write (MidiRingBuffer<framepos_t>& source, framepos_t source_st
|
|||
Glib::Mutex::Lock lm (_lock);
|
||||
cerr << "MidiSource calling write unlocked\n";
|
||||
const framecnt_t ret = write_unlocked (source, source_start, duration);
|
||||
_last_write_end += duration;
|
||||
|
||||
if (duration == max_framecnt) {
|
||||
_last_read_end = 0;
|
||||
} else {
|
||||
_last_write_end += duration;
|
||||
}
|
||||
|
||||
cerr << name() << " last write end now @ " << _last_write_end << endl;
|
||||
return ret;
|
||||
}
|
||||
|
@ -311,15 +317,21 @@ MidiSource::mark_streaming_write_started ()
|
|||
}
|
||||
|
||||
void
|
||||
MidiSource::mark_streaming_write_completed ()
|
||||
MidiSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption option, Evoral::MusicalTime end)
|
||||
{
|
||||
if (_model) {
|
||||
_model->end_write (false);
|
||||
_model->end_write (option, end);
|
||||
}
|
||||
|
||||
_writing = false;
|
||||
}
|
||||
|
||||
void
|
||||
MidiSource::mark_streaming_write_completed ()
|
||||
{
|
||||
mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
|
||||
}
|
||||
|
||||
boost::shared_ptr<MidiSource>
|
||||
MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
||||
{
|
||||
|
|
|
@ -103,7 +103,7 @@ MidiStretch::run (boost::shared_ptr<Region> r, Progress* progress)
|
|||
new_model->append(ev, Evoral::next_event_id());
|
||||
}
|
||||
|
||||
new_model->end_write ();
|
||||
new_model->end_write (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
|
||||
new_model->set_edited (true);
|
||||
|
||||
new_src->copy_interpolation_from (src);
|
||||
|
|
|
@ -243,16 +243,21 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source, framepos_t positi
|
|||
cerr << "SMFSource::write unlocked, begins writing from src buffer with _last_write_end = " << _last_write_end << " dur = " << duration << endl;
|
||||
|
||||
while (true) {
|
||||
bool ret = source.peek ((uint8_t*)&time, sizeof (time));
|
||||
if (!ret || time > _last_write_end + duration) {
|
||||
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("SMFSource::write_unlocked: dropping event @ %1 because ret %4 or it is later than %2 + %3\n",
|
||||
time, _last_write_end, duration, ret));
|
||||
bool ret;
|
||||
|
||||
if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
|
||||
/* no more events to consider */
|
||||
break;
|
||||
}
|
||||
|
||||
ret = source.read_prefix(&time, &type, &size);
|
||||
if (!ret) {
|
||||
cerr << "ERROR: Unable to read event prefix, corrupt MIDI ring buffer" << endl;
|
||||
if ((duration != max_framecnt) && (time > (_last_write_end + duration))) {
|
||||
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("SMFSource::write_unlocked: dropping event @ %1 because it is later than %2 + %3\n",
|
||||
time, _last_write_end, duration));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(ret = source.read_prefix (&time, &type, &size))) {
|
||||
error << _("Unable to read event prefix, corrupt MIDI ring buffer") << endmsg;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -263,7 +268,7 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source, framepos_t positi
|
|||
|
||||
ret = source.read_contents(size, buf);
|
||||
if (!ret) {
|
||||
cerr << "ERROR: Read time/size but not buffer, corrupt MIDI ring buffer" << endl;
|
||||
error << _("Read time/size but not buffer, corrupt MIDI ring buffer") << endmsg;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -430,9 +435,15 @@ SMFSource::mark_streaming_midi_write_started (NoteMode mode)
|
|||
|
||||
void
|
||||
SMFSource::mark_streaming_write_completed ()
|
||||
{
|
||||
mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
MidiSource::mark_streaming_write_completed();
|
||||
MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
|
||||
|
||||
if (!writable()) {
|
||||
return;
|
||||
|
@ -551,8 +562,7 @@ SMFSource::load_model (bool lock, bool force_reload)
|
|||
have_event_id = false;
|
||||
}
|
||||
|
||||
_model->end_write (_length_beats, false, true);
|
||||
//_model->end_write (false);
|
||||
_model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
|
||||
_model->set_edited (false);
|
||||
|
||||
_model_iter = _model->begin();
|
||||
|
|
|
@ -97,7 +97,14 @@ public:
|
|||
|
||||
void start_write();
|
||||
bool writing() const { return _writing; }
|
||||
void end_write (Time when=0, bool delete_stuck=false, bool resolve=false);
|
||||
|
||||
enum StuckNoteOption {
|
||||
Relax,
|
||||
DeleteStuckNotes,
|
||||
ResolveStuckNotes
|
||||
};
|
||||
|
||||
void end_write (StuckNoteOption, Time when = 0);
|
||||
|
||||
void append(const Event<Time>& ev, Evoral::event_id_t evid);
|
||||
|
||||
|
|
|
@ -623,7 +623,7 @@ Sequence<Time>::start_write()
|
|||
*/
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
|
||||
Sequence<Time>::end_write (StuckNoteOption option, Time when)
|
||||
{
|
||||
WriteLock lock(write_lock());
|
||||
|
||||
|
@ -631,11 +631,7 @@ Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
|
|||
return;
|
||||
}
|
||||
|
||||
if (resolve) {
|
||||
assert (!delete_stuck);
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 : end_write (%2 notes)\n", this, _notes.size()));
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 : end_write (%2 notes) delete stuck option %3 @ %4\n", this, _notes.size(), option, when));
|
||||
|
||||
if (!_percussive) {
|
||||
|
||||
|
@ -643,11 +639,17 @@ Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
|
|||
typename Notes::iterator next = n;
|
||||
++next;
|
||||
|
||||
cerr << "!!!!!!! note length = " << (*n)->length() << endl;
|
||||
|
||||
if ((*n)->length() == 0) {
|
||||
if (delete_stuck) {
|
||||
switch (option) {
|
||||
case Relax:
|
||||
break;
|
||||
case DeleteStuckNotes:
|
||||
cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
|
||||
_notes.erase(n);
|
||||
} else if (resolve) {
|
||||
break;
|
||||
case ResolveStuckNotes:
|
||||
if (when <= (*n)->time()) {
|
||||
cerr << "WARNING: Stuck note resolution - end time @ "
|
||||
<< when << " is before note on: " << (**n) << endl;
|
||||
|
@ -656,6 +658,7 @@ Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
|
|||
(*n)->set_length (when - (*n)->time());
|
||||
cerr << "WARNING: resolved note-on with no note-off to generate " << (**n) << endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ StateButton::avoid_prelight_on_style_changed (const Glib::RefPtr<Gtk::Style>& /*
|
|||
gtk_widget_modify_style (GTK_WIDGET(child->gobj()), rcstyle);
|
||||
}
|
||||
|
||||
|
||||
g_object_unref (rcstyle);
|
||||
style_changing = false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user