13
0

Deliver MIDI events to atom ports that support it, merged with transport events.

Eliminate delivery of non-MIDI events to Ardour MIDI buffers.
Fix the general mess of LV2 port flags and types.
Unify old MIDI API and new atom API MIDI stuff.

Making this work required changing the way we do LV2 MIDI slightly: instead of
BufferSet::get_lv2_midi doing the translation automatically, it returns an
empty buffer and the caller is responsible for actually filling it.  This
allows the LV2 code to iterate over the MIDI and transport changes in one go to
merge them into the buffer at the correct times.

Synth plugins using the new atom API now work and can get accurate transport
information along with the MIDI that drives them.


git-svn-id: svn://localhost/ardour2/branches/3.0@13522 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2012-11-18 04:35:31 +00:00
parent 95bd893d5e
commit b47619c458
3 changed files with 76 additions and 75 deletions

View File

@ -179,7 +179,6 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
float* _bpm_control_port; ///< Special input set by ardour
float* _freewheel_control_port; ///< Special input set by ardour
float* _latency_control_port; ///< Special output set by ardour
uint32_t _position_seq_port_idx; ///< Index of Sequence port for position
framepos_t _next_cycle_start; ///< Expected start frame of next run cycle
double _next_cycle_speed; ///< Expected start frame of next run cycle
PBD::ID _insert_id;
@ -190,13 +189,14 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
uint32_t* type);
typedef enum {
PORT_INPUT = 1,
PORT_OUTPUT = 1 << 1,
PORT_AUDIO = 1 << 2,
PORT_CONTROL = 1 << 3,
PORT_EVENT = 1 << 4,
PORT_MESSAGE = 1 << 5,
PORT_ATOM = 1 << 6
PORT_INPUT = 1, ///< Input port
PORT_OUTPUT = 1 << 1, ///< Output port
PORT_AUDIO = 1 << 2, ///< Audio (buffer of float)
PORT_CONTROL = 1 << 3, ///< Control (single float)
PORT_EVENT = 1 << 4, ///< Old event API event port
PORT_SEQUENCE = 1 << 5, ///< New atom API event port
PORT_MIDI = 1 << 6, ///< Event port understands MIDI
PORT_POSITION = 1 << 7 ///< Event port understands position
} PortFlag;
typedef unsigned PortFlags;

View File

@ -257,34 +257,11 @@ BufferSet::get_lv2_midi(bool input, size_t i, bool old_api)
{
assert(count().get(DataType::MIDI) > i);
MidiBuffer& mbuf = get_midi(i);
LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1));
LV2_Evbuf* evbuf = b.second;
lv2_evbuf_set_type(evbuf, old_api ? LV2_EVBUF_EVENT : LV2_EVBUF_ATOM);
lv2_evbuf_set_type(evbuf, old_api ? LV2_EVBUF_EVENT : LV2_EVBUF_ATOM);
lv2_evbuf_reset(evbuf, input);
if (input) {
DEBUG_TRACE(PBD::DEBUG::LV2,
string_compose("%1 bytes of MIDI waiting @ %2\n",
mbuf.size(), (void*) mbuf.data()));
LV2_Evbuf_Iterator i = lv2_evbuf_begin(evbuf);
const uint32_t type = LV2Plugin::urids.midi_MidiEvent;
for (MidiBuffer::iterator e = mbuf.begin(); e != mbuf.end(); ++e) {
const Evoral::MIDIEvent<framepos_t> ev(*e, false);
#ifndef NDEBUG
DEBUG_TRACE(PBD::DEBUG::LV2,
string_compose("\tMIDI event of size %1 @ %2\n",
ev.size(), ev.time()));
for (uint16_t x = 0; x < ev.size(); ++x) {
std::stringstream ss;
ss << "\t\tev[" << x << "] = " << std::hex << (int) ev.buffer()[x] << std::dec << std::endl;
DEBUG_TRACE (PBD::DEBUG::LV2, ss.str());
}
#endif
lv2_evbuf_write(&i, ev.time(), 0, type, ev.size(), ev.buffer());
}
}
return evbuf;
}
@ -311,7 +288,10 @@ BufferSet::flush_lv2_midi(bool input, size_t i)
DEBUG_TRACE (PBD::DEBUG::LV2, string_compose ("\tByte[%1] = %2\n", x, (int) data[x]));
}
#endif
mbuf.push_back(frames, size, data);
if (type == LV2Plugin::urids.midi_MidiEvent) {
// TODO: Make Ardour event buffers generic so plugins can communicate
mbuf.push_back(frames, size, data);
}
}
}

View File

@ -274,7 +274,6 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
_bpm_control_port = 0;
_freewheel_control_port = 0;
_latency_control_port = 0;
_position_seq_port_idx = std::numeric_limits<uint32_t>::max();
_next_cycle_start = std::numeric_limits<framepos_t>::max();
_next_cycle_speed = 1.0;
_block_length = _engine.frames_per_cycle();
@ -405,6 +404,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
flags |= PORT_AUDIO;
} else if (lilv_port_is_a(_impl->plugin, port, _world.ev_EventPort)) {
flags |= PORT_EVENT;
flags |= PORT_MIDI; // We assume old event API ports are for MIDI
} else if (lilv_port_is_a(_impl->plugin, port, _world.atom_AtomPort)) {
LilvNodes* buffer_types = lilv_port_get_value(
_impl->plugin, port, _world.atom_bufferType);
@ -412,13 +412,12 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
_impl->plugin, port, _world.atom_supports);
if (lilv_nodes_contains(buffer_types, _world.atom_Sequence)) {
flags |= PORT_SEQUENCE;
if (lilv_nodes_contains(atom_supports, _world.midi_MidiEvent)) {
flags |= PORT_MESSAGE;
} else {
flags |= PORT_ATOM;
flags |= PORT_MIDI;
}
if (lilv_nodes_contains(atom_supports, _world.time_Position)) {
_position_seq_port_idx = i;
flags |= PORT_POSITION;
}
}
lilv_nodes_free(buffer_types);
@ -1066,7 +1065,8 @@ bool
LV2Plugin::has_message_output() const
{
for (uint32_t i = 0; i < num_ports(); ++i) {
if ((_port_flags[i] & (PORT_MESSAGE|PORT_ATOM)) && _port_flags[i] & PORT_OUTPUT) {
if ((_port_flags[i] & PORT_SEQUENCE) &&
(_port_flags[i] & PORT_OUTPUT)) {
return true;
}
}
@ -1485,17 +1485,18 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
BufferSet& silent_bufs = _session.get_silent_buffers(bufs_count);
BufferSet& scratch_bufs = _session.get_scratch_buffers(bufs_count);
uint32_t const num_ports = parameter_count();
uint32_t const nil_index = std::numeric_limits<uint32_t>::max();
uint32_t audio_in_index = 0;
uint32_t audio_out_index = 0;
uint32_t midi_in_index = 0;
uint32_t midi_out_index = 0;
uint32_t atom_port_index = 0;
bool valid;
for (uint32_t port_index = 0; port_index < num_ports; ++port_index) {
void* buf = NULL;
uint32_t index = 0;
uint32_t index = nil_index;
PortFlags flags = _port_flags[port_index];
bool valid = false;
if (flags & PORT_AUDIO) {
if (flags & PORT_INPUT) {
index = in_map.get(DataType::AUDIO, audio_in_index++, &valid);
@ -1508,59 +1509,78 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
? bufs.get_audio(index).data(offset)
: scratch_bufs.get_audio(0).data(offset);
}
} else if (flags & (PORT_EVENT|PORT_MESSAGE)) {
} else if (flags & (PORT_EVENT|PORT_SEQUENCE)) {
/* FIXME: The checks here for bufs.count().n_midi() > index shouldn't
be necessary, but the mapping is illegal in some cases. Ideally
that should be fixed, but this is easier...
*/
if (flags & PORT_INPUT) {
index = in_map.get(DataType::MIDI, midi_in_index++, &valid);
_ev_buffers[port_index] = (valid && bufs.count().n_midi() > index)
? bufs.get_lv2_midi(true, index, flags & PORT_EVENT)
: silent_bufs.get_lv2_midi(true, 0, flags & PORT_EVENT);
buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
} else {
index = out_map.get(DataType::MIDI, midi_out_index++, &valid);
_ev_buffers[port_index] = (valid && bufs.count().n_midi() > index)
? bufs.get_lv2_midi(false, index, flags & PORT_EVENT)
: scratch_bufs.get_lv2_midi(false, 0, flags & PORT_EVENT);
buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
}
} else if (flags & (PORT_ATOM)) {
assert(_atom_ev_buffers && _atom_ev_buffers[atom_port_index]);
if (flags & PORT_INPUT) {
if (flags & PORT_MIDI) {
if (flags & PORT_INPUT) {
index = in_map.get(DataType::MIDI, midi_in_index++, &valid);
} else {
index = out_map.get(DataType::MIDI, midi_out_index++, &valid);
}
if (valid && bufs.count().n_midi() > index) {
_ev_buffers[port_index] = bufs.get_lv2_midi(
(flags & PORT_INPUT), index, (flags & PORT_EVENT));
}
} else if ((flags & PORT_POSITION) && (flags & PORT_INPUT)) {
lv2_evbuf_reset(_atom_ev_buffers[atom_port_index], true);
_ev_buffers[port_index] = _atom_ev_buffers[atom_port_index++];
valid = true;
}
if (port_index == _position_seq_port_idx) {
Timecode::BBT_Time bbt;
if (valid && (flags & PORT_INPUT)) {
Timecode::BBT_Time bbt;
if ((flags & PORT_POSITION)) {
if (_session.transport_frame() != _next_cycle_start ||
_session.transport_speed() != _next_cycle_speed) {
// Something has changed, write the position at cycle start
// Transport has changed, write position at cycle start
tmap.bbt_time(_session.transport_frame(), bbt);
write_position(&_impl->forge, _ev_buffers[port_index],
tmetric, bbt, _session.transport_speed(),
_session.transport_frame(), 0);
}
}
// Write a position event for every metric change within this cycle
while (++metric_i != tmap.metrics_end() &&
(*metric_i)->frame() < _session.transport_frame() + nframes) {
MetricSection* section = *metric_i;
tmetric.set_metric(section);
bbt = section->start();
// Get MIDI iterator range (empty range if no MIDI)
MidiBuffer::iterator m = (index != nil_index)
? bufs.get_midi(index).begin()
: silent_bufs.get_midi(0).end();
MidiBuffer::iterator m_end = (index != nil_index)
? bufs.get_midi(index).end()
: m;
// Now merge MIDI and any transport events into the buffer
LV2_Evbuf_Iterator i = lv2_evbuf_end(_ev_buffers[port_index]);
const uint32_t type = LV2Plugin::urids.midi_MidiEvent;
const framepos_t tend = _session.transport_frame() + nframes;
++metric_i;
while (m != m_end || (metric_i != tmap.metrics_end() &&
(*metric_i)->frame() < tend)) {
MetricSection* metric = (metric_i != tmap.metrics_end())
? *metric_i : NULL;
if (m != m_end && (!metric || metric->frame() > (*m).time())) {
const Evoral::MIDIEvent<framepos_t> ev(*m, false);
LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]);
lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer());
++m;
} else {
tmetric.set_metric(metric);
bbt = metric->start();
write_position(&_impl->forge, _ev_buffers[port_index],
tmetric, bbt, _session.transport_speed(),
section->frame(),
section->frame() - _session.transport_frame());
metric->frame(),
metric->frame() - _session.transport_frame());
++metric_i;
}
}
} else {
lv2_evbuf_reset(_atom_ev_buffers[atom_port_index], false);
_ev_buffers[port_index] = _atom_ev_buffers[atom_port_index++];
} else if (!valid) {
// Nothing we understand or care about, connect to scratch
_ev_buffers[port_index] = silent_bufs.get_lv2_midi(
(flags & PORT_INPUT), 0, (flags & PORT_EVENT));
}
buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
assert(buf);
} else {
continue; // Control port, leave buffer alone
}
@ -1601,9 +1621,10 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
midi_out_index = 0;
for (uint32_t port_index = 0; port_index < num_ports; ++port_index) {
PortFlags flags = _port_flags[port_index];
bool valid = false;
// Flush MIDI (write back to Ardour MIDI buffers)
if ((flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_MESSAGE))) {
if ((flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
const uint32_t buf_index = out_map.get(
DataType::MIDI, midi_out_index++, &valid);
if (valid) {
@ -1612,7 +1633,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
}
// Write messages to UI
if (_to_ui && (flags & PORT_OUTPUT) && (flags & (PORT_MESSAGE|PORT_ATOM))) {
if (_to_ui && (flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
LV2_Evbuf* buf = _ev_buffers[port_index];
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf);
lv2_evbuf_is_valid(i);