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:
parent
c64e96b6a8
commit
15cdf454ea
@ -264,7 +264,7 @@ def fetch_git_revision (path):
|
||||
output = commands.getoutput (cmd)
|
||||
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:
|
||||
|
@ -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
|
||||
|
@ -46,6 +46,10 @@ private:
|
||||
static EventTypeMap event_type_map;
|
||||
};
|
||||
|
||||
enum InternalEventType {
|
||||
LoopEventType = 1000
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_event_type_map_h__ */
|
||||
|
@ -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,6 +171,8 @@ 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;
|
||||
@ -177,6 +180,7 @@ class MidiDiskstream : public Diskstream
|
||||
boost::shared_ptr<SMFSource> _write_source;
|
||||
nframes_t _last_flush_frame;
|
||||
NoteMode _note_mode;
|
||||
MidiStateTracker _midistate_tracker;
|
||||
};
|
||||
|
||||
}; /* namespace ARDOUR */
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
assert(ev.time() < (nframes+offset));
|
||||
if (ev.time() >= offset)
|
||||
jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size());
|
||||
}
|
||||
}
|
||||
|
@ -110,13 +110,17 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
|
||||
|
||||
assert(_capacity >= msrc.size());
|
||||
|
||||
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 {
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user