Deliver MIDI events to atom ports that support it, merged with transport events.
authorDavid Robillard <d@drobilla.net>
Sun, 18 Nov 2012 04:35:31 +0000 (04:35 +0000)
committerDavid Robillard <d@drobilla.net>
Sun, 18 Nov 2012 04:35:31 +0000 (04:35 +0000)
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

libs/ardour/ardour/lv2_plugin.h
libs/ardour/buffer_set.cc
libs/ardour/lv2_plugin.cc

index 51fa5a2987d53b46eb103d0dda89030925e8438e..66c44884eac542a90b92026586d6fcd3d4457ce9 100644 (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;
index c2be5ca007e110db0b550429fc499ea2832658a3..1f8317ffe26a6a3240f21b9d3af77d224d04e7d2 100644 (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);
+               }
        }
 }
 
index 928c97a32e12c53354487f6db781aaf4f4793b53..b48c962d0c87c1bb583be1c34c1e2ca458016aa2 100644 (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);