13#include <clap/ext/draft/file-reference.h>
18#define CDEBUG(...) Ase::debug ("clap", __VA_ARGS__)
19#define CDEBUG_ENABLED() Ase::debug_key_enabled ("clap")
20#define PDEBUG(...) Ase::debug ("clapparam", __VA_ARGS__)
21#define PDEBUG_ENABLED() Ase::debug_key_enabled ("clapparam")
22#define CLAPEVENT_ENABLED() Ase::debug_key_enabled ("clapevent")
26ASE_CLASS_DECLS (ClapAudioProcessor);
27ASE_CLASS_DECLS (ClapPluginHandleImpl);
38static const char* anklang_host_name ();
39static String clapid (
const clap_host *host);
40static ClapPluginHandleImpl* handle_ptr (
const clap_host *host);
41static ClapPluginHandleImplP handle_sptr (
const clap_host *host);
42static const clap_plugin* access_clap_plugin (ClapPluginHandle *handle);
43static const void* host_get_extension_mt (
const clap_host *host,
const char *extension_id);
44static void host_request_restart_mt (
const clap_host *host);
45static void host_request_process_mt (
const clap_host *host);
46static void host_request_callback_mt (
const clap_host *host);
47static bool host_unregister_fd (
const clap_host_t *host,
int fd);
48static bool host_unregister_timer (
const clap_host *host, clap_id timer_id);
49static bool event_unions_try_push (ClapEventUnionS &events,
const clap_event_header_t *event);
50static void try_load_x11wrapper ();
51static Gtk2DlWrapEntry *x11wrapper =
nullptr;
56 void *dlhandle_ =
nullptr;
60 const clap_plugin_entry *pluginentry =
nullptr;
73 return dlhandle_ && pluginentry;
78 if (open_count_++ == 0 && !dlhandle_) {
79 const String dlfile_mem = Path::stringread (dlfile);
80 dlhandle_ =
dlopen (dlfile.
c_str(), RTLD_LOCAL | RTLD_NOW);
81 CDEBUG (
"%s: dlopen: %s", dlfile, dlhandle_ ?
"OK" : get_dlerror());
83 pluginentry = symbol<const clap_plugin_entry*> (
"clap_entry");
84 bool initialized =
false;
85 if (pluginentry && clap_version_is_compatible (pluginentry->clap_version))
86 initialized = pluginentry->init (dlfile.
c_str());
88 CDEBUG (
"unusable clap_entry: %s", !pluginentry ?
"NULL" :
89 string_format (
"clap-%u.%u.%u", pluginentry->clap_version.major, pluginentry->clap_version.minor,
90 pluginentry->clap_version.revision));
91 pluginentry =
nullptr;
105 pluginentry->deinit();
106 pluginentry =
nullptr;
109 const bool closingok =
dlclose (dlhandle_) == 0;
110 CDEBUG (
"%s: dlclose: %s", dlfile, closingok ?
"OK" : get_dlerror());
114 template<
typename Ptr> Ptr
115 symbol (
const char *symname)
const
117 void *p = dlhandle_ ?
dlsym (dlhandle_, symname) :
nullptr;
124 return err ? err :
"unknown dlerror";
130 void *cookie_ =
nullptr;
131 double next_value_ = NAN;
134 operator= (
const clap_param_info &cinfo)
140 module = cinfo.module;
141 min_value = cinfo.min_value;
142 max_value = cinfo.max_value;
143 default_value = cinfo.default_value;
144 cookie_ = cinfo.cookie;
151ClapParamInfo::ClapParamInfo()
157ClapParamInfo::unset()
159 param_id = CLAP_INVALID_ID;
169 default_value_text =
"";
170 current_value_text =
"";
174ClapParamInfo::hints_from_param_info_flags (clap_param_info_flags flags)
176 static constexpr const struct {
uint32_t bit;
const char *
const hint; } bits[] = {
177 { CLAP_PARAM_IS_STEPPED,
"stepped" },
178 { CLAP_PARAM_IS_PERIODIC,
"periodic" },
179 { CLAP_PARAM_IS_HIDDEN,
"hidden" },
180 { CLAP_PARAM_IS_READONLY,
"readonly" },
181 { CLAP_PARAM_IS_BYPASS,
"bypass" },
182 { CLAP_PARAM_IS_AUTOMATABLE,
"automatable" },
183 { CLAP_PARAM_IS_AUTOMATABLE_PER_NOTE_ID,
"automatable-note-id" },
184 { CLAP_PARAM_IS_AUTOMATABLE_PER_KEY,
"automatable-key" },
185 { CLAP_PARAM_IS_AUTOMATABLE_PER_CHANNEL,
"automatable-channel" },
186 { CLAP_PARAM_IS_AUTOMATABLE_PER_PORT,
"automatable-port" },
187 { CLAP_PARAM_IS_MODULATABLE,
"modulatable" },
188 { CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID,
"modulatable-note-id" },
189 { CLAP_PARAM_IS_MODULATABLE_PER_KEY,
"modulatable-key" },
190 { CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL,
"modulatable-channel" },
191 { CLAP_PARAM_IS_MODULATABLE_PER_PORT,
"modulatable-port" },
192 { CLAP_PARAM_REQUIRES_PROCESS,
"requires-process" },
196 if (flags & bits[i].bit)
197 hints += bits[i].hint +
String (
":");
198 if (!(flags & CLAP_PARAM_IS_HIDDEN))
200 if (flags & CLAP_PARAM_IS_READONLY)
209 clap_event_header_t header;
210 clap_event_note_t note;
211 clap_event_note_expression_t expression;
212 clap_event_param_value_t value;
213 clap_event_param_mod_t mod;
214 clap_event_param_gesture_t gesture;
215 clap_event_midi_t midi1;
216 clap_event_midi_sysex_t sysex;
217 clap_event_midi2_t midi2;
223 const clap_plugin *clapplugin_ =
nullptr;
226 uint imain_clapidx = ~0, omain_clapidx = ~0, iside_clapidx = ~0, oside_clapidx = ~0;
227 clap_note_dialect input_event_dialect = clap_note_dialect (0);
228 clap_note_dialect input_preferred_dialect = clap_note_dialect (0);
229 clap_note_dialect output_event_dialect = clap_note_dialect (0);
230 clap_note_dialect output_preferred_dialect = clap_note_dialect (0);
231 bool can_process_ =
false;
236 info.
label =
"Anklang.Devices.ClapAudioProcessor";
243 while (enqueued_events_.size()) {
245 enqueued_events_.pop_back();
253 handle_ = &*ClapDeviceImpl::access_clap_handle (
get_device());
255 clapplugin_ = access_clap_plugin (handle_);
259 const auto &audio_iport_infos = handle_->audio_iport_infos;
260 for (
size_t i = 0; i < audio_iport_infos.size(); i++)
261 if (audio_iport_infos[i].port_type &&
strcmp (audio_iport_infos[i].port_type, CLAP_PORT_STEREO) == 0 && audio_iport_infos[i].channel_count == 2) {
262 if (audio_iport_infos[i].flags & CLAP_AUDIO_PORT_IS_MAIN && imain_clapidx == ~0)
264 else if (!(audio_iport_infos[i].flags & CLAP_AUDIO_PORT_IS_MAIN) && iside_clapidx == ~0)
268 const clap_audio_port_info_t *main_oport =
nullptr, *side_oport =
nullptr;
269 const auto &audio_oport_infos = handle_->audio_oport_infos;
270 for (
size_t i = 0; i < audio_oport_infos.size(); i++)
271 if (audio_oport_infos[i].port_type &&
strcmp (audio_oport_infos[i].port_type, CLAP_PORT_STEREO) == 0 && audio_oport_infos[i].channel_count == 2) {
272 if (audio_oport_infos[i].flags & CLAP_AUDIO_PORT_IS_MAIN && !main_oport)
274 else if (!(audio_oport_infos[i].flags & CLAP_AUDIO_PORT_IS_MAIN) && !side_oport)
278 input_event_dialect = clap_note_dialect (handle_->note_iport_infos.
size() ? handle_->note_iport_infos[0].supported_dialects : 0);
279 input_preferred_dialect = clap_note_dialect (handle_->note_iport_infos.
size() ? handle_->note_iport_infos[0].preferred_dialect : 0);
280 output_event_dialect = clap_note_dialect (handle_->note_oport_infos.
size() ? handle_->note_oport_infos[0].supported_dialects : 0);
281 output_preferred_dialect = clap_note_dialect (handle_->note_oport_infos.
size() ? handle_->note_oport_infos[0].preferred_dialect : 0);
284 if (imain_clapidx < audio_iport_infos.size())
285 ibusid =
add_input_bus (audio_iport_infos[imain_clapidx].name, SpeakerArrangement::STEREO);
286 if (omain_clapidx < audio_oport_infos.size())
287 obusid =
add_output_bus (audio_oport_infos[omain_clapidx].name, SpeakerArrangement::STEREO);
289 if (input_event_dialect & (CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI)) {
291 input_events_.reserve (256);
293 if (output_event_dialect & (CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI)) {
295 output_events_.reserve (256);
297 enqueued_events_.reserve (8);
306 void convert_clap_events (
const clap_process_t &process,
bool as_clapnotes);
310 input_events_size (
const clap_input_events *evlist)
313 size_t param_events_size = 0;
314 for (
const auto &pevents_b : self->enqueued_events_)
315 param_events_size += pevents_b->size();
316 return param_events_size + self->input_events_.size();
318 static const clap_event_header_t*
319 input_events_get (
const clap_input_events *evlist, uint32_t index)
321 ClapAudioProcessor *self = (ClapAudioProcessor*) evlist->ctx;
322 for (
const auto &pevents_b : self->enqueued_events_) {
323 if (index < pevents_b->size())
324 return &(*pevents_b)[index].header;
325 index -= pevents_b->size();
327 return index < self->input_events_.size() ? &self->input_events_[index].header :
nullptr;
330 output_events_try_push (
const clap_output_events *evlist,
const clap_event_header_t *event)
332 ClapAudioProcessor *self = (ClapAudioProcessor*) evlist->ctx;
333 return event_unions_try_push (self->output_events_, event);
335 const clap_input_events_t plugin_input_events = {
336 .ctx = (ClapAudioProcessor*)
this,
337 .size = input_events_size,
338 .get = input_events_get,
340 const clap_output_events_t plugin_output_events = {
341 .ctx = (ClapAudioProcessor*)
this,
342 .try_push = output_events_try_push,
344 const ClapParamInfoMap *param_info_map_ =
nullptr;
345 const ClapParamInfoImpl *param_info_map_start_ =
nullptr;
346 clap_process_t processinfo = { 0, };
347 clap_event_transport_t transportinfo = { { 0, }, };
350 enqueue_events (ClapEventParamS *pevents)
353 if (pevents && pevents->size() && pevents->back().header.time == 0)
354 for (
size_t i = 0; i < enqueued_events_.size(); i++)
355 if (enqueued_events_[i]->
size() && enqueued_events_[i]->back().header.time > 0) {
356 enqueued_events_.insert (enqueued_events_.begin() + i, pevents);
360 enqueued_events_.push_back (pevents);
363 start_processing (
const ClapParamInfoMap *param_info_map,
const ClapParamInfoImpl *map_start,
size_t map_size)
367 param_info_map_ = param_info_map;
368 param_info_map_start_ = map_start;
370 can_process_ = clapplugin_->start_processing (clapplugin_);
371 CDEBUG (
"%s: %s: %d", handle_->clapid(), __func__, can_process_);
373 processinfo = clap_process_t {
375 .frames_count = 0, .transport = &transportinfo,
376 .audio_inputs = &handle_->audio_inputs_[0], .audio_outputs = &handle_->audio_outputs_[0],
377 .audio_inputs_count =
uint32_t (handle_->audio_inputs_.
size()),
378 .audio_outputs_count =
uint32_t (handle_->audio_outputs_.
size()),
379 .in_events = &plugin_input_events, .out_events = &plugin_output_events,
381 transportinfo = clap_event_transport_t {
382 .header = clap_event_header_t {
383 .size =
sizeof (clap_event_transport_t),
384 .space_id = CLAP_CORE_EVENT_SPACE_ID,
385 .type = CLAP_EVENT_TRANSPORT
388 input_events_.resize (0);
389 output_events_.resize (0);
397 can_process_ =
false;
398 clapplugin_->stop_processing (clapplugin_);
399 param_info_map_ =
nullptr;
400 param_info_map_start_ =
nullptr;
401 CDEBUG (
"%s: %s", handle_->clapid(), __func__);
402 input_events_.resize (0);
403 output_events_.resize (0);
406 update_transportinfo()
409 const auto &tick_sig = trans.tick_sig;
410 transportinfo.flags = CLAP_TRANSPORT_HAS_TEMPO |
411 CLAP_TRANSPORT_HAS_BEATS_TIMELINE |
412 CLAP_TRANSPORT_HAS_SECONDS_TIMELINE |
413 CLAP_TRANSPORT_HAS_TIME_SIGNATURE;
415 transportinfo.flags |= CLAP_TRANSPORT_IS_PLAYING;
416 double sec_pos = trans.current_seconds + trans.current_minutes * 60;
418 transportinfo.song_pos_beats =
llrint (beat_pos * CLAP_BEATTIME_FACTOR);
419 transportinfo.song_pos_seconds =
llrint (sec_pos * CLAP_SECTIME_FACTOR);
420 transportinfo.tempo = tick_sig.bpm();
421 transportinfo.tempo_inc = 0;
422 transportinfo.loop_start_beats = 0;
423 transportinfo.loop_end_beats = 0;
424 transportinfo.loop_start_seconds = 0;
425 transportinfo.loop_end_seconds = 0;
426 double bar_start = trans.current_bar_tick * (1.0 /
TRANSPORT_PPQN);
427 transportinfo.bar_start =
llrint (bar_start * CLAP_BEATTIME_FACTOR);
428 transportinfo.bar_number = trans.current_bar;
429 transportinfo.tsig_num = tick_sig.beats_per_bar();
430 transportinfo.tsig_denom = tick_sig.beat_unit();
437 update_transportinfo();
438 for (
size_t i = 0; i < icount; i++) {
439 assert_return (processinfo.audio_inputs[imain_clapidx].channel_count == icount);
440 processinfo.audio_inputs[imain_clapidx].data32[i] =
const_cast<float*
> (
ifloats (ibusid, i));
443 for (
size_t i = 0; i < ocount; i++) {
444 assert_return (processinfo.audio_outputs[omain_clapidx].channel_count == ocount);
445 processinfo.audio_outputs[omain_clapidx].data32[i] =
oblock (obusid, i);
447 processinfo.frames_count = n_frames;
448 convert_clap_events (processinfo, input_preferred_dialect & CLAP_NOTE_DIALECT_CLAP);
449 processinfo.steady_time += processinfo.frames_count;
450 const clap_process_status status = clapplugin_->process (clapplugin_, &processinfo);
452 bool need_wakeup = dequeue_events (n_frames);
453 for (
const auto &e : output_events_)
454 need_wakeup |= apply_param_value_event (e.value);
455 output_events_.resize (0);
459 CDEBUG (
"render: status=%d", status);
463 apply_param_value_event (
const clap_event_param_value &e)
465 bool need_wakeup =
false;
466 if (e.header.type == CLAP_EVENT_PARAM_VALUE && e.header.size >= sizeof (clap_event_param_value))
468 const clap_event_param_value &
event = e;
469 const auto it = param_info_map_->find (event.param_id);
470 if (it != param_info_map_->end())
473 pinfo->next_value_ =
event.value;
476 PDEBUG (
"%s: PROCESS: %08x=%f: (%s)\n", clapplugin_->desc->name, pinfo->param_id, event.value, pinfo->name);
482 dequeue_events (
size_t nframes)
486 bool need_wakeup =
false;
487 while (enqueued_events_.size() && (enqueued_events_[0]->empty() || enqueued_events_[0]->back().header.time < nframes)) {
488 ClapEventParamS *
const pevents = enqueued_events_[0];
489 enqueued_events_.erase (enqueued_events_.begin());
490 for (
const auto &e : *pevents)
491 need_wakeup |= apply_param_value_event (e);
492 main_rt_jobs += RtCall (call_delete<ClapEventParamS>, pevents);
497static CString clap_audio_wrapper_aseid = register_audio_processor<ClapAudioProcessor>();
499static inline clap_event_midi*
500setup_midi1 (ClapEventUnion *evunion, uint32_t time, uint16_t port_index)
502 clap_event_midi *midi1 = &evunion->midi1;
503 midi1->header.size =
sizeof (*midi1);
504 midi1->header.time =
time;
505 midi1->header.space_id = CLAP_CORE_EVENT_SPACE_ID;
506 midi1->header.type = CLAP_EVENT_MIDI;
507 midi1->header.flags = 0;
508 midi1->port_index = port_index;
512static inline clap_event_note*
513setup_evnote (ClapEventUnion *evunion, uint32_t time, uint16_t port_index)
515 clap_event_note *evnote = &evunion->note;
516 evnote->header.size =
sizeof (*evnote);
517 evnote->header.type = 0;
518 evnote->header.time =
time;
519 evnote->header.space_id = CLAP_CORE_EVENT_SPACE_ID;
520 evnote->header.flags = 0;
521 evnote->port_index = port_index;
525static inline clap_event_note_expression*
526setup_expression (ClapEventUnion *evunion, uint32_t time, uint16_t port_index)
528 clap_event_note_expression *expr = &evunion->expression;
529 expr->header.size =
sizeof (*expr);
530 expr->header.type = CLAP_EVENT_NOTE_EXPRESSION;
531 expr->header.time =
time;
532 expr->header.space_id = CLAP_CORE_EVENT_SPACE_ID;
533 expr->header.flags = 0;
534 expr->port_index = port_index;
539ClapAudioProcessor::convert_clap_events (
const clap_process_t &process,
const bool as_clapnotes)
542 if (input_events_.capacity() < evinput.events_pending())
543 input_events_.reserve (evinput.events_pending() + 128);
544 input_events_.resize (evinput.events_pending());
546 for (
const auto &ev : evinput)
547 switch (ev.message())
549 clap_event_note_expression *expr;
550 clap_event_note_t *evnote;
551 clap_event_midi_t *midi1;
553 case MidiMessage::NOTE_ON:
554 case MidiMessage::NOTE_OFF:
555 case MidiMessage::AFTERTOUCH:
556 if (as_clapnotes && ev.type == MidiEvent::AFTERTOUCH) {
557 expr = setup_expression (&input_events_[j++], ev.frame, 0);
558 expr->expression_id = CLAP_NOTE_EXPRESSION_PRESSURE;
559 expr->note_id = ev.noteid;
560 expr->channel = ev.channel;
562 expr->value = ev.velocity;
563 }
else if (as_clapnotes) {
564 evnote = setup_evnote (&input_events_[j++], ev.frame, 0);
565 evnote->header.type = ev.type == MidiEvent::NOTE_ON ? CLAP_EVENT_NOTE_ON : CLAP_EVENT_NOTE_OFF;
566 evnote->note_id = ev.noteid;
567 evnote->channel = ev.channel;
568 evnote->key = ev.key;
569 evnote->velocity = ev.velocity;
571 midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0);
572 midi1->data[0] =
uint8_t (ev.type) | (ev.channel & 0xf);
573 midi1->data[1] = ev.key;
574 midi1->data[2] =
std::min (uint8_t (ev.velocity * 127),
uint8_t (127));
577 case MidiMessage::ALL_NOTES_OFF:
579 evnote = setup_evnote (&input_events_[j++], ev.frame, 0);
580 evnote->header.type = CLAP_EVENT_NOTE_CHOKE;
581 evnote->note_id = -1;
582 evnote->channel = -1;
584 evnote->velocity = 0;
586 midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0);
587 midi1->data[0] = 0xB0 | (ev.channel & 0xf);
588 midi1->data[1] = 123;
592 case MidiMessage::CONTROL_CHANGE:
593 midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0);
594 midi1->data[0] = 0xB0 | (ev.channel & 0xf);
595 midi1->data[1] = ev.param;
596 midi1->data[2] = ev.cval;
598 case MidiMessage::CHANNEL_PRESSURE:
599 midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0);
600 midi1->data[0] = 0xD0 | (ev.channel & 0xf);
601 midi1->data[1] =
std::min (uint8_t (ev.velocity * 127),
uint8_t (127));
604 case MidiMessage::PITCH_BEND:
605 midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0);
606 midi1->data[0] = 0xE0 | (ev.channel & 0xf);
607 midi1->data[1] =
std::min (uint8_t (ev.velocity * 127),
uint8_t (127));
609 i16 = ev.value < 0 ? ev.value * 8192.0 : ev.value * 8191.0;
611 midi1->data[1] = i16 & 127;
612 midi1->data[2] = (i16 >> 7) & 127;
616 input_events_.resize (j);
619 static bool evdebug = CLAPEVENT_ENABLED();
621 for (
const auto &ev : input_events_) {
622 if (ev.header.type == CLAP_EVENT_MIDI)
623 printerr (
"%+4d ch=%-2u %-14s %02X %02X %02X sz=%d spc=%d flags=%x port=%d\n",
624 ev.midi1.header.time, ev.midi1.data[0] & 0xf,
"MIDI1",
625 ev.midi1.data[0], ev.midi1.data[1], ev.midi1.data[2],
626 ev.midi1.header.size, ev.midi1.header.space_id, ev.midi1.header.flags, ev.midi1.port_index);
628 printerr (
"%s\n", clap_event_to_string (&ev.note));
636 static String clapid (
const clap_host *host) {
return Ase::clapid (host); }
637 String clapid ()
const {
return ClapPluginHandle::clapid(); }
638 clap_host_t phost = {
639 .clap_version = CLAP_VERSION,
641 .name = anklang_host_name(), .vendor =
"anklang.testbit.eu",
642 .url =
"https://anklang.testbit.eu/", .version =
ase_version(),
643 .get_extension = [] (
const clap_host *host,
const char *extension_id) {
644 const void *ext = host_get_extension_mt (host, extension_id);
645 CDEBUG (
"%s: host_get_extension_mt(\"%s\"): %p", clapid (host), extension_id, ext);
648 .request_restart = host_request_restart_mt,
649 .request_process = host_request_process_mt,
650 .request_callback = host_request_callback_mt,
652 ClapAudioProcessorP proc_;
653 const clap_plugin_t *plugin_ =
nullptr;
654 const clap_plugin_gui *plugin_gui =
nullptr;
655 const clap_plugin_state *plugin_state =
nullptr;
656 const clap_plugin_file_reference *plugin_file_reference =
nullptr;
657 const clap_plugin_params *plugin_params =
nullptr;
658 const clap_plugin_timer_support *plugin_timer_support =
nullptr;
659 const clap_plugin_audio_ports_config *plugin_audio_ports_config =
nullptr;
660 const clap_plugin_audio_ports *plugin_audio_ports =
nullptr;
661 const clap_plugin_note_ports *plugin_note_ports =
nullptr;
662 const clap_plugin_posix_fd_support *plugin_posix_fd_support =
nullptr;
664 ClapPluginHandle (descriptor_), proc_ (shared_ptr_cast<ClapAudioProcessor> (aproc))
667 const clap_plugin_entry *pluginentry = descriptor.entry();
670 const auto *factory = (
const clap_plugin_factory*) pluginentry->get_factory (CLAP_PLUGIN_FACTORY_ID);
672 plugin_ = factory->create_plugin (factory, &phost, clapid().
c_str());
684 if (!plugin_->init (plugin_)) {
685 CDEBUG (
"%s: initialization failed", clapid());
689 CDEBUG (
"%s: initialized", clapid());
690 auto plugin_get_extension = [
this] (
const char *extname) {
691 const void *ext = plugin_->get_extension (plugin_, extname);
692 CDEBUG (
"%s: plugin_get_extension(\"%s\"): %p", clapid(), extname, ext);
695 plugin_gui = (
const clap_plugin_gui*) plugin_get_extension (CLAP_EXT_GUI);
696 plugin_params = (
const clap_plugin_params*) plugin_get_extension (CLAP_EXT_PARAMS);
697 plugin_timer_support = (
const clap_plugin_timer_support*) plugin_get_extension (CLAP_EXT_TIMER_SUPPORT);
698 plugin_audio_ports_config = (
const clap_plugin_audio_ports_config*) plugin_get_extension (CLAP_EXT_AUDIO_PORTS_CONFIG);
699 plugin_audio_ports = (
const clap_plugin_audio_ports*) plugin_get_extension (CLAP_EXT_AUDIO_PORTS);
700 plugin_note_ports = (
const clap_plugin_note_ports*) plugin_get_extension (CLAP_EXT_NOTE_PORTS);
701 plugin_posix_fd_support = (
const clap_plugin_posix_fd_support*) plugin_get_extension (CLAP_EXT_POSIX_FD_SUPPORT);
702 plugin_state = (
const clap_plugin_state*) plugin_get_extension (CLAP_EXT_STATE);
703 plugin_file_reference = (
const clap_plugin_file_reference*) plugin_get_extension (CLAP_EXT_FILE_REFERENCE);
704 const clap_plugin_render *plugin_render =
nullptr;
705 plugin_render = (
const clap_plugin_render*) plugin_get_extension (CLAP_EXT_RENDER);
706 (void) plugin_render;
707 const clap_plugin_latency *plugin_latency =
nullptr;
708 plugin_latency = (
const clap_plugin_latency*) plugin_get_extension (CLAP_EXT_LATENCY);
709 (void) plugin_latency;
710 const clap_plugin_tail *plugin_tail =
nullptr;
711 plugin_tail = (
const clap_plugin_tail*) plugin_get_extension (CLAP_EXT_TAIL);
716 bool plugin_activated =
false;
717 bool plugin_processing =
false;
718 bool gui_visible_ =
false;
719 bool gui_canresize =
false;
720 ulong gui_windowid = 0;
727 void get_port_infos ();
728 String get_param_value_text (clap_id param_id,
double value);
729 double get_param_value_double (clap_id param_id,
const String &text);
732 void params_changed()
override;
736 find_param_info (clap_id clapid)
738 auto it = param_ids_.
find (clapid);
739 return it != param_ids_.
end() ? it->second :
nullptr;
742 param_infos ()
override
745 for (
const auto &pinfo : param_infos_)
746 infos.push_back (pinfo);
750 param_set_property (clap_id param_id, PropertyP prop)
override
752 ClapParamInfoImpl *info = find_param_info (param_id);
754 info->aseprop_ = prop;
758 param_get_property (clap_id param_id)
override
760 ClapParamInfoImpl *info = find_param_info (param_id);
762 PropertyP prop = info->aseprop_.lock();
766 param_get_value (clap_id param_id,
String *text)
override
768 ClapParamInfoImpl *info = find_param_info (param_id);
769 const double v = info ? info->current_value : NAN;
771 *text = !info ?
"NAN" : get_param_value_text (param_id, v);
775 param_set_value (clap_id param_id,
const String &stringvalue)
override
777 ClapParamInfoImpl *info = find_param_info (param_id);
778 const double value = info ? get_param_value_double (param_id, stringvalue) : NAN;
779 return isnan (value) ? false : param_set_value (param_id, value);
782 param_set_value (clap_id param_id,
double v)
override
784 ClapParamInfoImpl *info = find_param_info (param_id);
786 return_unless (!(info->flags & CLAP_PARAM_IS_READONLY),
false);
787 ClapParamUpdateS updates;
788 if (info->flags & CLAP_PARAM_IS_STEPPED)
790 ClapParamUpdate update = {
792 .param_id = param_id,
793 .value =
CLAMP (v, info->min_value, info->max_value),
795 updates.push_back (update);
796 enqueue_updates (updates);
800 load_state (WritNode &xs)
override
806 ClapParamIdValueS params;
807 xs[
"param_values"] & params;
809 loader_updates_ =
new ClapParamUpdateS;
810 for (
const auto &[
id, value] : params)
811 loader_updates_->push_back ({
812 .steady_time = 0, .param_id = id, .value = value,
820 xs[
"state_blob"] & blobname;
821 StreamReaderP blob = blobname.
empty() ? nullptr :
_project()->load_blob (blobname);
822 const clap_istream istream = {
824 .read = [] (
const clap_istream *stream,
void *buffer,
uint64_t size) -> int64_t {
825 StreamReader *sr = (StreamReader*) stream->ctx;
826 return sr->read (buffer, size);
830 bool ok = !blob ? false : plugin_state->load (plugin_, &istream);
831 ok &= !blob ? false : blob->close();
832 if (!ok && blobname.size())
833 printerr (
"%s: blob read error: %s\n", clapid(), strerror (errno ? errno :
EIO));
836 ClapResourceHashS loader_hashes;
837 xs[
"resource_hashes"] & loader_hashes;
838 resolve_file_references (loader_hashes);
841 save_state (WritNode &xs,
const String &device_path)
override
844 bool need_save_resources =
false;
845 if (plugin_file_reference && plugin_file_reference->save_resources)
846 need_save_resources = !plugin_file_reference->save_resources (plugin_);
850 ClapParamIdValueS params;
851 for (
const auto &pinfo : param_infos_)
852 if (pinfo.param_id != CLAP_INVALID_ID)
853 params.push_back ({ pinfo.param_id, pinfo.current_value });
854 xs[
"param_values"] & params;
861 printerr (
"SAVE: blobname: %s\n", blobname);
862 const String blobfile =
_project()->writer_file_name (blobname) +
".zst";
863 StreamWriterP swp = stream_writer_zstd (stream_writer_create_file (blobfile));
864 const clap_ostream ostream = {
866 .write = [] (
const clap_ostream *stream,
const void *buffer,
uint64_t size) -> int64_t {
867 StreamWriter *sw = (StreamWriter*) stream->ctx;
868 return sw->write (buffer, size);
872 bool ok = plugin_state->save (plugin_, &ostream);
875 printerr (
"%s: %s: write error: %s\n", clapid(), blobfile, strerror (errno ? errno :
EIO));
885 xs[
"state_blob"] & blobname;
889 if (plugin_file_reference && plugin_file_reference->count && plugin_file_reference->get)
891 ClapResourceHashS hashes;
892 if (need_save_resources)
893 plugin_file_reference->save_resources (plugin_);
894 const size_t n_files = plugin_file_reference->count (plugin_);
895 for (
size_t i = 0; i < n_files; i++)
897 char buffer[ASE_PATH_MAX + 2] = { 0, };
898 const size_t path_capacity =
sizeof (buffer) - 1;
899 clap_file_reference pfile = {
900 .resource_id = CLAP_INVALID_ID,
901 .belongs_to_plugin_collection =
false,
902 .path_capacity = path_capacity,
903 .path_size = 0, .path = buffer,
905 if (!plugin_file_reference->get (plugin_, i, &pfile) || pfile.path_size < 1 ||
906 !pfile.path || pfile.path_size >= path_capacity)
908 pfile.path[pfile.path_size] = 0;
911 ClapResourceHash rhash = { pfile.resource_id,
"" };
914 hashes.push_back (rhash);
916 ASE_SERVER.user_note (
string_format (
"## Note Missing File\n%s: \\\nFailed to store: `%s` \\\n%s",
919 xs[
"resource_hashes"] & hashes;
923 clap_activated()
const override
925 return plugin_activated;
928 enqueue_updates (
const ClapParamUpdateS &updates)
930 ClapPluginHandleImplP selfp = shared_ptr_cast<ClapPluginHandleImpl> (
this);
932 ClapEventParamS *pevents = convert_param_updates (updates);
933 proc_->engine().async_jobs += [selfp, pevents] () {
934 selfp->proc_->enqueue_events (pevents);
939 clap_activate()
override
941 return_unless (plugin_ && !clap_activated(), clap_activated());
947 if (plugin_params && loader_updates_) {
948 ClapEventParamS *pevents = convert_param_updates (*loader_updates_);
949 ClapEventUnionS output_events;
950 flush_event_params (*pevents, output_events);
951 output_events.clear();
955 if (loader_updates_) {
956 delete loader_updates_;
957 loader_updates_ =
nullptr;
960 plugin_activated = plugin_->activate (plugin_, proc_->engine().sample_rate(), 32, 4096);
961 CDEBUG (
"%s: %s: %d", clapid(), __func__, plugin_activated);
962 if (plugin_activated) {
963 ClapPluginHandleImplP selfp = shared_ptr_cast<ClapPluginHandleImpl> (
this);
966 proc_->engine().async_jobs += [&sem, selfp] () {
967 selfp->proc_->start_processing (&selfp->param_ids_, &selfp->param_infos_[0], selfp->param_infos_.size());
973 return clap_activated();
976 clap_deactivate()
override
980 ClapPluginHandleImplP selfp = shared_ptr_cast<ClapPluginHandleImpl> (
this);
982 proc_->engine().async_jobs += [&sem, selfp] () {
983 selfp->proc_->stop_processing();
989 plugin_activated =
false;
990 plugin_->deactivate (plugin_);
991 CDEBUG (
"%s: plugin->deactivated", clapid());
993 void show_gui ()
override;
994 void hide_gui ()
override;
995 void destroy_gui ()
override;
996 bool gui_visible ()
override;
997 bool supports_gui ()
override;
1003 if (clap_activated())
1005 CDEBUG (
"%s: destroying", clapid());
1008 param_infos_.clear();
1009 while (fd_polls_.size())
1010 host_unregister_fd (&phost, fd_polls_.back().fd);
1011 while (timers_.size())
1012 host_unregister_timer (&phost, timers_.back());
1014 plugin_->destroy (plugin_);
1016 plugin_gui =
nullptr;
1017 plugin_state =
nullptr;
1018 plugin_file_reference =
nullptr;
1019 plugin_params =
nullptr;
1020 plugin_timer_support =
nullptr;
1021 plugin_audio_ports_config =
nullptr;
1022 plugin_audio_ports =
nullptr;
1023 plugin_note_ports =
nullptr;
1026 audio_processor ()
override
1034ClapPluginHandleImpl::get_param_value_text (clap_id param_id,
double value)
1036 constexpr uint LEN = 256;
1037 char buffer[LEN + 1] = { 0 };
1038 if (!plugin_params || !plugin_params->value_to_text (plugin_, param_id, value, buffer, LEN))
1045ClapPluginHandleImpl::get_param_value_double (clap_id param_id,
const String &text)
1048 if (!plugin_params || !plugin_params->text_to_value ||
1049 !plugin_params->text_to_value (plugin_, param_id, text.c_str(), &value))
1056ClapPluginHandleImpl::scan_params()
1059 param_infos_.clear();
1061 param_infos_.resize (count);
1062 param_ids_.
reserve (param_infos_.size());
1063 for (
size_t i = 0; i < param_infos_.size(); i++) {
1064 ClapParamInfoImpl &pinfo = param_infos_[i];
1066 clap_param_info cinfo = { CLAP_INVALID_ID, 0,
nullptr, {0}, {0}, NAN, NAN, NAN };
1067 if (plugin_params->get_info (plugin_, i, &cinfo) && cinfo.id != CLAP_INVALID_ID) {
1069 param_ids_[cinfo.id] = ¶m_infos_[i];
1072 for (
size_t i = 0; i < param_infos_.size(); i++) {
1073 ClapParamInfoImpl &pinfo = param_infos_[i];
1074 if (pinfo.param_id == CLAP_INVALID_ID)
1076 pinfo.min_value_text = get_param_value_text (pinfo.param_id, pinfo.min_value);
1077 pinfo.max_value_text = get_param_value_text (pinfo.param_id, pinfo.max_value);
1078 pinfo.default_value_text = get_param_value_text (pinfo.param_id, pinfo.default_value);
1079 if (!plugin_params->get_value (plugin_, pinfo.param_id, &pinfo.current_value)) {
1080 pinfo.current_value = pinfo.default_value;
1081 pinfo.current_value_text = pinfo.default_value_text;
1083 pinfo.current_value_text = get_param_value_text (pinfo.param_id, pinfo.current_value);
1084 PDEBUG (
"%s: SCAN: %08x=%f: %s (%s)\n", clapid(), pinfo.param_id, pinfo.current_value, pinfo.current_value_text, pinfo.name);
1090ClapPluginHandleImpl::params_changed ()
1092 for (AtomicBits::Iter it = proc_->atomic_bits_iter(); it.valid(); it.big_inc1())
1095 ClapParamInfoImpl &pinfo = param_infos_[it.position()];
1096 if (
ASE_ISLIKELY (isnan (pinfo.next_value_)) || pinfo.param_id == CLAP_INVALID_ID)
1098 pinfo.current_value = pinfo.next_value_;
1099 pinfo.next_value_ = NAN;
1100 pinfo.current_value_text = get_param_value_text (pinfo.param_id, pinfo.current_value);
1101 PDEBUG (
"%s: UPDATE: %08x=%f: %s (%s)\n", clapid(), pinfo.param_id, pinfo.current_value, pinfo.current_value_text, pinfo.name);
1102 PropertyP prop = pinfo.aseprop_.lock();
1104 dynamic_cast<EmittableImpl*
> (prop.get())->
emit_notify (pinfo.ident);
1110ClapPluginHandleImpl::convert_param_updates (
const ClapParamUpdateS &updates)
1112 ClapEventParamS *param_events =
new ClapEventParamS();
1113 for (
size_t i = 0; i < updates.size(); i++)
1115 const ClapParamInfoImpl *pinfo = find_param_info (updates[i].param_id);
1118 const clap_event_param_value
event = {
1120 .size =
sizeof (clap_event_param_value),
1122 .space_id = CLAP_CORE_EVENT_SPACE_ID,
1123 .type = CLAP_EVENT_PARAM_VALUE,
1124 .flags = CLAP_EVENT_DONT_RECORD,
1126 .param_id = pinfo->param_id,
1127 .cookie = pinfo->cookie_,
1132 .value = updates[i].value
1134 param_events->push_back (event);
1135 PDEBUG (
"%s: CONVERT: %08x=%f: (%s)\n", clapid(), pinfo->param_id, event.value, pinfo->name);
1137 return param_events;
1142ClapPluginHandleImpl::flush_event_params (
const ClapEventParamS &inevents, ClapEventUnionS &outevents)
1144 const clap_output_events output_events = {
1146 .try_push = [] (
const clap_output_events *list,
const clap_event_header_t *event) {
1147 ClapEventUnionS *outevents = (ClapEventUnionS*) list->ctx;
1148 return event_unions_try_push (*outevents, event);
1151 const clap_input_events input_events = {
1152 .ctx =
const_cast<ClapEventParamS*
> (&inevents),
1153 .size = [] (
const struct clap_input_events *list) {
1154 const ClapEventParamS *inevents = (
const ClapEventParamS*) list->ctx;
1155 return uint32_t (inevents->size());
1157 .get = [] (
const clap_input_events *list,
uint32_t index) ->
const clap_event_header_t* {
1158 const ClapEventParamS *inevents = (
const ClapEventParamS*) list->ctx;
1159 return index < inevents->size() ? &(*inevents)[index].header :
nullptr;
1162 if (PDEBUG_ENABLED())
1163 for (
const auto &p : inevents)
1164 PDEBUG (
"%s: FLUSH: %08x=%f\n", clapid(), p.param_id, p.value);
1165 plugin_params->flush (plugin_, &input_events, &output_events);
1170ClapPluginHandleImpl::get_port_infos()
1173 uint total_channels = 0;
1175 audio_ports_configs_.
resize (!plugin_audio_ports_config ? 0 : plugin_audio_ports_config->
count (plugin_));
1176 for (
size_t i = 0; i < audio_ports_configs_.
size(); i++)
1177 if (!plugin_audio_ports_config->get (plugin_, i, &audio_ports_configs_[i]))
1178 audio_ports_configs_[i] = { CLAP_INVALID_ID, { 0, }, 0, 0, 0, 0,
"", 0, 0,
"" };
1179 if (audio_ports_configs_.
size()) {
1181 for (
size_t i = 0; i < audio_ports_configs_.
size(); i++)
1182 if (audio_ports_configs_[i].
id != CLAP_INVALID_ID)
1183 s +=
string_format (
" %u:%s:iports=%u:oports=%u:imain=%u,%s:omain=%u,%s",
1184 audio_ports_configs_[i].
id,
1185 audio_ports_configs_[i].name,
1186 audio_ports_configs_[i].input_port_count,
1187 audio_ports_configs_[i].output_port_count,
1188 audio_ports_configs_[i].has_main_input * audio_ports_configs_[i].main_input_channel_count,
1189 audio_ports_configs_[i].main_input_port_type,
1190 audio_ports_configs_[i].has_main_output * audio_ports_configs_[i].main_output_channel_count,
1191 audio_ports_configs_[i].main_output_port_type);
1192 CDEBUG (
"%s: %s", clapid(), s);
1195 note_iport_infos_.
resize (!plugin_note_ports ? 0 : plugin_note_ports->
count (plugin_, true));
1196 for (
size_t i = 0; i < note_iport_infos_.
size(); i++)
1197 if (!plugin_note_ports->get (plugin_, i,
true, ¬e_iport_infos_[i]))
1198 note_iport_infos_[i] = { CLAP_INVALID_ID, 0, 0, { 0, }, };
1199 if (note_iport_infos_.
size()) {
1201 for (
size_t i = 0; i < note_iport_infos_.
size(); i++)
1202 if (note_iport_infos_[i].
id != CLAP_INVALID_ID)
1204 note_iport_infos_[i].
id,
1205 note_iport_infos_[i].name,
1206 note_iport_infos_[i].supported_dialects,
1207 note_iport_infos_[i].preferred_dialect);
1208 CDEBUG (
"%s: %s", clapid(), s);
1211 note_oport_infos_.
resize (!plugin_note_ports ? 0 : plugin_note_ports->
count (plugin_, false));
1212 for (
size_t i = 0; i < note_oport_infos_.
size(); i++)
1213 if (!plugin_note_ports->get (plugin_, i,
false, ¬e_oport_infos_[i]))
1214 note_oport_infos_[i] = { CLAP_INVALID_ID, 0, 0, { 0, }, };
1215 if (note_oport_infos_.
size()) {
1217 for (
size_t i = 0; i < note_oport_infos_.
size(); i++)
1218 if (note_oport_infos_[i].
id != CLAP_INVALID_ID)
1220 note_oport_infos_[i].
id,
1221 note_oport_infos_[i].name,
1222 note_oport_infos_[i].supported_dialects,
1223 note_oport_infos_[i].preferred_dialect);
1224 CDEBUG (
"%s: %s", clapid(), s);
1227 audio_iport_infos_.
resize (!plugin_audio_ports ? 0 : plugin_audio_ports->
count (plugin_, true));
1228 for (
size_t i = 0; i < audio_iport_infos_.
size(); i++)
1229 if (!plugin_audio_ports->get (plugin_, i,
true, &audio_iport_infos_[i]))
1230 audio_iport_infos_[i] = { CLAP_INVALID_ID, { 0 }, 0, 0,
"", CLAP_INVALID_ID };
1232 total_channels += audio_iport_infos_[i].channel_count;
1233 if (audio_iport_infos_.
size()) {
1235 for (
size_t i = 0; i < audio_iport_infos_.
size(); i++)
1236 if (audio_iport_infos_[i].
id != CLAP_INVALID_ID && audio_iport_infos_[i].port_type)
1238 audio_iport_infos_[i].
id,
1239 audio_iport_infos_[i].channel_count,
1240 audio_iport_infos_[i].name,
1241 audio_iport_infos_[i].flags & CLAP_AUDIO_PORT_IS_MAIN,
1242 audio_iport_infos_[i].port_type);
1243 CDEBUG (
"%s: %s", clapid(), s);
1246 audio_oport_infos_.
resize (!plugin_audio_ports ? 0 : plugin_audio_ports->
count (plugin_, false));
1247 for (
size_t i = 0; i < audio_oport_infos_.
size(); i++)
1248 if (!plugin_audio_ports->get (plugin_, i,
false, &audio_oport_infos_[i]))
1249 audio_oport_infos_[i] = { CLAP_INVALID_ID, { 0 }, 0, 0,
"", CLAP_INVALID_ID };
1251 total_channels += audio_oport_infos_[i].channel_count;
1252 if (audio_oport_infos_.
size()) {
1254 for (
size_t i = 0; i < audio_oport_infos_.
size(); i++)
1255 if (audio_oport_infos_[i].
id != CLAP_INVALID_ID && audio_oport_infos_[i].port_type)
1257 audio_oport_infos_[i].
id,
1258 audio_oport_infos_[i].channel_count,
1259 audio_oport_infos_[i].name,
1260 audio_oport_infos_[i].flags & CLAP_AUDIO_PORT_IS_MAIN,
1261 audio_oport_infos_[i].port_type);
1262 CDEBUG (
"%s: %s", clapid(), s);
1265 data32ptrs_.
resize (total_channels);
1267 audio_inputs_.
resize (audio_iport_infos_.
size());
1268 for (
size_t i = 0; i < audio_inputs_.
size(); i++) {
1269 audio_inputs_[i] = {
nullptr,
nullptr, 0, 0, 0 };
1270 if (audio_iport_infos_[i].
id == CLAP_INVALID_ID)
continue;
1271 audio_inputs_[i].channel_count = audio_iport_infos_[i].channel_count;
1272 total_channels -= audio_inputs_[i].channel_count;
1273 audio_inputs_[i].data32 = &data32ptrs_[total_channels];
1274 for (
size_t j = 0; j < audio_inputs_[i].channel_count; j++)
1278 audio_outputs_.
resize (audio_oport_infos_.
size());
1279 for (
size_t i = 0; i < audio_outputs_.
size(); i++) {
1280 audio_outputs_[i] = {
nullptr,
nullptr, 0, 0, 0 };
1281 if (audio_oport_infos_[i].
id == CLAP_INVALID_ID)
continue;
1282 audio_outputs_[i].channel_count = audio_oport_infos_[i].channel_count;
1283 total_channels -= audio_outputs_[i].channel_count;
1284 audio_outputs_[i].data32 = &data32ptrs_[total_channels];
1285 for (
size_t j = 0; j < audio_outputs_[i].channel_count; j++)
1286 audio_outputs_[i].data32[j] = scratch_float_buffer;
1296 return name.c_str();
1300feature_canonify (
const String &str)
1306clapid (
const clap_host *host)
1308 ClapPluginHandleImpl *handle = handle_ptr (host);
1309 return handle->clapid();
1312static ClapPluginHandleImpl*
1313handle_ptr (
const clap_host *host)
1315 ClapPluginHandleImpl *handle = (ClapPluginHandleImpl*) host->host_data;
1319static ClapPluginHandleImplP
1320handle_sptr (
const clap_host *host)
1322 ClapPluginHandleImpl *handle = handle_ptr (host);
1323 return shared_ptr_cast<ClapPluginHandleImpl> (handle);
1326static const clap_plugin*
1327access_clap_plugin (ClapPluginHandle *handle)
1329 ClapPluginHandleImpl *handle_ =
dynamic_cast<ClapPluginHandleImpl*
> (handle);
1330 return handle_ ? handle_->plugin_ :
nullptr;
1334event_unions_try_push (ClapEventUnionS &events,
const clap_event_header_t *event)
1336 const size_t esize =
event->size;
1337 if (esize <=
sizeof (events[0]))
1339 events.resize (events.size() + 1);
1340 memcpy (&events.back(), event, esize);
1348host_log (
const clap_host_t *host, clap_log_severity severity,
const char *msg)
1350 static const char *severtities[] = {
"DEBUG",
"INFO",
"WARNING",
"ERROR",
"FATAL",
1351 "BADHOST",
"BADPLUGIN", };
1352 const char *cls = severity <
sizeof (severtities) /
sizeof (severtities[0]) ? severtities[severity] :
"MISC";
1353 if (severity == CLAP_LOG_DEBUG)
1354 CDEBUG (
"%s: %s", clapid (host), msg);
1356 printerr (
"CLAP-%s:%s: %s\n", cls, clapid (host), msg);
1358static const clap_host_log host_ext_log = { .log = host_log };
1362host_call_on_timer (ClapPluginHandleImplP handlep, clap_id timer_id)
1365 if (handlep->plugin_timer_support)
1366 handlep->plugin_timer_support->on_timer (handlep->plugin_, timer_id);
1372host_register_timer (
const clap_host *host, uint32_t period_ms, clap_id *timer_id)
1375 ClapPluginHandleImplP handlep = handle_sptr (host);
1376 period_ms =
MAX (30, period_ms);
1378 *timeridp = main_loop->exec_timer ([handlep, timeridp] () {
1379 return host_call_on_timer (handlep, *timeridp);
1381 *timer_id = *timeridp;
1382 handlep->timers_.push_back (*timeridp);
1383 CDEBUG (
"%s: %s: ms=%u: id=%u", clapid (host), __func__, period_ms, *timer_id);
1388host_unregister_timer (
const clap_host *host, clap_id timer_id)
1391 ClapPluginHandleImpl *handle = handle_ptr (host);
1392 const bool deleted =
Aux::erase_first (handle->timers_, [timer_id] (
uint id) { return id == timer_id; });
1394 main_loop->remove (timer_id);
1395 CDEBUG (
"%s: %s: deleted=%u: id=%u", clapid (host), __func__, deleted, timer_id);
1398static const clap_host_timer_support host_ext_timer_support = {
1399 .register_timer = host_register_timer,
1400 .unregister_timer = host_unregister_timer,
1405host_is_main_thread (
const clap_host_t *host)
1407 return this_thread_is_ase();
1411host_is_audio_thread (
const clap_host_t *host)
1413 return AudioEngine::thread_is_engine();
1416static const clap_host_thread_check host_ext_thread_check = {
1417 .is_main_thread = host_is_main_thread,
1418 .is_audio_thread = host_is_audio_thread,
1423host_is_rescan_flag_supported (
const clap_host_t *host, uint32_t flag)
1425 const bool supported =
false;
1426 CDEBUG (
"%s: %s: %s", clapid (host), __func__, supported);
1431host_rescan (
const clap_host_t *host, uint32_t flag)
1433 CDEBUG (
"%s: %s", clapid (host), __func__);
1437static const clap_host_audio_ports host_ext_audio_ports = {
1438 .is_rescan_flag_supported = host_is_rescan_flag_supported,
1439 .rescan = host_rescan,
1444host_register_fd (
const clap_host_t *host,
int fd, clap_posix_fd_flags_t flags)
1446 ClapPluginHandleImplP handlep = handle_sptr (host);
1447 auto plugin_on_fd = [handlep] (PollFD &pfd) {
1453 CDEBUG (
"%s: plugin_on_fd: fd=%d revents=%u: %u", handlep->clapid(), pfd.fd, revents,
1454 handlep->plugin_posix_fd_support && handlep->plugin_posix_fd_support->on_fd);
1455 if (handlep->plugin_posix_fd_support)
1456 handlep->plugin_posix_fd_support->on_fd (handlep->plugin_, pfd.fd, revents);
1460 if (flags & CLAP_POSIX_FD_READ)
1462 if (flags & CLAP_POSIX_FD_WRITE)
1464 ClapPluginHandleImpl::FdPoll fdpoll = {
1466 .source = main_loop->exec_io_handler (plugin_on_fd, fd, mode),
1470 handlep->fd_polls_.push_back (fdpoll);
1471 CDEBUG (
"%s: %s: fd=%d flags=%u mode=\"%s\" (nfds=%u): id=%u", clapid (host), __func__, fd, flags, mode,
1472 handlep->fd_polls_.size(), fdpoll.source);
1473 return fdpoll.source != 0;
1477host_unregister_fd (
const clap_host_t *host,
int fd)
1479 ClapPluginHandleImplP handlep = handle_sptr (host);
1480 for (
size_t i = 0; i < handlep->fd_polls_.size(); i++)
1481 if (handlep->fd_polls_[i].fd == fd) {
1482 const bool deleted = main_loop->try_remove (handlep->fd_polls_[i].source);
1483 CDEBUG (
"%s: %s: fd=%d id=%u (nfds=%u): deleted=%u", clapid (host), __func__, fd, handlep->fd_polls_[i].source,
1484 handlep->fd_polls_.size(), deleted);
1485 handlep->fd_polls_.erase (handlep->fd_polls_.begin() + i);
1488 CDEBUG (
"%s: %s: fd=%d: deleted=0", clapid (host), __func__, fd);
1493host_modify_fd (
const clap_host_t *host,
int fd, clap_posix_fd_flags_t flags)
1495 ClapPluginHandleImplP handlep = handle_sptr (host);
1496 for (
size_t i = 0; i < handlep->fd_polls_.size(); i++)
1497 if (handlep->fd_polls_[i].fd == fd) {
1498 CDEBUG (
"%s: %s: fd=%d flags=%u", clapid (host), __func__, fd, flags);
1499 if (handlep->fd_polls_[i].flags == flags)
1502 host_unregister_fd (host, fd);
1503 return host_register_fd (host, fd, flags);
1505 CDEBUG (
"%s: %s: fd=%d flags=%u: false", clapid (host), __func__, fd, flags);
1509static const clap_host_posix_fd_support host_ext_posix_fd_support = {
1510 .register_fd = host_register_fd,
1511 .modify_fd = host_modify_fd,
1512 .unregister_fd = host_unregister_fd,
1517host_params_rescan (
const clap_host_t *host, clap_param_rescan_flags flags)
1519 CDEBUG (
"%s: %s(0x%x)", clapid (host), __func__, flags);
1523host_params_clear (
const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags)
1525 CDEBUG (
"%s: %s(%u,0x%x)", clapid (host), __func__, param_id, flags);
1529host_request_flush (
const clap_host_t *host)
1531 CDEBUG (
"%s: %s", clapid (host), __func__);
1534static const clap_host_params host_ext_params = {
1535 .rescan = host_params_rescan,
1536 .clear = host_params_clear,
1537 .request_flush = host_request_flush,
1542host_gui_delete_request (ClapPluginHandleImplP handlep)
1544 CDEBUG (
"%s: %s", handlep->clapid(), __func__);
1545 handlep->destroy_gui();
1549host_gui_create_x11_window (ClapPluginHandleImplP handlep,
int width,
int height)
1551 Gtk2WindowSetup wsetup {
1552 .title = handlep->clapid(), .width = width, .height = height,
1553 .deleterequest_mt = [handlep] () {
1554 main_loop->exec_callback ([handlep]() {
1555 host_gui_delete_request (handlep);
1559 const ulong windowid = x11wrapper->create_window (wsetup);
1564ClapPluginHandleImpl::gui_visible ()
1566 return gui_visible_;
1570ClapPluginHandleImpl::supports_gui ()
1572 return plugin_gui !=
nullptr;
1576ClapPluginHandleImpl::show_gui()
1579 try_load_x11wrapper();
1580 if (!gui_windowid && plugin_gui && x11wrapper)
1582 ClapPluginHandleImplP handlep = handle_sptr (&phost);
1583 const bool floating =
false;
1584 clap_window_t cwindow = {
1585 .api = CLAP_WINDOW_API_X11,
1588 if (plugin_gui->is_api_supported (plugin_, cwindow.api, floating))
1590 bool created = plugin_gui->create (plugin_, cwindow.api, floating);
1591 CDEBUG (
"%s: gui_create: %d\n", clapid(), created);
1592 gui_canresize = plugin_gui->can_resize (plugin_);
1593 CDEBUG (
"%s: gui_can_resize: %d\n", clapid(), gui_canresize);
1594 const double scale = 1.0;
1595 const bool scaled = scale > 0 ? plugin_gui->set_scale (plugin_, scale) :
false;
1596 CDEBUG (
"%s: gui_set_scale(%f): %d\n", clapid(), scale, scaled);
1598 const bool sized = plugin_gui->get_size (plugin_, &width, &height);
1599 CDEBUG (
"%s: gui_get_size: %ux%u: %d\n", clapid(), width, height, sized);
1600 cwindow.x11 = host_gui_create_x11_window (handlep, width, height);
1601 const bool parentset = plugin_gui->set_parent (plugin_, &cwindow);
1602 CDEBUG (
"%s: gui_set_parent: %d\n", clapid(), parentset);
1603 gui_windowid = cwindow.x11;
1606 if (gui_windowid && plugin_gui) {
1607 gui_visible_ = plugin_gui->show (plugin_);
1608 CDEBUG (
"%s: gui_show: %d\n", clapid(), gui_visible_);
1611 x11wrapper->show_window (gui_windowid);
1616ClapPluginHandleImpl::hide_gui()
1620 plugin_gui->hide (plugin_);
1621 x11wrapper->hide_window (gui_windowid);
1622 gui_visible_ =
false;
1627ClapPluginHandleImpl::destroy_gui()
1631 plugin_gui->destroy (plugin_);
1632 x11wrapper->destroy_window (gui_windowid);
1638host_resize_hints_changed (
const clap_host_t *host)
1640 CDEBUG (
"%s: %s", clapid (host), __func__);
1644host_request_resize (
const clap_host_t *host, uint32_t width, uint32_t height)
1646 ClapPluginHandleImpl *handle = handle_ptr (host);
1647 CDEBUG (
"%s: %s(%u,%u)", clapid (host), __func__, width, height);
1648 if (handle->gui_windowid) {
1649 if (x11wrapper->resize_window (handle->gui_windowid, width, height)) {
1650 if (handle->plugin_gui->can_resize (handle->plugin_))
1651 handle->plugin_gui->set_size (handle->plugin_, width, height);
1659host_request_show (
const clap_host_t *host)
1661 const bool supported =
false;
1662 CDEBUG (
"%s: %s: %d", clapid (host), __func__, supported);
1667host_request_hide (
const clap_host_t *host)
1669 const bool supported =
false;
1670 CDEBUG (
"%s: %s: %d", clapid (host), __func__, supported);
1675host_gui_closed (
const clap_host_t *host,
bool was_destroyed)
1677 ClapPluginHandleImpl *handle = handle_ptr (host);
1678 CDEBUG (
"%s: %s(was_destroyed=%u)", clapid (host), __func__, was_destroyed);
1679 handle->gui_visible_ =
false;
1680 if (was_destroyed && handle->plugin_gui) {
1681 handle->gui_windowid = 0;
1682 handle->plugin_gui->destroy (handle->plugin_);
1686static const clap_host_gui host_ext_gui = {
1687 .resize_hints_changed = host_resize_hints_changed,
1688 .request_resize = host_request_resize,
1689 .request_show = host_request_show,
1690 .request_hide = host_request_hide,
1691 .closed = host_gui_closed,
1696ClapPluginHandleImpl::resolve_file_references (ClapResourceHashS loader_hashes)
1698 if (!plugin_file_reference || !plugin_file_reference->update_path ||
1699 !plugin_file_reference->count || !plugin_file_reference->get)
1701 const size_t n_files = plugin_file_reference->count (plugin_);
1702 for (
size_t i = 0; i < n_files; i++)
1704 char buffer[ASE_PATH_MAX + 2] = { 0, };
1705 const size_t path_capacity =
sizeof (buffer) - 1;
1706 clap_file_reference pfile = {
1707 .resource_id = CLAP_INVALID_ID,
1708 .belongs_to_plugin_collection =
false,
1709 .path_capacity = path_capacity,
1710 .path_size = 0, .path = buffer,
1712 if (!plugin_file_reference->get (plugin_, i, &pfile) ||
1713 pfile.path_size >= path_capacity)
1720 if (plugin_file_reference->get_blake3_digest &&
1721 plugin_file_reference->get_blake3_digest (plugin_, pfile.resource_id, digest))
1724 for (
size_t i = 0; i < loader_hashes.size(); i++)
1732 if (content_file.size())
1733 plugin_file_reference->update_path (plugin_, pfile.resource_id, content_file.c_str());
1738 what +=
" hash=" + hash;
1739 if (pfile.path && pfile.path[0])
1740 what +=
String (
" ") + pfile.path;
1741 ASE_SERVER.user_note (
string_format (
"## Note Missing File\n%s: \\\nFailed to find file: \\\n%s",
1748host_file_reference_changed (
const clap_host_t *host)
1750 CDEBUG (
"%s: %s", clapid (host), __func__);
1754host_file_reference_set_dirty (
const clap_host_t *host, clap_id resource_id)
1756 CDEBUG (
"%s: %s: %d", clapid (host), __func__, resource_id);
1759static const clap_host_file_reference host_ext_file_reference = {
1760 .changed = host_file_reference_changed,
1761 .set_dirty = host_file_reference_set_dirty,
1766host_get_extension_mt (
const clap_host *host,
const char *extension_id)
1768 const String ext = extension_id;
1769 if (ext == CLAP_EXT_LOG)
return &host_ext_log;
1770 if (ext == CLAP_EXT_GUI)
return &host_ext_gui;
1771 if (ext == CLAP_EXT_FILE_REFERENCE)
return &host_ext_file_reference;
1772 if (ext == CLAP_EXT_TIMER_SUPPORT)
return &host_ext_timer_support;
1773 if (ext == CLAP_EXT_THREAD_CHECK)
return &host_ext_thread_check;
1774 if (ext == CLAP_EXT_AUDIO_PORTS)
return &host_ext_audio_ports;
1775 if (ext == CLAP_EXT_PARAMS)
return &host_ext_params;
1776 if (ext == CLAP_EXT_POSIX_FD_SUPPORT)
return &host_ext_posix_fd_support;
1777 else return nullptr;
1781host_request_restart_mt (
const clap_host *host)
1783 CDEBUG (
"%s: %s", clapid (host), __func__);
1787host_request_process_mt (
const clap_host *host)
1789 CDEBUG (
"%s: %s", clapid (host), __func__);
1793host_request_callback_mt (
const clap_host *host)
1795 CDEBUG (
"%s: %s", clapid (host), __func__);
1796 ClapPluginHandleImplP handlep = handle_sptr (host);
1797 main_loop->exec_callback ([handlep] () {
1798 if (handlep->plugin_) {
1800 handlep->plugin_->on_main_thread (handlep->plugin_);
1807ClapPluginDescriptor::ClapPluginDescriptor (ClapFileHandle &clapfile) :
1808 clapfile_ (clapfile)
1812ClapPluginDescriptor::open()
const
1818ClapPluginDescriptor::close()
const
1823const clap_plugin_entry*
1824ClapPluginDescriptor::entry()
const
1826 return clapfile_.opened() ? clapfile_.pluginentry :
nullptr;
1830ClapPluginDescriptor::add_descriptor (
const String &pluginpath, Collection &infos)
1832 ClapFileHandle *filehandle =
new ClapFileHandle (pluginpath);
1834 if (!filehandle->opened()) {
1835 filehandle->close();
1839 const clap_plugin_factory *pluginfactory = (
const clap_plugin_factory *) filehandle->pluginentry->get_factory (CLAP_PLUGIN_FACTORY_ID);
1840 const uint32_t plugincount = !pluginfactory ? 0 : pluginfactory->get_plugin_count (pluginfactory);
1841 for (
size_t i = 0; i < plugincount; i++)
1843 const clap_plugin_descriptor_t *pdesc = pluginfactory->get_plugin_descriptor (pluginfactory, i);
1844 if (!pdesc || !pdesc->id || !pdesc->id[0])
1846 const std::string clapversion =
string_format (
"clap-%u.%u.%u", pdesc->clap_version.major, pdesc->clap_version.minor, pdesc->clap_version.revision);
1847 if (!clap_version_is_compatible (pdesc->clap_version)) {
1848 CDEBUG (
"invalid plugin: %s (%s)", pdesc->id, clapversion);
1851 ClapPluginDescriptor *descriptor =
new ClapPluginDescriptor (*filehandle);
1852 descriptor->id = pdesc->id;
1853 descriptor->name = pdesc->name ? pdesc->name : pdesc->id;
1854 descriptor->version = pdesc->version ? pdesc->version :
"0.0.0-unknown";
1855 descriptor->vendor = pdesc->vendor ? pdesc->vendor :
"";
1856 descriptor->url = pdesc->url ? pdesc->url :
"";
1857 descriptor->manual_url = pdesc->manual_url ? pdesc->manual_url :
"";
1858 descriptor->support_url = pdesc->support_url ? pdesc->support_url :
"";
1859 descriptor->description = pdesc->description ? pdesc->description :
"";
1861 if (pdesc->features)
1862 for (
size_t ft = 0; pdesc->features[ft]; ft++)
1863 if (pdesc->features[ft][0])
1864 features.
push_back (feature_canonify (pdesc->features[ft]));
1865 descriptor->features =
":" +
string_join (
":", features) +
":";
1867 CDEBUG (
"Plugin: %s %s %s (%s, %s)%s", descriptor->name, descriptor->version,
1868 descriptor->vendor.empty() ?
"" :
"- " + descriptor->vendor,
1869 descriptor->id, clapversion,
1870 descriptor->features.
empty() ?
"" :
": " + descriptor->features);
1872 filehandle->close();
1875const ClapPluginDescriptor::Collection&
1876ClapPluginDescriptor::collect_descriptors ()
1878 static Collection collection;
1879 if (collection.empty()) {
1880 for (
const auto &clapfile : list_clap_files())
1881 add_descriptor (clapfile, collection);
1893 const char *clapsearchpath =
getenv (
"CLAP_PATH");
1895 for (
const String &spath : Path::searchpath_split (clapsearchpath))
1896 Path::
rglob (spath,
"*.clap", files);
1902clap_event_type_string (
int etype)
1906 case CLAP_EVENT_NOTE_ON:
return "NOTE_ON";
1907 case CLAP_EVENT_NOTE_OFF:
return "NOTE_OFF";
1908 case CLAP_EVENT_NOTE_CHOKE:
return "NOTE_CHOKE";
1909 case CLAP_EVENT_NOTE_END:
return "NOTE_END";
1910 case CLAP_EVENT_NOTE_EXPRESSION:
return "NOTE_EXPRESSION";
1911 case CLAP_EVENT_PARAM_VALUE:
return "PARAM_VALUE";
1912 case CLAP_EVENT_PARAM_MOD:
return "PARAM_MOD";
1913 case CLAP_EVENT_PARAM_GESTURE_BEGIN:
return "PARAM_GESTURE_BEGIN";
1914 case CLAP_EVENT_PARAM_GESTURE_END:
return "PARAM_GESTURE_END";
1915 case CLAP_EVENT_TRANSPORT:
return "TRANSPORT";
1916 case CLAP_EVENT_MIDI:
return "MIDI";
1917 case CLAP_EVENT_MIDI_SYSEX:
return "MIDI_SYSEX";
1918 case CLAP_EVENT_MIDI2:
return "MIDI2";
1919 default:
return "<UNKNOWN>";
1924clap_event_to_string (
const clap_event_note_t *enote)
1926 const char *et = clap_event_type_string (enote->header.type);
1927 switch (enote->header.type)
1929 case CLAP_EVENT_NOTE_ON:
1930 case CLAP_EVENT_NOTE_OFF:
1931 case CLAP_EVENT_NOTE_CHOKE:
1932 case CLAP_EVENT_NOTE_END:
1933 return string_format (
"%+4d ch=%-2u %-14s pitch=%d vel=%f id=%x sz=%d spc=%d flags=%x port=%d",
1934 enote->header.time, enote->channel, et, enote->key, enote->velocity, enote->note_id,
1935 enote->header.size, enote->header.space_id, enote->header.flags, enote->port_index);
1937 return string_format (
"%+4d %-20s sz=%d spc=%d flags=%x port=%d",
1938 enote->header.time, et, enote->header.size, enote->header.space_id, enote->header.flags);
1943clap_device_info (
const ClapPluginDescriptor &descriptor)
1946 di.
uri =
"CLAP:" + descriptor.id;
1947 di.name = descriptor.name;
1948 di.description = descriptor.description;
1949 di.website_url = descriptor.url;
1950 di.creator_name = descriptor.vendor;
1951 di.creator_url = descriptor.manual_url;
1952 const char *
const cfeatures = descriptor.features.
c_str();
1953 if (strstr (cfeatures,
":instrument:"))
1954 di.category =
"Instrument";
1955 else if (strstr (cfeatures,
":analyzer:"))
1956 di.category =
"Analyzer";
1957 else if (strstr (cfeatures,
":note-effect:"))
1958 di.category =
"Note FX";
1959 else if (strstr (cfeatures,
":audio-effect:"))
1960 di.category =
"Audio FX";
1961 else if (strstr (cfeatures,
":effect:"))
1962 di.category =
"Audio FX";
1964 di.category =
"Clap Device";
1970try_load_x11wrapper()
1973 static Gtk2DlWrapEntry *gtk2wrapentry = [] () {
1975 void *gtkdlhandle_ =
dlopen (gtk2wrapso.c_str(), RTLD_LOCAL | RTLD_NOW);
1976 const char *symname =
"Ase__Gtk2__wrapentry";
1977 void *ptr = gtkdlhandle_ ?
dlsym (gtkdlhandle_, symname) : nullptr;
1978 return (Gtk2DlWrapEntry*) ptr;
1980 x11wrapper = gtk2wrapentry;
1984ClapPluginHandle::ClapPluginHandle (
const ClapPluginDescriptor &descriptor_) :
1985 descriptor (descriptor_)
1990ClapPluginHandle::~ClapPluginHandle()
1996ClapPluginHandle::audio_processor_type()
1998 return clap_audio_wrapper_aseid;
2002ClapPluginHandle::make_clap_handle (
const ClapPluginDescriptor &descriptor, AudioProcessorP audio_processor)
2005 handlep->init_plugin();
Audio signal AudioProcessor base class, implemented by all effects and instruments.
void enotify_enqueue_mt(uint32 pushmask)
void remove_all_buses()
Remove existing bus configurations, useful at the start of configure().
const float * ifloats(IBusId b, uint c) const
Access readonly float buffer of input bus b, channel c, see also ofloats().
void prepare_event_input()
MidiEventInput midi_event_input()
Access the current MidiEvent inputs during render(), needs prepare_event_input().
uint n_ochannels(OBusId busid) const
Number of channels of output bus busid configured for this AudioProcessor.
const AudioTransport & transport() const
Sample rate mixing frequency in Hz as unsigned, used for render().
OBusId add_output_bus(CString uilabel, SpeakerArrangement speakerarrangement, const String &hints="", const String &blurb="")
Add an output bus with uilabel and channels configured via speakerarrangement.
bool has_event_input() const
Returns true if this AudioProcessor has an event input stream.
void atomic_bits_resize(size_t count)
Prepare count bits for atomic notifications.
void prepare_event_output()
bool atomic_bit_notify(size_t nth)
Set the nth atomic notification bit, return if enotify_enqueue_mt() is needed.
AudioEngine & engine() const
Retrieve AudioEngine handle for this AudioProcessor.
IBusId add_input_bus(CString uilabel, SpeakerArrangement speakerarrangement, const String &hints="", const String &blurb="")
Add an input bus with uilabel and channels configured via speakerarrangement.
bool has_event_output() const
Returns true if this AudioProcessor has an event output stream.
uint n_ichannels(IBusId busid) const
Number of channels of input bus busid configured for this AudioProcessor.
float * oblock(OBusId b, uint c)
DeviceP get_device() const
Gain access to the Device handle of this AudioProcessor.
void initialize(SpeakerArrangement busses) override
void render(uint n_frames) override
void reset(uint64 target_stamp) override
Reset all state variables.
void emit_notify(const String &detail) override
Emit notify:detail, multiple notifications maybe coalesced if a CoalesceNotifies instance exists.
static const int16 PRIORITY_UPDATE
Mildly important, used for GUI updates or user information.
GadgetImpl * _parent() const override
Retrieve parent container.
ProjectImpl * _project() const
Find Project in parent ancestry.
String loader_resolve(const String &hexhash)
Find file from hash code, returns fspath.
#define ASE_ARRAY_SIZE(array)
Yield the number of C array elements.
#define ASE_UNLIKELY(expr)
Compiler hint to optimize for expr evaluating to false.
#define ASE_ISLIKELY(expr)
Compiler hint to optimize for expr evaluating to true.
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
#define MAX(a, b)
Yield maximum of a and b.
size_t erase_first(C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Erase first element for which pred() is true in vector or list.
void unique_realpaths(StringS &pathnames)
Convert all pathnames via realpath() and eliminate duplicates.
bool check(const String &file, const String &mode)
void rmrf(const String &dir)
Recursively delete directory tree.
void rglob(const String &basedir, const String &pattern, StringS &matches)
Recursively match files with glob pattern under basedir.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
constexpr const uint AUDIO_BLOCK_FLOAT_ZEROS_SIZE
Maximum number of values in the const_float_zeros block.
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
IBusId
ID type for AudioProcessor input buses, buses are numbered with increasing index.
String string_join(const String &junctor, const StringS &strvec)
OBusId
ID type for AudioProcessor output buses, buses are numbered with increasing index.
String string_to_hex(const String &input)
Convert bytes in string input to hexadecimal numbers.
float const_float_zeros[AUDIO_BLOCK_FLOAT_ZEROS_SIZE]
Block of const floats allof value 0.
String uri
Unique identifier for de-/serialization.
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
Error
Enum representing Error states.
const char * ase_error_blurb(Error error)
Describe Error condition.
std::string anklang_runpath(RPath rpath, const String &segment)
Retrieve various resource paths at runtime.
bool debug_enabled() ASE_PURE
Check if any kind of debugging is enabled by $ASE_DEBUG.
CString label
Preferred user interface name.
String program_alias()
Retrieve the program name as used for logging or debug messages.
const char * ase_version()
Provide a string containing the package version.
const String & string_set_a2z()
Returns a string containing all of a-z.
std::string String
Convenience alias for std::string.
std::string executable_name()
Retrieve the name part of executable_path().
String string_canonify(const String &string, const String &valid_chars, const String &substitute)
const String & string_set_A2Z()
Returns a string containing all of A-Z.
constexpr const int64 TRANSPORT_PPQN
Maximum number of sample frames to calculate in Processor::render().
uint32_t uint
Provide 'uint' as convenience type.
RtJobQueue main_rt_jobs
Queue a callback for the main_loop without invoking malloc(), addition is obstruction free.
String json_stringify(const T &source, Writ::Flags flags=Writ::Flags(0))
Create JSON string from source.
Detailed information and common properties of AudioProcessor subclasses.
@ HUP
file descriptor closed
@ OUT
writing data will not block
Wrap simple callback pointers, without using malloc (obstruction free).