implement midi capture alignment:

For audio: not writing frames to the capture ringbuffer offsets
the recording.
For midi: we need to keep track of the record range
and subtract the accumulated difference from the event time.
This commit is contained in:
Robin Gareus 2014-06-08 01:07:03 +02:00
parent 5a41487a08
commit 6416a429a8
2 changed files with 29 additions and 6 deletions

View file

@ -173,6 +173,7 @@ class LIBARDOUR_API MidiDiskstream : public Diskstream
gint _frames_read_from_ringbuffer; gint _frames_read_from_ringbuffer;
volatile gint _frames_pending_write; volatile gint _frames_pending_write;
volatile gint _num_captured_loops; volatile gint _num_captured_loops;
framepos_t _accumulated_capture_offset;
/** A buffer that we use to put newly-arrived MIDI data in for /** A buffer that we use to put newly-arrived MIDI data in for
the GUI to read (so that it can update itself). the GUI to read (so that it can update itself).

View file

@ -75,6 +75,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
, _frames_read_from_ringbuffer(0) , _frames_read_from_ringbuffer(0)
, _frames_pending_write(0) , _frames_pending_write(0)
, _num_captured_loops(0) , _num_captured_loops(0)
, _accumulated_capture_offset(0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
{ {
in_set_state = true; in_set_state = true;
@ -99,6 +100,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
, _frames_read_from_ringbuffer(0) , _frames_read_from_ringbuffer(0)
, _frames_pending_write(0) , _frames_pending_write(0)
, _num_captured_loops(0) , _num_captured_loops(0)
, _accumulated_capture_offset(0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
{ {
in_set_state = true; in_set_state = true;
@ -362,6 +364,15 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset); calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset);
/* For audio: not writing frames to the capture ringbuffer offsets
* the recording. For midi: we need to keep track of the record range
* and subtract the accumulated difference from the event time.
*/
if (rec_nframes) {
_accumulated_capture_offset += rec_offset;
} else {
_accumulated_capture_offset += nframes;
}
if (rec_nframes && !was_recording) { if (rec_nframes && !was_recording) {
if (loop_loc) { if (loop_loc) {
@ -394,6 +405,9 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false); Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
if (ev.time() + rec_offset > rec_nframes) {
break;
}
#ifndef NDEBUG #ifndef NDEBUG
if (DEBUG::MidiIO & PBD::debug_bits) { if (DEBUG::MidiIO & PBD::debug_bits) {
const uint8_t* __data = ev.buffer(); const uint8_t* __data = ev.buffer();
@ -416,23 +430,26 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
any desirable behaviour. We don't want to send event with any desirable behaviour. We don't want to send event with
transport time here since that way the source can not transport time here since that way the source can not
reconstruct their actual time; future clever MIDI looping should reconstruct their actual time; future clever MIDI looping should
probabl be implemented in the source instead of here. probably be implemented in the source instead of here.
*/ */
const framecnt_t loop_offset = _num_captured_loops * loop_length; const framecnt_t loop_offset = _num_captured_loops * loop_length;
const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time();
if (event_time < 0 || event_time < first_recordable_frame) {
continue;
}
switch (mode) { switch (mode) {
case AllChannels: case AllChannels:
_capture_buf->write(transport_frame + loop_offset + ev.time(), _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer()); ev.type(), ev.size(), ev.buffer());
break; break;
case FilterChannels: case FilterChannels:
if (ev.is_channel_event()) { if (ev.is_channel_event()) {
if ((1<<ev.channel()) & mask) { if ((1<<ev.channel()) & mask) {
_capture_buf->write(transport_frame + loop_offset + ev.time(), _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer()); ev.type(), ev.size(), ev.buffer());
} }
} else { } else {
_capture_buf->write(transport_frame + loop_offset + ev.time(), _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer()); ev.type(), ev.size(), ev.buffer());
} }
break; break;
@ -440,7 +457,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
if (ev.is_channel_event()) { if (ev.is_channel_event()) {
ev.set_channel (PBD::ffs(mask) - 1); ev.set_channel (PBD::ffs(mask) - 1);
} }
_capture_buf->write(transport_frame + loop_offset + ev.time(), _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer()); ev.type(), ev.size(), ev.buffer());
break; break;
} }
@ -472,6 +489,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
if (was_recording) { if (was_recording) {
finish_capture (); finish_capture ();
} }
_accumulated_capture_offset = 0;
} }
@ -972,6 +990,10 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
RegionFactory::region_name (region_name, _write_source->name(), false); RegionFactory::region_name (region_name, _write_source->name(), false);
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n",
_name, (*ci)->start, (*ci)->frames, region_name));
// cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n"; // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
try { try {