Fix MIDI loop recording.

This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.

I have tested the following use cases with live input and audible output:

 * Non-loop recording, armed before roll
 * Non-loop recording, arm while rolling
 * Loop recording, armed before roll
 * Loop recording, arm during roll

In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.

See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether.  Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.

Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing.  Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option.  We will need to
add more later.

Display while recording is a little bit wacky, but whatever.


git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2013-01-21 06:00:15 +00:00
parent 76547b5c4b
commit e781c1cf0d
5 changed files with 139 additions and 98 deletions

View file

@ -59,7 +59,8 @@ MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
, _model_iter_valid(false)
, _length_beats(0.0)
, _last_read_end(0)
, _last_write_end(0)
, _capture_length(0)
, _capture_loop_length(0)
{
}
@ -69,7 +70,8 @@ MidiSource::MidiSource (Session& s, const XMLNode& node)
, _model_iter_valid(false)
, _length_beats(0.0)
, _last_read_end(0)
, _last_write_end(0)
, _capture_length(0)
, _capture_loop_length(0)
{
if (set_state (node, Stateful::loading_state_version)) {
throw failed_constructor();
@ -266,7 +268,7 @@ MidiSource::midi_write (MidiRingBuffer<framepos_t>& source,
if (cnt == max_framecnt) {
_last_read_end = 0;
} else {
_last_write_end += cnt;
_capture_length += cnt;
}
return ret;
@ -284,10 +286,12 @@ MidiSource::mark_streaming_midi_write_started (NoteMode mode)
}
void
MidiSource::mark_write_starting_now ()
MidiSource::mark_write_starting_now (framecnt_t position,
framecnt_t capture_length,
framecnt_t loop_length)
{
/* I'm not sure if this is the best way to approach this, but
_last_write_end needs to be set up with the transport frame
_capture_length needs to be set up with the transport frame
when a record actually starts, as it is used by
SMFSource::write_unlocked to decide whether incoming notes
are within the correct time range.
@ -297,8 +301,12 @@ MidiSource::mark_write_starting_now ()
because it is not RT-safe.
*/
set_timeline_position (_session.transport_frame ());
_last_write_end = _session.transport_frame ();
set_timeline_position(position);
_capture_length = capture_length;
_capture_loop_length = loop_length;
BeatsFramesConverter converter(_session.tempo_map(), position);
_length_beats = converter.from(capture_length);
}
void