13
0

Apply MIDI looping patch from torbenh, with minor changes.

General idea: use internal events to mark loop boundaries in MIDI buffers so readers can make sense of timestamps.


git-svn-id: svn://localhost/ardour2/branches/3.0@3905 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2008-10-10 00:39:29 +00:00
parent c64e96b6a8
commit 15cdf454ea
10 changed files with 61 additions and 23 deletions

View File

@ -262,9 +262,9 @@ def fetch_git_revision (path):
cmd += "git log --abbrev HEAD^..HEAD "
cmd += path
output = commands.getoutput (cmd)
output = output.splitlines()
output = output.splitlines ()
rev = output[0].replace( "commit", "git")[0:7]
rev = output[0].replace ("commit", "git")[0:10]
for line in output:
try:
if "git-svn-id" in line:

View File

@ -110,6 +110,7 @@ midi_playlist.cc
midi_port.cc
midi_region.cc
midi_source.cc
midi_state_tracker.cc
midi_stretch.cc
midi_track.cc
mix.cc

View File

@ -46,6 +46,10 @@ private:
static EventTypeMap event_type_map;
};
enum InternalEventType {
LoopEventType = 1000
};
} // namespace ARDOUR
#endif /* __ardour_event_type_map_h__ */

View File

@ -46,6 +46,7 @@
#include <ardour/diskstream.h>
#include <ardour/midi_playlist.h>
#include <ardour/midi_ring_buffer.h>
#include <ardour/midi_state_tracker.h>
struct tm;
@ -68,7 +69,7 @@ class MidiDiskstream : public Diskstream
float playback_buffer_load() const;
float capture_buffer_load() const;
void get_playback(MidiBuffer& dst, nframes_t start, nframes_t end);
void get_playback(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset);
void set_record_enabled (bool yn);
@ -170,13 +171,16 @@ class MidiDiskstream : public Diskstream
void engage_record_enable ();
void disengage_record_enable ();
void check_note_onoffs(Evoral::MIDIEvent &event);
void emit_pending_note_offs(MidiBuffer &dst, nframes_t time);
MidiRingBuffer* _playback_buf;
MidiRingBuffer* _capture_buf;
MidiPort* _source_port;
boost::shared_ptr<SMFSource> _write_source;
nframes_t _last_flush_frame;
NoteMode _note_mode;
MidiStateTracker _midistate_tracker;
};
}; /* namespace ARDOUR */

View File

@ -23,6 +23,7 @@
#include <algorithm>
#include <ardour/types.h>
#include <ardour/buffer.h>
#include <ardour/event_type_map.h>
#include <evoral/EventSink.hpp>
#include <evoral/EventRingBuffer.hpp>
@ -148,6 +149,18 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
continue;
}
// This event marks a loop happening. this means that
// the next events timestamp will be non-monotonic.
if (ev_type == LoopEventType) {
ev_time -= start;
Evoral::MIDIEvent loopevent(LoopEventType, ev_time);
dst.push_back(loopevent);
// We can safely return, without reading the data, because
// a LoopEvent does not have data.
return count + 1;
}
uint8_t status;
success = full_peek(sizeof(uint8_t), &status);
assert(success); // If this failed, buffer is corrupt, all hope is lost
@ -168,6 +181,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
assert(ev_time >= start);
ev_time -= start;
ev_time += offset;
uint8_t* write_loc = dst.reserve(ev_time, ev_size);
if (write_loc == NULL) {

View File

@ -110,6 +110,7 @@ JackMidiPort::cycle_end (nframes_t nframes, nframes_t offset)
for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
const Evoral::Event& ev = *i;
// event times should be frames, relative to cycle start
assert(ev.time() >= 0);
assert(ev.time() < nframes);
@ -133,7 +134,8 @@ JackMidiPort::flush_buffers (nframes_t nframes, nframes_t offset)
const Evoral::Event& ev = *i;
// event times should be frames, relative to cycle start
assert(ev.time() >= 0);
assert(ev.time() < nframes);
jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size());
assert(ev.time() < (nframes+offset));
if (ev.time() >= offset)
jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size());
}
}

View File

@ -110,13 +110,17 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
assert(_capacity >= msrc.size());
clear();
assert(_size == 0);
if (offset == 0) {
clear();
assert(_size == 0);
}
// FIXME: slow
for (size_t i=0; i < msrc.size(); ++i) {
const Evoral::MIDIEvent& ev = msrc[i];
if (ev.time() >= offset && ev.time() < offset+nframes) {
if (ev.time() < offset)
continue;
if (ev.time() < (nframes + offset)) {
//cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
push_back(ev);
} else {

View File

@ -728,7 +728,7 @@ MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed)
position within the loop.
*/
if (loc && start >= loop_end) {
if (loc && (start >= loop_end)) {
//cerr << "start adjusted from " << start;
start = loop_start + ((start - loop_start) % loop_length);
//cerr << "to " << start << endl;
@ -774,6 +774,9 @@ MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed)
/* if we read to the end of the loop, go back to the beginning */
if (reloop) {
// Synthesize LoopEvent here, because the next events
// written will have non-monotonic timestamps.
_playback_buf->write(loop_end - 1, LoopEventType, 0, 0);
start = loop_start;
} else {
start += this_read;
@ -1435,7 +1438,6 @@ MidiDiskstream::capture_buffer_load () const
(double) _capture_buf->capacity());
}
int
MidiDiskstream::use_pending_capture_data (XMLNode& node)
{
@ -1446,16 +1448,29 @@ MidiDiskstream::use_pending_capture_data (XMLNode& node)
* so that an event at \a start has time = 0
*/
void
MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end)
MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset)
{
dst.clear();
assert(dst.size() == 0);
if (offset == 0) {
dst.clear();
assert(dst.size() == 0);
}
// Reverse. ... We just don't do reverse, ok? Back off.
if (end <= start) {
return;
}
// Translates stamps to be relative to start
_playback_buf->read(dst, start, end);
// Check only events added this offset cycle
MidiBuffer::iterator this_cycle_start = dst.end();
// Translates stamps to be relative to start, but add offset.
_playback_buf->read(dst, start, end, offset);
// Now feed the data through the MidiStateTracker.
// In case it detects a LoopEvent it will add necessary note
// offs.
if (_midistate_tracker.track(this_cycle_start, dst.end()))
_midistate_tracker.resolve_notes(dst, end-start - 1 + offset);
}

View File

@ -510,7 +510,7 @@ MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
//const size_t limit = n_process_buffers().n_audio();
BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame);
diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame, offset);
process_output_buffers (bufs, start_frame, end_frame, nframes, offset,
(!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick, (_meter_point != MeterInput));

View File

@ -970,12 +970,6 @@ Panner::state (bool full)
node.add_property (X_("link_direction"), enum_2_string (_link_direction));
node.add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
snprintf (buf, sizeof (buf), "%d", _streampanners.size());
node.add_property (X_("ins"), buf);
snprintf (buf, sizeof (buf), "%d", outputs.size());
node.add_property (X_("outs"), buf);
/* add each output */
for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
XMLNode* onode = new XMLNode (X_("Output"));
snprintf (buf, sizeof (buf), "%.12g", (*o).x);