void reload() override { CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,); CARLA_SAFE_ASSERT_RETURN(fInstance != nullptr,); carla_debug("CarlaPluginJuce::reload() - start"); const EngineProcessMode processMode(pData->engine->getProccessMode()); // Safely disable plugin for reload const ScopedDisabler sd(this); if (pData->active) deactivate(); clearBuffers(); fInstance->refreshParameterList(); uint32_t aIns, aOuts, mIns, mOuts, params; mIns = mOuts = 0; bool needsCtrlIn, needsCtrlOut; needsCtrlIn = needsCtrlOut = false; aIns = (fInstance->getTotalNumInputChannels() > 0) ? static_cast<uint32_t>(fInstance->getTotalNumInputChannels()) : 0; aOuts = (fInstance->getTotalNumOutputChannels() > 0) ? static_cast<uint32_t>(fInstance->getTotalNumOutputChannels()) : 0; params = (fInstance->getNumParameters() > 0) ? static_cast<uint32_t>(fInstance->getNumParameters()) : 0; if (fInstance->acceptsMidi()) { mIns = 1; needsCtrlIn = true; } if (fInstance->producesMidi()) { mOuts = 1; needsCtrlOut = true; } if (aIns > 0) { pData->audioIn.createNew(aIns); } if (aOuts > 0) { pData->audioOut.createNew(aOuts); needsCtrlIn = true; } if (params > 0) { pData->param.createNew(params, false); needsCtrlIn = true; } const uint portNameSize(pData->engine->getMaxPortNameSize()); CarlaString portName; // Audio Ins for (uint32_t j=0; j < aIns; ++j) { portName.clear(); if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT) { portName = pData->name; portName += ":"; } if (aIns > 1) { portName += "input_"; portName += CarlaString(j+1); } else portName += "input"; portName.truncate(portNameSize); pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, j); pData->audioIn.ports[j].rindex = j; } // Audio Outs for (uint32_t j=0; j < aOuts; ++j) { portName.clear(); if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT) { portName = pData->name; portName += ":"; } if (aOuts > 1) { portName += "output_"; portName += CarlaString(j+1); } else portName += "output"; portName.truncate(portNameSize); pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, j); pData->audioOut.ports[j].rindex = j; } for (uint32_t j=0; j < params; ++j) { pData->param.data[j].type = PARAMETER_INPUT; pData->param.data[j].index = static_cast<int32_t>(j); pData->param.data[j].rindex = static_cast<int32_t>(j); float min, max, def, step, stepSmall, stepLarge; // TODO //const int numSteps(fInstance->getParameterNumSteps(static_cast<int>(j))); { min = 0.0f; max = 1.0f; step = 0.001f; stepSmall = 0.0001f; stepLarge = 0.1f; } pData->param.data[j].hints |= PARAMETER_IS_ENABLED; #ifndef BUILD_BRIDGE pData->param.data[j].hints |= PARAMETER_USES_CUSTOM_TEXT; #endif if (fInstance->isParameterAutomatable(static_cast<int>(j))) pData->param.data[j].hints |= PARAMETER_IS_AUTOMABLE; // FIXME? def = fInstance->getParameterDefaultValue(static_cast<int>(j)); if (def < min) def = min; else if (def > max) def = max; pData->param.ranges[j].min = min; pData->param.ranges[j].max = max; pData->param.ranges[j].def = def; pData->param.ranges[j].step = step; pData->param.ranges[j].stepSmall = stepSmall; pData->param.ranges[j].stepLarge = stepLarge; } if (needsCtrlIn) { portName.clear(); if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT) { portName = pData->name; portName += ":"; } portName += "events-in"; portName.truncate(portNameSize); pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0); } if (needsCtrlOut) { portName.clear(); if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT) { portName = pData->name; portName += ":"; } portName += "events-out"; portName.truncate(portNameSize); pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0); } // plugin hints pData->hints = 0x0; pData->hints |= PLUGIN_NEEDS_FIXED_BUFFERS; if (fDesc.isInstrument) pData->hints |= PLUGIN_IS_SYNTH; if (fInstance->hasEditor()) { pData->hints |= PLUGIN_HAS_CUSTOM_UI; pData->hints |= PLUGIN_NEEDS_UI_MAIN_THREAD; } if (aOuts > 0 && (aIns == aOuts || aIns == 1)) pData->hints |= PLUGIN_CAN_DRYWET; if (aOuts > 0) pData->hints |= PLUGIN_CAN_VOLUME; if (aOuts >= 2 && aOuts % 2 == 0) pData->hints |= PLUGIN_CAN_BALANCE; // extra plugin hints pData->extraHints = 0x0; if (mIns > 0) pData->extraHints |= PLUGIN_EXTRA_HINT_HAS_MIDI_IN; if (mOuts > 0) pData->extraHints |= PLUGIN_EXTRA_HINT_HAS_MIDI_OUT; if (aIns <= 2 && aOuts <= 2 && (aIns == aOuts || aIns == 0 || aOuts == 0)) pData->extraHints |= PLUGIN_EXTRA_HINT_CAN_RUN_RACK; fInstance->setPlayConfigDetails(static_cast<int>(aIns), static_cast<int>(aOuts), pData->engine->getSampleRate(), static_cast<int>(pData->engine->getBufferSize())); bufferSizeChanged(pData->engine->getBufferSize()); reloadPrograms(true); if (pData->active) activate(); carla_debug("CarlaPluginJuce::reload() - end"); }
const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, uint index) { carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype, CB::PluginType2Str(ptype), index); static CarlaCachedPluginInfo info; switch (ptype) { case CB::PLUGIN_INTERNAL: { uint32_t count = 0; const NativePluginDescriptor* const descs(carla_get_native_plugins_data(&count)); CARLA_SAFE_ASSERT_BREAK(index < count); CARLA_SAFE_ASSERT_BREAK(descs != nullptr); const NativePluginDescriptor& desc(descs[index]); info.category = static_cast<CB::PluginCategory>(desc.category); info.hints = 0x0; if (desc.hints & NATIVE_PLUGIN_IS_RTSAFE) info.hints |= CB::PLUGIN_IS_RTSAFE; if (desc.hints & NATIVE_PLUGIN_IS_SYNTH) info.hints |= CB::PLUGIN_IS_SYNTH; if (desc.hints & NATIVE_PLUGIN_HAS_UI) info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; if (desc.hints & NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS) info.hints |= CB::PLUGIN_NEEDS_FIXED_BUFFERS; if (desc.hints & NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD) info.hints |= CB::PLUGIN_NEEDS_UI_MAIN_THREAD; if (desc.hints & NATIVE_PLUGIN_USES_MULTI_PROGS) info.hints |= CB::PLUGIN_USES_MULTI_PROGS; info.audioIns = desc.audioIns; info.audioOuts = desc.audioOuts; info.midiIns = desc.midiIns; info.midiOuts = desc.midiOuts; info.parameterIns = desc.paramIns; info.parameterOuts = desc.paramOuts; info.name = desc.name; info.label = desc.label; info.maker = desc.maker; info.copyright = desc.copyright; return &info; } case CB::PLUGIN_LV2: { Lv2WorldClass& lv2World(Lv2WorldClass::getInstance()); const LilvPlugin* const cPlugin(lv2World.getPluginFromIndex(index)); CARLA_SAFE_ASSERT_BREAK(cPlugin != nullptr); Lilv::Plugin lilvPlugin(cPlugin); CARLA_SAFE_ASSERT_BREAK(lilvPlugin.get_uri().is_uri()); carla_stdout("Filling info for LV2 with URI '%s'", lilvPlugin.get_uri().as_uri()); // features info.hints = 0x0; if (lilvPlugin.get_uis().size() > 0 || lilvPlugin.get_modgui_resources_directory().as_uri() != nullptr) info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; { Lilv::Nodes lilvFeatureNodes(lilvPlugin.get_supported_features()); LILV_FOREACH(nodes, it, lilvFeatureNodes) { Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it)); const char* const featureURI(lilvFeatureNode.as_uri()); CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr); if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0) info.hints |= CB::PLUGIN_IS_RTSAFE; } lilv_nodes_free(const_cast<LilvNodes*>(lilvFeatureNodes.me)); } // category info.category = CB::PLUGIN_CATEGORY_NONE; { Lilv::Nodes typeNodes(lilvPlugin.get_value(lv2World.rdf_type)); if (typeNodes.size() > 0) { if (typeNodes.contains(lv2World.class_allpass)) info.category = CB::PLUGIN_CATEGORY_FILTER; if (typeNodes.contains(lv2World.class_amplifier)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_analyzer)) info.category = CB::PLUGIN_CATEGORY_UTILITY; if (typeNodes.contains(lv2World.class_bandpass)) info.category = CB::PLUGIN_CATEGORY_FILTER; if (typeNodes.contains(lv2World.class_chorus)) info.category = CB::PLUGIN_CATEGORY_MODULATOR; if (typeNodes.contains(lv2World.class_comb)) info.category = CB::PLUGIN_CATEGORY_FILTER; if (typeNodes.contains(lv2World.class_compressor)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_constant)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_converter)) info.category = CB::PLUGIN_CATEGORY_UTILITY; if (typeNodes.contains(lv2World.class_delay)) info.category = CB::PLUGIN_CATEGORY_DELAY; if (typeNodes.contains(lv2World.class_distortion)) info.category = CB::PLUGIN_CATEGORY_DISTORTION; if (typeNodes.contains(lv2World.class_dynamics)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_eq)) info.category = CB::PLUGIN_CATEGORY_EQ; if (typeNodes.contains(lv2World.class_envelope)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_expander)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_filter)) info.category = CB::PLUGIN_CATEGORY_FILTER; if (typeNodes.contains(lv2World.class_flanger)) info.category = CB::PLUGIN_CATEGORY_MODULATOR; if (typeNodes.contains(lv2World.class_function)) info.category = CB::PLUGIN_CATEGORY_UTILITY; if (typeNodes.contains(lv2World.class_gate)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_generator)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_highpass)) info.category = CB::PLUGIN_CATEGORY_FILTER; if (typeNodes.contains(lv2World.class_limiter)) info.category = CB::PLUGIN_CATEGORY_DYNAMICS; if (typeNodes.contains(lv2World.class_lowpass)) info.category = CB::PLUGIN_CATEGORY_FILTER; if (typeNodes.contains(lv2World.class_mixer)) info.category = CB::PLUGIN_CATEGORY_UTILITY; if (typeNodes.contains(lv2World.class_modulator)) info.category = CB::PLUGIN_CATEGORY_MODULATOR; if (typeNodes.contains(lv2World.class_multiEQ)) info.category = CB::PLUGIN_CATEGORY_EQ; if (typeNodes.contains(lv2World.class_oscillator)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_paraEQ)) info.category = CB::PLUGIN_CATEGORY_EQ; if (typeNodes.contains(lv2World.class_phaser)) info.category = CB::PLUGIN_CATEGORY_MODULATOR; if (typeNodes.contains(lv2World.class_pitch)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_reverb)) info.category = CB::PLUGIN_CATEGORY_DELAY; if (typeNodes.contains(lv2World.class_simulator)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_spatial)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_spectral)) info.category = CB::PLUGIN_CATEGORY_OTHER; if (typeNodes.contains(lv2World.class_utility)) info.category = CB::PLUGIN_CATEGORY_UTILITY; if (typeNodes.contains(lv2World.class_waveshaper)) info.category = CB::PLUGIN_CATEGORY_DISTORTION; if (typeNodes.contains(lv2World.class_instrument)) { info.category = CB::PLUGIN_CATEGORY_SYNTH; info.hints |= CB::PLUGIN_IS_SYNTH; } } lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me)); } // number data info.audioIns = 0; info.audioOuts = 0; info.midiIns = 0; info.midiOuts = 0; info.parameterIns = 0; info.parameterOuts = 0; for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i) { Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i)); bool isInput; /**/ if (lilvPort.is_a(lv2World.port_input)) isInput = true; else if (lilvPort.is_a(lv2World.port_output)) isInput = false; else continue; /**/ if (lilvPort.is_a(lv2World.port_control)) { // skip some control ports if (lilvPort.has_property(lv2World.reportsLatency)) continue; if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me)) { bool skip = false; if (const char* const designation = lilv_node_as_string(designationNode)) { /**/ if (std::strcmp(designation, LV2_CORE__control) == 0) skip = true; else if (std::strcmp(designation, LV2_CORE__freeWheeling) == 0) skip = true; else if (std::strcmp(designation, LV2_CORE__latency) == 0) skip = true; else if (std::strcmp(designation, LV2_PARAMETERS__sampleRate) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__bar) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__barBeat) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__beat) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__beatUnit) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__beatsPerBar) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__beatsPerMinute) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__frame) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__framesPerSecond) == 0) skip = true; else if (std::strcmp(designation, LV2_TIME__speed) == 0) skip = true; else if (std::strcmp(designation, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat) == 0) skip = true; } lilv_node_free(designationNode); if (skip) continue; } if (isInput) ++(info.parameterIns); else ++(info.parameterOuts); } else if (lilvPort.is_a(lv2World.port_audio)) { if (isInput) ++(info.audioIns); else ++(info.audioOuts); } else if (lilvPort.is_a(lv2World.port_cv)) { } else if (lilvPort.is_a(lv2World.port_atom)) { Lilv::Nodes supportNodes(lilvPort.get_value(lv2World.atom_supports)); for (LilvIter *it = lilv_nodes_begin(supportNodes.me); ! lilv_nodes_is_end(supportNodes.me, it); it = lilv_nodes_next(supportNodes.me, it)) { const Lilv::Node node(lilv_nodes_get(supportNodes.me, it)); CARLA_SAFE_ASSERT_CONTINUE(node.is_uri()); if (node.equals(lv2World.midi_event)) { if (isInput) ++(info.midiIns); else ++(info.midiOuts); } } lilv_nodes_free(const_cast<LilvNodes*>(supportNodes.me)); } else if (lilvPort.is_a(lv2World.port_event)) { if (lilvPort.supports_event(lv2World.midi_event)) { if (isInput) ++(info.midiIns); else ++(info.midiOuts); } } else if (lilvPort.is_a(lv2World.port_midi)) { if (isInput) ++(info.midiIns); else ++(info.midiOuts); } } // text data static CarlaString suri, sname, smaker, slicense; suri.clear(); sname.clear(); smaker.clear(); slicense.clear(); suri = lilvPlugin.get_uri().as_uri(); if (LilvNode* const nameNode = lilv_plugin_get_name(lilvPlugin.me)) { if (const char* const name = lilv_node_as_string(nameNode)) sname = name; lilv_node_free(nameNode); } if (const char* const author = lilvPlugin.get_author_name().as_string()) smaker = author; Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license)); if (licenseNodes.size() > 0) { if (const char* const license = licenseNodes.get_first().as_string()) slicense = license; } lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me)); info.name = sname; info.label = suri; info.maker = smaker; info.copyright = slicense; return &info; } case CB::PLUGIN_AU: { #ifdef CARLA_OS_MAC const int indexi(static_cast<int>(index)); CARLA_SAFE_ASSERT_BREAK(indexi < gCachedAuPluginResults.size()); using namespace juce; String pluginId(gCachedAuPluginResults[indexi]); OwnedArray<PluginDescription> results; AudioUnitPluginFormat auFormat; auFormat.findAllTypesForFile(results, pluginId); CARLA_SAFE_ASSERT_BREAK(results.size() > 0); CARLA_SAFE_ASSERT(results.size() == 1); PluginDescription* const desc(results[0]); CARLA_SAFE_ASSERT_BREAK(desc != nullptr); info.category = CB::getPluginCategoryFromName(desc->category.toRawUTF8()); info.hints = 0x0; if (desc->isInstrument) info.hints |= CB::PLUGIN_IS_SYNTH; if (true) info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; info.audioIns = static_cast<uint32_t>(desc->numInputChannels); info.audioOuts = static_cast<uint32_t>(desc->numOutputChannels); info.midiIns = desc->isInstrument ? 1 : 0; info.midiOuts = 0; info.parameterIns = 0; info.parameterOuts = 0; static CarlaString sname, slabel, smaker; sname = desc->name.toRawUTF8(); slabel = desc->fileOrIdentifier.toRawUTF8(); smaker = desc->manufacturerName.toRawUTF8(); info.name = sname; info.label = slabel; info.maker = smaker; info.copyright = gNullCharPtr; return &info; #else break; #endif } default: break; }