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).