21#include <lv2/atom/atom.h>
22#include <lv2/atom/util.h>
23#include <lv2/buf-size/buf-size.h>
24#include <lv2/core/lv2.h>
25#include <lv2/midi/midi.h>
26#include <lv2/state/state.h>
27#include <lv2/time/time.h>
28#include <lv2/urid/urid.h>
42inline const char*
g_uri =
nullptr;
54 sizeof(LV2_Atom_Sequence) +
56 "lv2:minimumSize must cover a full block of outgoing MIDI");
64 double& out)
noexcept {
67 const void* body = LV2_ATOM_BODY_CONST(atom);
68 if (urids.atom_float != 0 && atom->type == urids.atom_float && atom->size ==
sizeof(
float)) {
70 std::memcpy(&v, body,
sizeof(v));
71 out =
static_cast<double>(v);
74 if (urids.atom_double != 0 && atom->type == urids.atom_double && atom->size ==
sizeof(
double)) {
76 std::memcpy(&v, body,
sizeof(v));
80 if (urids.atom_int != 0 && atom->type == urids.atom_int && atom->size ==
sizeof(int32_t)) {
82 std::memcpy(&v, body,
sizeof(v));
83 out =
static_cast<double>(v);
86 if (urids.atom_long != 0 && atom->type == urids.atom_long && atom->size ==
sizeof(int64_t)) {
88 std::memcpy(&v, body,
sizeof(v));
89 out =
static_cast<double>(v);
92 if (urids.atom_bool != 0 && atom->type == urids.atom_bool && atom->size ==
sizeof(int32_t)) {
94 std::memcpy(&v, body,
sizeof(v));
95 out = v != 0 ? 1.0 : 0.0;
108 if (!obj || urids.time_position == 0)
110 if (obj->body.otype != urids.time_position)
113 LV2_ATOM_OBJECT_FOREACH(obj, prop) {
114 const LV2_Atom* value = &prop->value;
115 double numeric = 0.0;
118 const LV2_URID key = prop->key;
119 if (urids.time_speed != 0 && key == urids.time_speed) {
120 out.speed =
static_cast<float>(numeric);
121 out.has_speed =
true;
122 }
else if (urids.time_frame != 0 && key == urids.time_frame) {
123 out.frame =
static_cast<int64_t
>(numeric);
124 out.has_frame =
true;
125 }
else if (urids.time_bar != 0 && key == urids.time_bar) {
126 out.bar =
static_cast<int64_t
>(numeric);
128 }
else if (urids.time_beat != 0 && key == urids.time_beat) {
131 }
else if (urids.time_beats_per_bar != 0 && key == urids.time_beats_per_bar) {
132 out.beats_per_bar =
static_cast<float>(numeric);
133 out.has_beats_per_bar =
true;
134 }
else if (urids.time_beat_unit != 0 && key == urids.time_beat_unit) {
135 out.beat_unit =
static_cast<int32_t
>(numeric);
136 out.has_beat_unit =
true;
137 }
else if (urids.time_beats_per_minute != 0 && key == urids.time_beats_per_minute) {
138 out.beats_per_minute =
static_cast<float>(numeric);
139 out.has_beats_per_minute =
true;
149 double sample_rate)
noexcept {
150 if (!pos.has_speed || pos.speed == 0.0f)
154 const double frames =
static_cast<double>(n_samples) *
static_cast<double>(pos.speed);
156 pos.frame +=
static_cast<int64_t
>(frames);
158 if (pos.has_beat && pos.has_beats_per_minute && sample_rate > 0.0 &&
159 pos.beats_per_minute > 0.0f) {
160 pos.beat += (frames / sample_rate) * (
static_cast<double>(pos.beats_per_minute) / 60.0);
178 if (pos.has_beats_per_minute) {
179 transport.
tempo_bpm =
static_cast<double>(pos.beats_per_minute);
186 const bool time_sig =
187 pos.has_beats_per_bar && pos.has_beat_unit && pos.beats_per_bar > 0.0f && pos.beat_unit > 0;
194 const double quarter_notes_per_beat = (pos.has_beat_unit && pos.beat_unit > 0)
195 ? 4.0 /
static_cast<double>(pos.beat_unit)
210 const LV2_Descriptor*,
213 const LV2_Feature*
const* features)
223 "lv2: host did not provide LV2_URID__map; refusing to instantiate");
229 inst->sample_rate = sample_rate;
230 inst->urid_map = urid_map;
231 inst->urid_midi_event = urid_map->map(urid_map->handle, LV2_MIDI__MidiEvent);
232 inst->urid_atom_sequence = urid_map->map(urid_map->handle, LV2_ATOM__Sequence);
233 inst->urid_atom_chunk = urid_map->map(urid_map->handle, LV2_ATOM__Chunk);
236 auto& time_urids = inst->time_urids;
237 time_urids.atom_object = urid_map->map(urid_map->handle, LV2_ATOM__Object);
238 time_urids.atom_blank = urid_map->map(urid_map->handle, LV2_ATOM__Blank);
239 time_urids.atom_float = urid_map->map(urid_map->handle, LV2_ATOM__Float);
240 time_urids.atom_double = urid_map->map(urid_map->handle, LV2_ATOM__Double);
241 time_urids.atom_int = urid_map->map(urid_map->handle, LV2_ATOM__Int);
242 time_urids.atom_long = urid_map->map(urid_map->handle, LV2_ATOM__Long);
243 time_urids.atom_bool = urid_map->map(urid_map->handle, LV2_ATOM__Bool);
244 time_urids.time_position = urid_map->map(urid_map->handle, LV2_TIME__Position);
245 time_urids.time_bar = urid_map->map(urid_map->handle, LV2_TIME__bar);
246 time_urids.time_beat = urid_map->map(urid_map->handle, LV2_TIME__beat);
247 time_urids.time_beat_unit = urid_map->map(urid_map->handle, LV2_TIME__beatUnit);
248 time_urids.time_beats_per_bar = urid_map->map(urid_map->handle, LV2_TIME__beatsPerBar);
249 time_urids.time_beats_per_minute = urid_map->map(urid_map->handle, LV2_TIME__beatsPerMinute);
250 time_urids.time_frame = urid_map->map(urid_map->handle, LV2_TIME__frame);
251 time_urids.time_speed = urid_map->map(urid_map->handle, LV2_TIME__speed);
260 urid_map->map(urid_map->handle, LV2_BUF_SIZE__maxBlockLength), time_urids.atom_int,
263 if (!inst->processor) {
267 inst->processor->set_state_store(&inst->store);
277 const auto admission_desc = inst->processor->descriptor();
278 int declared_inputs = 0;
279 for (
const auto& bus : admission_desc.input_buses) {
280 declared_inputs += bus.default_channels;
282 int declared_outputs = 0;
283 for (
const auto& bus : admission_desc.output_buses) {
284 declared_outputs += bus.default_channels;
289 "lv2: descriptor declares {} input and {} output channels; this adapter carries at "
290 "most {} per direction, refusing to instantiate",
297 inst->processor->define_parameters(inst->store);
298 auto params = inst->store.all_params();
299 inst->num_params =
static_cast<int>(params.size());
300 inst->param_ids.reserve(params.size());
301 for (
const auto& p : params) {
302 inst->param_ids.push_back(p.id);
306 inst->control_in_ports =
new float*[inst->num_params]();
307 inst->control_out_ports =
new float*[inst->num_params]();
310 inst->num_audio_inputs = declared_inputs;
311 inst->num_main_audio_inputs = admission_desc.input_buses.empty()
313 : std::max(0, admission_desc.input_buses[0].default_channels);
314 inst->num_audio_outputs = declared_outputs;
315 inst->accepts_midi = admission_desc.accepts_midi;
316 inst->produces_midi = admission_desc.produces_midi;
323 inst->midi_in.set_realtime_capacity_limit(
true);
324 inst->midi_out.set_realtime_capacity_limit(
true);
333 if (inst->num_audio_inputs > 0)
335 if (inst->num_audio_outputs > 0)
337 inst->processor->prepare(ctx);
339 return static_cast<LV2_Handle
>(inst);
342inline void connect_port(LV2_Handle handle, uint32_t port,
void* data) {
354 const int idx = port <= static_cast<uint32_t>(std::numeric_limits<int>::max())
355 ?
static_cast<int>(port)
360 inst->audio_in_ports[layout.
slot_of(idx)] =
static_cast<float*
>(data);
363 inst->audio_out_ports[layout.
slot_of(idx)] =
static_cast<float*
>(data);
366 inst->control_in_ports[layout.
slot_of(idx)] =
static_cast<float*
>(data);
370 inst->midi_in_atom = data;
376 inst->midi_out_atom = data;
381 inst->latency_port =
static_cast<float*
>(data);
400 LV2_Atom_Sequence* out_seq,
401 LV2_URID urid_atom_sequence,
402 LV2_URID urid_midi_event,
404 if (!out_seq || urid_atom_sequence == 0 || urid_midi_event == 0)
return;
405 const uint32_t capacity = out_seq->atom.size;
406 out_seq->atom.type = urid_atom_sequence;
407 out_seq->body.unit = 0;
408 out_seq->body.pad = 0;
409 lv2_atom_sequence_clear(out_seq);
411 for (
const auto& ev : midi_out) {
412 const uint32_t msg_size = ev.size();
413 if (msg_size == 0 || msg_size > 3)
continue;
420 pkt.hdr.time.frames = ev.sample_offset;
421 pkt.hdr.body.type = urid_midi_event;
422 pkt.hdr.body.size = msg_size;
423 std::memcpy(pkt.payload, ev.data(), msg_size);
424 if (!lv2_atom_sequence_append_event(out_seq, capacity, &pkt.hdr)) {
430inline void run(LV2_Handle handle, uint32_t n_samples) {
432 if (!inst->processor)
return;
439 const uint32_t requested_samples = n_samples;
440 n_samples =
static_cast<uint32_t
>(
455 if (inst->latency_port) {
456 const int reported = inst->processor->latency_samples();
457 *inst->latency_port =
static_cast<float>(reported > 0 ? reported : 0);
464 for (
int i = 0; i < inst->num_params; ++i) {
465 if (inst->control_in_ports[i]) {
466 float value = *inst->control_in_ports[i];
467 inst->store.set_value_rt(inst->param_ids[i], value);
478 in_ptrs[i] = inst->audio_in_ports[i];
481 out_ptrs[i] = inst->audio_out_ports[i];
486 if (requested_samples > n_samples) {
490 std::fill(out_ptrs[i] + n_samples, out_ptrs[i] + requested_samples, 0.0f);
498 const int main_inputs = std::min(inst->num_main_audio_inputs, inst->num_audio_inputs);
499 const int sidechain_inputs = inst->num_audio_inputs - main_inputs;
500 const auto bus_connected = [&](
int begin,
int count) {
501 for (
int i = begin; i < begin + count; ++i)
502 if (in_ptrs[i] ==
nullptr)
506 const bool main_connected = bus_connected(0, main_inputs);
507 const bool sidechain_connected = bus_connected(main_inputs, sidechain_inputs);
509 in_ptrs,
static_cast<size_t>(main_connected ? main_inputs : 0), n_samples);
511 in_ptrs + main_inputs,
static_cast<size_t>(sidechain_connected ? sidechain_inputs : 0),
514 static_cast<size_t>(inst->num_audio_outputs), n_samples);
515 std::array<ProcessBusBufferView<const float>, 2> input_buses{{
518 main_inputs == 0, main_connected},
523 true, sidechain_connected},
527 const std::size_t input_bus_count = sidechain_inputs > 0 ? 2 : 1;
528 std::array<ProcessBusBufferView<float>, 1> output_buses{{
531 inst->num_audio_outputs,
false, inst->num_audio_outputs > 0},
543 proc_ctx.
num_samples =
static_cast<int>(n_samples);
549 inst->param_events.clear();
550 inst->processor->set_param_events(&inst->param_events);
563 inst->midi_in.clear();
564 inst->midi_out.clear();
565 bool transport_received =
false;
566 if (inst->midi_in_atom && inst->urid_atom_sequence && inst->urid_midi_event) {
567 const auto* seq =
static_cast<const LV2_Atom_Sequence*
>(inst->midi_in_atom);
568 if (seq->atom.type == inst->urid_atom_sequence) {
569 const auto& time_urids = inst->time_urids;
570 LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
571 if (ev->body.type == inst->urid_midi_event) {
572 const auto* data =
reinterpret_cast<const uint8_t*
>(ev + 1);
573 const uint32_t size = ev->body.size;
574 if (size >= 1 && size <= 3 && (data[0] & 0x80)) {
577 choc::midi::ShortMessage(data[0], size > 1 ? data[1] : uint8_t{0},
578 size > 2 ? data[2] : uint8_t{0});
580 inst->midi_in.add(me);
587 const bool is_object =
588 (time_urids.atom_object != 0 && ev->body.type == time_urids.atom_object) ||
589 (time_urids.atom_blank != 0 && ev->body.type == time_urids.atom_blank);
592 const auto* obj =
reinterpret_cast<const LV2_Atom_Object*
>(&ev->body);
594 inst->has_transport =
true;
595 transport_received =
true;
605 if (inst->has_transport) {
606 if (!transport_received) {
610 inst->playhead_prev);
615 inst->playhead_prev);
618 inst->processor->process(process_buffers, inst->midi_in, inst->midi_out,
623 static_cast<LV2_Atom_Sequence*
>(inst->midi_out_atom),
624 inst->urid_atom_sequence,
625 inst->urid_midi_event,
644inline LV2_State_Status
save_state(LV2_Handle instance, LV2_State_Store_Function store,
645 LV2_State_Handle handle, uint32_t,
const LV2_Feature*
const*) {
647 if (!inst || !inst->processor || !store)
648 return LV2_STATE_ERR_UNKNOWN;
649 if (inst->urid_state_blob == 0 || inst->urid_atom_chunk == 0) {
650 return LV2_STATE_ERR_NO_FEATURE;
657 return LV2_STATE_SUCCESS;
659 return store(handle, inst->urid_state_blob, data.data(), data.size(), inst->urid_atom_chunk,
660 LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
663inline LV2_State_Status
restore_state(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
664 LV2_State_Handle handle, uint32_t,
665 const LV2_Feature*
const*) {
667 if (!inst || !inst->processor || !retrieve)
668 return LV2_STATE_ERR_UNKNOWN;
669 if (inst->urid_state_blob == 0)
670 return LV2_STATE_ERR_NO_FEATURE;
672 std::size_t size = 0;
675 const void* value = retrieve(handle, inst->urid_state_blob, &size, &type, &flags);
678 if (!value || size == 0)
679 return LV2_STATE_SUCCESS;
680 if (inst->urid_atom_chunk != 0 && type != 0 && type != inst->urid_atom_chunk) {
681 return LV2_STATE_ERR_BAD_TYPE;
684 const auto* bytes =
static_cast<const uint8_t*
>(value);
691 return LV2_STATE_ERR_UNKNOWN;
697 inst->processor->on_non_realtime_tick();
698 return LV2_STATE_SUCCESS;
704 if (uri && std::strcmp(uri, LV2_STATE__interface) == 0) {
713 inst->processor->release();
714 delete[] inst->control_in_ports;
715 delete[] inst->control_out_ports;
732 if (bundle_dir ==
nullptr || binary_name ==
nullptr ||
g_factory ==
nullptr ||
g_uri ==
nullptr)
751 processor->set_state_store(&store);
752 processor->define_parameters(store);
753 const auto descriptor = processor->descriptor();
755 const std::string dir(bundle_dir);
756 const std::string binary(binary_name);
757 const auto stem = binary.substr(0, binary.rfind(
'.'));
759 std::ofstream manifest(dir +
"/manifest.ttl", std::ios::binary | std::ios::trunc);
763 if (!manifest.flush())
769 std::ofstream
plugin(dir +
"/" + stem +
".ttl", std::ios::binary | std::ios::trunc);
787#define PULP_LV2_PLUGIN(factory_fn, plugin_uri) \
789 struct PulpLv2Init { \
791 pulp::format::lv2_generic::g_factory = factory_fn; \
792 pulp::format::lv2_generic::g_uri = plugin_uri; \
793 pulp::format::lv2_generic::g_lv2_descriptor.URI = plugin_uri; \
800 const LV2_Descriptor* lv2_descriptor(uint32_t index) { \
801 return (index == 0) ? &pulp::format::lv2_generic::g_lv2_descriptor : nullptr; \
804 int pulp_lv2_write_bundle_ttl(const char* bundle_dir, const char* binary) { \
805 return pulp::format::lv2_generic::write_bundle_ttl(bundle_dir, binary); \
The shared adapter-boundary core.
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
RAII guard that marks the current thread as "must not allocate".
Definition scoped_no_alloc.hpp:27
Definition scoped_flush_denormals.hpp:106
static constexpr std::size_t kCapacity
Definition parameter_event_queue.hpp:84
Centralized parameter storage with lock-free value access.
Definition store.hpp:164
The one max-block-overrun contract shared by every format adapter.
void log_warn(std::format_string< Args... > fmt, Args &&... args)
Definition log.hpp:190
Hardware flush-to-zero RAII guard for the audio callback boundary.
Definition prepare_resources.hpp:102
double sample_rate
Definition prepare_resources.hpp:103
int input_channels
Definition prepare_resources.hpp:105
int max_buffer_size
Definition prepare_resources.hpp:104
int output_channels
Definition prepare_resources.hpp:106
Process context — passed every audio callback with transport state.
Definition process_context.hpp:89
int num_samples
Definition process_context.hpp:91
RenderSpeedHint render_speed_hint
Advisory render-speed category for hosts that know whether the current pass is live,...
Definition process_context.hpp:121
double sample_rate
Definition process_context.hpp:90
ProcessMode process_mode
Runtime mode for this block.
Definition process_context.hpp:96
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32
choc::midi::ShortMessage message
Definition message.hpp:33
int32_t sample_offset
Sample position within the current buffer block.
Definition message.hpp:34