diff --git a/hi_backend/backend/BackendApplicationCommands.cpp b/hi_backend/backend/BackendApplicationCommands.cpp index c47372bf71..f260e0ac89 100644 --- a/hi_backend/backend/BackendApplicationCommands.cpp +++ b/hi_backend/backend/BackendApplicationCommands.cpp @@ -112,6 +112,7 @@ void BackendCommandTarget::getAllCommands(Array& commands) MenuFileSettingCheckSanity, MenuFileSettingsCleanBuildDirectory, MenuFileCreateThirdPartyNode, + MenuFileExtractEmbeddeSnippetFiles, MenuReplaceWithClipboardContent, MenuExportFileAsPlugin, MenuExportFileAsEffectPlugin, @@ -195,6 +196,22 @@ void BackendCommandTarget::getAllCommands(Array& commands) commands.addArray(id, numElementsInArray(id)); } +void BackendCommandTarget::setCopyPasteTarget(CopyPasteTarget* newTarget) +{ + if (currentCopyPasteTarget.get() != nullptr) + { + currentCopyPasteTarget->deselect(); + } + else + { + mainCommandManager->setFirstCommandTarget(this); + } + + currentCopyPasteTarget = newTarget; + + updateCommands(); +} + void BackendCommandTarget::createMenuBarNames() { menuNames.clear(); @@ -296,6 +313,10 @@ void BackendCommandTarget::getCommandInfo(CommandID commandID, ApplicationComman setCommandTarget(result, "Archive Project", GET_PROJECT_HANDLER(bpe->getMainSynthChain()).isActive(), false, 'X', false); result.categoryName = "File"; break; + case MenuFileExtractEmbeddeSnippetFiles: + setCommandTarget(result, "Extract embedded files from HISE snippet", GET_PROJECT_HANDLER(bpe->getMainSynthChain()).isActive(), false, 'X', false); + result.categoryName = "File"; + break; case MenuFileDownloadNewProject: setCommandTarget(result, "Download archived Project", true, false, 'X', false); result.categoryName = "File"; @@ -733,6 +754,7 @@ bool BackendCommandTarget::perform(const InvocationInfo &info) case MenuFileSettingsCleanBuildDirectory: Actions::cleanBuildDirectory(bpe); return true; case MenuFileCreateThirdPartyNode: Actions::createThirdPartyNode(bpe); return true; case MenuReplaceWithClipboardContent: Actions::replaceWithClipboardContent(bpe); return true; + case MenuFileExtractEmbeddeSnippetFiles: Actions::extractEmbeddedFilesFromSnippet(bpe); return true; case MenuFileQuit: if (PresetHandler::showYesNoWindow("Quit Application", "Do you want to quit?")) JUCEApplicationBase::quit(); return true; case MenuEditUndo: bpe->owner->getControlUndoManager()->undo(); updateCommands(); return true; @@ -940,6 +962,7 @@ PopupMenu BackendCommandTarget::getMenuForIndex(int topLevelMenuIndex, const Str ADD_ALL_PLATFORMS(MenuReplaceWithClipboardContent); + ADD_ALL_PLATFORMS(MenuFileExtractEmbeddeSnippetFiles); ADD_ALL_PLATFORMS(MenuFileCreateRecoveryXml); @@ -1289,6 +1312,7 @@ void BackendCommandTarget::Actions::replaceWithClipboardContent(BackendRootWindo if (v.isValid()) { bpe->loadNewContainer(v); + return; } } @@ -1736,13 +1760,27 @@ void BackendCommandTarget::Actions::exportFileAsSnippet(BackendProcessor* bp) MainController::ScopedEmbedAllResources sd(bp); ValueTree v = bp->getMainSynthChain()->exportAsValueTree(); + + auto scriptRootFolder = bp->getCurrentFileHandler().getSubDirectory(FileHandlerBase::Scripts); + auto snexRootFolder = BackendDllManager::getSubFolder(bp, BackendDllManager::FolderSubType::CodeLibrary); + auto embeddedScripts = bp->collectIncludedScriptFilesForSnippet("embeddedScripts", scriptRootFolder); + auto embeddedSnexFiles = bp->collectIncludedScriptFilesForSnippet("embeddedSnexFiles", snexRootFolder); + MemoryOutputStream mos; - v.writeToStream(mos); + if(embeddedScripts.getNumChildren() > 0 || embeddedSnexFiles.getNumChildren() > 0) + { + ValueTree nv("extended_snippet"); + nv.addChild(v, -1, nullptr); + nv.addChild(embeddedScripts, -1, nullptr); + nv.addChild(embeddedSnexFiles, -1, nullptr); + nv.writeToStream(mos); + } + else + v.writeToStream(mos); MemoryOutputStream mos2; - GZIPCompressorOutputStream zipper(&mos2, 9); zipper.write(mos.getData(), mos.getDataSize()); @@ -1756,7 +1794,6 @@ void BackendCommandTarget::Actions::exportFileAsSnippet(BackendProcessor* bp) { PresetHandler::showMessageWindow("Preset copied as compressed snippet", "You can paste the clipboard content to share this preset", PresetHandler::IconType::Info); } - } void BackendCommandTarget::Actions::createRnboTemplate(BackendRootWindow* bpe) @@ -3111,6 +3148,27 @@ void BackendCommandTarget::Actions::restoreToDefault(BackendRootWindow * bpe) debugToConsole(mp, message); } +void BackendCommandTarget::Actions::extractEmbeddedFilesFromSnippet(BackendRootWindow* bpe) +{ + auto gp = dynamic_cast(bpe->getBackendProcessor()); + + int numWritten = 0; + + auto chain = bpe->getBackendProcessor()->getMainSynthChain(); + + for(int i = 0; i < gp->getNumExternalScriptFiles(); i++) + { + if(gp->getExternalScriptFile(i)->extractEmbedded()) + { + debugToConsole(chain, "Extracted " + gp->getExternalScriptFile(i)->getFile().getFullPathName()); + numWritten++; + } + } + + debugToConsole(chain, "Extracted " + String(numWritten) + " files from currently loaded HISE snippet"); + +} + #undef REPLACE_WILDCARD #undef REPLACE_WILDCARD_WITH_STRING diff --git a/hi_backend/backend/BackendApplicationCommands.h b/hi_backend/backend/BackendApplicationCommands.h index 51506b9bcb..6996f2f1f3 100644 --- a/hi_backend/backend/BackendApplicationCommands.h +++ b/hi_backend/backend/BackendApplicationCommands.h @@ -94,6 +94,7 @@ class BackendCommandTarget: public ApplicationCommandTarget, MenuCloseProject, MenuFileArchiveProject, MenuFileImportProjectFromHXI, + MenuFileExtractEmbeddeSnippetFiles, MenuFileDownloadNewProject, MenuFileCreateRecoveryXml, MenuProjectShowInFinder, @@ -276,21 +277,7 @@ class BackendCommandTarget: public ApplicationCommandTarget, menuItemsChanged(); } - void setCopyPasteTarget(CopyPasteTarget *newTarget) - { - if (currentCopyPasteTarget.get() != nullptr) - { - currentCopyPasteTarget->deselect(); - } - else - { - mainCommandManager->setFirstCommandTarget(this); - } - - currentCopyPasteTarget = newTarget; - - updateCommands(); - } + void setCopyPasteTarget(CopyPasteTarget *newTarget); void createMenuBarNames(); @@ -404,6 +391,8 @@ class BackendCommandTarget: public ApplicationCommandTarget, static void createThirdPartyNode(BackendRootWindow* bpe); static void restoreToDefault(BackendRootWindow * bpe); + + static void extractEmbeddedFilesFromSnippet(BackendRootWindow* bpe); }; private: diff --git a/hi_core/hi_core/GlobalScriptCompileBroadcaster.cpp b/hi_core/hi_core/GlobalScriptCompileBroadcaster.cpp index 4c61d706fd..76b7a42b68 100644 --- a/hi_core/hi_core/GlobalScriptCompileBroadcaster.cpp +++ b/hi_core/hi_core/GlobalScriptCompileBroadcaster.cpp @@ -92,6 +92,57 @@ void GlobalScriptCompileBroadcaster::clearIncludedFiles() includedFiles.clear(); } +void GlobalScriptCompileBroadcaster::restoreIncludedScriptFilesFromSnippet(const ValueTree& snippetTree) +{ +#if USE_BACKEND + auto mc = dynamic_cast(this); + auto scriptRootFolder = mc->getActiveFileHandler()->getSubDirectory(FileHandlerBase::Scripts); + auto snexRootFolder = BackendDllManager::getSubFolder(mc, BackendDllManager::FolderSubType::CodeLibrary); + + auto restoreFromChild = [&](const Identifier& id, const File& rootDirectory) + { + auto v = snippetTree.getChildWithName(id); + jassert(v.isValid()); + + auto chain = dynamic_cast(this)->getMainSynthChain(); + + for(auto c: v) + { + debugToConsole(const_cast(chain), "loaded embedded file " + c["filename"].toString() + " from HISE snippet"); + auto n = new ExternalScriptFile(rootDirectory, c); + includedFiles.add(n); + } + }; + + restoreFromChild("embeddedScripts", scriptRootFolder); + restoreFromChild("embeddedSnexFiles", snexRootFolder); +#endif +} + +ValueTree GlobalScriptCompileBroadcaster::collectIncludedScriptFilesForSnippet(const Identifier& id, const File& root) const +{ +#if USE_BACKEND + ValueTree d(id); + + auto chain = dynamic_cast(this)->getMainSynthChain(); + + for(auto f: includedFiles) + { + if(!f->getFile().isAChildOf(root)) + continue; + + auto c = f->toEmbeddableValueTree(root); + debugToConsole(const_cast(chain), "embedded " + c["filename"].toString() + " into HISE snippet"); + d.addChild(c, -1, nullptr); + } + + return d; +#else + jassertfalse; + return {}; +#endif +} + ScriptComponentEditBroadcaster* GlobalScriptCompileBroadcaster::getScriptComponentEditBroadcaster() { return globalEditBroadcaster; @@ -234,7 +285,7 @@ String GlobalScriptCompileBroadcaster::getExternalScriptFromCollection(const Str return String(); } -ExternalScriptFile::Ptr GlobalScriptCompileBroadcaster::getExternalScriptFile(const File& fileToInclude) +ExternalScriptFile::Ptr GlobalScriptCompileBroadcaster::getExternalScriptFile(const File& fileToInclude, bool createIfNotFound) { for (int i = 0; i < includedFiles.size(); i++) { @@ -242,9 +293,10 @@ ExternalScriptFile::Ptr GlobalScriptCompileBroadcaster::getExternalScriptFile(co return includedFiles[i]; } - includedFiles.add(new ExternalScriptFile(fileToInclude)); + if(createIfNotFound) + return includedFiles.add(new ExternalScriptFile(fileToInclude)); - return includedFiles.getLast(); + return nullptr; } juce::ValueTree GlobalScriptCompileBroadcaster::exportWebViewResources() @@ -400,6 +452,7 @@ ExternalScriptFile::RuntimeError::RuntimeError() = default; ExternalScriptFile::ExternalScriptFile(const File& file): file(file), + resourceType(ResourceType::FileBased), currentResult(Result::ok()) { #if USE_BACKEND diff --git a/hi_core/hi_core/GlobalScriptCompileBroadcaster.h b/hi_core/hi_core/GlobalScriptCompileBroadcaster.h index 00e43acc4e..dd119e1f6f 100644 --- a/hi_core/hi_core/GlobalScriptCompileBroadcaster.h +++ b/hi_core/hi_core/GlobalScriptCompileBroadcaster.h @@ -67,6 +67,13 @@ class ExternalScriptFile : public ReferenceCountedObject { public: + enum class ResourceType + { + EmbeddedInSnippet, + FileBased, + numResourceTypes + }; + struct RuntimeError { using Broadcaster = LambdaBroadcaster*>; @@ -102,6 +109,19 @@ class ExternalScriptFile : public ReferenceCountedObject ExternalScriptFile(const File&file); + ExternalScriptFile(const File& rootDirectory, const ValueTree& v): + resourceType(ResourceType::EmbeddedInSnippet), + file(rootDirectory.getChildFile(v["filename"].toString())), + currentResult(Result::ok()) + { + +#if USE_BACKEND + content.replaceAllContent(v["content"]); + content.setSavePoint(); + content.clearUndoHistory(); +#endif + } + ~ExternalScriptFile(); void setResult(Result r); @@ -112,14 +132,48 @@ class ExternalScriptFile : public ReferenceCountedObject File getFile() const; + ValueTree toEmbeddableValueTree(const File& rootDirectory) const + { + auto fn = getFile(); + auto name = fn.getRelativePathFrom(rootDirectory); + + ValueTree c("file"); + c.setProperty("filename", name.replaceCharacter('\\', '/'), nullptr); + c.setProperty("content", content.getAllContent(), nullptr); + return c; + } + typedef ReferenceCountedObjectPtr Ptr; void setRuntimeErrors(const Result& r); RuntimeError::Broadcaster& getRuntimeErrorBroadcaster(); + ResourceType getResourceType() const + { + return resourceType; + } + + bool extractEmbedded() + { + if(resourceType == ResourceType::EmbeddedInSnippet) + { + if(!file.existsAsFile() || PresetHandler::showYesNoWindow("Overwrite local file", "The file " + getFile().getFileName() + " from the snippet already exists. Do you want to overwrite your local file?")) + { + file.getParentDirectory().createDirectory(); + file.replaceWithText(content.getAllContent()); + resourceType = ResourceType::FileBased; + return true; + } + } + + return false; + } + private: + ResourceType resourceType; + RuntimeError::Broadcaster runtimeErrorBroadcaster; Array runtimeErrors; @@ -179,12 +233,16 @@ class GlobalScriptCompileBroadcaster int getNumExternalScriptFiles() const; - ExternalScriptFile::Ptr getExternalScriptFile(const File& fileToInclude); + ExternalScriptFile::Ptr getExternalScriptFile(const File& fileToInclude, bool createIfNotFound); ExternalScriptFile::Ptr getExternalScriptFile(int index) const; void clearIncludedFiles(); + void restoreIncludedScriptFilesFromSnippet(const ValueTree& snippetTree); + + ValueTree collectIncludedScriptFilesForSnippet(const Identifier& id, const File& root) const; + ScriptComponentEditBroadcaster* getScriptComponentEditBroadcaster(); const ScriptComponentEditBroadcaster* getScriptComponentEditBroadcaster() const; diff --git a/hi_core/hi_core/MainController.cpp b/hi_core/hi_core/MainController.cpp index 9c2dadba5d..33e4e77e8a 100644 --- a/hi_core/hi_core/MainController.cpp +++ b/hi_core/hi_core/MainController.cpp @@ -344,16 +344,14 @@ void MainController::loadPresetFromValueTree(const ValueTree &v, Component* /*ma ignoreUnused(isCommandLine, isSampleLoadingThread); #endif - if (v.isValid() && v.getProperty("Type", var::undefined()).toString() == "SynthChain") - { - if (v.getType() != Identifier("Processor")) - { - jassertfalse; - - } + if (v.isValid() ) + { + auto isExtendedSnippet = v.getType() == Identifier("extended_snippet"); + auto isValidPreset = (v.getType() == Identifier("Processor") && v.getProperty("Type", var::undefined()).toString() == "SynthChain"); - loadPresetInternal(v); + if(isExtendedSnippet || isValidPreset) + loadPresetInternal(v); } else { @@ -362,9 +360,9 @@ void MainController::loadPresetFromValueTree(const ValueTree &v, Component* /*ma } -void MainController::loadPresetInternal(const ValueTree& v) +void MainController::loadPresetInternal(const ValueTree& valueTreeToLoad) { - auto f = [this, v](Processor* ) + auto f = [this, valueTreeToLoad](Processor* ) { LockHelpers::freeToGo(this); @@ -388,7 +386,22 @@ void MainController::loadPresetInternal(const ValueTree& v) getSampleManager().setShouldSkipPreloading(true); - + ValueTree v; + + if(valueTreeToLoad.getType() == Identifier("Processor")) + { + v = valueTreeToLoad; + } + else + { + v = valueTreeToLoad.getChildWithName("Processor"); + + // restore the included files now... + + restoreIncludedScriptFilesFromSnippet(valueTreeToLoad); + } + + jassert(v.isValid()); // Reset the sample rate so that prepareToPlay does not get called in restoreFromValueTree // synthChain->setCurrentPlaybackSampleRate(-1.0); diff --git a/hi_core/hi_sampler/sampler/ModulatorSamplerVoice.cpp b/hi_core/hi_sampler/sampler/ModulatorSamplerVoice.cpp index 68f9e49649..8101d1b712 100644 --- a/hi_core/hi_sampler/sampler/ModulatorSamplerVoice.cpp +++ b/hi_core/hi_sampler/sampler/ModulatorSamplerVoice.cpp @@ -54,7 +54,7 @@ void ModulatorSamplerVoice::startVoiceInternal(int midiNoteNumber, float velocit int ModulatorSamplerVoice::calculateSampleStartMod() { - int sampleStartModulationDelta; + int sampleStartModulationDelta = 0; // if value is >= 0, then it comes from the modulator chain value const bool sampleStartModIsFromChain = sampleStartModValue >= 0.0f; diff --git a/hi_faust_jit/FaustJitNode.cpp b/hi_faust_jit/FaustJitNode.cpp index 420c002356..5d343a3d13 100644 --- a/hi_faust_jit/FaustJitNode.cpp +++ b/hi_faust_jit/FaustJitNode.cpp @@ -263,17 +263,24 @@ void faust_jit_node_base::reinitFaustWrapper() //resetParameters(); File sourceFile = getFaustFile(newClassId); - // Do nothing if file doesn't exist: - if (!sourceFile.existsAsFile()) { + + String code; + + auto mc = getRootNetwork()->getMainController(); + auto ef = mc->getExternalScriptFile(sourceFile, false); + + if(ef != nullptr) + code = ef->getFileDocument().getAllContent(); + else if (sourceFile.existsAsFile()) + code = sourceFile.loadFileAsString(); + else + { DBG("Could not load Faust source file (" + sourceFile.getFullPathName() + "): File not found."); return; } - + DBG("Faust DSP file to load:" << sourceFile.getFullPathName()); - - // Load file and recompile - String code = sourceFile.loadFileAsString(); - + bool classIdValid = setFaustCode(newClassId, code.toStdString()); if (!classIdValid) diff --git a/hi_scripting/scripting/ScriptProcessor.cpp b/hi_scripting/scripting/ScriptProcessor.cpp index 0e63091270..4ba96d6d73 100644 --- a/hi_scripting/scripting/ScriptProcessor.cpp +++ b/hi_scripting/scripting/ScriptProcessor.cpp @@ -458,7 +458,7 @@ FileChangeListener::~FileChangeListener() ExternalScriptFile::Ptr FileChangeListener::addFileWatcher(const File &file) { - auto p = dynamic_cast(this)->getMainController()->getExternalScriptFile(file); + auto p = dynamic_cast(this)->getMainController()->getExternalScriptFile(file, true); watchers.add(p); diff --git a/hi_scripting/scripting/ScriptProcessor.h b/hi_scripting/scripting/ScriptProcessor.h index 2fe31d4854..9a33a4fe68 100644 --- a/hi_scripting/scripting/ScriptProcessor.h +++ b/hi_scripting/scripting/ScriptProcessor.h @@ -238,6 +238,16 @@ class FileChangeListener File getWatchedFile(int index) const; + bool isEmbeddedSnippetFile(int index) const + { + if(isPositiveAndBelow(index, watchers.size())) + { + return watchers[index]->getResourceType() == ExternalScriptFile::ResourceType::EmbeddedInSnippet; + } + + return false; + } + CodeDocument& getWatchedFileDocument(int index); void setCurrentPopup(DocumentWindow *window); diff --git a/hi_scripting/scripting/api/ScriptingGraphics.cpp b/hi_scripting/scripting/api/ScriptingGraphics.cpp index 685d2ebc13..1b1f91cc05 100644 --- a/hi_scripting/scripting/api/ScriptingGraphics.cpp +++ b/hi_scripting/scripting/api/ScriptingGraphics.cpp @@ -99,9 +99,19 @@ String ScriptingObjects::ScriptShader::FileParser::createLinePointer(int i) cons String ScriptingObjects::ScriptShader::FileParser::loadFileContent() { #if USE_BACKEND - auto f = getMainController()->getCurrentFileHandler().getSubDirectory(FileHandlerBase::Scripts).getChildFile(fileNameWithoutExtension).withFileExtension("glsl"); - if(!f.existsAsFile()) + auto glslFile = getMainController()->getCurrentFileHandler().getSubDirectory(FileHandlerBase::Scripts).getChildFile(fileNameWithoutExtension).withFileExtension("glsl"); + + + auto ef = getMainController()->getExternalScriptFile(glslFile, false); + + String content; + + if(ef != nullptr) + content = ef->getFileDocument().getAllContent(); + else if (glslFile.existsAsFile()) + content = glslFile.loadFileAsString(); + else { String s; String nl = "\n"; @@ -118,21 +128,22 @@ String ScriptingObjects::ScriptShader::FileParser::loadFileContent() s << " fragColor = pixelAlpha * vec4(col,1.0);" << nl; s << "}" << nl; - f.replaceWithText(s); + content = s; + glslFile.replaceWithText(s); } - + if (auto jp = dynamic_cast(sp)) { - auto ef = jp->addFileWatcher(f); + ef = jp->addFileWatcher(glslFile); if (includedFiles.contains(ef)) throw String("Trying to include " + fileNameWithoutExtension + " multiple times"); includedFiles.add(ef); - jp->getScriptEngine()->addShaderFile(f); + jp->getScriptEngine()->addShaderFile(glslFile); } - return f.loadFileAsString(); + return content; #else if (!fileNameWithoutExtension.endsWith(".glsl")) diff --git a/hi_scripting/scripting/components/PopupEditors.cpp b/hi_scripting/scripting/components/PopupEditors.cpp index 1b9ff38608..cccb908ebd 100644 --- a/hi_scripting/scripting/components/PopupEditors.cpp +++ b/hi_scripting/scripting/components/PopupEditors.cpp @@ -108,7 +108,7 @@ PopupIncludeEditor::PopupIncludeEditor(JavascriptProcessor *s, const File &fileT Processor *p = dynamic_cast(jp.get()); - externalFile = p->getMainController()->getExternalScriptFile(fileToEdit); + externalFile = p->getMainController()->getExternalScriptFile(fileToEdit, true); p->getMainController()->addScriptListener(this); @@ -480,12 +480,17 @@ File PopupIncludeEditor::getFile() const void PopupIncludeEditor::compileInternal() { - - if (externalFile != nullptr) { - externalFile->getFile().replaceWithText(externalFile->getFileDocument().getAllContent()); - externalFile->getFileDocument().setSavePoint(); + if(externalFile->getResourceType() == ExternalScriptFile::ResourceType::EmbeddedInSnippet) + { + debugToConsole(dynamic_cast(getScriptProcessor()), "Skip writing embedded file " + externalFile->getFile().getFileName() + " to disk..."); + } + else + { + externalFile->getFile().replaceWithText(externalFile->getFileDocument().getAllContent()); + externalFile->getFileDocument().setSavePoint(); + } } Component::SafePointer safeP(this); diff --git a/hi_scripting/scripting/components/ScriptingPanelTypes.cpp b/hi_scripting/scripting/components/ScriptingPanelTypes.cpp index 6dee556c9b..57bfefeef9 100644 --- a/hi_scripting/scripting/components/ScriptingPanelTypes.cpp +++ b/hi_scripting/scripting/components/ScriptingPanelTypes.cpp @@ -274,6 +274,10 @@ void CodeEditorPanel::fillIndexList(StringArray& indexList) { auto f = p->getWatchedFile(i); auto path = f.getRelativePathFrom(scriptRoot); + + if(p->isEmbeddedSnippetFile(i)) + path << " (embedded)"; + indexList.add(path.replaceCharacter('\\', '/')); } diff --git a/hi_scripting/scripting/engine/JavascriptEngineParser.cpp b/hi_scripting/scripting/engine/JavascriptEngineParser.cpp index 2208f950f0..c46c47ac8b 100644 --- a/hi_scripting/scripting/engine/JavascriptEngineParser.cpp +++ b/hi_scripting/scripting/engine/JavascriptEngineParser.cpp @@ -800,9 +800,18 @@ struct HiseJavascriptEngine::RootObject::ExpressionTreeBuilder : private TokenIt File f(refFileName); const String shortFileName = f.getFileName(); - if (!f.existsAsFile()) - throwError("File " + refFileName + " not found"); + auto mc = dynamic_cast(hiseSpecialData->processor)->getMainController(); + auto ef = mc->getExternalScriptFile(f, false); + + String code; + if(ef != nullptr) + code = ef->getFileDocument().getAllContent(); + else if (f.existsAsFile()) + code = f.loadFileAsString(); + else + throwError("File " + refFileName + " not found"); + if (!allowMultipleIncludes) { for (int i = 0; i < hiseSpecialData->includedFiles.size(); i++) @@ -815,7 +824,7 @@ struct HiseJavascriptEngine::RootObject::ExpressionTreeBuilder : private TokenIt } } - return f.loadFileAsString(); + return code; #else diff --git a/hi_scripting/scripting/scriptnode/api/DspNetwork.cpp b/hi_scripting/scripting/scriptnode/api/DspNetwork.cpp index d955e6e5dc..84f7c5c381 100644 --- a/hi_scripting/scripting/scriptnode/api/DspNetwork.cpp +++ b/hi_scripting/scripting/scriptnode/api/DspNetwork.cpp @@ -1007,6 +1007,11 @@ Result DspNetwork::checkBeforeCompilation() return Result::ok(); } +void DspNetwork::deselect(NodeBase* node) +{ + selection.deselect(node); +} + void DspNetwork::addToSelection(NodeBase* node, ModifierKeys mods) { auto pNode = node->getParentNode(); @@ -1544,10 +1549,45 @@ void DspNetwork::Holder::saveNetworks(ValueTree& d) const if (embedResources) { - // Include all SNEX files here + auto mc = dynamic_cast(this)->getMainController(); + auto snexRootFolder = BackendDllManager::getSubFolder(mc, BackendDllManager::FolderSubType::CodeLibrary); + + valuetree::Helpers::forEach(c, [snexRootFolder, mc](ValueTree& v) + { + if(v.getType() == PropertyIds::Node) + { + auto props = v.getChildWithName(PropertyIds::Properties); + + auto ct = props.getChildWithProperty(PropertyIds::ID, PropertyIds::ClassId.toString()); + + if(ct.isValid()) + { + auto fileName = ct[PropertyIds::Value].toString(); + auto rootDir = v[PropertyIds::FactoryPath].toString().fromFirstOccurrenceOf(".", false, false); + + auto extension = rootDir == "faust" ? ".dsp" : ".h"; + + auto f = snexRootFolder.getChildFile(rootDir).getChildFile(fileName).withFileExtension(extension); + jassert(f.existsAsFile()); + + auto path = f.getRelativePathFrom(snexRootFolder); + auto content = f.loadFileAsString(); + + // just add it so it will be embedded into the snippet + const_cast(mc)->getExternalScriptFile(f, true); - // implement me... - jassertfalse; + auto pfile = f.withFileExtension(".xml"); + + if(pfile.existsAsFile()) + { + // add the parameter metadata file too... + const_cast(mc)->getExternalScriptFile(pfile, true); + } + + } + } + return false; + }); } else { @@ -1779,8 +1819,14 @@ snex::ui::WorkbenchData::Ptr DspNetwork::CodeManager::getOrCreate(const Identifi } auto targetFile = getCodeFolder().getChildFile(typeId.toString()).getChildFile(classId.toString()).withFileExtension("h"); - entries.add(new Entry(typeId, targetFile, parent.getScriptProcessor())); - return entries.getLast()->wb; + + auto ef = parent.getMainController()->getExternalScriptFile(targetFile, false); + + if(ef != nullptr) + return entries.add(new Entry(typeId, ef, parent.getScriptProcessor()))->wb; + else + return entries.add(new Entry(typeId, targetFile, parent.getScriptProcessor()))->wb; + } ValueTree DspNetwork::CodeManager::getParameterTree(const Identifier& typeId, const Identifier& classId) @@ -1814,22 +1860,58 @@ StringArray DspNetwork::CodeManager::getClassList(const Identifier& id, const St DspNetwork::CodeManager::Entry::Entry(const Identifier& t, const File& targetFile, ProcessorWithScriptingContent* sp): type(t), - parameterFile(targetFile.withFileExtension("xml")) + parameterFile(targetFile.withFileExtension("xml")), + resourceType(ExternalScriptFile::ResourceType::FileBased) { targetFile.create(); - cp = new snex::ui::WorkbenchData::DefaultCodeProvider(wb.get(), targetFile); - wb = new snex::ui::WorkbenchData(); - wb->setCodeProvider(cp, dontSendNotification); - wb->setCompileHandler(new SnexSourceCompileHandler(wb.get(), sp)); + ValueTree pTree; if (auto xml = XmlDocument::parse(parameterFile)) - parameterTree = ValueTree::fromXml(*xml); - else - parameterTree = ValueTree(PropertyIds::Parameters); + pTree = ValueTree::fromXml(*xml); + + init(new snex::ui::WorkbenchData::DefaultCodeProvider(wb.get(), targetFile), pTree, sp); +} + +struct EmbeddedSnippetCodeProvider: public snex::ui::WorkbenchData::CodeProvider +{ + EmbeddedSnippetCodeProvider(ExternalScriptFile::Ptr ef_): + CodeProvider(nullptr), + ef(ef_) + { + + } + + String loadCode() const override + { + return ef->getFileDocument().getAllContent(); + }; + + bool saveCode(const String& s) override + { + jassertfalse; + return true; + } + + /** Override this method and return the instance id. This will be used to find the main class in nodes. */ + virtual Identifier getInstanceId() const override { return ef->getFile().getFileNameWithoutExtension(); } + + ExternalScriptFile::Ptr ef; +}; + +DspNetwork::CodeManager::Entry::Entry(const Identifier& t, const ExternalScriptFile::Ptr& embeddedFile, + ProcessorWithScriptingContent* sp): + type(t), + resourceType(ExternalScriptFile::ResourceType::EmbeddedInSnippet) +{ + parameterExternalFile = sp->getMainController_()->getExternalScriptFile(embeddedFile->getFile().withFileExtension(".xml"), true); + + ValueTree pTree; + + if(auto xml = XmlDocument::parse(parameterExternalFile->getFileDocument().getAllContent())) + pTree = ValueTree::fromXml(*xml); - pListener.setCallback(parameterTree, valuetree::AsyncMode::Asynchronously, BIND_MEMBER_FUNCTION_2(Entry::parameterAddedOrRemoved)); - propListener.setCallback(parameterTree, RangeHelpers::getRangeIds(), valuetree::AsyncMode::Asynchronously, BIND_MEMBER_FUNCTION_2(Entry::propertyChanged)); + init(new EmbeddedSnippetCodeProvider(embeddedFile), pTree, sp); } void DspNetwork::CodeManager::Entry::parameterAddedOrRemoved(ValueTree, bool) @@ -1845,7 +1927,16 @@ void DspNetwork::CodeManager::Entry::propertyChanged(ValueTree, Identifier) void DspNetwork::CodeManager::Entry::updateFile() { auto xml = parameterTree.createXml(); - parameterFile.replaceWithText(xml->createDocument("")); + auto c = xml->createDocument(""); + + if(resourceType == ExternalScriptFile::ResourceType::FileBased) + { + parameterFile.replaceWithText(c); + } + else + { + parameterExternalFile->getFileDocument().replaceAllContent(c); + } } DspNetwork::CodeManager::SnexSourceCompileHandler::SnexSourceCompileHandler(snex::ui::WorkbenchData* d, ProcessorWithScriptingContent* sp_) : diff --git a/hi_scripting/scripting/scriptnode/api/DspNetwork.h b/hi_scripting/scripting/scriptnode/api/DspNetwork.h index beab33b2c4..2ff5f79c16 100644 --- a/hi_scripting/scripting/scriptnode/api/DspNetwork.h +++ b/hi_scripting/scripting/scriptnode/api/DspNetwork.h @@ -381,13 +381,13 @@ class DspNetwork : public ConstScriptingObject, { Entry(const Identifier& t, const File& targetFile, ProcessorWithScriptingContent* sp); + Entry(const Identifier& t, const ExternalScriptFile::Ptr& embeddedFile, ProcessorWithScriptingContent* sp); + const Identifier type; const File parameterFile; ScopedPointer cp; - - snex::ui::WorkbenchData::Ptr wb; void parameterAddedOrRemoved(ValueTree, bool); @@ -398,8 +398,28 @@ class DspNetwork : public ConstScriptingObject, private: + ExternalScriptFile::ResourceType resourceType; + + void init(snex::ui::WorkbenchData::CodeProvider* codeProvider, const ValueTree& pTree, ProcessorWithScriptingContent* sp) + { + cp = codeProvider; + wb = new snex::ui::WorkbenchData(); + wb->setCodeProvider(cp, dontSendNotification); + wb->setCompileHandler(new SnexSourceCompileHandler(wb.get(), sp)); + + parameterTree = pTree; + + if(!parameterTree.isValid()) + parameterTree = ValueTree(PropertyIds::Parameters); + + pListener.setCallback(parameterTree, valuetree::AsyncMode::Asynchronously, BIND_MEMBER_FUNCTION_2(Entry::parameterAddedOrRemoved)); + propListener.setCallback(parameterTree, RangeHelpers::getRangeIds(), valuetree::AsyncMode::Asynchronously, BIND_MEMBER_FUNCTION_2(Entry::propertyChanged)); + } + void updateFile(); + ExternalScriptFile::Ptr parameterExternalFile; + valuetree::ChildListener pListener; valuetree::RecursivePropertyListener propListener; }; @@ -566,10 +586,7 @@ class DspNetwork : public ConstScriptingObject, bool isSelected(NodeBase* node) const { return selection.isSelected(node); } - void deselect(NodeBase* node) - { - selection.deselect(node); - } + void deselect(NodeBase* node); void deselectAll() { selection.deselectAll(); } diff --git a/hi_scripting/scripting/scriptnode/ui/ScriptNodeFloatingTiles.cpp b/hi_scripting/scripting/scriptnode/ui/ScriptNodeFloatingTiles.cpp index b1debe3797..18be0d2ccb 100644 --- a/hi_scripting/scripting/scriptnode/ui/ScriptNodeFloatingTiles.cpp +++ b/hi_scripting/scripting/scriptnode/ui/ScriptNodeFloatingTiles.cpp @@ -389,7 +389,7 @@ struct FaustEditorWrapper: public Component, { if(currentDocument != nullptr) { - currentDocument->file.replaceWithText(currentDocument->d.getAllContent()); + currentDocument->writeFile(); network->faustManager.sendCompileMessage(currentDocument->file, sendNotificationSync); } } @@ -409,8 +409,14 @@ struct FaustEditorWrapper: public Component, if(currentDocument == nullptr) { - documents.add(new FaustDocument(f)); - currentDocument = documents.getLast(); + auto ef = network->getMainController()->getExternalScriptFile(f, false); + + if(ef != nullptr) + currentDocument = documents.add(new FaustDocument(ef)); + else if(f.existsAsFile()) + currentDocument = documents.add(new FaustDocument(f)); + else + return; } bottomBar = new EditorBottomBar(dynamic_cast(network->getScriptProcessor())); @@ -475,18 +481,48 @@ struct FaustEditorWrapper: public Component, editor->setBounds(b); } } - + struct FaustDocument { FaustDocument(const File& f): file(f), - doc(d) + docToUse(&d), + doc(*docToUse), + resourceType(ExternalScriptFile::ResourceType::FileBased) { + // should use the other constructor... + jassert(f.existsAsFile()); d.replaceAllContent(f.loadFileAsString()); }; - - File file; - CodeDocument d; + + FaustDocument(ExternalScriptFile::Ptr p): + file(p->getFile()), + docToUse(&p->getFileDocument()), + doc(*docToUse), + resourceType(ExternalScriptFile::ResourceType::EmbeddedInSnippet) + {} + + bool writeFile() + { + if(resourceType == ExternalScriptFile::ResourceType::FileBased) + { + return file.replaceWithText(docToUse->getAllContent()); + } + + return false; + } + + File file; + + private: + + CodeDocument d; + + public: + + const ExternalScriptFile::ResourceType resourceType; + + CodeDocument* docToUse = nullptr; mcl::TextDocument doc; }; diff --git a/hi_tools/hi_standalone_components/SliderPack.cpp b/hi_tools/hi_standalone_components/SliderPack.cpp index 8845bc357d..a025ac14ec 100644 --- a/hi_tools/hi_standalone_components/SliderPack.cpp +++ b/hi_tools/hi_standalone_components/SliderPack.cpp @@ -683,7 +683,7 @@ void SliderPack::mouseDown(const MouseEvent &e) auto thisValue = sliders[sliderIndex]->getValue(); auto useGhostNoteValue = e.mods.isAnyModifierKeyDown(); - auto ghostNoteValue = rng.getStart() + 0.25 * rng.getLength(); + auto ghostNoteValue = rng.getStart() + 0.5 * rng.getLength(); if(thisValue == rng.getStart()) {