diff --git a/.gitignore b/.gitignore index c1dfb9c..7895c8c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/ !/build/.gitkeep .vscode export/* +.cache diff --git a/App.cmake b/App.cmake index f2b9798..8ca00d7 100644 --- a/App.cmake +++ b/App.cmake @@ -8,13 +8,18 @@ # up by default. This function accepts many optional arguments. Check the readme at # `docs/CMake API.md` in the JUCE repo for the full list. +if(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + set(_needs_web_browser NEEDS_WEB_BROWSER TRUE) +endif() + juce_add_gui_app(RNBOApp # VERSION ... # Set this if the app version is different to the project version # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon # ICON_SMALL ... # DOCUMENT_EXTENSIONS ... # Specify file extensions that should be associated with this app COMPANY_NAME "cycling74" # Specify the name of the app's author - PRODUCT_NAME "RNBO App Example") # The name of the final executable, which can differ from the target name + PRODUCT_NAME "RNBO App Example" # The name of the final executable, which can differ from the target name + ${_needs_web_browser}) # `juce_generate_juce_header` will create a JuceHeader.h for a given target, which will be generated # into your build tree. This should be included with `#include `. The include path for @@ -34,7 +39,6 @@ target_sources(RNBOApp PRIVATE src/Main.cpp src/MainComponent.cpp - src/CustomAudioEditor.cpp src/CustomAudioProcessor.cpp ${RNBO_CLASS_FILE} @@ -45,6 +49,12 @@ target_sources(RNBOApp ${RNBO_CPP_DIR}/adapters/juce/RNBO_JuceAudioProcessor.cpp ) +if(RNBO_EDITOR_MODE STREQUAL "NATIVE") + target_sources(RNBOApp PRIVATE src/CustomAudioEditor.cpp) +elseif(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + target_sources(RNBOApp PRIVATE src/WebBrowserAudioEditor.cpp) +endif() + if (EXISTS ${RNBO_BINARY_DATA_FILE}) target_sources(RNBOApp PRIVATE ${RNBO_BINARY_DATA_FILE}) endif() @@ -61,17 +71,23 @@ target_include_directories(RNBOApp target_compile_definitions(RNBOApp PRIVATE - # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them. - JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_gui_app` call JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_gui_app` call JUCE_APPLICATION_NAME_STRING="$" - JUCE_APPLICATION_VERSION_STRING="$") + JUCE_APPLICATION_VERSION_STRING="$" + RNBO_JUCE_PARAM_DEFAULT_NOTIFY=$) # `target_link_libraries` links libraries and JUCE modules to other libraries or executables. Here, # we're linking our executable target to the `juce::juce_gui_extra` module. Inter-module # dependencies are resolved automatically, so `juce_core`, `juce_events` and so on will also be # linked automatically. If we'd generated a binary data target above, we would need to link to it # here too. This is a standard CMake command. +if(RNBO_EDITOR_MODE STREQUAL "NATIVE") + target_compile_definitions(RNBOApp PRIVATE RNBO_EDITOR_NATIVE) +elseif(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + target_compile_definitions(RNBOApp PRIVATE RNBO_EDITOR_WEBVIEW JUCE_WEB_BROWSER=1) + target_link_libraries(RNBOApp PRIVATE RNBOUIData) +endif() + target_link_libraries(RNBOApp PRIVATE juce::juce_gui_extra diff --git a/CMakeLists.txt b/CMakeLists.txt index 559d570..ca03ab6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.22) #JUCE 8 requires CMake 3.22+ # On M1 Macs, uncomment to enable universal binaries # set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "") @@ -7,7 +7,7 @@ set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build, options are: project(RNBO_JUCE_EXAMPLE VERSION 2.0.0) -set(CMAKE_CXX_STANDARD 14) #JUCE requires 14 +set(CMAKE_CXX_STANDARD 17) #JUCE 8 requires 17 set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_COLOR_DIAGNOSTICS ON) @@ -25,6 +25,13 @@ set(RNBO_BINARY_DATA_FILE "${RNBO_EXPORT_DIR}/${RNBO_CLASS_NAME}_binary.cpp") set(RNBO_BINARY_DATA_STORAGE_NAME "${RNBO_CLASS_NAME}_binary") set(PLUGIN_PARAM_DEFAULT_NOTIFY ON CACHE BOOL "Should parameter changes from inside your rnbo patch send output by default?") +# Choose which editor is shown when the plugin/app opens. +# DEFAULT — the generic editor provided by RNBO::JuceAudioProcessor +# NATIVE — native JUCE controls (src/CustomAudioEditor) +# WEBVIEW — WebBrowserComponent UI (src/WebBrowserAudioEditor) +set(RNBO_EDITOR_MODE "DEFAULT" CACHE STRING "UI editor: DEFAULT (Generic JUCE controls), NATIVE (Custom JUCE controls), WEBVIEW (WebBrowserComponent)") +set_property(CACHE RNBO_EDITOR_MODE PROPERTY STRINGS DEFAULT NATIVE WEBVIEW) + #write description header file if description.json exists, sets RNBO_INCLUDE_DESCRIPTION_FILE if the file exists include(${RNBO_CPP_DIR}/cmake/RNBODescriptionHeader.cmake) set(DESCRIPTION_INCLUDE_DIR ${CMAKE_BINARY_DIR}/include) @@ -42,6 +49,45 @@ add_subdirectory( EXCLUDE_FROM_ALL #don't build examples etc, also don't install ) +# Compile web UI files into the binary — only needed for the WEBVIEW editor. +if(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + set(WEBUI_DIR "${CMAKE_CURRENT_LIST_DIR}/src/webui") + set(WEBUI_DIST_HTML "${WEBUI_DIR}/dist/index.html") + set(WEBUI_DIST_JS "${WEBUI_DIR}/dist/index.js") + + # List source files so the build reruns when they change. + set(WEBUI_SOURCES + "${WEBUI_DIR}/index.html" + "${WEBUI_DIR}/main.js" + "${WEBUI_DIR}/vite.config.js" + "${WEBUI_DIR}/package.json" + ) + + find_program(NPM_EXECUTABLE npm REQUIRED) + + add_custom_command( + OUTPUT ${WEBUI_DIST_HTML} ${WEBUI_DIST_JS} + COMMAND ${NPM_EXECUTABLE} install + COMMAND ${NPM_EXECUTABLE} run build + WORKING_DIRECTORY ${WEBUI_DIR} + DEPENDS ${WEBUI_SOURCES} + COMMENT "Building web UI (npm run build)" + VERBATIM + ) + + add_custom_target(RNBOWebUIBuild + DEPENDS ${WEBUI_DIST_HTML} ${WEBUI_DIST_JS} + ) + + juce_add_binary_data(RNBOUIData + NAMESPACE RNBOUIData + SOURCES + ${WEBUI_DIST_HTML} + ${WEBUI_DIST_JS} + ) + add_dependencies(RNBOUIData RNBOWebUIBuild) +endif() + # Or, instead, use Conan to get JUCE, should speed up clean builds # include(${RNBO_CPP_DIR}/cmake/RNBOJuce.cmake) diff --git a/CUSTOM_UI.md b/CUSTOM_UI.md index 0638c6d..e5f359c 100644 --- a/CUSTOM_UI.md +++ b/CUSTOM_UI.md @@ -1,32 +1,50 @@ # Making a Custom UI -This document covers two use cases. First, we'll look at the entry points for a custom interface, and see how to swap out the default RNBO interface for one that you write yourself. Next, we'll go through an example of building an interface with the Projucer, and binding UI elements to RNBO code. +This guide shows how to build a custom UI for your plugin or standalone app. It does not cover UX best practices or graphic design, but it will show you how to set up a project that creates bidirectional bindings between UI elements and RNBO parameters. -Please note, if you haven't yet followed the setup steps in `README.md`, you should do so before you follow this tutorial. +We'll look at the two main approaches that you can use: building a native interface using the JUCE library in C++, or creating a web browser interface that binds to your audio processor. -## Switching to a Custom UI +Each approach has its own advantages. Building a native interface with JUCE can be more efficient, as it doesn't load a separate web browser component. However, using JUCE's WebBrowserComponent lets you work with HTML/CSS/JS to build your interface, which can be much easier. If you run a local web server, you can also modify your interface while your plugin is running using hot reloading. -The files `src/CustomAudioProcessor` and `src/CustomAudioEditor` are starting points for a custom UI. -`CustomAudioProcessor` returns `RNBO::JuceAudioProcessorEditor` by default, so the first step to making your own UI is to modify the code to return `CustomAudioProcessor` instead. +For most people, we recommend starting with the web-based approach. -Open up `CustomAudioProcessor.cpp` and modify the section at the bottom so you are using the `CustomAudioEditor`. +## Prerequisites -```cpp -AudioProcessorEditor* CustomAudioProcessor::createEditor() -{ - //Change this to use your CustomAudioEditor - return new CustomAudioEditor (this, this->_rnboObject); - //return RNBO::JuceAudioProcessor::createEditor(); -} +If you haven't yet followed the setup steps in [README.md](./README.md), you should do so before you follow this tutorial. If you want to follow the guide for building a web-based interface, you will also need to install [Node.js](https://nodejs.org/). + +## Switching UI + +Take a look at `CMakeLists.txt`. This is the main build configuration file for this project. It defines what files should be compiled together to produce a VST/AU plugin, as well as a standalone application. + +If you look through that file, you will see a line like this: + +```cmake +set(RNBO_EDITOR_MODE "DEFAULT" CACHE STRING "UI editor: DEFAULT (Generic JUCE controls), NATIVE (Custom JUCE controls), WEBVIEW (WebBrowserComponent)") +``` + +This defines a configuration variable called `RNBO_EDITOR_MODE`, which can be set to `DEFAULT`, `NATIVE`, or `WEBVIEW`. + +| Value | Interface | +| - | - | +| DEFAULT | Creates a generic audio parameter editor by calling `RNBO::JuceAudioProcessor::createEditor()`. No customization is possible. | +| NATIVE | Uses the JUCE library to create a native interface in C++ | +| WEBVIEW | Uses WebBrowserComponent to create a web-based interface using browser technology | + +In order to set this configuration variable, use `-DRNBO_EDITOR_MODE` during CMake configuration. If you're using Ninja to build, that might look like this: + +```sh +cmake .. -DRNBO_EDITOR_MODE=WEBVIEW -G Ninja ``` -## Building a Custom UI with the Projucer +This command will configure a Ninja-based build, using a WebBrowserComponent to support a custom UI. -### A Word on the Projucer +The equivalent command on a Windows machine (building with Visual Studio) could look like this: -JUCE uses the Projucer to set up JUCE projects. It doesn't build your plugin directly, but rather it builds an Xcode or Visual Studio project that you can then use to build your plugin. We're using CMake instead of the Projucer in this project, as the CMake-based system that we're using is more flexible and easier to maintain. However, that doesn't mean that we can't use the UI editor inside of the Projucer if we want to. Basically, we can use the Projucer to design our user interface, but we don't use the Projucer to build our build system. We use a mixed approach, with CMake for building and the Projucer for editing the UI. +```sh +cmake .. -DRNBO_EDITOR_MODE=WEBVIEW -G "Visual Studio 17 2022" +``` -### Getting Started +## Exporting the example patcher For this example, we're going to create a custom user interface for a simple drone patcher, which you can export into `/export` as per the instructions over in `README.md`. These instructions in this document should work for both a plugin as well as a desktop app. @@ -34,285 +52,360 @@ The `three-param-kink.maxpat` patcher will produce a drone with adjustable timbr ![](./img/droner.png) -Let's make a simple interface for this patcher with three JUCE sliders. First, download the Projucer if you haven’t already. The Projucer is part of JUCE, but the JUCE Github repository does not contain a Projucer executable. To get the Projucer, instead download a JUCE installer for your platform from the JUCE website. +Before you continue, make sure that you've exported this patcher into the _export/_ directory. -Create a new project, selecting the Basic plug-in project template. Use the defaults for modules and exporters (we won't be using these anyway). Now we need to decide where to save the `.jucer` file. We're not really going to be using this file too much, so it might be nice to keep it isolated from the rest of our code. I'm going to make a new folder in the root of the repository called `ui`, and I'll save the JUCE project there. After creating the project, your directory structure should look something like this: +> You'll notice that the example patcher will have the name `three-param-kink.cpp`. When you configure CMake, be sure to set `RNBO_CLASS_FILE_NAME` to `three-param-kink.cpp`, so that the build script can find the exported RNBO file. -![](./img/directory_structure.png) +## Creating a Web-based UI -The Projucer will automatically create four files for the PluginProcessor and PluginEditor. We won't be using these, so you can just delete them. +The easiest and fastest way to build a custom UI is by using WebBrowserComponent, which will let you design and style your UI using HTML/CSS/JavaScript. To start, open your terminal and configure CMake to use the web-based editor. -By default the GUI editor is not enabled. You may need to enable it from the Tools menu. +``` +cd build +cmake .. -DRNBO_CLASS_FILE_NAME=three-param-kink.cpp -DRNBO_EDITOR_MODE=WEBVIEW +``` -![](./img/tools_menu.png) +If you look in `src/CustomAudioProcessor.cpp`, you'll see code like this: -From the "GUI Editor" menu, select "Add new GUI Component" to add a `.cpp` and `.h` file for your new component. I named mine `RootComponent` because it's hard to come up with a good, original name. You can call yours whatever you want. Now let's add three sliders to our component. Navigate to "Subcomponents" and right-click to add these sliders. Let's also be careful to change the name of each component. Our three parameters are called `kink1`, `kink2`, and `kink3`, so give the sliders each one of these names. Later on, we'll use this name to map each slider to the RNBO parameter with the same name. +```cpp +AudioProcessorEditor* CustomAudioProcessor::createEditor() +{ +#if defined(RNBO_EDITOR_NATIVE) + return new CustomAudioEditor (this, this->_rnboObject); +#elif defined(RNBO_EDITOR_WEBVIEW) + return new WebBrowserAudioEditor (this, this->_rnboObject); +#else + return RNBO::JuceAudioProcessor::createEditor(); +#endif +} +``` -When you are done, "Save All." +As you can see, when the `RNBO_EDITOR_MODE` is set to `WEBVIEW`, the plugin will load `WebBrowserAudioEditor`. -![](./img/named_slider.png) +### Customizing the web UI with hot reloading -Now we have two main tasks ahead of us. +This example comes with a very simple web UI already in place. One thing that's really useful about using a WebBrowserComponent to serve our UI is that we can update our UI while the application is still running. -1. Replace the default RNBO plugin UI with our custom interface. -2. Connect our sliders to the RNBO parameters. +The `WebBrowserAudioEditor` class is responsible for providing resources to the WebBrowserComponent, like HTML files, images, and JavaScript files. At the top of `WebBrowserAudioEditor.cpp`, you'll see code like this: -### Adding the Interface to CMake +```cpp +#if JUCE_ANDROID +static const juce::String kDevServerAddress = "http://10.0.2.2:3000/"; +#else +static const juce::String kDevServerAddress = "http://localhost:3000/"; +#endif +``` -First, we need to make sure that the RootComponent.cpp and RootComponent.h files get added to our project. First, add these files to `Plugin.cmake` in the repository root. +That means that if we run a development server on port 3000, then the web interface will load files from that server. Open your terminal and navigate to `src/webui/`. Assuming you are currently in the root directory, you can run: -```cmake -target_sources(RNBOAudioPlugin PRIVATE - "${RNBO_CPP_DIR}/adapters/juce/RNBO_JuceAudioProcessor.cpp" - "${RNBO_CPP_DIR}/adapters/juce/RNBO_JuceAudioProcessorEditor.cpp" - "${RNBO_CPP_DIR}/RNBO.cpp" - ${RNBO_CLASS_FILE} - src/Plugin.cpp - src/CustomAudioEditor.cpp - src/CustomAudioProcessor.cpp - ui/NewProject/Source/RootComponent.cpp - ) - -include_directories( - "${RNBO_CPP_DIR}/" - "${RNBO_CPP_DIR}/common/" - "${RNBO_CPP_DIR}/adapters/juce/" - "${RNBO_CPP_DIR}/src/3rdparty/" - "src" - "ui/NewProject/Source" - ) +```sh +cd src/webui ``` -Next, find `App.cmake` and add these files there as well. +The example UI uses a popular application framework for JavaScript called `vite`. To install all the necessary dependencies, first run `npm install`. You should only need to do this once. -```cmake -target_sources(RNBOApp - PRIVATE - src/Main.cpp - src/MainComponent.cpp - src/CustomAudioEditor.cpp - src/CustomAudioProcessor.cpp - ui/NewProject/Source/RootComponent.cpp - - ${RNBO_CLASS_FILE} - - ${RNBO_CPP_DIR}/RNBO.cpp - ${RNBO_CPP_DIR}/adapters/juce/RNBO_JuceAudioProcessorUtils.cpp - ${RNBO_CPP_DIR}/adapters/juce/RNBO_JuceAudioProcessorEditor.cpp - ${RNBO_CPP_DIR}/adapters/juce/RNBO_JuceAudioProcessor.cpp - ) - -include_directories( - "src" - "${RNBO_CPP_DIR}/" - "${RNBO_CPP_DIR}/src" - "${RNBO_CPP_DIR}/common/" - "${RNBO_CPP_DIR}/adapters/juce/" - "${RNBO_CPP_DIR}/src/3rdparty/" - "ui/NewProject/Source" - ) +```sh +npm install +``` + +This should create a folder `node_modules` in `src/webui`, containing all the JavaScript dependencies for the web UI. Now, from the `src/webui` directory, run `npm run dev`. + +```sh +npm run dev ``` -Now use CMake in the usual way to generate and build. Remember to set the correct name for `RNBO_CLASS_FILE` on line 17 of `CMakeLists.txt`, for example, `three-param-kink.cpp`. Once you've done so, you can enter something like this into your terminal: +You should see something in the terminal like the following: ```sh + VITE v5.4.21 ready in 169 ms + + ➜ Local: http://localhost:3000/ + ➜ Network: use --host to expose + ➜ press h + enter to show help +``` + +If you open up a web server and go to the web address `http://localhost:3000/`, you should see something like this: + +![](./img/001-kink-synth-ui.png) + +This is the interface to your app/plugin, as it will appear in your final build. Because the `vite` server supports hot reloading, you can make changes to this web page and see them appear instantly in your JUCE build. + +Close the web browser, but leave the dev server running. Now we can build the app and plugin. Assuming again that you are in the root directory, you can run: + +``` cd build -cmake -G Ninja .. cmake --build . ``` -The plugin should build without errors, but of course we don't see our new `RootComponent` with its sliders yet. We need to add the `RootComponent` to our custom UI. +This should build a JUCE standalone app to `build/RNBOApp_artefacts/Release/RNBO App Example.app` (or to `build/RNBOApp_artefacts/Debug/RNBO App Example.app` if you're using a debug configuration). After the build script finishes running, open the app. When you open the standalone app, you'll see something like this: -### Adding the Custom Root Component -Open up `src/CustomAudioEditor.h`. First, add `RootComponent.h` to the include definitions. +![](./img/kink-synth-app.png) + +Now, if you left your dev server running, you can go into the HTML file at `src/webui/index.html` and make some changes and see them update live. You can change whatever you want, for example adding some inline style or changing the text. + +```html + + {#

Kink Drones

#} +

My new cool title

+
+``` + +![](./img/kink-synth-app-new-title.png) + +### Binding the interface to the engine + +Let's take a closer look at the definition of `WebBrowserAudioEditor` to see how this class binds the sliders in the app to the parameters of the RNBO export. In `src/WebBrowserAudioEditor.h`, you'll see where the editor creates some controller objects called _relays_. ```cpp -#include "JuceHeader.h" -#include "RNBO.h" -#include "RNBO_JuceAudioProcessor.h" -#include "RootComponent.h" + WebSliderRelay _kink1Relay { "kink1" }; + WebSliderRelay _kink2Relay { "kink2" }; + WebSliderRelay _kink3Relay { "kink3" }; + WebToggleButtonRelay _automateRelay { "automate" }; ``` -Next, find the declaration for the default `_label` member variable and replace it with one for a `RootComponent` component. +These classes are new to JUCE 8. When we construct the `WebBrowserComponent` subclass (`SinglePageBrowser`), we pass these relays as options to the constructor. This creates event handlers on the JavaScript end. + +| The identifier string that you pass to the relay constructor will be how the named parameter appears on the JavaScript end. + + ```cpp -// Label _label; -RootComponent _rootComponent; + SinglePageBrowser _webComponent { + WebBrowserComponent::Options{} + .withBackend (WebBrowserComponent::Options::Backend::webview2) + .withWinWebView2Options (WebBrowserComponent::Options::WinWebView2{} + .withUserDataFolder (File::getSpecialLocation ( + File::SpecialLocationType::tempDirectory))) + .withNativeIntegrationEnabled() + .withOptionsFrom (_kink1Relay) + .withOptionsFrom (_kink2Relay) + .withOptionsFrom (_kink3Relay) + .withOptionsFrom (_automateRelay) + .withKeepPageLoadedWhenBrowserIsHidden() + .withResourceProvider ([this] (const auto& url) { return getResource (url); }) + }; ``` -Finally, open up `src/CustomAudioEditor.cpp`. Find the constructor, where the default label is configured and sized. Replace that code with new code to size and configure the `RootComponent`. +This provides the webpage with the name of the available relays, so that the sliders in the web page can connect to them. In the definiton of `SinglePageBrowser`, we also declare some attachments. + +``` + WebSliderParameterAttachment _kink1Attachment; + WebSliderParameterAttachment _kink2Attachment; + WebSliderParameterAttachment _kink3Attachment; + WebToggleButtonParameterAttachment _automateAttachment; +``` + +These attachments create a bidirectional attachment between the relay and the Audio Parameters themselves. In the `SinglePageBrowser` constructor, we initialize these with both the relays and the parameters. ```cpp -CustomAudioEditor::CustomAudioEditor (RNBO::JuceAudioProcessor* const p, RNBO::CoreObject& rnboObject) +static juce::RangedAudioParameter& +findParameter(RNBO::JuceAudioProcessor* p, const juce::String& name) +{ + for (auto* param : p->getParameters()) + { + if (param->getName(128) == name) + return static_cast(*param); + } + + throw std::runtime_error("Parameter not found: " + name.toStdString()); +} + +//============================================================================== +// Members are initialized in declaration order (see WebBrowserAudioEditor.h). +// Relays and _webComponent use default member initializers; the +// WebSliderParameterAttachment members need the processor's parameters so they +// are initialized here. +WebBrowserAudioEditor::WebBrowserAudioEditor (RNBO::JuceAudioProcessor* const p, + RNBO::CoreObject& rnboObject) : AudioProcessorEditor (p) - , _rnboObject(rnboObject) - , _audioProcessor(p) + , _audioProcessor (p) + , _rnboObject (rnboObject) + , _kink1Attachment(findParameter(p, "kink1"), _kink1Relay, nullptr) + , _kink2Attachment(findParameter(p, "kink2"), _kink2Relay, nullptr) + , _kink3Attachment(findParameter(p, "kink3"), _kink3Relay, nullptr) + , _automateAttachment(findParameter(p, "automate"), _automateRelay, nullptr) { - _audioProcessor->AudioProcessor::addListener(this); + // Start hidden — pageFinishedLoading will reveal the webview once window.__JUCE__ is ready. + addChildComponent (_webComponent); - // _label.setText("Hi I'm Custom Interface", NotificationType::dontSendNotification); - // _label.setBounds(0, 0, 400, 300); - // _label.setColour(Label::textColourId, Colours::black); - // addAndMakeVisible(_label); - // setSize (_label.getWidth(), _label.getHeight()); + // Try the dev server first. If nothing is listening on that port, + // pageLoadHadNetworkError fires quickly and redirects to getResourceProviderRoot(). + _webComponent.goToURL (kDevServerAddress); - addAndMakeVisible(_rootComponent); - setSize(_rootComponent.getWidth(), _rootComponent.getHeight()); + setSize (400, 320); } ``` -Rebuild using CMake, and you should see the generated UI loading in place of the default custom UI. +This is everything that we need to do on the C++ side. Creating and positioning the UI elements is all handled in JavaScript. -```sh -cd build -cmake .. -cmake --build . +### Creating the web interface + +JUCE provides a JavaScript framework to expose the parameters to the web interface. At the top of `src/webui/main.js`, you'll see an import for some of these functions. + +```js +import { getSliderState, getToggleState } from 'juce-framework-frontend'; ``` -### Connecting the Sliders +These functions return a `state` object that provides a getter and setter for the parameter state. The `bindToggleParam` function demonstrates how to use these: -To make the sliders functional, we modify `RootComponent.h` and `RootComponent.cpp`. When the sliders change, we want to update the parameters of the `AudioProcessor`. When we get a parameter change notification from the `AudioProcessor`, we want to update the sliders. +```js +function bindToggleParam(name, toggleId) { + const toggle = document.getElementById(toggleId); + const state = getToggleState(name); -Open up `RootComponent.h`. At the top of the file, include these RNBO header files. + state.valueChangedEvent.addListener(() => { + toggle.checked = state.getValue(); + }); -```cpp -//[Headers] -- You can add your own extra header files here -- -#include -#include "RNBO.h" -#include "RNBO_JuceAudioProcessor.h" -//[/Headers] + toggle.addEventListener('change', () => { + state.setValue(toggle.checked); + }); +} + +bindToggleParam('automate', 'toggle-automate'); ``` -Now add the following between the `[UserMethods]` tags: +As you can see, this function calls `document.getElementById` to get an HTML element with a given ID. For this function to return successfully, there must be a corresponding element in the HTML. Looking at `src/webui/index.html`, we can find the toggle in question: -```cpp -//[UserMethods] -- You can add your own custom methods in this section. -void setAudioProcessor(RNBO::JuceAudioProcessor *p); -void updateSliderForParam(unsigned long index, double value); -//[/UserMethods] +```html +
+ + +
``` -Also add the following private instance variables -```cpp -//[UserVariables] -- You can add your own custom variables in this section. -RNBO::JuceAudioProcessor *processor = nullptr; -HashMap slidersByParameterIndex; // used to map parameter index to slider we want to control -//[/UserVariables] +### Building the web application + +We've already seen how to use the dev server to change the webpage dynamically while the app is running. However, for the release version of the plugin, you will load the web page from the application binary itself. In `CMakeLists.txt`, you'll see where the web page is loaded into the binary: + +```cmake +# Compile web UI files into the binary — only needed for the WEBVIEW editor. +if(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + set(WEBUI_DIR "${CMAKE_CURRENT_LIST_DIR}/src/webui") + set(WEBUI_DIST_HTML "${WEBUI_DIR}/dist/index.html") + set(WEBUI_DIST_JS "${WEBUI_DIR}/dist/index.js") ``` -We'll need to call `setAudioProcessor` from the `CustomAudioEditor`. Open `CustomAudioEditor.cpp` and add the following line: +These files `src/webui/dist/index.html` and `src/webui/dist/index.js` are the bundled version of the Vite application. To generate these, change to the `src/webui` directory and run `npm run build`. -```cpp -_rootComponent.setAudioProcessor(p); // <--- add this line -addAndMakeVisible(_rootComponent); -setSize(_rootComponent.getWidth(), _rootComponent.getHeight()); +```sh +cd src/webui +npm run build ``` -Now let's implement `setAudioProcessor`. Open up `RootComponent.cpp` and add the following after `[MiscUserCode]`. +Now when you build your application or plugin, the compiled and bundled version of the Vite application will be included in the program binary. + +> For your convienience, the CMakeLists.txt file is sit up to build the Vite project automatically if the WEBVIEW interface is enabled. It's not in fact necessary to change to the `src/webui` directory and run `npm run build` manually, as CMake will take care of this automatically. However, it's good to know that this is what CMake is doing. If you change the structure of the Vite project and need to bundle your webpage assets differently, you'll need to modify CMakeLists.txt to include these changes. + +## Creating a Native UI + +If you don't want your plugin to use a web component, you can use JUCE's UI classes to build a native C++ interface. The file `src/CustomAudioEditor.h` defines the `CustomAudioEditor` class, which implements a simple native interface for the RNBO patcher `patches/three-param-kink.maxpat`. + +### Configuring CMake + +To configure the template to use a native JUCE interface, set `RNBO_EDITOR_MODE` to `NATIVE` when configuring CMake: + +```sh +cmake .. -DRNBO_EDITOR_MODE=NATIVE -G Ninja +``` + +If you look at `src/CustomAudioProcessor.cpp`, you can see where the definition will tell the processor to load `CustomAudioEditor`: ```cpp -//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... -void RootComponent::setAudioProcessor(RNBO::JuceAudioProcessor *p) +juce::AudioProcessorEditor* CustomAudioProcessor::createEditor() { - processor = p; - - RNBO::ParameterInfo parameterInfo; - RNBO::CoreObject& coreObject = processor->getRnboObject(); - - for (unsigned long i = 0; i < coreObject.getNumParameters(); i++) { - auto parameterName = coreObject.getParameterId(i); - RNBO::ParameterValue value = coreObject.getParameterValue(i); - Slider *slider = nullptr; - if (juce::String(parameterName) == juce__slider.get()->getName()) { - slider = juce__slider.get(); - } else if (juce::String(parameterName) == juce__slider2.get()->getName()) { - slider = juce__slider2.get(); - } else if (juce::String(parameterName) == juce__slider3.get()->getName()) { - slider = juce__slider3.get(); - } - - if (slider) { - slidersByParameterIndex.set(i, slider); - coreObject.getParameterInfo(i, ¶meterInfo); - slider->setRange(parameterInfo.min, parameterInfo.max); - slider->setValue(value); - } - } +#if defined(RNBO_EDITOR_NATIVE) + return new CustomAudioEditor (this, this->_rnboObject); +#elif defined(RNBO_EDITOR_WEBVIEW) + return new WebBrowserAudioEditor (this, this->_rnboObject); +#else + return RNBO::JuceAudioProcessor::createEditor(); +#endif } -//[/MiscUserCode] ``` -Notice how we use the name of the slider to map the slider to a parameter with the matching ID. Now, in `RootComponent.cpp` find the function called `sliderValueChanged` and update it as follows: +If you build the project now with `cmake --build .`, and open the application in `build/RNBOApp_artefacts`, you should see something like this: + +![](./img/native-ui.png) + +### Creating the interface + +In `CustomAudioEditor.h`, you can see where the class creates the sliders, and the labels for the slider: ```cpp -void RootComponent::sliderValueChanged (juce::Slider* sliderThatWasMoved) -{ - //[UsersliderValueChanged_Pre] - if (processor == nullptr) return; - RNBO::CoreObject& coreObject = processor->getRnboObject(); - auto parameters = processor->getParameters(); - //[/UsersliderValueChanged_Pre] +Slider _kink1Slider, _kink2Slider, _kink3Slider; +Label _kink1Label, _kink2Label, _kink3Label; +``` - if (sliderThatWasMoved == juce__slider.get()) - { - //[UserSliderCode_juce__slider] -- add your slider handling code here.. - //[/UserSliderCode_juce__slider] - } - else if (sliderThatWasMoved == juce__slider2.get()) - { - //[UserSliderCode_juce__slider2] -- add your slider handling code here.. - //[/UserSliderCode_juce__slider2] - } - else if (sliderThatWasMoved == juce__slider3.get()) - { - //[UserSliderCode_juce__slider3] -- add your slider handling code here.. - //[/UserSliderCode_juce__slider3] - } +You will also see the attachments for each of these sliders: - //[UsersliderValueChanged_Post] - RNBO::ParameterIndex index = coreObject.getParameterIndexForID(sliderThatWasMoved->getName().toRawUTF8()); - if (index != -1) { - const auto param = processor->getParameters()[index]; - auto newVal = sliderThatWasMoved->getValue(); - - if (param && param->getValue() != newVal) - { - auto normalizedValue = coreObject.convertToNormalizedParameterValue(index, newVal); - param->beginChangeGesture(); - param->setValueNotifyingHost(normalizedValue); - param->endChangeGesture(); - } - } - //[/UsersliderValueChanged_Post] -} +```cpp +SliderParameterAttachment _kink1Attachment; +SliderParameterAttachment _kink2Attachment; +SliderParameterAttachment _kink3Attachment; ``` -This is all we need to control the RNBO patch using the sliders in our custom UI. However, to be really complete, we should also make sure that the sliders will update if RNBO changes the value of a parameter internally. Open `CustomAudioEditor.cpp` and add the following to `audioProcessorParameterChanged`. +Unlike the web-based interface, there's no need for a relay here. In `src/CustomAudioEditor.cpp`, you can see how the attachments are bound to RNBO audio parameters. ```cpp -void CustomAudioEditor::audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float value) +static juce::RangedAudioParameter& +findParameter(RNBO::JuceAudioProcessor* p, const juce::String& name) { - _rootComponent.updateSliderForParam(parameterIndex, value); + for (auto* param : p->getParameters()) + { + if (param->getName(128) == name) + return static_cast(*param); + } + + throw std::runtime_error("Parameter not found: " + name.toStdString()); } + +CustomAudioEditor::CustomAudioEditor (RNBO::JuceAudioProcessor* const p, + RNBO::CoreObject& rnboObject) + : AudioProcessorEditor (p) + , _audioProcessor (p) + , _rnboObject (rnboObject) + , _kink1Attachment(findParameter(p, "kink1"), _kink1Slider) + , _kink2Attachment(findParameter(p, "kink2"), _kink2Slider) + , _kink3Attachment(findParameter(p, "kink3"), _kink3Slider) ``` -Now open `RootComponent.cpp` and implement `updateSliderForParam` inside of the `[MiscUserCode]` tags. +Remarkably, this is really all we need to do in order to synchronize the state of the slider and the state of the corresponding audio parameter. If you're curious, you can also see how JUCE handles layout for the various controls: ```cpp -void RootComponent::updateSliderForParam(unsigned long index, double value) +void CustomAudioEditor::resized() { - if (processor == nullptr) return; - RNBO::CoreObject& coreObject = processor->getRnboObject(); - auto denormalizedValue = coreObject.convertFromNormalizedParameterValue(index, value); - auto slider = slidersByParameterIndex.getReference((int) index); - if (slider && (slider->getThumbBeingDragged() == -1)) { - slider->setValue(denormalizedValue, NotificationType::dontSendNotification); - } + auto area = getLocalBounds().reduced (16); + const int rowHeight = area.getHeight() / 3; + + auto layoutRow = [&] (Label& label, Slider& slider) + { + auto row = area.removeFromTop (rowHeight); + label.setBounds (row.removeFromLeft (60)); + slider.setBounds (row); + }; + + layoutRow (_kink1Label, _kink1Slider); + layoutRow (_kink2Label, _kink2Slider); + layoutRow (_kink3Label, _kink3Slider); } ``` -That's it. Compile and build. You may need to restart Max, or whatever DAW you're using, in order to see changes to your plugin. - ## Epilogue -Obviously this has just scratched the surface of what's possible with custom C++ interfaces. If you want to read more, a great place to get started would be https://www.theaudioprogrammer.com/. In particular, they have a #design-ux-and-ui channel in their Discord that is full of helpful and supportive people. Best of luck and have fun building your custom UI. +We've just scratched the surface of what's possible with custom HTML and native C++ interfaces. + +The examples in this guide intentionally stay small: a few sliders, a toggle, and direct parameter bindings. Real applications will often need more than this. You might want to add waveform displays, parameter grouping, preset browsers, MIDI visualizers, spectrum analyzers, patch management, custom graphics, or entirely different interaction models. + +The important thing to understand is that the binding layer scales naturally. Whether you're using native JUCE widgets or a web interface, the core idea stays the same: + +1. Export parameters from RNBO +2. Locate the corresponding audio parameters in the processor +3. Attach those parameters to UI controls +4. Let JUCE synchronize state changes in both directions + +Once this connection is in place, the rest of the interface becomes a normal application development problem. + +If you're experimenting, the web workflow is usually the fastest place to start. Hot reloading makes iteration very quick, and modern HTML/CSS tooling gives you access to a large ecosystem of UI libraries and design tools. If you later need tighter integration, lower overhead, or access to platform-specific functionality, moving to a native JUCE interface is straightforward. + +From here, the next step is simply to start replacing the example controls with your own interface and connecting them to your patcher's parameters. diff --git a/Plugin.cmake b/Plugin.cmake index da57902..d789127 100644 --- a/Plugin.cmake +++ b/Plugin.cmake @@ -16,11 +16,15 @@ endif() # the formats specified by the FORMATS arguments. This function accepts many optional arguments. # Check the readme at `docs/CMake API.md` in the JUCE repo for the full list. +if(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + set(_needs_web_browser NEEDS_WEB_BROWSER TRUE) +endif() + juce_add_plugin(RNBOAudioPlugin # VERSION ... # Set this if the plugin version is different to the project version # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone # ICON_SMALL ... - COMPANY_NAME "Your Company Name" # Specify the name of the plugin's author + COMPANY_NAME "Your_Company_Name" # Specify the name of the plugin's author IS_SYNTH TRUE # Is this a synth or an effect? NEEDS_MIDI_INPUT TRUE # Does the plugin need midi input? NEEDS_MIDI_OUTPUT TRUE # Does the plugin need midi output? @@ -30,7 +34,8 @@ juce_add_plugin(RNBOAudioPlugin PLUGIN_MANUFACTURER_CODE "Exmp" # A four-character manufacturer id with at least one upper-case character PLUGIN_CODE "Rnb0" # A unique four-character plugin id with at least one upper-case character FORMATS ${PLUGIN_FORMATS} # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 - PRODUCT_NAME "RNBO Plugin") # The name of the final executable, which can differ from the target name + PRODUCT_NAME "RNBO Plugin" # The name of the final executable, which can differ from the target name + ${_needs_web_browser}) # `juce_generate_juce_header` will create a JuceHeader.h for a given target, which will be generated # into your build tree. This should be included with `#include `. The include path for @@ -52,10 +57,15 @@ target_sources(RNBOAudioPlugin PRIVATE "${RNBO_CPP_DIR}/RNBO.cpp" ${RNBO_CLASS_FILE} src/Plugin.cpp - src/CustomAudioEditor.cpp src/CustomAudioProcessor.cpp ) +if(RNBO_EDITOR_MODE STREQUAL "NATIVE") + target_sources(RNBOAudioPlugin PRIVATE src/CustomAudioEditor.cpp) +elseif(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + target_sources(RNBOAudioPlugin PRIVATE src/WebBrowserAudioEditor.cpp) +endif() + if (EXISTS ${RNBO_BINARY_DATA_FILE}) target_sources(RNBOAudioPlugin PRIVATE ${RNBO_BINARY_DATA_FILE}) endif() @@ -77,20 +87,12 @@ target_include_directories(RNBOAudioPlugin # definitions will be visible both to your code, and also the JUCE module code, so for new # definitions, pick unique names that are unlikely to collide! This is a standard CMake command. -set(RNBO_JUCE_PARAM_DEFAULT_NOTIFY 1) -if (NOT PLUGIN_PARAM_DEFAULT_NOTIFY) - set(RNBO_JUCE_PARAM_DEFAULT_NOTIFY 0) -endif() - target_compile_definitions(RNBOAudioPlugin PUBLIC - # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them. - JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call JUCE_VST3_CAN_REPLACE_VST2=0 RNBO_JUCE_NO_CREATE_PLUGIN_FILTER=1 #don't have RNBO create its own createPluginFilter function, we'll create it ourselves - RNBO_JUCE_PARAM_DEFAULT_NOTIFY=${RNBO_JUCE_PARAM_DEFAULT_NOTIFY} - ) + RNBO_JUCE_PARAM_DEFAULT_NOTIFY=$) # `target_link_libraries` links libraries and JUCE modules to other libraries or executables. Here, # we're linking our executable target to the `juce::juce_audio_utils` module. Inter-module @@ -98,9 +100,18 @@ target_compile_definitions(RNBOAudioPlugin # linked automatically. If we'd generated a binary data target above, we would need to link to it # here too. This is a standard CMake command. +if(RNBO_EDITOR_MODE STREQUAL "NATIVE") + target_compile_definitions(RNBOAudioPlugin PRIVATE RNBO_EDITOR_NATIVE) +elseif(RNBO_EDITOR_MODE STREQUAL "WEBVIEW") + target_compile_definitions(RNBOAudioPlugin PRIVATE RNBO_EDITOR_WEBVIEW) + target_compile_definitions(RNBOAudioPlugin PUBLIC JUCE_WEB_BROWSER=1) + target_link_libraries(RNBOAudioPlugin PRIVATE RNBOUIData) +endif() + target_link_libraries(RNBOAudioPlugin PRIVATE juce::juce_audio_utils + juce::juce_gui_extra PUBLIC juce::juce_recommended_config_flags juce::juce_recommended_lto_flags diff --git a/README.md b/README.md index b35bd27..bd3b29a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # RNBO JUCE Examples -So you want to build your own DAW or a Plugin? This template should get you started with your own Standalone Desktop application and Audio Plugin, using the source code export feature of RNBO, part of [Max 8](https://cycling74.com/max8/) made by [Cycling '74](https://cycling74.com/). +This template demonstrates how to use RNBO in either a Standalone Desktop application or an Audio Plugin (VST/AU). It uses the C++ source code export feature of RNBO, together with the JUCE application framework. RNBO is part of [Max 8](https://cycling74.com/max8/) made by [Cycling '74](https://cycling74.com/). -This project is based on the cross-platform JUCE framework for handling audio processing. You have the option of using JUCE to manage your UI as well. Please be aware that the JUCE has its own license terms (mostly GPL with the availability of commercial licenses). See their [website](http://www.juce.com/) for further details. +If you want to build an app or plugin for sale, be aware that the JUCE has its own license terms (mostly GPL with the availability of commercial licenses). See their [website](http://www.juce.com/) for further details. ## Prerequisites @@ -49,7 +49,7 @@ git submodule update --init --recursive --progress If the above command doesn't work, check your version if git by runing `git --version`. The `--progress` flag wasn't introduced until git `2.11.0`, so if your version is earlier than this you won't have access to it. Strictly speaking you don't need that last `--progress` flag, but it's nice to have some progress indication, especially since installing the JUCE submodule can take a while. That's all you'll need to do to get set up! Now you can start exporting from RNBO and building your project. -### Working with RNBO and Building Your Project +### Exporting RNBO Next, open the RNBO patcher you'd like to work with, and navigate to the export sidebar. Find "C++ Source Code Export" target. @@ -64,20 +64,74 @@ export/ ├─ README.md ``` -Whenever you make a change to your RNBO patch, remember to export the source code again to update this file. Now that you've exported your RNBO code, it's time to build. This project uses CMake, which gives us the flexibility of using whatever build system we want. Start by moving to the build directory. +**Note:** By default, this project expects the exported source file to be named `rnbo_source.cpp`. If you want to use a different name, see [Renaming your export source](#renaming-your-export-source). + +Whenever you make a change to your RNBO patch, remember to export the patch to C++ again, so that your changes will be incorporated when you rebuild. Now that you've exported your RNBO code, it's time to build. + +## Using CMake + +CMake is a build system generator. You run CMake to configure a system to build your project, and then run the build script to actually create your program. A typical CMake flow looks something like this: + +1. Create the folder (usually `build`) where you actually want to build your project. `cd` into that folder. +2. Run `cmake ..` to configure your build system. +3. Run `cmake --build .` to actually build. + +To build this template, start by moving to the build directory (this repository should already have an empty `build` directory). ```sh cd build ``` -Now you have a choice of what build system you want to use. Any one of the following will work: +### Configuring CMake + +Now that we're in the `build` directory, the next step is to configure CMake. At this stage, you choose what build system you want to use, and pass any configuration flags to set up your project to build in a particular way. + +#### Choosing a build system + +You have a choice of what build system you want to use. Any one of the following will work: - `cmake .. -G Xcode` (create an Xcode project) - `cmake .. -G "Visual Studio 16"` (create a Visual Studio 2019 project) - `cmake .. -G Ninja` (use Ninja to build) - `cmake ..` (just use the default, which will be `make` on MacOS, Linux and other Unix-like platforms) -You might be wondering which on is "best". We say, if you're familiar with Xcode or Visual Studio or Ninja, just go with that. This might be a good time to get a snack, as CMake can take a few minutes to get everything ready, especially when generating the build files for the first time. You may also see a number of warnings in the console, which you can (probably) safely ignore. +You might be wondering which on is "best". We say, if you're familiar with Xcode or Visual Studio or Ninja, just go with that. + +#### Choosing debug or release + +You can configure CMake to build either a Debug or a Release target. The Debug target is larger and less optimized, but includes symbols that map from the compiled code back to your source file. This lets you set breakpoints in your code, which is helpful for debugging. Use the flag `CMAKE_BUILD_TYPE` flag to choose Debug or Release. + +```sh +cmake -DCMAKE_BUILD_TYPE=Release .. +``` + +#### Renaming your export source + +By default, the build script looks for a RNBO export in the `export` folder named `rnbo_source.cpp`. If you export your RNBO patch with a different name, pass that as a configuration argument when you run CMake. For example, to use the Ninja build system and a RNBO export named `slime_sound.cpp`, you could use the following command. + +```sh +cmake -DRNBO_CLASS_FILE_NAME=slime_sound.cpp -G Ninja .. +``` + +#### Choosing a UI system + +RNBO provides a default interface for audio plugins, which simply creates a slider for each parameter in your RNBO patch. If you want to create a custom interface, you can configure CMake to use a different interface system. + +This template is set up with a starting point for two different custom UI systems. The first uses JUCE to build a native C++ UI. + +```sh +cmake -DRNBO_EDITOR_MODE=NATIVE .. +``` + +The second uses a WebBrowserComponent to build a UI using HTML/CSS/JS. + +```sh +cmake -DRNBO_EDITOR_MODE=WEBVIEW .. +``` + +See [CUSTOM_UI.md](./CUSTOM_UI.md) for more details on how to build your own UI. + +### Building with CMake Once CMake has finished generating your build system, you can finally build your project. diff --git a/build/.gitkeep b/build/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/export/README.md b/export/README.md deleted file mode 100644 index 4d59d4b..0000000 --- a/export/README.md +++ /dev/null @@ -1 +0,0 @@ -Export your RNBO code into this directory diff --git a/img/001-kink-synth-ui.png b/img/001-kink-synth-ui.png new file mode 100644 index 0000000..4d31e72 Binary files /dev/null and b/img/001-kink-synth-ui.png differ diff --git a/img/droner.png b/img/droner.png index 95aab9c..7ff3beb 100644 Binary files a/img/droner.png and b/img/droner.png differ diff --git a/img/kink-synth-app-new-title.png b/img/kink-synth-app-new-title.png new file mode 100644 index 0000000..5d0d2c0 Binary files /dev/null and b/img/kink-synth-app-new-title.png differ diff --git a/img/kink-synth-app.png b/img/kink-synth-app.png new file mode 100644 index 0000000..590a0e5 Binary files /dev/null and b/img/kink-synth-app.png differ diff --git a/img/native-ui.png b/img/native-ui.png new file mode 100644 index 0000000..98e48bf Binary files /dev/null and b/img/native-ui.png differ diff --git a/patches/three-param-kink.maxpat b/patches/three-param-kink.maxpat index f87d7cd..5f2d90a 100644 --- a/patches/three-param-kink.maxpat +++ b/patches/three-param-kink.maxpat @@ -1,2273 +1,3105 @@ { - "patcher" : { - "fileversion" : 1, - "appversion" : { - "major" : 8, - "minor" : 5, - "revision" : 3, - "architecture" : "x64", - "modernui" : 1 - } -, - "classnamespace" : "box", - "rect" : [ 894.0, 526.0, 640.0, 480.0 ], - "bglocked" : 0, - "openinpresentation" : 0, - "default_fontsize" : 12.0, - "default_fontface" : 0, - "default_fontname" : "Arial", - "gridonopen" : 1, - "gridsize" : [ 15.0, 15.0 ], - "gridsnaponopen" : 1, - "objectsnaponopen" : 1, - "statusbarvisible" : 2, - "toolbarvisible" : 1, - "lefttoolbarpinned" : 0, - "toptoolbarpinned" : 0, - "righttoolbarpinned" : 0, - "bottomtoolbarpinned" : 0, - "toolbars_unpinned_last_save" : 0, - "tallnewobj" : 0, - "boxanimatetime" : 200, - "enablehscroll" : 1, - "enablevscroll" : 1, - "devicewidth" : 0.0, - "description" : "", - "digest" : "", - "tags" : "", - "style" : "", - "subpatcher_template" : "", - "assistshowspatchername" : 0, - "boxes" : [ { - "box" : { - "id" : "obj-5", - "lastchannelcount" : 0, - "maxclass" : "live.gain~", - "numinlets" : 2, - "numoutlets" : 5, - "outlettype" : [ "signal", "signal", "", "float", "list" ], - "parameter_enable" : 1, - "patching_rect" : [ 145.0, 135.0, 48.0, 136.0 ], - "saved_attribute_attributes" : { - "valueof" : { - "parameter_longname" : "live.gain~", - "parameter_mmax" : 6.0, - "parameter_mmin" : -70.0, - "parameter_shortname" : "live.gain~", - "parameter_type" : 0, - "parameter_unitstyle" : 4 - } - - } -, - "varname" : "live.gain~" - } - - } -, { - "box" : { - "id" : "obj-2", - "maxclass" : "ezdac~", - "numinlets" : 2, - "numoutlets" : 0, - "patching_rect" : [ 146.5, 290.0, 45.0, 45.0 ] - } - - } -, { - "box" : { - "autosave" : 1, - "id" : "obj-1", - "inletInfo" : { - "IOInfo" : [ ] - } -, - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 3, - "outletInfo" : { - "IOInfo" : [ { - "type" : "signal", - "index" : 1, - "tag" : "out1", - "comment" : "" - } -, { - "type" : "signal", - "index" : 2, - "tag" : "out2", - "comment" : "" - } - ] - } -, - "outlettype" : [ "signal", "signal", "list" ], - "patcher" : { - "fileversion" : 1, - "appversion" : { - "major" : 8, - "minor" : 5, - "revision" : 3, - "architecture" : "x64", - "modernui" : 1 - } -, - "classnamespace" : "rnbo", - "rect" : [ 234.0, 380.0, 859.0, 480.0 ], - "bglocked" : 0, - "openinpresentation" : 0, - "default_fontsize" : 12.0, - "default_fontface" : 0, - "default_fontname" : "Lato", - "gridonopen" : 1, - "gridsize" : [ 15.0, 15.0 ], - "gridsnaponopen" : 1, - "objectsnaponopen" : 1, - "statusbarvisible" : 2, - "toolbarvisible" : 1, - "lefttoolbarpinned" : 0, - "toptoolbarpinned" : 0, - "righttoolbarpinned" : 0, - "bottomtoolbarpinned" : 0, - "toolbars_unpinned_last_save" : 0, - "tallnewobj" : 0, - "boxanimatetime" : 200, - "enablehscroll" : 1, - "enablevscroll" : 1, - "devicewidth" : 0.0, - "description" : "", - "digest" : "", - "tags" : "", - "style" : "", - "subpatcher_template" : "", - "assistshowspatchername" : 0, - "title" : "three-param-kink", - "boxes" : [ { - "box" : { - "id" : "obj-24", - "maxclass" : "newobj", - "numinlets" : 6, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 638.0, 97.0, 82.0, 23.0 ], - "rnbo_classname" : "scale", - "rnbo_serial" : 4, - "rnbo_uniqueid" : "scale_obj-24", - "text" : "scale -1 1 0 1." - } - - } -, { - "box" : { - "id" : "obj-23", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 638.0, 64.0, 80.0, 23.0 ], - "rnbo_classname" : "snapshot~", - "rnbo_extra_attributes" : { - "mode" : 0.0 - } -, - "rnbo_serial" : 1, - "rnbo_uniqueid" : "snapshot~_obj-23", - "text" : "snapshot~ 30" - } - - } -, { - "box" : { - "id" : "obj-22", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 638.0, 33.0, 52.0, 23.0 ], - "rnbo_classname" : "cycle~", - "rnbo_extra_attributes" : { - "interp" : "linear", - "buffername" : "RNBODefaultSinus", - "index" : "freq" - } -, - "rnbo_serial" : 6, - "rnbo_uniqueid" : "cycle~_obj-22", - "text" : "cycle~ 1" - } - - } -, { - "box" : { - "id" : "obj-21", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 0, - "patching_rect" : [ 119.0, 336.0, 43.0, 23.0 ], - "rnbo_classname" : "out~", - "rnbo_extra_attributes" : { - "meta" : "", - "comment" : "" - } -, - "rnbo_serial" : 2, - "rnbo_uniqueid" : "out~_obj-21", - "rnboinfo" : { - "needsInstanceInfo" : 1, - "argnames" : { - "in1" : { - "attrOrProp" : 1, - "digest" : "signal sent to outlet with index 2", - "isalias" : 0, - "aliases" : [ ], - "settable" : 0, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "inlet" : 1, - "type" : "signal" - } -, - "index" : { - "attrOrProp" : 2, - "digest" : "outlet number", - "defaultarg" : 1, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "mandatory" : 1 - } -, - "comment" : { - "attrOrProp" : 2, - "digest" : "mouse over comment", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol" - } -, - "meta" : { - "attrOrProp" : 2, - "digest" : "A JSON formatted string containing metadata for use by the exported code", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Metadata", - "displayorder" : 3 - } - - } -, - "inputs" : [ { - "name" : "in1", - "type" : "signal", - "digest" : "signal sent to outlet with index 2", - "displayName" : "", - "hot" : 1, - "docked" : 0 - } - ], - "outputs" : [ ], - "helpname" : "out~", - "aliasOf" : "out~", - "classname" : "out~", - "operator" : 0, - "versionId" : 374499139, - "changesPatcherIO" : 1 - } -, - "text" : "out~ 2" - } - - } -, { - "box" : { - "id" : "obj-15", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 556.0, 238.0, 37.0, 23.0 ], - "rnbo_classname" : "kink~", - "rnbo_serial" : 1, - "rnbo_uniqueid" : "kink~_obj-15", - "text" : "kink~" - } - - } -, { - "box" : { - "id" : "obj-16", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "" ], - "patching_rect" : [ 638.0, 203.0, 44.0, 23.0 ], - "rnbo_classname" : "line~", - "rnbo_serial" : 1, - "rnbo_uniqueid" : "line~_obj-16", - "text" : "line~ 1" - } - - } -, { - "box" : { - "id" : "obj-17", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 638.0, 178.0, 65.0, 23.0 ], - "rnbo_classname" : "append", - "rnbo_extra_attributes" : { - "hot" : 0 - } -, - "rnbo_serial" : 1, - "rnbo_uniqueid" : "append_obj-17", - "text" : "append 40" - } - - } -, { - "box" : { - "id" : "obj-18", - "maxclass" : "newobj", - "numinlets" : 6, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 638.0, 153.0, 100.0, 23.0 ], - "rnbo_classname" : "scale", - "rnbo_serial" : 1, - "rnbo_uniqueid" : "scale_obj-18", - "text" : "scale 0. 1. 1. 0.51" - } - - } -, { - "box" : { - "id" : "obj-19", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "", "" ], - "patching_rect" : [ 638.0, 128.0, 161.0, 23.0 ], - "rnbo_classname" : "param", - "rnbo_extra_attributes" : { - "steps" : 0.0, - "fromnormalized" : "", - "sendinit" : 1, - "displayname" : "", - "meta" : "", - "unit" : "", - "exponent" : 1.0, - "preset" : 1, - "enum" : "", - "order" : "0", - "value" : 0.0, - "tonormalized" : "", - "displayorder" : "-", - "ctlin" : 0.0 - } -, - "rnbo_serial" : 1, - "rnbo_uniqueid" : "kink3", - "rnboinfo" : { - "needsInstanceInfo" : 1, - "argnames" : { - "value" : { - "attrOrProp" : 2, - "digest" : "Set initial value", - "defaultarg" : 2, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Initial Value", - "displayorder" : 3 - } -, - "normalizedvalue" : { - "attrOrProp" : 1, - "digest" : "Set value normalized. ", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "inlet" : 1, - "type" : "number" - } -, - "reset" : { - "attrOrProp" : 1, - "digest" : "Reset param to initial value", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 1, - "isparam" : 0, - "deprecated" : 0, - "type" : "bang" - } -, - "normalized" : { - "attrOrProp" : 1, - "digest" : "Normalized parameter value.", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "outlet" : 1, - "type" : "number" - } -, - "name" : { - "attrOrProp" : 2, - "digest" : "Name of the parameter", - "defaultarg" : 1, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "Parameter Name", - "mandatory" : 1 - } -, - "enum" : { - "attrOrProp" : 2, - "digest" : "Use an enumerated output", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "list", - "label" : "Enum Values", - "displayorder" : 6 - } -, - "minimum" : { - "attrOrProp" : 2, - "digest" : "Minimum value", - "isalias" : 0, - "aliases" : [ "min" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Minimum", - "displayorder" : 1 - } -, - "min" : { - "attrOrProp" : 2, - "digest" : "Minimum value", - "isalias" : 1, - "aliasOf" : "minimum", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Minimum", - "displayorder" : 1 - } -, - "maximum" : { - "attrOrProp" : 2, - "digest" : "Maximum value", - "isalias" : 0, - "aliases" : [ "max" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Maximum", - "displayorder" : 2 - } -, - "max" : { - "attrOrProp" : 2, - "digest" : "Maximum value", - "isalias" : 1, - "aliasOf" : "maximum", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Maximum", - "displayorder" : 2 - } -, - "exponent" : { - "attrOrProp" : 2, - "digest" : "Scale values exponentially", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Exponent", - "displayorder" : 7 - } -, - "steps" : { - "attrOrProp" : 2, - "digest" : "Divide the output into a number of discrete steps", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Steps", - "displayorder" : 8 - } -, - "displayname" : { - "attrOrProp" : 2, - "digest" : "A more readable name for the parameter in an external RNBO target", - "isalias" : 0, - "aliases" : [ "displayName" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Display Name", - "displayorder" : 14 - } -, - "displayName" : { - "attrOrProp" : 2, - "digest" : "A more readable name for the parameter in an external RNBO target", - "isalias" : 1, - "aliasOf" : "displayname", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Display Name", - "displayorder" : 14 - } -, - "unit" : { - "attrOrProp" : 2, - "digest" : "A symbol to describe the unit of the parameter in an external RNBO target", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Unit", - "displayorder" : 15 - } -, - "tonormalized" : { - "attrOrProp" : 2, - "digest" : "Converts a real parameter value to its normalized form.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "To Normalized Expression", - "displayorder" : 10 - } -, - "fromnormalized" : { - "attrOrProp" : 2, - "digest" : "Converts a normalized parameter into its actual parameter value.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "From Normalized Expression", - "displayorder" : 9 - } -, - "order" : { - "attrOrProp" : 2, - "digest" : "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "0", - "label" : "Restore Order", - "displayorder" : 12 - } -, - "displayorder" : { - "attrOrProp" : 2, - "digest" : "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "-", - "label" : "Display Order", - "displayorder" : 13 - } -, - "sendinit" : { - "attrOrProp" : 2, - "digest" : "Send initial value", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "bool", - "defaultValue" : "true", - "label" : "Send Init", - "displayorder" : 4 - } -, - "ctlin" : { - "attrOrProp" : 2, - "digest" : "MIDI controller number to control this parameter.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "label" : "MIDI Controller Number.", - "displayorder" : 16 - } -, - "meta" : { - "attrOrProp" : 2, - "digest" : "A JSON formatted string containing metadata for use by the exported code", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Metadata", - "displayorder" : 17 - } -, - "nopreset" : { - "attrOrProp" : 2, - "digest" : "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 1, - "type" : "bool", - "defaultValue" : "false" - } -, - "preset" : { - "attrOrProp" : 2, - "digest" : "Add this value to the preset.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "bool", - "defaultValue" : "true", - "label" : "Include In Preset", - "displayorder" : 11 - } - - } -, - "inputs" : [ { - "name" : "value", - "type" : "number", - "digest" : "Parameter value", - "hot" : 1, - "docked" : 0 - } -, { - "name" : "normalizedvalue", - "type" : "number", - "digest" : "Set value normalized. ", - "docked" : 0 - } - ], - "outputs" : [ { - "name" : "value", - "type" : "number", - "digest" : "Parameter value", - "hot" : 1, - "docked" : 0 - } -, { - "name" : "normalized", - "type" : "number", - "digest" : "Normalized parameter value.", - "docked" : 0 - } - ], - "helpname" : "param", - "aliasOf" : "param", - "classname" : "param", - "operator" : 0, - "versionId" : -1445040964, - "changesPatcherIO" : 0 - } -, - "text" : "param kink3 @min 0 @max 1", - "varname" : "kink3" - } - - } -, { - "box" : { - "id" : "obj-20", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 556.0, 203.0, 66.0, 23.0 ], - "rnbo_classname" : "cycle~", - "rnbo_extra_attributes" : { - "interp" : "linear", - "buffername" : "RNBODefaultSinus", - "index" : "freq" - } -, - "rnbo_serial" : 5, - "rnbo_uniqueid" : "cycle~_obj-20", - "text" : "cycle~ 187" - } - - } -, { - "box" : { - "id" : "obj-9", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 304.0, 238.0, 37.0, 23.0 ], - "rnbo_classname" : "kink~", - "rnbo_serial" : 2, - "rnbo_uniqueid" : "kink~_obj-9", - "text" : "kink~" - } - - } -, { - "box" : { - "id" : "obj-10", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "" ], - "patching_rect" : [ 386.0, 203.0, 44.0, 23.0 ], - "rnbo_classname" : "line~", - "rnbo_serial" : 2, - "rnbo_uniqueid" : "line~_obj-10", - "text" : "line~ 1" - } - - } -, { - "box" : { - "id" : "obj-11", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 386.0, 178.0, 65.0, 23.0 ], - "rnbo_classname" : "append", - "rnbo_extra_attributes" : { - "hot" : 0 - } -, - "rnbo_serial" : 2, - "rnbo_uniqueid" : "append_obj-11", - "text" : "append 40" - } - - } -, { - "box" : { - "id" : "obj-12", - "maxclass" : "newobj", - "numinlets" : 6, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 386.0, 153.0, 100.0, 23.0 ], - "rnbo_classname" : "scale", - "rnbo_serial" : 2, - "rnbo_uniqueid" : "scale_obj-12", - "text" : "scale 0. 1. 1. 0.51" - } - - } -, { - "box" : { - "id" : "obj-13", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "", "" ], - "patching_rect" : [ 386.0, 128.0, 161.0, 23.0 ], - "rnbo_classname" : "param", - "rnbo_extra_attributes" : { - "steps" : 0.0, - "fromnormalized" : "", - "sendinit" : 1, - "displayname" : "", - "meta" : "", - "unit" : "", - "exponent" : 1.0, - "preset" : 1, - "enum" : "", - "order" : "0", - "value" : 0.0, - "tonormalized" : "", - "displayorder" : "-", - "ctlin" : 0.0 - } -, - "rnbo_serial" : 2, - "rnbo_uniqueid" : "kink2", - "rnboinfo" : { - "needsInstanceInfo" : 1, - "argnames" : { - "value" : { - "attrOrProp" : 2, - "digest" : "Set initial value", - "defaultarg" : 2, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Initial Value", - "displayorder" : 3 - } -, - "normalizedvalue" : { - "attrOrProp" : 1, - "digest" : "Set value normalized. ", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "inlet" : 1, - "type" : "number" - } -, - "reset" : { - "attrOrProp" : 1, - "digest" : "Reset param to initial value", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 1, - "isparam" : 0, - "deprecated" : 0, - "type" : "bang" - } -, - "normalized" : { - "attrOrProp" : 1, - "digest" : "Normalized parameter value.", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "outlet" : 1, - "type" : "number" - } -, - "name" : { - "attrOrProp" : 2, - "digest" : "Name of the parameter", - "defaultarg" : 1, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "Parameter Name", - "mandatory" : 1 - } -, - "enum" : { - "attrOrProp" : 2, - "digest" : "Use an enumerated output", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "list", - "label" : "Enum Values", - "displayorder" : 6 - } -, - "minimum" : { - "attrOrProp" : 2, - "digest" : "Minimum value", - "isalias" : 0, - "aliases" : [ "min" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Minimum", - "displayorder" : 1 - } -, - "min" : { - "attrOrProp" : 2, - "digest" : "Minimum value", - "isalias" : 1, - "aliasOf" : "minimum", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Minimum", - "displayorder" : 1 - } -, - "maximum" : { - "attrOrProp" : 2, - "digest" : "Maximum value", - "isalias" : 0, - "aliases" : [ "max" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Maximum", - "displayorder" : 2 - } -, - "max" : { - "attrOrProp" : 2, - "digest" : "Maximum value", - "isalias" : 1, - "aliasOf" : "maximum", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Maximum", - "displayorder" : 2 - } -, - "exponent" : { - "attrOrProp" : 2, - "digest" : "Scale values exponentially", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Exponent", - "displayorder" : 7 - } -, - "steps" : { - "attrOrProp" : 2, - "digest" : "Divide the output into a number of discrete steps", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Steps", - "displayorder" : 8 - } -, - "displayname" : { - "attrOrProp" : 2, - "digest" : "A more readable name for the parameter in an external RNBO target", - "isalias" : 0, - "aliases" : [ "displayName" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Display Name", - "displayorder" : 14 - } -, - "displayName" : { - "attrOrProp" : 2, - "digest" : "A more readable name for the parameter in an external RNBO target", - "isalias" : 1, - "aliasOf" : "displayname", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Display Name", - "displayorder" : 14 - } -, - "unit" : { - "attrOrProp" : 2, - "digest" : "A symbol to describe the unit of the parameter in an external RNBO target", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Unit", - "displayorder" : 15 - } -, - "tonormalized" : { - "attrOrProp" : 2, - "digest" : "Converts a real parameter value to its normalized form.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "To Normalized Expression", - "displayorder" : 10 - } -, - "fromnormalized" : { - "attrOrProp" : 2, - "digest" : "Converts a normalized parameter into its actual parameter value.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "From Normalized Expression", - "displayorder" : 9 - } -, - "order" : { - "attrOrProp" : 2, - "digest" : "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "0", - "label" : "Restore Order", - "displayorder" : 12 - } -, - "displayorder" : { - "attrOrProp" : 2, - "digest" : "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "-", - "label" : "Display Order", - "displayorder" : 13 - } -, - "sendinit" : { - "attrOrProp" : 2, - "digest" : "Send initial value", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "bool", - "defaultValue" : "true", - "label" : "Send Init", - "displayorder" : 4 - } -, - "ctlin" : { - "attrOrProp" : 2, - "digest" : "MIDI controller number to control this parameter.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "label" : "MIDI Controller Number.", - "displayorder" : 16 - } -, - "meta" : { - "attrOrProp" : 2, - "digest" : "A JSON formatted string containing metadata for use by the exported code", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Metadata", - "displayorder" : 17 - } -, - "nopreset" : { - "attrOrProp" : 2, - "digest" : "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 1, - "type" : "bool", - "defaultValue" : "false" - } -, - "preset" : { - "attrOrProp" : 2, - "digest" : "Add this value to the preset.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "bool", - "defaultValue" : "true", - "label" : "Include In Preset", - "displayorder" : 11 - } - - } -, - "inputs" : [ { - "name" : "value", - "type" : "number", - "digest" : "Parameter value", - "hot" : 1, - "docked" : 0 - } -, { - "name" : "normalizedvalue", - "type" : "number", - "digest" : "Set value normalized. ", - "docked" : 0 - } - ], - "outputs" : [ { - "name" : "value", - "type" : "number", - "digest" : "Parameter value", - "hot" : 1, - "docked" : 0 - } -, { - "name" : "normalized", - "type" : "number", - "digest" : "Normalized parameter value.", - "docked" : 0 - } - ], - "helpname" : "param", - "aliasOf" : "param", - "classname" : "param", - "operator" : 0, - "versionId" : -1445040964, - "changesPatcherIO" : 0 - } -, - "text" : "param kink2 @min 0 @max 1", - "varname" : "kink2" - } - - } -, { - "box" : { - "id" : "obj-14", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 304.0, 203.0, 66.0, 23.0 ], - "rnbo_classname" : "cycle~", - "rnbo_extra_attributes" : { - "interp" : "linear", - "buffername" : "RNBODefaultSinus", - "index" : "freq" - } -, - "rnbo_serial" : 4, - "rnbo_uniqueid" : "cycle~_obj-14", - "text" : "cycle~ 167" - } - - } -, { - "box" : { - "id" : "obj-8", - "maxclass" : "newobj", - "numinlets" : 1, - "numoutlets" : 0, - "patching_rect" : [ 52.0, 336.0, 43.0, 23.0 ], - "rnbo_classname" : "out~", - "rnbo_extra_attributes" : { - "meta" : "", - "comment" : "" - } -, - "rnbo_serial" : 1, - "rnbo_uniqueid" : "out~_obj-8", - "rnboinfo" : { - "needsInstanceInfo" : 1, - "argnames" : { - "in1" : { - "attrOrProp" : 1, - "digest" : "signal sent to outlet with index 1", - "isalias" : 0, - "aliases" : [ ], - "settable" : 0, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "inlet" : 1, - "type" : "signal" - } -, - "index" : { - "attrOrProp" : 2, - "digest" : "outlet number", - "defaultarg" : 1, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "mandatory" : 1 - } -, - "comment" : { - "attrOrProp" : 2, - "digest" : "mouse over comment", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol" - } -, - "meta" : { - "attrOrProp" : 2, - "digest" : "A JSON formatted string containing metadata for use by the exported code", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Metadata", - "displayorder" : 3 - } - - } -, - "inputs" : [ { - "name" : "in1", - "type" : "signal", - "digest" : "signal sent to outlet with index 1", - "displayName" : "", - "hot" : 1, - "docked" : 0 - } - ], - "outputs" : [ ], - "helpname" : "out~", - "aliasOf" : "out~", - "classname" : "out~", - "operator" : 0, - "versionId" : 374499139, - "changesPatcherIO" : 1 - } -, - "text" : "out~ 1" - } - - } -, { - "box" : { - "id" : "obj-7", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 52.0, 288.0, 47.0, 23.0 ], - "rnbo_classname" : "*~", - "rnbo_serial" : 1, - "rnbo_uniqueid" : "*~_obj-7", - "text" : "*~ 0.25" - } - - } -, { - "box" : { - "id" : "obj-6", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "signal" ], - "patching_rect" : [ 52.0, 238.0, 37.0, 23.0 ], - "rnbo_classname" : "kink~", - "rnbo_serial" : 3, - "rnbo_uniqueid" : "kink~_obj-6", - "text" : "kink~" - } - - } -, { - "box" : { - "id" : "obj-5", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "" ], - "patching_rect" : [ 134.0, 203.0, 44.0, 23.0 ], - "rnbo_classname" : "line~", - "rnbo_serial" : 3, - "rnbo_uniqueid" : "line~_obj-5", - "text" : "line~ 1" - } - - } -, { - "box" : { - "id" : "obj-4", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 134.0, 178.0, 65.0, 23.0 ], - "rnbo_classname" : "append", - "rnbo_extra_attributes" : { - "hot" : 0 - } -, - "rnbo_serial" : 3, - "rnbo_uniqueid" : "append_obj-4", - "text" : "append 40" - } - - } -, { - "box" : { - "id" : "obj-3", - "maxclass" : "newobj", - "numinlets" : 6, - "numoutlets" : 1, - "outlettype" : [ "" ], - "patching_rect" : [ 134.0, 153.0, 100.0, 23.0 ], - "rnbo_classname" : "scale", - "rnbo_serial" : 3, - "rnbo_uniqueid" : "scale_obj-3", - "text" : "scale 0. 1. 1. 0.51" - } - - } -, { - "box" : { - "id" : "obj-2", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "", "" ], - "patching_rect" : [ 134.0, 128.0, 161.0, 23.0 ], - "rnbo_classname" : "param", - "rnbo_extra_attributes" : { - "steps" : 0.0, - "fromnormalized" : "", - "sendinit" : 1, - "displayname" : "", - "meta" : "", - "unit" : "", - "exponent" : 1.0, - "preset" : 1, - "enum" : "", - "order" : "0", - "value" : 0.0, - "tonormalized" : "", - "displayorder" : "-", - "ctlin" : 0.0 - } -, - "rnbo_serial" : 3, - "rnbo_uniqueid" : "kink1", - "rnboinfo" : { - "needsInstanceInfo" : 1, - "argnames" : { - "value" : { - "attrOrProp" : 2, - "digest" : "Set initial value", - "defaultarg" : 2, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Initial Value", - "displayorder" : 3 - } -, - "normalizedvalue" : { - "attrOrProp" : 1, - "digest" : "Set value normalized. ", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "inlet" : 1, - "type" : "number" - } -, - "reset" : { - "attrOrProp" : 1, - "digest" : "Reset param to initial value", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 1, - "isparam" : 0, - "deprecated" : 0, - "type" : "bang" - } -, - "normalized" : { - "attrOrProp" : 1, - "digest" : "Normalized parameter value.", - "isalias" : 0, - "aliases" : [ ], - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "outlet" : 1, - "type" : "number" - } -, - "name" : { - "attrOrProp" : 2, - "digest" : "Name of the parameter", - "defaultarg" : 1, - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "Parameter Name", - "mandatory" : 1 - } -, - "enum" : { - "attrOrProp" : 2, - "digest" : "Use an enumerated output", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "list", - "label" : "Enum Values", - "displayorder" : 6 - } -, - "minimum" : { - "attrOrProp" : 2, - "digest" : "Minimum value", - "isalias" : 0, - "aliases" : [ "min" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Minimum", - "displayorder" : 1 - } -, - "min" : { - "attrOrProp" : 2, - "digest" : "Minimum value", - "isalias" : 1, - "aliasOf" : "minimum", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Minimum", - "displayorder" : 1 - } -, - "maximum" : { - "attrOrProp" : 2, - "digest" : "Maximum value", - "isalias" : 0, - "aliases" : [ "max" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Maximum", - "displayorder" : 2 - } -, - "max" : { - "attrOrProp" : 2, - "digest" : "Maximum value", - "isalias" : 1, - "aliasOf" : "maximum", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Maximum", - "displayorder" : 2 - } -, - "exponent" : { - "attrOrProp" : 2, - "digest" : "Scale values exponentially", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "1", - "label" : "Exponent", - "displayorder" : 7 - } -, - "steps" : { - "attrOrProp" : 2, - "digest" : "Divide the output into a number of discrete steps", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "defaultValue" : "0", - "label" : "Steps", - "displayorder" : 8 - } -, - "displayname" : { - "attrOrProp" : 2, - "digest" : "A more readable name for the parameter in an external RNBO target", - "isalias" : 0, - "aliases" : [ "displayName" ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Display Name", - "displayorder" : 14 - } -, - "displayName" : { - "attrOrProp" : 2, - "digest" : "A more readable name for the parameter in an external RNBO target", - "isalias" : 1, - "aliasOf" : "displayname", - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Display Name", - "displayorder" : 14 - } -, - "unit" : { - "attrOrProp" : 2, - "digest" : "A symbol to describe the unit of the parameter in an external RNBO target", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Unit", - "displayorder" : 15 - } -, - "tonormalized" : { - "attrOrProp" : 2, - "digest" : "Converts a real parameter value to its normalized form.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "To Normalized Expression", - "displayorder" : 10 - } -, - "fromnormalized" : { - "attrOrProp" : 2, - "digest" : "Converts a normalized parameter into its actual parameter value.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "label" : "From Normalized Expression", - "displayorder" : 9 - } -, - "order" : { - "attrOrProp" : 2, - "digest" : "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "0", - "label" : "Restore Order", - "displayorder" : 12 - } -, - "displayorder" : { - "attrOrProp" : 2, - "digest" : "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "-", - "label" : "Display Order", - "displayorder" : 13 - } -, - "sendinit" : { - "attrOrProp" : 2, - "digest" : "Send initial value", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "bool", - "defaultValue" : "true", - "label" : "Send Init", - "displayorder" : 4 - } -, - "ctlin" : { - "attrOrProp" : 2, - "digest" : "MIDI controller number to control this parameter.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "number", - "label" : "MIDI Controller Number.", - "displayorder" : 16 - } -, - "meta" : { - "attrOrProp" : 2, - "digest" : "A JSON formatted string containing metadata for use by the exported code", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "symbol", - "defaultValue" : "", - "label" : "Metadata", - "displayorder" : 17 - } -, - "nopreset" : { - "attrOrProp" : 2, - "digest" : "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 1, - "type" : "bool", - "defaultValue" : "false" - } -, - "preset" : { - "attrOrProp" : 2, - "digest" : "Add this value to the preset.", - "isalias" : 0, - "aliases" : [ ], - "settable" : 1, - "attachable" : 0, - "isparam" : 0, - "deprecated" : 0, - "type" : "bool", - "defaultValue" : "true", - "label" : "Include In Preset", - "displayorder" : 11 - } - - } -, - "inputs" : [ { - "name" : "value", - "type" : "number", - "digest" : "Parameter value", - "hot" : 1, - "docked" : 0 - } -, { - "name" : "normalizedvalue", - "type" : "number", - "digest" : "Set value normalized. ", - "docked" : 0 - } - ], - "outputs" : [ { - "name" : "value", - "type" : "number", - "digest" : "Parameter value", - "hot" : 1, - "docked" : 0 - } -, { - "name" : "normalized", - "type" : "number", - "digest" : "Normalized parameter value.", - "docked" : 0 - } - ], - "helpname" : "param", - "aliasOf" : "param", - "classname" : "param", - "operator" : 0, - "versionId" : -1445040964, - "changesPatcherIO" : 0 - } -, - "text" : "param kink1 @min 0 @max 1", - "varname" : "kink1" - } - - } -, { - "box" : { - "id" : "obj-1", - "maxclass" : "newobj", - "numinlets" : 2, - "numoutlets" : 2, - "outlettype" : [ "signal", "signal" ], - "patching_rect" : [ 52.0, 203.0, 66.0, 23.0 ], - "rnbo_classname" : "cycle~", - "rnbo_extra_attributes" : { - "interp" : "linear", - "buffername" : "RNBODefaultSinus", - "index" : "freq" - } -, - "rnbo_serial" : 3, - "rnbo_uniqueid" : "cycle~_obj-1", - "text" : "cycle~ 242" - } - - } - ], - "lines" : [ { - "patchline" : { - "destination" : [ "obj-6", 0 ], - "source" : [ "obj-1", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-9", 1 ], - "source" : [ "obj-10", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-10", 0 ], - "source" : [ "obj-11", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-11", 0 ], - "source" : [ "obj-12", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-12", 0 ], - "source" : [ "obj-13", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-9", 0 ], - "source" : [ "obj-14", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-7", 0 ], - "source" : [ "obj-15", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-15", 1 ], - "source" : [ "obj-16", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-16", 0 ], - "source" : [ "obj-17", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-17", 0 ], - "source" : [ "obj-18", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-18", 0 ], - "source" : [ "obj-19", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-3", 0 ], - "source" : [ "obj-2", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-15", 0 ], - "source" : [ "obj-20", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-23", 0 ], - "source" : [ "obj-22", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-24", 0 ], - "source" : [ "obj-23", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-19", 0 ], - "source" : [ "obj-24", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-4", 0 ], - "source" : [ "obj-3", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-5", 0 ], - "source" : [ "obj-4", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-6", 1 ], - "source" : [ "obj-5", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-7", 0 ], - "source" : [ "obj-6", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-21", 0 ], - "order" : 0, - "source" : [ "obj-7", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-8", 0 ], - "order" : 1, - "source" : [ "obj-7", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-7", 0 ], - "source" : [ "obj-9", 0 ] - } - - } - ], - "default_bgcolor" : [ 0.031372549019608, 0.125490196078431, 0.211764705882353, 1.0 ], - "color" : [ 0.929412, 0.929412, 0.352941, 1.0 ], - "elementcolor" : [ 0.357540726661682, 0.515565991401672, 0.861786782741547, 1.0 ], - "accentcolor" : [ 0.343034118413925, 0.506230533123016, 0.86220508813858, 1.0 ], - "stripecolor" : [ 0.258338063955307, 0.352425158023834, 0.511919498443604, 1.0 ], - "bgfillcolor_type" : "color", - "bgfillcolor_color" : [ 0.031372549019608, 0.125490196078431, 0.211764705882353, 1.0 ], - "bgfillcolor_color1" : [ 0.031372549019608, 0.125490196078431, 0.211764705882353, 1.0 ], - "bgfillcolor_color2" : [ 0.263682, 0.004541, 0.038797, 1.0 ], - "bgfillcolor_angle" : 270.0, - "bgfillcolor_proportion" : 0.39, - "bgfillcolor_autogradient" : 0.0 - } -, - "patching_rect" : [ 145.0, 99.0, 168.0, 22.0 ], - "rnboattrcache" : { - "kink3" : { - "label" : "kink3", - "isEnum" : 0, - "parsestring" : "" - } -, - "kink1" : { - "label" : "kink1", - "isEnum" : 0, - "parsestring" : "" - } -, - "kink2" : { - "label" : "kink2", - "isEnum" : 0, - "parsestring" : "" - } - - } -, - "rnboversion" : "1.1.0-dev.57", - "saved_attribute_attributes" : { - "valueof" : { - "parameter_invisible" : 1, - "parameter_longname" : "rnbo~", - "parameter_shortname" : "rnbo~", - "parameter_type" : 3 - } - - } -, - "saved_object_attributes" : { - "optimization" : "O1", - "parameter_enable" : 1, - "uuid" : "fafce8f5-9839-11ed-a8f5-56ea1db75653" - } -, - "snapshot" : { - "filetype" : "C74Snapshot", - "version" : 2, - "minorversion" : 0, - "name" : "snapshotlist", - "origin" : "rnbo~", - "type" : "list", - "subtype" : "Undefined", - "embed" : 1, - "snapshot" : { - "kink2" : { - "value" : 0.0 - } -, - "kink1" : { - "value" : 0.0 - } -, - "kink3" : { - "value" : 0.864957910740879 - } -, - "__presetid" : "three-param-kink" - } -, - "snapshotlist" : { - "current_snapshot" : 0, - "entries" : [ { - "filetype" : "C74Snapshot", - "version" : 2, - "minorversion" : 0, - "name" : "untitled", - "origin" : "three-param-kink", - "type" : "rnbo", - "subtype" : "", - "embed" : 0, - "snapshot" : { - "kink2" : { - "value" : 0.0 - } -, - "kink1" : { - "value" : 0.0 - } -, - "kink3" : { - "value" : 0.864957910740879 - } -, - "__presetid" : "three-param-kink" - } -, - "fileref" : { - "name" : "untitled", - "filename" : "untitled_20230120.maxsnap", - "filepath" : "~/Documents/Max 8/Snapshots", - "filepos" : -1, - "snapshotfileid" : "3299593f9ab13c679c459c9ee61caf27" - } - - } - ] - } - - } -, - "text" : "rnbo~ @title three-param-kink", - "varname" : "rnbo~" - } - - } - ], - "lines" : [ { - "patchline" : { - "destination" : [ "obj-5", 1 ], - "source" : [ "obj-1", 1 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-5", 0 ], - "source" : [ "obj-1", 0 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-2", 1 ], - "source" : [ "obj-5", 1 ] - } - - } -, { - "patchline" : { - "destination" : [ "obj-2", 0 ], - "source" : [ "obj-5", 0 ] - } - - } - ], - "parameters" : { - "obj-1" : [ "rnbo~", "rnbo~", 0 ], - "obj-5" : [ "live.gain~", "live.gain~", 0 ], - "parameterbanks" : { - "0" : { - "index" : 0, - "name" : "", - "parameters" : [ "-", "-", "-", "-", "-", "-", "-", "-" ] - } - - } -, - "inherited_shortname" : 1 - } -, - "dependency_cache" : [ { - "name" : "untitled_20230120.maxsnap", - "bootpath" : "~/Documents/Max 8/Snapshots", - "patcherrelativepath" : "../../../Documents/Max 8/Snapshots", - "type" : "mx@s", - "implicit" : 1 - } - ], - "autosave" : 0 - } - -} + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 1, + "revision": 3, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "box", + "rect": [ 353.0, 290.0, 640.0, 480.0 ], + "boxes": [ + { + "box": { + "id": "obj-11", + "maxclass": "live.slider", + "numinlets": 1, + "numoutlets": 2, + "orientation": 1, + "outlettype": [ "", "float" ], + "param_connect": "rnbo~::kink3", + "parameter_enable": 1, + "patching_rect": [ 167.0, 197.0, 111.0, 41.0 ], + "saved_attribute_attributes": { + "valueof": { + "parameter_initial": [ 0.0 ], + "parameter_initial_enable": 1, + "parameter_longname": "kink3", + "parameter_mmax": 1.0, + "parameter_modmode": 3, + "parameter_shortname": "kink3", + "parameter_type": 0, + "parameter_unitstyle": 1 + } + }, + "varname": "kink1[2]" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "live.slider", + "numinlets": 1, + "numoutlets": 2, + "orientation": 1, + "outlettype": [ "", "float" ], + "param_connect": "rnbo~::kink2", + "parameter_enable": 1, + "patching_rect": [ 167.0, 139.0, 111.0, 41.0 ], + "saved_attribute_attributes": { + "valueof": { + "parameter_initial": [ 0.0 ], + "parameter_initial_enable": 1, + "parameter_longname": "kink2", + "parameter_mmax": 1.0, + "parameter_modmode": 3, + "parameter_shortname": "kink2", + "parameter_type": 0, + "parameter_unitstyle": 1 + } + }, + "varname": "kink1[1]" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "live.slider", + "numinlets": 1, + "numoutlets": 2, + "orientation": 1, + "outlettype": [ "", "float" ], + "param_connect": "rnbo~::kink1", + "parameter_enable": 1, + "patching_rect": [ 167.0, 81.0, 111.0, 41.0 ], + "saved_attribute_attributes": { + "valueof": { + "parameter_initial": [ 0.0 ], + "parameter_initial_enable": 1, + "parameter_longname": "kink1", + "parameter_mmax": 1.0, + "parameter_modmode": 3, + "parameter_shortname": "kink1", + "parameter_type": 0, + "parameter_unitstyle": 1 + } + }, + "varname": "kink1" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "toggle", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ "int" ], + "parameter_enable": 0, + "patching_rect": [ 298.0, 81.0, 24.0, 24.0 ] + } + }, + { + "box": { + "attr": "automate", + "id": "obj-4", + "maxclass": "attrui", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ "" ], + "parameter_enable": 0, + "patching_rect": [ 298.0, 122.0, 150.0, 22.0 ] + } + }, + { + "box": { + "id": "obj-5", + "lastchannelcount": 0, + "maxclass": "live.gain~", + "numinlets": 2, + "numoutlets": 5, + "outlettype": [ "signal", "signal", "", "float", "list" ], + "parameter_enable": 1, + "patching_rect": [ 298.0, 196.0, 48.0, 136.0 ], + "saved_attribute_attributes": { + "valueof": { + "parameter_longname": "live.gain~", + "parameter_mmax": 6.0, + "parameter_mmin": -70.0, + "parameter_modmode": 0, + "parameter_shortname": "live.gain~", + "parameter_type": 0, + "parameter_unitstyle": 4 + } + }, + "varname": "live.gain~" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "ezdac~", + "numinlets": 2, + "numoutlets": 0, + "patching_rect": [ 299.0, 351.0, 45.0, 45.0 ] + } + }, + { + "box": { + "autosave": 1, + "id": "obj-1", + "inletInfo": { + "IOInfo": [ + { + "type": "midi", + "index": -1, + "tag": "", + "comment": "" + } + ] + }, + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 3, + "outletInfo": { + "IOInfo": [ + { + "type": "signal", + "index": 1, + "tag": "out1", + "comment": "" + }, + { + "type": "signal", + "index": 2, + "tag": "out2", + "comment": "" + } + ] + }, + "outlettype": [ "signal", "signal", "list" ], + "patcher": { + "fileversion": 1, + "appversion": { + "major": 9, + "minor": 1, + "revision": 3, + "architecture": "x64", + "modernui": 1 + }, + "classnamespace": "rnbo", + "rect": [ 162.0, 195.0, 917.0, 449.0 ], + "default_fontname": "Lato", + "title": "three-param-kink", + "boxes": [ + { + "box": { + "id": "obj-26", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 711.0, 64.0, 57.0, 23.0 ], + "rnbo_classname": "metro", + "rnbo_extra_attributes": { + "lock": 0.0 + }, + "rnbo_serial": 1, + "rnbo_uniqueid": "metro_obj-26", + "text": "metro 30" + } + }, + { + "box": { + "id": "obj-25", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "", "" ], + "patching_rect": [ 711.0, 33.0, 169.0, 23.0 ], + "rnbo_classname": "param", + "rnbo_extra_attributes": { + "meta": "", + "ctlin": -1.0, + "unit": "", + "steps": 2.0, + "maximum": 1.0, + "sendinit": 1, + "displayorder": "-", + "order": "0", + "displayname": "", + "minimum": 0.0, + "preset": 1, + "exponent": 1.0, + "tonormalized": "", + "fromnormalized": "" + }, + "rnbo_serial": 4, + "rnbo_uniqueid": "automate", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "value": { + "attrOrProp": 1, + "digest": "Parameter value", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 1, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "normalizedvalue": { + "attrOrProp": 1, + "digest": "Set value normalized. ", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number" + }, + "reset": { + "attrOrProp": 1, + "digest": "Reset param to initial value", + "isalias": 0, + "aliases": [], + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bang" + }, + "normalized": { + "attrOrProp": 1, + "digest": "Normalized parameter value.", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "number" + }, + "maximum": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 0, + "aliases": [ "max" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 1, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2, + "disabledInMaxInspector": 1 + }, + "max": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 1, + "aliasOf": "maximum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "minimum": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 0, + "aliases": [ "min" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 1, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1, + "disabledInMaxInspector": 1 + }, + "min": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 1, + "aliasOf": "minimum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "steps": { + "attrOrProp": 2, + "digest": "Divide the output into a number of discrete steps", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 1, + "type": "number", + "defaultValue": "2", + "label": "Steps", + "displayorder": 8, + "disabledInMaxInspector": 1 + }, + "exponent": { + "attrOrProp": 2, + "digest": "Scale values exponentially", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 1, + "type": "number", + "defaultValue": "1", + "label": "Exponent", + "displayorder": 7, + "disabledInMaxInspector": 1 + }, + "name": { + "attrOrProp": 2, + "digest": "Name of the parameter", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "Parameter Name", + "mandatory": 1 + }, + "enum": { + "attrOrProp": 2, + "digest": "Use an enumerated output", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "list", + "label": "Enum Values", + "displayorder": 6 + }, + "displayName": { + "attrOrProp": 2, + "digest": "DEPRECATED: Use the lower case 'displayname' instead", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "symbol", + "label": "Display Name" + }, + "displayname": { + "attrOrProp": 2, + "digest": "A more readable name for the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Display Name", + "displayorder": 14 + }, + "unit": { + "attrOrProp": 2, + "digest": "A symbol to describe the unit of the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Unit", + "displayorder": 15 + }, + "tonormalized": { + "attrOrProp": 2, + "digest": "Converts a real parameter value to its normalized form", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "To Normalized Expression", + "displayorder": 10 + }, + "fromnormalized": { + "attrOrProp": 2, + "digest": "Converts a normalized parameter into its actual parameter value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "From Normalized Expression", + "displayorder": 9 + }, + "order": { + "attrOrProp": 2, + "digest": "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "0", + "label": "Restore Order", + "displayorder": 12 + }, + "displayorder": { + "attrOrProp": 2, + "digest": "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "-", + "label": "Display Order", + "displayorder": 13 + }, + "sendinit": { + "attrOrProp": 2, + "digest": "Send initial value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Send Init", + "displayorder": 4 + }, + "ctlin": { + "attrOrProp": 2, + "digest": "MIDI controller number to control this parameter.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "-1", + "label": "MIDI Controller Number.", + "displayorder": 16 + }, + "meta": { + "attrOrProp": 2, + "digest": "A JSON formatted string containing metadata for use by the exported code", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Metadata", + "displayorder": 17 + }, + "nopreset": { + "attrOrProp": 2, + "digest": "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "preset": { + "attrOrProp": 2, + "digest": "Add this value to the preset.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Include In Preset", + "displayorder": 11 + } + }, + "inputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalizedvalue", + "type": "number", + "digest": "Set value normalized. ", + "docked": 0 + } + ], + "outputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalized", + "type": "number", + "digest": "Normalized parameter value.", + "docked": 0 + } + ], + "helpname": "param", + "aliasOf": "param", + "classname": "param", + "operator": 0, + "versionId": -1093178486, + "changesPatcherIO": 0 + }, + "text": "param automate @enum off on", + "varname": "automate" + } + }, + { + "box": { + "id": "obj-24", + "maxclass": "newobj", + "numinlets": 6, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 638.0, 97.0, 82.0, 23.0 ], + "rnbo_classname": "scale", + "rnbo_serial": 1, + "rnbo_uniqueid": "scale_obj-24", + "text": "scale -1 1 0 1." + } + }, + { + "box": { + "id": "obj-23", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 638.0, 64.0, 63.0, 23.0 ], + "rnbo_classname": "snapshot~", + "rnbo_extra_attributes": { + "mode": 0.0 + }, + "rnbo_serial": 2, + "rnbo_uniqueid": "snapshot~_obj-23", + "text": "snapshot~" + } + }, + { + "box": { + "id": "obj-22", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "signal" ], + "patching_rect": [ 638.0, 33.0, 52.0, 23.0 ], + "rnbo_classname": "cycle~", + "rnbo_extra_attributes": { + "index": "freq", + "buffername": "RNBODefaultSinus", + "interp": "linear" + }, + "rnbo_serial": 1, + "rnbo_uniqueid": "cycle~_obj-22", + "text": "cycle~ 1" + } + }, + { + "box": { + "id": "obj-21", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ 119.0, 336.0, 43.0, 23.0 ], + "rnbo_classname": "out~", + "rnbo_extra_attributes": { + "meta": "", + "comment": "" + }, + "rnbo_serial": 1, + "rnbo_uniqueid": "out~_obj-21", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "in1": { + "attrOrProp": 1, + "digest": "signal sent to outlet with index 2", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "signal" + }, + "index": { + "attrOrProp": 2, + "digest": "outlet number", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "mandatory": 1 + }, + "comment": { + "attrOrProp": 2, + "digest": "mouse over comment", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol" + }, + "meta": { + "attrOrProp": 2, + "digest": "A JSON formatted string containing metadata for use by the exported code", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Metadata", + "displayorder": 3 + } + }, + "inputs": [ + { + "name": "in1", + "type": "signal", + "digest": "signal sent to outlet with index 2", + "displayName": "", + "hot": 1, + "docked": 0 + } + ], + "outputs": [], + "helpname": "out~", + "aliasOf": "out~", + "classname": "out~", + "operator": 0, + "versionId": 1989326771, + "changesPatcherIO": 1 + }, + "text": "out~ 2" + } + }, + { + "box": { + "id": "obj-15", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "signal" ], + "patching_rect": [ 556.0, 238.0, 38.0, 23.0 ], + "rnbo_classname": "kink~", + "rnbo_serial": 1, + "rnbo_uniqueid": "kink~_obj-15", + "text": "kink~" + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "" ], + "patching_rect": [ 638.0, 203.0, 44.0, 23.0 ], + "rnbo_classname": "line~", + "rnbo_serial": 1, + "rnbo_uniqueid": "line~_obj-16", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "segments": { + "attrOrProp": 1, + "digest": "Target value or target value/ramp time pairs", + "isalias": 0, + "aliases": [ "dest" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "list", + "defaultValue": "" + }, + "dest": { + "attrOrProp": 1, + "digest": "Target value or target value/ramp time pairs", + "isalias": 1, + "aliasOf": "segments", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "list", + "defaultValue": "" + }, + "time": { + "attrOrProp": 1, + "digest": "Ramp time", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "keepramp": { + "attrOrProp": 1, + "digest": "Keep last ramp", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "out": { + "attrOrProp": 1, + "digest": "Ramp out", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "signal" + }, + "target": { + "attrOrProp": 1, + "digest": "Bang when ramp has finished", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "bang" + }, + "value": { + "attrOrProp": 2, + "digest": "Initial value.", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0" + } + }, + "inputs": [ + { + "name": "segments", + "type": "list", + "digest": "Target value or target value/ramp time pairs", + "hot": 1, + "docked": 0 + }, + { + "name": "time", + "type": "number", + "digest": "Ramp time", + "defaultarg": 2, + "docked": 0 + } + ], + "outputs": [ + { + "name": "out", + "type": "signal", + "digest": "Ramp out", + "docked": 0 + }, + { + "name": "target", + "type": "bang", + "digest": "Bang when ramp has finished", + "docked": 0 + } + ], + "helpname": "line~", + "aliasOf": "line~", + "classname": "line~", + "operator": 0, + "versionId": 2134689829, + "changesPatcherIO": 0 + }, + "text": "line~ 1" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 638.0, 178.0, 65.0, 23.0 ], + "rnbo_classname": "append", + "rnbo_extra_attributes": { + "hot": 0 + }, + "rnbo_serial": 1, + "rnbo_uniqueid": "append_obj-17", + "text": "append 40" + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", + "numinlets": 6, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 638.0, 153.0, 100.0, 23.0 ], + "rnbo_classname": "scale", + "rnbo_serial": 2, + "rnbo_uniqueid": "scale_obj-18", + "text": "scale 0. 1. 1. 0.51" + } + }, + { + "box": { + "id": "obj-19", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "", "" ], + "patching_rect": [ 638.0, 128.0, 161.0, 23.0 ], + "rnbo_classname": "param", + "rnbo_extra_attributes": { + "meta": "", + "ctlin": 0.0, + "unit": "", + "steps": 0.0, + "sendinit": 1, + "displayorder": "-", + "order": "0", + "displayname": "", + "preset": 1, + "exponent": 1.0, + "tonormalized": "", + "enum": "", + "fromnormalized": "" + }, + "rnbo_serial": 1, + "rnbo_uniqueid": "kink3", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "value": { + "attrOrProp": 1, + "digest": "Parameter value", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 1, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "normalizedvalue": { + "attrOrProp": 1, + "digest": "Set value normalized. ", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number" + }, + "reset": { + "attrOrProp": 1, + "digest": "Reset param to initial value", + "isalias": 0, + "aliases": [], + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bang" + }, + "normalized": { + "attrOrProp": 1, + "digest": "Normalized parameter value.", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "number" + }, + "name": { + "attrOrProp": 2, + "digest": "Name of the parameter", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "Parameter Name", + "mandatory": 1 + }, + "enum": { + "attrOrProp": 2, + "digest": "Use an enumerated output", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "list", + "label": "Enum Values", + "displayorder": 6 + }, + "minimum": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 0, + "aliases": [ "min" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "min": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 1, + "aliasOf": "minimum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "maximum": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 0, + "aliases": [ "max" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "max": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 1, + "aliasOf": "maximum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "exponent": { + "attrOrProp": 2, + "digest": "Scale values exponentially", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Exponent", + "displayorder": 7 + }, + "steps": { + "attrOrProp": 2, + "digest": "Divide the output into a number of discrete steps", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Steps", + "displayorder": 8 + }, + "displayName": { + "attrOrProp": 2, + "digest": "DEPRECATED: Use the lower case 'displayname' instead", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "symbol", + "label": "Display Name" + }, + "displayname": { + "attrOrProp": 2, + "digest": "A more readable name for the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Display Name", + "displayorder": 14 + }, + "unit": { + "attrOrProp": 2, + "digest": "A symbol to describe the unit of the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Unit", + "displayorder": 15 + }, + "tonormalized": { + "attrOrProp": 2, + "digest": "Converts a real parameter value to its normalized form", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "To Normalized Expression", + "displayorder": 10 + }, + "fromnormalized": { + "attrOrProp": 2, + "digest": "Converts a normalized parameter into its actual parameter value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "From Normalized Expression", + "displayorder": 9 + }, + "order": { + "attrOrProp": 2, + "digest": "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "0", + "label": "Restore Order", + "displayorder": 12 + }, + "displayorder": { + "attrOrProp": 2, + "digest": "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "-", + "label": "Display Order", + "displayorder": 13 + }, + "sendinit": { + "attrOrProp": 2, + "digest": "Send initial value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Send Init", + "displayorder": 4 + }, + "ctlin": { + "attrOrProp": 2, + "digest": "MIDI controller number to control this parameter.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "-1", + "label": "MIDI Controller Number.", + "displayorder": 16 + }, + "meta": { + "attrOrProp": 2, + "digest": "A JSON formatted string containing metadata for use by the exported code", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Metadata", + "displayorder": 17 + }, + "nopreset": { + "attrOrProp": 2, + "digest": "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "preset": { + "attrOrProp": 2, + "digest": "Add this value to the preset.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Include In Preset", + "displayorder": 11 + } + }, + "inputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalizedvalue", + "type": "number", + "digest": "Set value normalized. ", + "docked": 0 + } + ], + "outputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalized", + "type": "number", + "digest": "Normalized parameter value.", + "docked": 0 + } + ], + "helpname": "param", + "aliasOf": "param", + "classname": "param", + "operator": 0, + "versionId": -1093178486, + "changesPatcherIO": 0 + }, + "text": "param kink3 @min 0 @max 1", + "varname": "kink3" + } + }, + { + "box": { + "id": "obj-20", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "signal" ], + "patching_rect": [ 556.0, 203.0, 66.0, 23.0 ], + "rnbo_classname": "cycle~", + "rnbo_extra_attributes": { + "index": "freq", + "buffername": "RNBODefaultSinus", + "interp": "linear" + }, + "rnbo_serial": 2, + "rnbo_uniqueid": "cycle~_obj-20", + "text": "cycle~ 187" + } + }, + { + "box": { + "id": "obj-9", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "signal" ], + "patching_rect": [ 304.0, 238.0, 38.0, 23.0 ], + "rnbo_classname": "kink~", + "rnbo_serial": 2, + "rnbo_uniqueid": "kink~_obj-9", + "text": "kink~" + } + }, + { + "box": { + "id": "obj-10", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "" ], + "patching_rect": [ 386.0, 203.0, 44.0, 23.0 ], + "rnbo_classname": "line~", + "rnbo_serial": 2, + "rnbo_uniqueid": "line~_obj-10", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "segments": { + "attrOrProp": 1, + "digest": "Target value or target value/ramp time pairs", + "isalias": 0, + "aliases": [ "dest" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "list", + "defaultValue": "" + }, + "dest": { + "attrOrProp": 1, + "digest": "Target value or target value/ramp time pairs", + "isalias": 1, + "aliasOf": "segments", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "list", + "defaultValue": "" + }, + "time": { + "attrOrProp": 1, + "digest": "Ramp time", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "keepramp": { + "attrOrProp": 1, + "digest": "Keep last ramp", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "out": { + "attrOrProp": 1, + "digest": "Ramp out", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "signal" + }, + "target": { + "attrOrProp": 1, + "digest": "Bang when ramp has finished", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "bang" + }, + "value": { + "attrOrProp": 2, + "digest": "Initial value.", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0" + } + }, + "inputs": [ + { + "name": "segments", + "type": "list", + "digest": "Target value or target value/ramp time pairs", + "hot": 1, + "docked": 0 + }, + { + "name": "time", + "type": "number", + "digest": "Ramp time", + "defaultarg": 2, + "docked": 0 + } + ], + "outputs": [ + { + "name": "out", + "type": "signal", + "digest": "Ramp out", + "docked": 0 + }, + { + "name": "target", + "type": "bang", + "digest": "Bang when ramp has finished", + "docked": 0 + } + ], + "helpname": "line~", + "aliasOf": "line~", + "classname": "line~", + "operator": 0, + "versionId": 2134689829, + "changesPatcherIO": 0 + }, + "text": "line~ 1" + } + }, + { + "box": { + "id": "obj-11", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 386.0, 178.0, 65.0, 23.0 ], + "rnbo_classname": "append", + "rnbo_extra_attributes": { + "hot": 0 + }, + "rnbo_serial": 2, + "rnbo_uniqueid": "append_obj-11", + "text": "append 40" + } + }, + { + "box": { + "id": "obj-12", + "maxclass": "newobj", + "numinlets": 6, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 386.0, 153.0, 100.0, 23.0 ], + "rnbo_classname": "scale", + "rnbo_serial": 3, + "rnbo_uniqueid": "scale_obj-12", + "text": "scale 0. 1. 1. 0.51" + } + }, + { + "box": { + "id": "obj-13", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "", "" ], + "patching_rect": [ 386.0, 128.0, 161.0, 23.0 ], + "rnbo_classname": "param", + "rnbo_extra_attributes": { + "meta": "", + "ctlin": 0.0, + "unit": "", + "steps": 0.0, + "sendinit": 1, + "displayorder": "-", + "order": "0", + "displayname": "", + "preset": 1, + "exponent": 1.0, + "tonormalized": "", + "enum": "", + "fromnormalized": "" + }, + "rnbo_serial": 2, + "rnbo_uniqueid": "kink2", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "value": { + "attrOrProp": 1, + "digest": "Parameter value", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 1, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "normalizedvalue": { + "attrOrProp": 1, + "digest": "Set value normalized. ", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number" + }, + "reset": { + "attrOrProp": 1, + "digest": "Reset param to initial value", + "isalias": 0, + "aliases": [], + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bang" + }, + "normalized": { + "attrOrProp": 1, + "digest": "Normalized parameter value.", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "number" + }, + "name": { + "attrOrProp": 2, + "digest": "Name of the parameter", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "Parameter Name", + "mandatory": 1 + }, + "enum": { + "attrOrProp": 2, + "digest": "Use an enumerated output", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "list", + "label": "Enum Values", + "displayorder": 6 + }, + "minimum": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 0, + "aliases": [ "min" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "min": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 1, + "aliasOf": "minimum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "maximum": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 0, + "aliases": [ "max" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "max": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 1, + "aliasOf": "maximum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "exponent": { + "attrOrProp": 2, + "digest": "Scale values exponentially", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Exponent", + "displayorder": 7 + }, + "steps": { + "attrOrProp": 2, + "digest": "Divide the output into a number of discrete steps", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Steps", + "displayorder": 8 + }, + "displayName": { + "attrOrProp": 2, + "digest": "DEPRECATED: Use the lower case 'displayname' instead", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "symbol", + "label": "Display Name" + }, + "displayname": { + "attrOrProp": 2, + "digest": "A more readable name for the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Display Name", + "displayorder": 14 + }, + "unit": { + "attrOrProp": 2, + "digest": "A symbol to describe the unit of the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Unit", + "displayorder": 15 + }, + "tonormalized": { + "attrOrProp": 2, + "digest": "Converts a real parameter value to its normalized form", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "To Normalized Expression", + "displayorder": 10 + }, + "fromnormalized": { + "attrOrProp": 2, + "digest": "Converts a normalized parameter into its actual parameter value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "From Normalized Expression", + "displayorder": 9 + }, + "order": { + "attrOrProp": 2, + "digest": "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "0", + "label": "Restore Order", + "displayorder": 12 + }, + "displayorder": { + "attrOrProp": 2, + "digest": "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "-", + "label": "Display Order", + "displayorder": 13 + }, + "sendinit": { + "attrOrProp": 2, + "digest": "Send initial value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Send Init", + "displayorder": 4 + }, + "ctlin": { + "attrOrProp": 2, + "digest": "MIDI controller number to control this parameter.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "-1", + "label": "MIDI Controller Number.", + "displayorder": 16 + }, + "meta": { + "attrOrProp": 2, + "digest": "A JSON formatted string containing metadata for use by the exported code", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Metadata", + "displayorder": 17 + }, + "nopreset": { + "attrOrProp": 2, + "digest": "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "preset": { + "attrOrProp": 2, + "digest": "Add this value to the preset.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Include In Preset", + "displayorder": 11 + } + }, + "inputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalizedvalue", + "type": "number", + "digest": "Set value normalized. ", + "docked": 0 + } + ], + "outputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalized", + "type": "number", + "digest": "Normalized parameter value.", + "docked": 0 + } + ], + "helpname": "param", + "aliasOf": "param", + "classname": "param", + "operator": 0, + "versionId": -1093178486, + "changesPatcherIO": 0 + }, + "text": "param kink2 @min 0 @max 1", + "varname": "kink2" + } + }, + { + "box": { + "id": "obj-14", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "signal" ], + "patching_rect": [ 304.0, 203.0, 66.0, 23.0 ], + "rnbo_classname": "cycle~", + "rnbo_extra_attributes": { + "index": "freq", + "buffername": "RNBODefaultSinus", + "interp": "linear" + }, + "rnbo_serial": 3, + "rnbo_uniqueid": "cycle~_obj-14", + "text": "cycle~ 167" + } + }, + { + "box": { + "id": "obj-8", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 0, + "patching_rect": [ 52.0, 336.0, 43.0, 23.0 ], + "rnbo_classname": "out~", + "rnbo_extra_attributes": { + "meta": "", + "comment": "" + }, + "rnbo_serial": 2, + "rnbo_uniqueid": "out~_obj-8", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "in1": { + "attrOrProp": 1, + "digest": "signal sent to outlet with index 1", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "signal" + }, + "index": { + "attrOrProp": 2, + "digest": "outlet number", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "mandatory": 1 + }, + "comment": { + "attrOrProp": 2, + "digest": "mouse over comment", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol" + }, + "meta": { + "attrOrProp": 2, + "digest": "A JSON formatted string containing metadata for use by the exported code", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Metadata", + "displayorder": 3 + } + }, + "inputs": [ + { + "name": "in1", + "type": "signal", + "digest": "signal sent to outlet with index 1", + "displayName": "", + "hot": 1, + "docked": 0 + } + ], + "outputs": [], + "helpname": "out~", + "aliasOf": "out~", + "classname": "out~", + "operator": 0, + "versionId": 1989326771, + "changesPatcherIO": 1 + }, + "text": "out~ 1" + } + }, + { + "box": { + "id": "obj-7", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "signal" ], + "patching_rect": [ 52.0, 288.0, 47.0, 23.0 ], + "rnbo_classname": "*~", + "rnbo_serial": 1, + "rnbo_uniqueid": "*~_obj-7", + "text": "*~ 0.25" + } + }, + { + "box": { + "id": "obj-6", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "signal" ], + "patching_rect": [ 52.0, 238.0, 38.0, 23.0 ], + "rnbo_classname": "kink~", + "rnbo_serial": 3, + "rnbo_uniqueid": "kink~_obj-6", + "text": "kink~" + } + }, + { + "box": { + "id": "obj-5", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "" ], + "patching_rect": [ 134.0, 203.0, 44.0, 23.0 ], + "rnbo_classname": "line~", + "rnbo_serial": 3, + "rnbo_uniqueid": "line~_obj-5", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "segments": { + "attrOrProp": 1, + "digest": "Target value or target value/ramp time pairs", + "isalias": 0, + "aliases": [ "dest" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "list", + "defaultValue": "" + }, + "dest": { + "attrOrProp": 1, + "digest": "Target value or target value/ramp time pairs", + "isalias": 1, + "aliasOf": "segments", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "list", + "defaultValue": "" + }, + "time": { + "attrOrProp": 1, + "digest": "Ramp time", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "keepramp": { + "attrOrProp": 1, + "digest": "Keep last ramp", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "out": { + "attrOrProp": 1, + "digest": "Ramp out", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "signal" + }, + "target": { + "attrOrProp": 1, + "digest": "Bang when ramp has finished", + "isalias": 0, + "aliases": [], + "settable": 0, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "bang" + }, + "value": { + "attrOrProp": 2, + "digest": "Initial value.", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0" + } + }, + "inputs": [ + { + "name": "segments", + "type": "list", + "digest": "Target value or target value/ramp time pairs", + "hot": 1, + "docked": 0 + }, + { + "name": "time", + "type": "number", + "digest": "Ramp time", + "defaultarg": 2, + "docked": 0 + } + ], + "outputs": [ + { + "name": "out", + "type": "signal", + "digest": "Ramp out", + "docked": 0 + }, + { + "name": "target", + "type": "bang", + "digest": "Bang when ramp has finished", + "docked": 0 + } + ], + "helpname": "line~", + "aliasOf": "line~", + "classname": "line~", + "operator": 0, + "versionId": 2134689829, + "changesPatcherIO": 0 + }, + "text": "line~ 1" + } + }, + { + "box": { + "id": "obj-4", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 134.0, 178.0, 65.0, 23.0 ], + "rnbo_classname": "append", + "rnbo_extra_attributes": { + "hot": 0 + }, + "rnbo_serial": 3, + "rnbo_uniqueid": "append_obj-4", + "text": "append 40" + } + }, + { + "box": { + "id": "obj-3", + "maxclass": "newobj", + "numinlets": 6, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 134.0, 153.0, 100.0, 23.0 ], + "rnbo_classname": "scale", + "rnbo_serial": 4, + "rnbo_uniqueid": "scale_obj-3", + "text": "scale 0. 1. 1. 0.51" + } + }, + { + "box": { + "id": "obj-2", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "", "" ], + "patching_rect": [ 134.0, 128.0, 161.0, 23.0 ], + "rnbo_classname": "param", + "rnbo_extra_attributes": { + "meta": "", + "ctlin": 0.0, + "unit": "", + "steps": 0.0, + "sendinit": 1, + "displayorder": "-", + "order": "0", + "displayname": "", + "preset": 1, + "exponent": 1.0, + "tonormalized": "", + "enum": "", + "fromnormalized": "" + }, + "rnbo_serial": 3, + "rnbo_uniqueid": "kink1", + "rnboinfo": { + "needsInstanceInfo": 1, + "argnames": { + "value": { + "attrOrProp": 1, + "digest": "Parameter value", + "defaultarg": 2, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 1, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number", + "defaultValue": "0" + }, + "normalizedvalue": { + "attrOrProp": 1, + "digest": "Set value normalized. ", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "inlet": 1, + "type": "number" + }, + "reset": { + "attrOrProp": 1, + "digest": "Reset param to initial value", + "isalias": 0, + "aliases": [], + "attachable": 1, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bang" + }, + "normalized": { + "attrOrProp": 1, + "digest": "Normalized parameter value.", + "isalias": 0, + "aliases": [], + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "outlet": 1, + "type": "number" + }, + "name": { + "attrOrProp": 2, + "digest": "Name of the parameter", + "defaultarg": 1, + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "Parameter Name", + "mandatory": 1 + }, + "enum": { + "attrOrProp": 2, + "digest": "Use an enumerated output", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "list", + "label": "Enum Values", + "displayorder": 6 + }, + "minimum": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 0, + "aliases": [ "min" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "min": { + "attrOrProp": 2, + "digest": "Minimum value", + "isalias": 1, + "aliasOf": "minimum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Minimum", + "displayorder": 1 + }, + "maximum": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 0, + "aliases": [ "max" ], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "max": { + "attrOrProp": 2, + "digest": "Maximum value", + "isalias": 1, + "aliasOf": "maximum", + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Maximum", + "displayorder": 2 + }, + "exponent": { + "attrOrProp": 2, + "digest": "Scale values exponentially", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "1", + "label": "Exponent", + "displayorder": 7 + }, + "steps": { + "attrOrProp": 2, + "digest": "Divide the output into a number of discrete steps", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "0", + "label": "Steps", + "displayorder": 8 + }, + "displayName": { + "attrOrProp": 2, + "digest": "DEPRECATED: Use the lower case 'displayname' instead", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "symbol", + "label": "Display Name" + }, + "displayname": { + "attrOrProp": 2, + "digest": "A more readable name for the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Display Name", + "displayorder": 14 + }, + "unit": { + "attrOrProp": 2, + "digest": "A symbol to describe the unit of the parameter in an external RNBO target", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Unit", + "displayorder": 15 + }, + "tonormalized": { + "attrOrProp": 2, + "digest": "Converts a real parameter value to its normalized form", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "To Normalized Expression", + "displayorder": 10 + }, + "fromnormalized": { + "attrOrProp": 2, + "digest": "Converts a normalized parameter into its actual parameter value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "label": "From Normalized Expression", + "displayorder": 9 + }, + "order": { + "attrOrProp": 2, + "digest": "Order in which initial parameter values will be sent out on patcher load. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "0", + "label": "Restore Order", + "displayorder": 12 + }, + "displayorder": { + "attrOrProp": 2, + "digest": "Order in which parameters will show up in a list of all parameters. The order can be numeric or symbolic ('first' and 'last')", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "-", + "label": "Display Order", + "displayorder": 13 + }, + "sendinit": { + "attrOrProp": 2, + "digest": "Send initial value", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Send Init", + "displayorder": 4 + }, + "ctlin": { + "attrOrProp": 2, + "digest": "MIDI controller number to control this parameter.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "number", + "defaultValue": "-1", + "label": "MIDI Controller Number.", + "displayorder": 16 + }, + "meta": { + "attrOrProp": 2, + "digest": "A JSON formatted string containing metadata for use by the exported code", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "symbol", + "defaultValue": "", + "label": "Metadata", + "displayorder": 17 + }, + "nopreset": { + "attrOrProp": 2, + "digest": "Do not add this value to the preset [DEPRECATED - USE @preset 0 instead].", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 1, + "touched": 0, + "type": "bool", + "defaultValue": "false" + }, + "preset": { + "attrOrProp": 2, + "digest": "Add this value to the preset.", + "isalias": 0, + "aliases": [], + "settable": 1, + "attachable": 0, + "isparam": 0, + "deprecated": 0, + "touched": 0, + "type": "bool", + "defaultValue": "true", + "label": "Include In Preset", + "displayorder": 11 + } + }, + "inputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalizedvalue", + "type": "number", + "digest": "Set value normalized. ", + "docked": 0 + } + ], + "outputs": [ + { + "name": "value", + "type": "number", + "digest": "Parameter value", + "defaultarg": 2, + "hot": 1, + "docked": 0 + }, + { + "name": "normalized", + "type": "number", + "digest": "Normalized parameter value.", + "docked": 0 + } + ], + "helpname": "param", + "aliasOf": "param", + "classname": "param", + "operator": 0, + "versionId": -1093178486, + "changesPatcherIO": 0 + }, + "text": "param kink1 @min 0 @max 1", + "varname": "kink1" + } + }, + { + "box": { + "id": "obj-1", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 2, + "outlettype": [ "signal", "signal" ], + "patching_rect": [ 52.0, 203.0, 66.0, 23.0 ], + "rnbo_classname": "cycle~", + "rnbo_extra_attributes": { + "index": "freq", + "buffername": "RNBODefaultSinus", + "interp": "linear" + }, + "rnbo_serial": 4, + "rnbo_uniqueid": "cycle~_obj-1", + "text": "cycle~ 242" + } + } + ], + "lines": [ + { + "patchline": { + "destination": [ "obj-6", 0 ], + "source": [ "obj-1", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-9", 1 ], + "source": [ "obj-10", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-10", 0 ], + "source": [ "obj-11", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-11", 0 ], + "source": [ "obj-12", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-12", 0 ], + "source": [ "obj-13", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-9", 0 ], + "source": [ "obj-14", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-7", 0 ], + "source": [ "obj-15", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-15", 1 ], + "source": [ "obj-16", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-16", 0 ], + "source": [ "obj-17", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-17", 0 ], + "source": [ "obj-18", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-18", 0 ], + "source": [ "obj-19", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-3", 0 ], + "source": [ "obj-2", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-15", 0 ], + "source": [ "obj-20", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-23", 0 ], + "source": [ "obj-22", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-24", 0 ], + "source": [ "obj-23", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-19", 0 ], + "source": [ "obj-24", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-26", 0 ], + "source": [ "obj-25", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-23", 0 ], + "source": [ "obj-26", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-4", 0 ], + "source": [ "obj-3", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-5", 0 ], + "source": [ "obj-4", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-6", 1 ], + "source": [ "obj-5", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-7", 0 ], + "source": [ "obj-6", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-21", 0 ], + "order": 0, + "source": [ "obj-7", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-8", 0 ], + "order": 1, + "source": [ "obj-7", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-7", 0 ], + "source": [ "obj-9", 0 ] + } + } + ] + }, + "patching_rect": [ 298.0, 160.0, 168.0, 22.0 ], + "rnboattrcache": { + "kink1": { + "label": "kink1", + "isEnum": 0, + "parsestring": "" + }, + "kink2": { + "label": "kink2", + "isEnum": 0, + "parsestring": "" + }, + "kink3": { + "label": "kink3", + "isEnum": 0, + "parsestring": "" + }, + "automate": { + "label": "automate", + "isEnum": 1, + "parsestring": "\"off\" \"on\"" + } + }, + "rnboversion": "1.4.3", + "saved_attribute_attributes": { + "valueof": { + "parameter_invisible": 1, + "parameter_longname": "rnbo~", + "parameter_modmode": 0, + "parameter_shortname": "rnbo~", + "parameter_type": 3 + } + }, + "saved_object_attributes": { + "optimization": "O1", + "parameter_enable": 1, + "uuid": "fafce8f5-9839-11ed-a8f5-56ea1db75653" + }, + "snapshot": { + "filetype": "C74Snapshot", + "version": 2, + "minorversion": 0, + "name": "snapshotlist", + "origin": "rnbo~", + "type": "list", + "subtype": "Undefined", + "embed": 1, + "snapshot": { + "automate": { + "value": 0.0 + }, + "kink2": { + "value": 0.8355205599300087 + }, + "kink1": { + "value": 0.8885707468384627 + }, + "kink3": { + "value": 0.9255950094746556 + }, + "__presetid": "three-param-kink" + }, + "snapshotlist": { + "current_snapshot": 0, + "entries": [ + { + "filetype": "C74Snapshot", + "version": 2, + "minorversion": 0, + "name": "three-param-kink", + "origin": "three-param-kink", + "type": "rnbo", + "subtype": "", + "embed": 1, + "snapshot": { + "automate": { + "value": 0.0 + }, + "kink2": { + "value": 0.8355205599300087 + }, + "kink1": { + "value": 0.8885707468384627 + }, + "kink3": { + "value": 0.9255950094746556 + }, + "__presetid": "three-param-kink" + }, + "fileref": { + "name": "three-param-kink", + "filename": "three-param-kink.maxsnap", + "filepath": "~/Documents/Max 9/Snapshots", + "filepos": -1, + "snapshotfileid": "3fb6350da70dadf75d3de06c2a606c9e" + } + } + ] + } + }, + "text": "rnbo~ @title three-param-kink", + "varname": "rnbo~" + } + } + ], + "lines": [ + { + "patchline": { + "destination": [ "obj-5", 1 ], + "source": [ "obj-1", 1 ] + } + }, + { + "patchline": { + "destination": [ "obj-5", 0 ], + "source": [ "obj-1", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-1", 0 ], + "source": [ "obj-4", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-2", 1 ], + "source": [ "obj-5", 1 ] + } + }, + { + "patchline": { + "destination": [ "obj-2", 0 ], + "source": [ "obj-5", 0 ] + } + }, + { + "patchline": { + "destination": [ "obj-4", 0 ], + "source": [ "obj-7", 0 ] + } + } + ], + "parameters": { + "obj-1": [ "rnbo~", "rnbo~", 0 ], + "obj-10": [ "kink2", "kink2", 0 ], + "obj-11": [ "kink3", "kink3", 0 ], + "obj-5": [ "live.gain~", "live.gain~", 0 ], + "obj-9": [ "kink1", "kink1", 0 ], + "parameterbanks": { + "0": { + "index": 0, + "name": "", + "parameters": [ "-", "-", "-", "-", "-", "-", "-", "-" ], + "buttons": [ "-", "-", "-", "-", "-", "-", "-", "-" ] + } + }, + "inherited_shortname": 1 + }, + "autosave": 0 + } +} \ No newline at end of file diff --git a/src/CustomAudioEditor.cpp b/src/CustomAudioEditor.cpp index b55c7f0..01af514 100644 --- a/src/CustomAudioEditor.cpp +++ b/src/CustomAudioEditor.cpp @@ -1,30 +1,67 @@ #include "CustomAudioEditor.h" -CustomAudioEditor::CustomAudioEditor (RNBO::JuceAudioProcessor* const p, RNBO::CoreObject& rnboObject) - : AudioProcessorEditor (p) - , _rnboObject(rnboObject) - , _audioProcessor(p) +static juce::RangedAudioParameter& +findParameter(RNBO::JuceAudioProcessor* p, const juce::String& name) { - _audioProcessor->AudioProcessor::addListener(this); + for (auto* param : p->getParameters()) + { + if (param->getName(128) == name) + return static_cast(*param); + } - _label.setText("Hi I'm Custom Interface", NotificationType::dontSendNotification); - _label.setBounds(0, 0, 400, 300); - _label.setColour(Label::textColourId, Colours::black); - addAndMakeVisible(_label); - setSize (_label.getWidth(), _label.getHeight()); + throw std::runtime_error("Parameter not found: " + name.toStdString()); } -CustomAudioEditor::~CustomAudioEditor() +CustomAudioEditor::CustomAudioEditor (RNBO::JuceAudioProcessor* const p, + RNBO::CoreObject& rnboObject) + : AudioProcessorEditor (p) + , _audioProcessor (p) + , _rnboObject (rnboObject) + , _kink1Attachment(findParameter(p, "kink1"), _kink1Slider) + , _kink2Attachment(findParameter(p, "kink2"), _kink2Slider) + , _kink3Attachment(findParameter(p, "kink3"), _kink3Slider) { - _audioProcessor->AudioProcessor::removeListener(this); + for (auto* s : { &_kink1Slider, &_kink2Slider, &_kink3Slider }) + { + s->setSliderStyle (Slider::LinearHorizontal); + s->setTextBoxStyle (Slider::TextBoxRight, false, 60, 20); + addAndMakeVisible (s); + } + + auto setupLabel = [this] (Label& label, const char* text) + { + label.setText (text, dontSendNotification); + label.setJustificationType (Justification::centredLeft); + addAndMakeVisible (label); + }; + + setupLabel (_kink1Label, "Kink 1"); + setupLabel (_kink2Label, "Kink 2"); + setupLabel (_kink3Label, "Kink 3"); + + setSize (400, 160); } +CustomAudioEditor::~CustomAudioEditor() = default; + void CustomAudioEditor::paint (Graphics& g) { - g.fillAll(Colours::white); + g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); } -void CustomAudioEditor::audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float value) +void CustomAudioEditor::resized() { - // Handle parameter changes here + auto area = getLocalBounds().reduced (16); + const int rowHeight = area.getHeight() / 3; + + auto layoutRow = [&] (Label& label, Slider& slider) + { + auto row = area.removeFromTop (rowHeight); + label.setBounds (row.removeFromLeft (60)); + slider.setBounds (row); + }; + + layoutRow (_kink1Label, _kink1Slider); + layoutRow (_kink2Label, _kink2Slider); + layoutRow (_kink3Label, _kink3Slider); } diff --git a/src/CustomAudioEditor.h b/src/CustomAudioEditor.h index c5a764d..7f5402d 100644 --- a/src/CustomAudioEditor.h +++ b/src/CustomAudioEditor.h @@ -1,22 +1,29 @@ +#pragma once + #include "JuceHeader.h" #include "RNBO.h" #include "RNBO_JuceAudioProcessor.h" -class CustomAudioEditor : public AudioProcessorEditor, private AudioProcessorListener +class CustomAudioEditor : public AudioProcessorEditor { public: - CustomAudioEditor(RNBO::JuceAudioProcessor* const p, RNBO::CoreObject& rnboObject); + CustomAudioEditor (RNBO::JuceAudioProcessor* const p, RNBO::CoreObject& rnboObject); ~CustomAudioEditor() override; void paint (Graphics& g) override; + void resized() override; private: - void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override { } - void audioProcessorParameterChanged(AudioProcessor*, int parameterIndex, float) override; + RNBO::JuceAudioProcessor* _audioProcessor; + RNBO::CoreObject& _rnboObject; + + // Sliders must be declared before attachments; SliderParameterAttachment + // registers with the slider on construction. + Slider _kink1Slider, _kink2Slider, _kink3Slider; + Label _kink1Label, _kink2Label, _kink3Label; -protected: - AudioProcessor *_audioProcessor; - RNBO::CoreObject& _rnboObject; - Label _label; + SliderParameterAttachment _kink1Attachment; + SliderParameterAttachment _kink2Attachment; + SliderParameterAttachment _kink3Attachment; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomAudioEditor) }; diff --git a/src/CustomAudioProcessor.cpp b/src/CustomAudioProcessor.cpp index 634c512..83c8e1c 100644 --- a/src/CustomAudioProcessor.cpp +++ b/src/CustomAudioProcessor.cpp @@ -1,5 +1,9 @@ #include "CustomAudioProcessor.h" +#if defined(RNBO_EDITOR_NATIVE) #include "CustomAudioEditor.h" +#elif defined(RNBO_EDITOR_WEBVIEW) +#include "WebBrowserAudioEditor.h" +#endif #include #ifdef RNBO_INCLUDE_DESCRIPTION_FILE @@ -34,10 +38,14 @@ CustomAudioProcessor::CustomAudioProcessor( { } -AudioProcessorEditor* CustomAudioProcessor::createEditor() +juce::AudioProcessorEditor* CustomAudioProcessor::createEditor() { - //Change this to use your CustomAudioEditor - //return new CustomAudioEditor (this, this->_rnboObject); +#if defined(RNBO_EDITOR_NATIVE) + return new CustomAudioEditor (this, this->_rnboObject); +#elif defined(RNBO_EDITOR_WEBVIEW) + return new WebBrowserAudioEditor (this, this->_rnboObject); +#else return RNBO::JuceAudioProcessor::createEditor(); +#endif } diff --git a/src/MainComponent.cpp b/src/MainComponent.cpp index c9d1247..3b08062 100644 --- a/src/MainComponent.cpp +++ b/src/MainComponent.cpp @@ -102,11 +102,11 @@ class MainContentComponent : public Component, public RNBO::PatcherChangedHand // let's listen to all midi inputs // enable all midi inputs - StringArray midiInputDevices = MidiInput::getDevices(); + auto midiInputDevices = MidiInput::getAvailableDevices(); for (const auto& input : midiInputDevices) { - _deviceManager.setMidiInputEnabled(input, true); + _deviceManager.setMidiInputDeviceEnabled(input.identifier, true); } - _deviceManager.addMidiInputCallback("", &_audioProcessorPlayer); + _deviceManager.addMidiInputDeviceCallback("", &_audioProcessorPlayer); // setup the midi keyboard _midiKeyboardState.addListener(&_audioProcessorPlayer.getMidiMessageCollector()); diff --git a/src/WebBrowserAudioEditor.cpp b/src/WebBrowserAudioEditor.cpp new file mode 100644 index 0000000..50ca1b6 --- /dev/null +++ b/src/WebBrowserAudioEditor.cpp @@ -0,0 +1,171 @@ +#include "WebBrowserAudioEditor.h" +#include "BinaryData.h" + +// The dev server address. When a server is listening here, the browser loads from it +// instead of the built-in resource provider, so you can iterate on src/webui/ without +// recompiling. Use a server that disables caching and (optionally) auto-reloads: +// +// # No-cache static server (manual page reload required after edits): +// python3 -c " +// import http.server +// class H(http.server.SimpleHTTPRequestHandler): +// def end_headers(self): +// self.send_header('Cache-Control', 'no-store') +// super().end_headers() +// http.server.test(HandlerClass=H, port=3000) +// " +// +// # Hot-reload (auto-reloads browser on file change, requires Node): +// npx live-server --port=3000 +// +// pageLoadHadNetworkError falls back to the resource provider when no server is running. +#if JUCE_ANDROID +static const juce::String kDevServerAddress = "http://10.0.2.2:3000/"; +#else +static const juce::String kDevServerAddress = "http://localhost:3000/"; +#endif + +//============================================================================== +// SinglePageBrowser implementation + +bool WebBrowserAudioEditor::SinglePageBrowser::pageAboutToLoad (const String& newURL) +{ + // Hide the webview on every navigation so emitEventIfBrowserIsVisible won't try to + // evaluate JS against a page that hasn't initialised window.__JUCE__ yet. + // pageFinishedLoading reveals it again once the page is ready. + setVisible (false); + + // Allow the dev server and the JUCE resource provider root; block everything else + // so the single-page UI can't accidentally navigate away. + return newURL.startsWith (kDevServerAddress) + || newURL == getResourceProviderRoot(); +} + +bool WebBrowserAudioEditor::SinglePageBrowser::pageLoadHadNetworkError (const String& /*errorInfo*/) +{ + // Dev server is not reachable — fall back to serving the file via the resource provider. + // The flag prevents an infinite loop if the resource provider also somehow fails. + if (! _devServerFailed) + { + _devServerFailed = true; + goToURL (getResourceProviderRoot()); + return false; // we handled it; don't show the browser's error page + } + + return true; // let the browser show its error page +} + +void WebBrowserAudioEditor::SinglePageBrowser::pageFinishedLoading (const String& /*url*/) +{ + // Reveal the webview now that window.__JUCE__ is initialised. Keeping it hidden until + // this point prevents emitEventIfBrowserIsVisible from evaluating JS on a blank page, + // which would throw "undefined is not an object (evaluating 'window.__JUCE__.backend')". + setVisible (true); +} + +//============================================================================== +namespace +{ + static const char* getMimeForExtension (const String& ext) + { + if (ext == "html" || ext == "htm") return "text/html"; + if (ext == "css") return "text/css"; + if (ext == "js") return "text/javascript"; + if (ext == "json") return "application/json"; + if (ext == "svg") return "image/svg+xml"; + if (ext == "png") return "image/png"; + if (ext == "jpg" || ext == "jpeg") return "image/jpeg"; + if (ext == "ico") return "image/vnd.microsoft.icon"; + if (ext == "woff2") return "font/woff2"; + return "application/octet-stream"; + } + + static std::vector stringToBytes (const String& s) + { + const auto* ptr = s.toRawUTF8(); + const auto len = s.getNumBytesAsUTF8(); + std::vector result (len); + std::memcpy (result.data(), ptr, len); + return result; + } +} + +static juce::RangedAudioParameter& +findParameter(RNBO::JuceAudioProcessor* p, const juce::String& name) +{ + for (auto* param : p->getParameters()) + { + if (param->getName(128) == name) + return static_cast(*param); + } + + throw std::runtime_error("Parameter not found: " + name.toStdString()); +} + +//============================================================================== +// Members are initialized in declaration order (see WebBrowserAudioEditor.h). +// Relays and _webComponent use default member initializers; the +// WebSliderParameterAttachment members need the processor's parameters so they +// are initialized here. +WebBrowserAudioEditor::WebBrowserAudioEditor (RNBO::JuceAudioProcessor* const p, + RNBO::CoreObject& rnboObject) + : AudioProcessorEditor (p) + , _audioProcessor (p) + , _rnboObject (rnboObject) + , _kink1Attachment(findParameter(p, "kink1"), _kink1Relay, nullptr) + , _kink2Attachment(findParameter(p, "kink2"), _kink2Relay, nullptr) + , _kink3Attachment(findParameter(p, "kink3"), _kink3Relay, nullptr) + , _automateAttachment(findParameter(p, "automate"), _automateRelay, nullptr) +{ + // Start hidden — pageFinishedLoading will reveal the webview once window.__JUCE__ is ready. + addChildComponent (_webComponent); + + // Try the dev server first. If nothing is listening on that port, + // pageLoadHadNetworkError fires quickly and redirects to getResourceProviderRoot(). + _webComponent.goToURL (kDevServerAddress); + + setSize (400, 320); +} + +WebBrowserAudioEditor::~WebBrowserAudioEditor() +{ + _audioProcessor->AudioProcessor::removeListener (this); +} + +void WebBrowserAudioEditor::paint (Graphics& g) +{ + g.fillAll (Colour (0xff16161e)); +} + +void WebBrowserAudioEditor::resized() +{ + _webComponent.setBounds (getLocalBounds()); +} + +std::optional +WebBrowserAudioEditor::getResource (const String& url) +{ + // Map "/" or "" to index.html; strip any leading slash for other paths. + const auto filename = (url == "/" || url.isEmpty()) + ? String ("index.html") + : url.fromFirstOccurrenceOf ("/", false, false); + + const auto ext = filename.fromLastOccurrenceOf (".", false, false).toLowerCase(); + const auto mime = getMimeForExtension (ext); + + // Production: serve from binary data compiled into the binary. + // Mangle the filename the same way juce_add_binary_data does (non-alphanumeric → '_'). + String resourceName; + for (auto c : filename) + resourceName += (CharacterFunctions::isLetterOrDigit (c) || c == '_') ? c : juce_wchar ('_'); + + int dataSize = 0; + const char* data = RNBOUIData::getNamedResource (resourceName.toRawUTF8(), dataSize); + if (data != nullptr) + { + const auto* begin = reinterpret_cast (data); + return WebBrowserComponent::Resource { std::vector (begin, begin + dataSize), mime }; + } + + return std::nullopt; +} diff --git a/src/WebBrowserAudioEditor.h b/src/WebBrowserAudioEditor.h new file mode 100644 index 0000000..85cc702 --- /dev/null +++ b/src/WebBrowserAudioEditor.h @@ -0,0 +1,66 @@ +#pragma once + +#include "JuceHeader.h" +#include "RNBO.h" +#include "RNBO_JuceAudioProcessor.h" + +class WebBrowserAudioEditor : public AudioProcessorEditor, + private AudioProcessorListener +{ +public: + WebBrowserAudioEditor (RNBO::JuceAudioProcessor* const p, RNBO::CoreObject& rnboObject); + ~WebBrowserAudioEditor() override; + void paint (Graphics& g) override; + void resized() override; + +private: + void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override {} + void audioProcessorParameterChanged (AudioProcessor*, int, float) override {} + + std::optional getResource (const String& url); + + // Relays must be declared before _webComponent so they are initialized first. + RNBO::JuceAudioProcessor* _audioProcessor; + RNBO::CoreObject& _rnboObject; + + WebSliderRelay _kink1Relay { "kink1" }; + WebSliderRelay _kink2Relay { "kink2" }; + WebSliderRelay _kink3Relay { "kink3" }; + WebToggleButtonRelay _automateRelay { "automate" }; + + // Defined in the .cpp so pageAboutToLoad/pageLoadHadNetworkError can reference + // the kDevServerAddress constant without it being visible in this header. + struct SinglePageBrowser : WebBrowserComponent + { + using WebBrowserComponent::WebBrowserComponent; + bool pageAboutToLoad (const String& newURL) override; + bool pageLoadHadNetworkError (const String& errorInfo) override; + void pageFinishedLoading (const String& url) override; + private: + bool _devServerFailed = false; + }; + + SinglePageBrowser _webComponent { + WebBrowserComponent::Options{} + .withBackend (WebBrowserComponent::Options::Backend::webview2) + .withWinWebView2Options (WebBrowserComponent::Options::WinWebView2{} + .withUserDataFolder (File::getSpecialLocation ( + File::SpecialLocationType::tempDirectory))) + .withNativeIntegrationEnabled() + .withOptionsFrom (_kink1Relay) + .withOptionsFrom (_kink2Relay) + .withOptionsFrom (_kink3Relay) + .withOptionsFrom (_automateRelay) + .withKeepPageLoadedWhenBrowserIsHidden() + .withResourceProvider ([this] (const auto& url) { return getResource (url); }) + }; + + // Attachments link each relay to the corresponding RNBO RangedAudioParameter. + // Declared after _webComponent; initialized in the constructor initializer list. + WebSliderParameterAttachment _kink1Attachment; + WebSliderParameterAttachment _kink2Attachment; + WebSliderParameterAttachment _kink3Attachment; + WebToggleButtonParameterAttachment _automateAttachment; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserAudioEditor) +}; diff --git a/src/webui/.gitignore b/src/webui/.gitignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/src/webui/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/src/webui/index.html b/src/webui/index.html new file mode 100644 index 0000000..4461a3a --- /dev/null +++ b/src/webui/index.html @@ -0,0 +1,41 @@ + + + + + + Kink Drones + + +

Kink Drones

+
+
+
+ Kink 1 + 0.000 +
+ +
+ +
+
+ Kink 2 + 0.000 +
+ +
+ +
+
+ Kink 3 + 0.000 +
+
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/src/webui/main.js b/src/webui/main.js new file mode 100644 index 0000000..1342ed4 --- /dev/null +++ b/src/webui/main.js @@ -0,0 +1,44 @@ +import { getSliderState, getToggleState } from 'juce-framework-frontend'; + +function bindSliderParam(name, sliderId, valueId) { + const slider = document.getElementById(sliderId); + const display = document.getElementById(valueId); + const state = getSliderState(name); + + state.valueChangedEvent.addListener(() => { + const norm = state.getNormalisedValue(); + slider.value = norm; + slider.style.setProperty('--fill', (norm * 100) + '%'); + display.textContent = state.getScaledValue().toFixed(3); + }); + + slider.addEventListener('mousedown', () => state.sliderDragStarted()); + slider.addEventListener('touchstart', () => state.sliderDragStarted(), { passive: true }); + slider.addEventListener('mouseup', () => state.sliderDragEnded()); + slider.addEventListener('touchend', () => state.sliderDragEnded()); + + slider.addEventListener('input', () => { + const norm = parseFloat(slider.value); + state.setNormalisedValue(norm); + slider.style.setProperty('--fill', (norm * 100) + '%'); + display.textContent = state.getScaledValue().toFixed(3); + }); +} + +function bindToggleParam(name, toggleId) { + const toggle = document.getElementById(toggleId); + const state = getToggleState(name); + + state.valueChangedEvent.addListener(() => { + toggle.checked = state.getValue(); + }); + + toggle.addEventListener('change', () => { + state.setValue(toggle.checked); + }); +} + +bindSliderParam('kink1', 'slider-kink1', 'val-kink1'); +bindSliderParam('kink2', 'slider-kink2', 'val-kink2'); +bindSliderParam('kink3', 'slider-kink3', 'val-kink3'); +bindToggleParam('automate', 'toggle-automate'); diff --git a/src/webui/package-lock.json b/src/webui/package-lock.json new file mode 100644 index 0000000..f3d4eff --- /dev/null +++ b/src/webui/package-lock.json @@ -0,0 +1,939 @@ +{ + "name": "rnbo-webui", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rnbo-webui", + "dependencies": { + "juce-framework-frontend": "file:../../thirdparty/juce/modules/juce_gui_extra/native/javascript" + }, + "devDependencies": { + "vite": "^5.0.0" + } + }, + "../../thirdparty/juce/modules/juce_gui_extra/native/javascript": { + "name": "juce-framework-frontend", + "version": "7.0.7" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/juce-framework-frontend": { + "resolved": "../../thirdparty/juce/modules/juce_gui_extra/native/javascript", + "link": true + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/src/webui/package.json b/src/webui/package.json new file mode 100644 index 0000000..c68f1a9 --- /dev/null +++ b/src/webui/package.json @@ -0,0 +1,15 @@ +{ + "name": "rnbo-webui", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "dependencies": { + "juce-framework-frontend": "file:../../thirdparty/juce/modules/juce_gui_extra/native/javascript" + }, + "devDependencies": { + "vite": "^5.0.0" + } +} diff --git a/src/webui/vite.config.js b/src/webui/vite.config.js new file mode 100644 index 0000000..289de8f --- /dev/null +++ b/src/webui/vite.config.js @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + server: { + port: 3000, + }, + build: { + assetsDir: '', + rollupOptions: { + output: { + entryFileNames: 'index.js', + chunkFileNames: '[name].js', + assetFileNames: '[name][extname]', + }, + }, + }, +}) diff --git a/thirdparty/juce b/thirdparty/juce index bbd6ccb..29396c2 160000 --- a/thirdparty/juce +++ b/thirdparty/juce @@ -1 +1 @@ -Subproject commit bbd6ccbc863edec3150e1e024e4fa3f636802596 +Subproject commit 29396c22c93392d6738e021b83196283d6e4d850