diff options
-rw-r--r-- | gtk2_ardour/lv2_plugin_ui.cc | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/lv2_plugin.h | 35 | ||||
-rw-r--r-- | libs/ardour/ardour/tempo.h | 21 | ||||
-rw-r--r-- | libs/ardour/buffer_set.cc | 6 | ||||
-rw-r--r-- | libs/ardour/lv2_plugin.cc | 155 | ||||
-rw-r--r-- | libs/ardour/tempo.cc | 26 |
6 files changed, 170 insertions, 75 deletions
diff --git a/gtk2_ardour/lv2_plugin_ui.cc b/gtk2_ardour/lv2_plugin_ui.cc index 177d27fa47..839bc6a4b8 100644 --- a/gtk2_ardour/lv2_plugin_ui.cc +++ b/gtk2_ardour/lv2_plugin_ui.cc @@ -56,7 +56,7 @@ LV2PluginUI::write_from_ui(void* controller, if (ac) { ac->set_value(*(const float*)buffer); } - } else if (format == me->_lv2->atom_eventTransfer()) { + } else if (format == me->_lv2->urids.atom_eventTransfer) { const int cnt = me->_pi->get_count(); for (int i=0; i < cnt; i++ ) { diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index 1090f30307..a4b89a5841 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -111,8 +111,6 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee boost::shared_ptr<Plugin::ScalePoints> get_scale_points(uint32_t port_index) const; - static uint32_t midi_event_type() { return _midi_event_type; } - void set_insert_info(const PluginInsert* insert); int set_state (const XMLNode& node, int version); @@ -124,8 +122,6 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee bool has_editor () const; bool has_message_output () const; - uint32_t atom_eventTransfer() const; - void write_from_ui(uint32_t index, uint32_t protocol, uint32_t size, @@ -147,14 +143,26 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee static URIMap _uri_map; - static uint32_t _midi_event_type; - static uint32_t _chunk_type; - static uint32_t _sequence_type; - static uint32_t _event_transfer_type; - static uint32_t _path_type; - static uint32_t _log_Error; - static uint32_t _log_Warning; - static uint32_t _log_Note; + struct URIDs { + uint32_t atom_Chunk; + uint32_t atom_Path; + uint32_t atom_Sequence; + uint32_t atom_eventTransfer; + uint32_t log_Error; + uint32_t log_Note; + uint32_t log_Warning; + uint32_t midi_MidiEvent; + uint32_t time_Position; + uint32_t time_bar; + uint32_t time_barBeat; + uint32_t time_beatUnit; + uint32_t time_beatsPerBar; + uint32_t time_beatsPerMinute; + uint32_t time_frame; + uint32_t time_speed; + }; + + static URIDs urids; private: struct Impl; @@ -171,6 +179,9 @@ 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; friend const void* lv2plugin_get_port_value(const char* port_symbol, diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 65e488d9a5..bedc825288 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -180,6 +180,19 @@ class TempoMetric { void set_frame (framepos_t f) { _frame = f; } void set_start (const Timecode::BBT_Time& t) { _start = t; } + void set_metric (const MetricSection* section) { + const MeterSection* meter; + const TempoSection* tempo; + if ((meter = dynamic_cast<const MeterSection*>(section))) { + set_meter(*meter); + } else if ((tempo = dynamic_cast<const TempoSection*>(section))) { + set_tempo(*tempo); + } + + set_frame(section->frame()); + set_start(section->start()); + } + const Meter& meter() const { return *_meter; } const Tempo& tempo() const { return *_tempo; } framepos_t frame() const { return _frame; } @@ -298,7 +311,13 @@ class TempoMap : public PBD::StatefulDestructible void clear (); TempoMetric metric_at (Timecode::BBT_Time bbt) const; - TempoMetric metric_at (framepos_t) const; + + /** Return the TempoMetric at frame @p t, and point @p last to the latest + * metric change <= t, if it is non-NULL. + */ + TempoMetric metric_at (framepos_t, Metrics::const_iterator* last=NULL) const; + + Metrics::const_iterator metrics_end() { return metrics.end(); } void change_existing_tempo_at (framepos_t, double bpm, double note_type); void change_initial_tempo (double bpm, double note_type); diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index f9e683cdda..c2be5ca007 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -192,8 +192,8 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac _lv2_buffers.push_back( std::make_pair(false, lv2_evbuf_new(buffer_capacity, LV2_EVBUF_EVENT, - LV2Plugin::_chunk_type, - LV2Plugin::_sequence_type))); + LV2Plugin::urids.atom_Chunk, + LV2Plugin::urids.atom_Sequence))); } } #endif @@ -269,7 +269,7 @@ BufferSet::get_lv2_midi(bool input, size_t i, bool old_api) mbuf.size(), (void*) mbuf.data())); LV2_Evbuf_Iterator i = lv2_evbuf_begin(evbuf); - const uint32_t type = LV2Plugin::midi_event_type(); + 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 diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index c7d363e512..71039ba845 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -19,6 +19,7 @@ #include <string> #include <vector> +#include <limits> #include <cmath> #include <cstdlib> @@ -51,7 +52,9 @@ #include <lilv/lilv.h> #include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" #include "lv2/lv2plug.in/ns/ext/log/log.h" +#include "lv2/lv2plug.in/ns/ext/midi/midi.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" @@ -74,22 +77,25 @@ using namespace ARDOUR; using namespace PBD; URIMap LV2Plugin::_uri_map; -uint32_t LV2Plugin::_midi_event_type = _uri_map.uri_to_id( - "http://lv2plug.in/ns/ext/midi#MidiEvent"); -uint32_t LV2Plugin::_chunk_type = _uri_map.uri_to_id( - LV2_ATOM__Chunk); -uint32_t LV2Plugin::_sequence_type = _uri_map.uri_to_id( - LV2_ATOM__Sequence); -uint32_t LV2Plugin::_event_transfer_type = _uri_map.uri_to_id( - LV2_ATOM__eventTransfer); -uint32_t LV2Plugin::_path_type = _uri_map.uri_to_id( - LV2_ATOM__Path); -uint32_t LV2Plugin::_log_Error = _uri_map.uri_to_id( - LV2_LOG__Error); -uint32_t LV2Plugin::_log_Warning = _uri_map.uri_to_id( - LV2_LOG__Warning); -uint32_t LV2Plugin::_log_Note = _uri_map.uri_to_id( - LV2_LOG__Note); + +LV2Plugin::URIDs LV2Plugin::urids = { + _uri_map.uri_to_id(LV2_ATOM__Chunk), + _uri_map.uri_to_id(LV2_ATOM__Path), + _uri_map.uri_to_id(LV2_ATOM__Sequence), + _uri_map.uri_to_id(LV2_ATOM__eventTransfer), + _uri_map.uri_to_id(LV2_LOG__Error), + _uri_map.uri_to_id(LV2_LOG__Note), + _uri_map.uri_to_id(LV2_LOG__Warning), + _uri_map.uri_to_id(LV2_MIDI__MidiEvent), + _uri_map.uri_to_id(LV2_TIME__Position), + _uri_map.uri_to_id(LV2_TIME__bar), + _uri_map.uri_to_id(LV2_TIME__barBeat), + _uri_map.uri_to_id(LV2_TIME__beatUnit), + _uri_map.uri_to_id(LV2_TIME__beatsPerBar), + _uri_map.uri_to_id(LV2_TIME__beatsPerMinute), + _uri_map.uri_to_id(LV2_TIME__frame), + _uri_map.uri_to_id(LV2_TIME__speed) +}; class LV2World : boost::noncopyable { public: @@ -102,8 +108,8 @@ public: LilvNode* atom_Chunk; LilvNode* atom_Sequence; LilvNode* atom_bufferType; - LilvNode* atom_supports; LilvNode* atom_eventTransfer; + LilvNode* atom_supports; LilvNode* ev_EventPort; LilvNode* ext_logarithmic; LilvNode* lv2_AudioPort; @@ -117,6 +123,7 @@ public: LilvNode* lv2_toggled; LilvNode* midi_MidiEvent; LilvNode* rdfs_comment; + LilvNode* time_Position; LilvNode* ui_GtkUI; LilvNode* ui_external; }; @@ -169,14 +176,14 @@ log_vprintf(LV2_Log_Handle handle, { char* str = NULL; const int ret = g_vasprintf(&str, fmt, args); - if (type == LV2Plugin::_log_Error) { + if (type == LV2Plugin::urids.log_Error) { error << str << endmsg; - } else if (type == LV2Plugin::_log_Warning) { + } else if (type == LV2Plugin::urids.log_Warning) { warning << str << endmsg; - } else if (type == LV2Plugin::_log_Note) { + } else if (type == LV2Plugin::urids.log_Note) { info << str << endmsg; } - // TODO: Togglable log:Trace message support + // TODO: Toggleable log:Trace message support return ret; } @@ -211,6 +218,7 @@ struct LV2Plugin::Impl { LilvInstance* instance; const LV2_Worker_Interface* work_iface; LilvState* state; + LV2_Atom_Forge forge; }; LV2Plugin::LV2Plugin (AudioEngine& engine, @@ -260,6 +268,9 @@ 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(); _seq_size = _engine.raw_buffer_size(DataType::MIDI); _state_version = 0; @@ -294,6 +305,8 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) _features[5] = _uri_map.urid_unmap_feature(); _features[6] = &_log_feature; + lv2_atom_forge_init(&_impl->forge, _uri_map.urid_map()); + unsigned n_features = 7; #ifdef HAVE_NEW_LV2 LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int); @@ -391,13 +404,16 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) LilvNodes* atom_supports = lilv_port_get_value( _impl->plugin, port, _world.atom_supports); - if (lilv_nodes_contains(buffer_types, _world.atom_Sequence) - && lilv_nodes_contains(atom_supports, _world.midi_MidiEvent) - ) { + if (lilv_nodes_contains(buffer_types, _world.atom_Sequence)) { + if (lilv_nodes_contains(atom_supports, _world.midi_MidiEvent)) { flags |= PORT_MESSAGE; } else { flags |= PORT_ATOM; } + if (lilv_nodes_contains(atom_supports, _world.time_Position)) { + _position_seq_port_idx = i; + } + } lilv_nodes_free(buffer_types); lilv_nodes_free(atom_supports); } else { @@ -862,7 +878,7 @@ LV2Plugin::add_state(XMLNode* root) const _session.externals_dir().c_str(), new_dir.c_str(), NULL, - (void*)this, + const_cast<LV2Plugin*>(this), 0, NULL); @@ -1067,12 +1083,6 @@ LV2Plugin::has_message_output() const return false; } -uint32_t -LV2Plugin::atom_eventTransfer() const -{ - return _event_transfer_type; -} - void LV2Plugin::write_to(RingBuffer<uint8_t>* dest, uint32_t index, @@ -1406,12 +1416,50 @@ LV2Plugin::allocate_atom_event_buffers() _atom_ev_buffers = (LV2_Evbuf**) malloc((total_atom_buffers + 1) * sizeof(LV2_Evbuf*)); for (int i = 0; i < total_atom_buffers; ++i ) { _atom_ev_buffers[i] = lv2_evbuf_new(32768, LV2_EVBUF_ATOM, - LV2Plugin::_chunk_type, LV2Plugin::_sequence_type); + LV2Plugin::urids.atom_Chunk, LV2Plugin::urids.atom_Sequence); } _atom_ev_buffers[total_atom_buffers] = 0; return; } +/** Write an ardour position/time/tempo/meter as an LV2 event. + * @return true on success. + */ +static bool +write_position(LV2_Atom_Forge* forge, + LV2_Evbuf* buf, + const TempoMetric& t, + Timecode::BBT_Time& bbt, + double speed, + framepos_t position, + framecnt_t offset) +{ + uint8_t pos_buf[256]; + lv2_atom_forge_set_buffer(forge, pos_buf, sizeof(pos_buf)); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.time_Position); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_frame, 0); + lv2_atom_forge_long(forge, position); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_speed, 0); + lv2_atom_forge_float(forge, speed); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_barBeat, 0); + lv2_atom_forge_float(forge, bbt.beats - 1 + + (bbt.ticks / Timecode::BBT_Time::ticks_per_beat)); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_bar, 0); + lv2_atom_forge_float(forge, bbt.bars - 1); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatUnit, 0); + lv2_atom_forge_float(forge, t.meter().note_divisor()); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerBar, 0); + lv2_atom_forge_float(forge, t.meter().divisions_per_bar()); + lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerMinute, 0); + lv2_atom_forge_float(forge, t.tempo().beats_per_minute()); + + LV2_Evbuf_Iterator end = lv2_evbuf_end(buf); + const LV2_Atom* const atom = (const LV2_Atom*)pos_buf; + return lv2_evbuf_write(&end, offset, 0, atom->type, atom->size, + (const uint8_t*)(atom + 1)); +} + int LV2Plugin::connect_and_run(BufferSet& bufs, ChanMapping in_map, ChanMapping out_map, @@ -1422,14 +1470,16 @@ LV2Plugin::connect_and_run(BufferSet& bufs, cycles_t then = get_cycles(); + TempoMap& tmap = _session.tempo_map(); + Metrics::const_iterator metric_i = tmap.metrics_end(); + TempoMetric tmetric = tmap.metric_at(_session.transport_frame(), &metric_i); + if (_freewheel_control_port) { - *_freewheel_control_port = _session.engine().freewheeling (); + *_freewheel_control_port = _session.engine().freewheeling(); } if (_bpm_control_port) { - TempoMap& tmap (_session.tempo_map ()); - Tempo tempo = tmap.tempo_at (_session.transport_frame () + offset); - *_bpm_control_port = tempo.beats_per_minute (); + *_bpm_control_port = tmetric.tempo().beats_per_minute(); } ChanCount bufs_count; @@ -1484,6 +1534,30 @@ LV2Plugin::connect_and_run(BufferSet& bufs, if (flags & PORT_INPUT) { lv2_evbuf_reset(_atom_ev_buffers[atom_port_index], true); _ev_buffers[port_index] = _atom_ev_buffers[atom_port_index++]; + + if (port_index == _position_seq_port_idx) { + Timecode::BBT_Time bbt; + if (_session.transport_frame() != _next_cycle_start || + _session.transport_speed() != _next_cycle_speed) { + // Something has changed, write the 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(); + write_position(&_impl->forge, _ev_buffers[port_index], + tmetric, bbt, _session.transport_speed(), + section->frame(), + section->frame() - _session.transport_frame()); + } + } } else { lv2_evbuf_reset(_atom_ev_buffers[atom_port_index], false); _ev_buffers[port_index] = _atom_ev_buffers[atom_port_index++]; @@ -1510,7 +1584,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, error << "Error reading from UI=>Plugin RingBuffer" << endmsg; break; } - if (msg.protocol == _event_transfer_type) { + if (msg.protocol == urids.atom_eventTransfer) { LV2_Evbuf* buf = _ev_buffers[msg.index]; LV2_Evbuf_Iterator i = lv2_evbuf_end(buf); const LV2_Atom* const atom = (const LV2_Atom*)body; @@ -1549,7 +1623,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, uint32_t frames, subframes, type, size; uint8_t* data; lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data); - write_to_ui(port_index, _event_transfer_type, + write_to_ui(port_index, urids.atom_eventTransfer, size + sizeof(LV2_Atom), data - sizeof(LV2_Atom)); } @@ -1559,6 +1633,10 @@ LV2Plugin::connect_and_run(BufferSet& bufs, cycles_t now = get_cycles(); set_cycles((uint32_t)(now - then)); + // Update expected transport information for next cycle so we can detect changes + _next_cycle_speed = _session.transport_speed(); + _next_cycle_start = _session.transport_frame() + (nframes * _next_cycle_speed); + return 0; } @@ -1734,6 +1812,7 @@ LV2World::LV2World() lv2_enumeration = lilv_new_uri(world, LV2_CORE__enumeration); midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT); rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment"); + time_Position = lilv_new_uri(world, LV2_TIME__Position); ui_GtkUI = lilv_new_uri(world, LV2_UI__GtkUI); ui_external = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external"); } diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 80091e7eec..0bb2fea0cf 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -1031,12 +1031,10 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, } TempoMetric -TempoMap::metric_at (framepos_t frame) const +TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const { Glib::Threads::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); - const Meter* meter; - const Tempo* tempo; /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing at something, because we insert the default tempo and meter during @@ -1051,14 +1049,11 @@ TempoMap::metric_at (framepos_t frame) const break; } - if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) { - m.set_tempo (*tempo); - } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) { - m.set_meter (*meter); - } + m.set_metric(*i); - m.set_frame ((*i)->frame ()); - m.set_start ((*i)->start ()); + if (last) { + *last = i; + } } return m; @@ -1069,8 +1064,6 @@ TempoMap::metric_at (BBT_Time bbt) const { Glib::Threads::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); - const Meter* meter; - const Tempo* tempo; /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing at something, because we insert the default tempo and meter during @@ -1087,14 +1080,7 @@ TempoMap::metric_at (BBT_Time bbt) const break; } - if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) { - m.set_tempo (*tempo); - } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) { - m.set_meter (*meter); - } - - m.set_frame ((*i)->frame ()); - m.set_start (section_start); + m.set_metric (*i); } return m; |