/* * Carla Plugin Engine (Native) * Copyright (C) 2013 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the GPL.txt file */ #ifndef BUILD_BRIDGE #include "CarlaEngineInternal.hpp" #include "CarlaStateUtils.hpp" #include "CarlaNative.hpp" #include CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- class CarlaEngineNative : public PluginDescriptorClass, public CarlaEngine { public: CarlaEngineNative(const HostDescriptor* const host) : PluginDescriptorClass(host), CarlaEngine() { carla_debug("CarlaEngineNative::CarlaEngineNative()"); // set-up engine fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK; fOptions.transportMode = TRANSPORT_MODE_PLUGIN; fOptions.forceStereo = true; fOptions.preferPluginBridges = false; fOptions.preferUiBridges = false; init("Carla-Plugin"); } ~CarlaEngineNative() override { carla_debug("CarlaEngineNative::~CarlaEngineNative()"); setAboutToClose(); removeAllPlugins(); close(); } protected: // ------------------------------------- // CarlaEngine virtual calls bool init(const char* const clientName) override { carla_debug("CarlaEngineNative::init(\"%s\")", clientName); fBufferSize = PluginDescriptorClass::getBufferSize(); fSampleRate = PluginDescriptorClass::getSampleRate(); CarlaEngine::init(clientName); return true; } bool close() override { carla_debug("CarlaEngineNative::close()"); proccessPendingEvents(); return CarlaEngine::close(); } bool isRunning() const override { return true; } bool isOffline() const override { return false; } EngineType type() const override { return kEngineTypePlugin; } // ------------------------------------------------------------------- // Plugin parameter calls uint32_t getParameterCount() override { if (kData->curPluginCount == 0 || kData->plugins == nullptr) return 0; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return 0; return kData->plugins[0].plugin->parameterCount(); } const Parameter* getParameterInfo(const uint32_t index) override { if (index >= getParameterCount()) return nullptr; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return nullptr; static ::Parameter param; static char strBufName[STR_MAX+1]; static char strBufUnit[STR_MAX+1]; const ParameterData& paramData(plugin->parameterData(index)); const ParameterRanges& paramRanges(plugin->parameterRanges(index)); plugin->getParameterName(index, strBufName); plugin->getParameterUnit(index, strBufUnit); unsigned int hints = 0x0; if (paramData.hints & PARAMETER_IS_BOOLEAN) hints |= ::PARAMETER_IS_BOOLEAN; if (paramData.hints & PARAMETER_IS_INTEGER) hints |= ::PARAMETER_IS_INTEGER; if (paramData.hints & PARAMETER_IS_LOGARITHMIC) hints |= ::PARAMETER_IS_LOGARITHMIC; if (paramData.hints & PARAMETER_IS_AUTOMABLE) hints |= ::PARAMETER_IS_AUTOMABLE; if (paramData.hints & PARAMETER_USES_SAMPLERATE) hints |= ::PARAMETER_USES_SAMPLE_RATE; if (paramData.hints & PARAMETER_USES_SCALEPOINTS) hints |= ::PARAMETER_USES_SCALEPOINTS; if (paramData.hints & PARAMETER_USES_CUSTOM_TEXT) hints |= ::PARAMETER_USES_CUSTOM_TEXT; if (paramData.type == PARAMETER_INPUT || paramData.type == PARAMETER_OUTPUT) { if (paramData.hints & PARAMETER_IS_ENABLED) hints |= ::PARAMETER_IS_ENABLED; if (paramData.type == PARAMETER_OUTPUT) hints |= ::PARAMETER_IS_OUTPUT; } param.hints = static_cast< ::ParameterHints>(hints); param.name = strBufName; param.unit = strBufUnit; param.ranges.def = paramRanges.def; param.ranges.min = paramRanges.min; param.ranges.max = paramRanges.max; param.ranges.step = paramRanges.step; param.ranges.stepSmall = paramRanges.stepSmall; param.ranges.stepLarge = paramRanges.stepLarge; param.scalePointCount = 0; // TODO param.scalePoints = nullptr; return ¶m; } float getParameterValue(const uint32_t index) override { if (index >= getParameterCount()) return 0.0f; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return 0.0f; return plugin->getParameterValue(index); } const char* getParameterText(const uint32_t index) override { if (index >= getParameterCount()) return nullptr; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return nullptr; static char strBuf[STR_MAX+1]; plugin->getParameterText(index, strBuf); return strBuf; } // ------------------------------------------------------------------- // Plugin midi-program calls uint32_t getMidiProgramCount() override { if (kData->curPluginCount == 0 || kData->plugins == nullptr) return 0; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return 0.0f; return plugin->midiProgramCount(); } const MidiProgram* getMidiProgramInfo(const uint32_t index) override { if (index >= getMidiProgramCount()) return nullptr; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return nullptr; static ::MidiProgram midiProg; { const MidiProgramData& midiProgData(plugin->midiProgramData(index)); midiProg.bank = midiProgData.bank; midiProg.program = midiProgData.program; midiProg.name = midiProgData.name; } return &midiProg; } // ------------------------------------------------------------------- // Plugin state calls void setParameterValue(const uint32_t index, const float value) override { if (index >= getParameterCount()) return; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return; plugin->setParameterValue(index, value, false, false, false); } void setMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override { if (kData->curPluginCount == 0 || kData->plugins == nullptr) return; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return; plugin->setMidiProgramById(bank, program, false, false, false); } void setCustomData(const char* const key, const char* const value) override { CARLA_ASSERT(key != nullptr); CARLA_ASSERT(value != nullptr); return; // TODO // unused (void)key; (void)value; } // ------------------------------------------------------------------- // Plugin process calls void activate() override { for (uint32_t i=0; i < kData->curPluginCount; ++i) { CarlaPlugin* const plugin(kData->plugins[i].plugin); if (plugin == nullptr || ! plugin->enabled()) continue; plugin->setActive(true, true, false); } } void deactivate() override { for (uint32_t i=0; i < kData->curPluginCount; ++i) { CarlaPlugin* const plugin(kData->plugins[i].plugin); if (plugin == nullptr || ! plugin->enabled()) continue; plugin->setActive(false, true, false); } // just in case proccessPendingEvents(); } void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const uint32_t midiEventCount, const ::MidiEvent* const midiEvents) override { if (kData->curPluginCount == 0) { carla_zeroFloat(outBuffer[0], frames); carla_zeroFloat(outBuffer[1], frames); return CarlaEngine::proccessPendingEvents(); } // --------------------------------------------------------------- // Time Info const ::TimeInfo* timeInfo(PluginDescriptorClass::getTimeInfo()); fTimeInfo.playing = timeInfo->playing; fTimeInfo.frame = timeInfo->frame; fTimeInfo.usecs = timeInfo->usecs; fTimeInfo.valid = 0x0; if (timeInfo->bbt.valid) { fTimeInfo.valid |= EngineTimeInfo::ValidBBT; fTimeInfo.bbt.bar = timeInfo->bbt.bar; fTimeInfo.bbt.beat = timeInfo->bbt.beat; fTimeInfo.bbt.tick = timeInfo->bbt.tick; fTimeInfo.bbt.barStartTick = timeInfo->bbt.barStartTick; fTimeInfo.bbt.beatsPerBar = timeInfo->bbt.beatsPerBar; fTimeInfo.bbt.beatType = timeInfo->bbt.beatType; fTimeInfo.bbt.ticksPerBeat = timeInfo->bbt.ticksPerBeat; fTimeInfo.bbt.beatsPerMinute = timeInfo->bbt.beatsPerMinute; } // --------------------------------------------------------------- // initialize input events carla_zeroStruct(kData->bufEvent.in, INTERNAL_EVENT_COUNT); { uint32_t engineEventIndex = 0; for (uint32_t i=0; i < midiEventCount && engineEventIndex < INTERNAL_EVENT_COUNT; ++i) { const ::MidiEvent& midiEvent(midiEvents[i]); if (midiEvent.size > 4) continue; const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.data); const uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data); // we don't want some events if (status == MIDI_STATUS_PROGRAM_CHANGE) continue; // handle note/sound off properly if (status == MIDI_STATUS_CONTROL_CHANGE) { const uint8_t control = midiEvent.data[1]; if (MIDI_IS_CONTROL_BANK_SELECT(control)) continue; if (control == MIDI_CONTROL_ALL_SOUND_OFF || control == MIDI_CONTROL_ALL_NOTES_OFF) { EngineEvent& engineEvent(kData->bufEvent.in[engineEventIndex++]); engineEvent.clear(); engineEvent.type = kEngineEventTypeControl; engineEvent.time = midiEvent.time; engineEvent.channel = channel; engineEvent.ctrl.type = (control == MIDI_CONTROL_ALL_SOUND_OFF) ? kEngineControlEventTypeAllSoundOff : kEngineControlEventTypeAllNotesOff; engineEvent.ctrl.param = 0; engineEvent.ctrl.value = 0.0f; continue; } } EngineEvent& engineEvent(kData->bufEvent.in[engineEventIndex++]); engineEvent.clear(); engineEvent.type = kEngineEventTypeMidi; engineEvent.time = midiEvent.time; engineEvent.channel = channel; engineEvent.midi.data[0] = MIDI_GET_STATUS_FROM_DATA(midiEvent.data); engineEvent.midi.data[1] = midiEvent.data[1]; engineEvent.midi.data[2] = midiEvent.data[2]; engineEvent.midi.data[3] = midiEvent.data[3]; engineEvent.midi.size = midiEvent.size; } } // --------------------------------------------------------------- // create audio buffers float* inBuf[2] = { inBuffer[0], inBuffer[1] }; float* outBuf[2] = { outBuffer[0], outBuffer[1] }; // --------------------------------------------------------------- // process CarlaEngine::processRack(inBuf, outBuf, frames); CarlaEngine::proccessPendingEvents(); } // ------------------------------------------------------------------- // Plugin UI calls void uiShow(const bool show) override { return; // TODO // unused (void)show; } void uiIdle() override { CarlaEngine::idle(); } void uiSetParameterValue(const uint32_t index, const float value) override { if (index >= getParameterCount()) return; CarlaPlugin* const plugin(kData->plugins[0].plugin); if (plugin == nullptr || ! plugin->enabled()) return; plugin->uiParameterChange(index, value); } void uiSetMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override { return; // TODO // unused (void)channel; (void)bank; (void)program; } void uiSetCustomData(const char* const key, const char* const value) override { CARLA_ASSERT(key != nullptr); CARLA_ASSERT(value != nullptr); return; // TODO // unused (void)key; (void)value; } // ------------------------------------------------------------------- // Plugin state calls char* getState() override { QString string; QTextStream out(&string); out << "\n"; out << "\n"; out << "\n"; bool firstPlugin = true; char strBuf[STR_MAX+1]; for (unsigned int i=0; i < kData->curPluginCount; ++i) { CarlaPlugin* const plugin = kData->plugins[i].plugin; if (plugin != nullptr && plugin->enabled()) { if (! firstPlugin) out << "\n"; plugin->getRealName(strBuf); if (*strBuf != 0) out << QString(" \n").arg(xmlSafeString(strBuf, true)); out << " \n"; out << getXMLFromSaveState(plugin->getSaveState()); out << " \n"; firstPlugin = false; } } out << "\n"; return strdup(string.toUtf8().constData()); } void setState(const char* const data) override { QDomDocument xml; xml.setContent(QString(data)); QDomNode xmlNode(xml.documentElement()); if (xmlNode.toElement().tagName() != "CARLA-PROJECT") { carla_stderr2("Not a valid Carla project"); return; } QDomNode node(xmlNode.firstChild()); while (! node.isNull()) { if (node.toElement().tagName() == "Plugin") { const SaveState& saveState(getSaveStateDictFromXML(node)); CARLA_ASSERT(saveState.type != nullptr); if (saveState.type == nullptr) continue; const void* extraStuff = nullptr; // FIXME //if (std::strcmp(saveState.type, "DSSI") == 0) // extraStuff = findDSSIGUI(saveState.binary, saveState.label); // TODO - proper find&load plugins if (addPlugin(getPluginTypeFromString(saveState.type), saveState.binary, saveState.name, saveState.label, extraStuff)) { if (CarlaPlugin* plugin = getPlugin(kData->curPluginCount-1)) plugin->loadSaveState(saveState); } } node = node.nextSibling(); } } // ------------------------------------------------------------------- private: PluginDescriptorClassEND(CarlaEngineNative) CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNative) }; // ----------------------------------------------------------------------- static const PluginDescriptor carlaDesc = { /* category */ ::PLUGIN_CATEGORY_OTHER, /* hints */ static_cast< ::PluginHints>(::PLUGIN_IS_SYNTH|::PLUGIN_USES_SINGLE_THREAD|::PLUGIN_USES_STATE), /* audioIns */ 2, /* audioOuts */ 2, /* midiIns */ 1, /* midiOuts */ 1, /* paramIns */ 0, /* paramOuts */ 0, /* name */ "Carla-Plugin", /* label */ "carla", /* maker */ "falkTX", /* copyright */ "GNU GPL v2+", PluginDescriptorFILL(CarlaEngineNative) }; void CarlaEngine::registerNativePlugin() { carla_register_native_plugin(&carlaDesc); } CARLA_BACKEND_END_NAMESPACE // ----------------------------------------------------------------------- #endif // ! BUILD_BRIDGE