Phase 0 — spread-aware API ported to nisps-core C++: - MLP::DrawWeightsSpread(T spread) — interpolate uniform↔Xavier per layer - MLP::MoveWeightsSpread(T speed, T spread) — per-layer noise + weight decay - IML::randomise_weights(Float spread) and IML::move_weights(speed, spread) - 5 unit tests (10/10 total pass) Phase 1 — VCV Rack 2 plugin skeleton: - Makefile with C++20, nisps-core include path - plugin.json manifest - Empty MEMLNaut module: 2 inputs, 12 outputs, placeholder SVG panel - static_assert verifies nisps-core headers resolve - C++20 confirmed working in VCV SDK (8 existing plugins use it) |
||
|---|---|---|
| .. | ||
| examples | ||
| include/nisps | ||
| test | ||
| CHANGELOG.md | ||
| CMakeLists.txt | ||
| README.md | ||
NISPS Core
Neural Interactive Shaping of Parameter Spaces - Core Library
A platform-agnostic C++20 header-only library for interactive machine learning. Train neural networks to map input parameters to output parameters through interactive demonstration.
What Is This?
NISPS core is a parameter mapping engine, not a synthesizer. It takes N input parameters (joystick position, sensor data, audio features) and maps them to M output parameters through an interactively-trained neural network.
Use it to control: synthesizers, effects, lights, robots, game parameters, or anything that responds to control data.
Features
- Header-only: No compilation needed, just include and use
- Platform-agnostic: Pure C++20 with standard library only
- Interactive learning: Train by demonstration, not by code
- Programmatic training:
add_example()API for non-interactive use - Lightweight: ~3,500 lines of optimized neural network code
- Flexible: Map 1-100 inputs to 1-100 outputs
Quick Start
Installation
Copy the include/nisps/ directory to your project, or add it to your include path:
# Option 1: Copy headers
cp -r nisps-core/include/nisps /path/to/your/project/include/
# Option 2: Add to CMakeLists.txt
target_include_directories(your_target PRIVATE /path/to/nisps-core/include)
Basic Usage
#include <nisps/nisps.hpp>
// Create IML with 2 inputs, 4 outputs
nisps::IML<float> iml(2, 4);
// Runtime: set inputs and get outputs
void update(float x, float y) {
iml.set_input(0, x);
iml.set_input(1, y);
iml.process();
const float* outputs = iml.get_outputs();
my_synth.set_filter_cutoff(outputs[0] * 10000.f);
my_synth.set_resonance(outputs[1]);
my_synth.set_envelope_attack(outputs[2] * 5.0f);
my_synth.set_envelope_release(outputs[3] * 10.0f);
}
Programmatic Training
// 1. Enter training mode
iml.set_mode(nisps::IML<float>::Mode::Training);
// 2. Add examples directly
float in1[] = {0.1f, 0.1f}; float out1[] = {0.9f, 0.1f, 0.5f, 0.8f};
float in2[] = {0.9f, 0.9f}; float out2[] = {0.1f, 0.9f, 0.2f, 0.3f};
iml.add_example(in1, 2, out1, 4);
iml.add_example(in2, 2, out2, 4);
// 3. Exit training mode (automatically trains the network)
iml.set_mode(nisps::IML<float>::Mode::Inference);
Interactive Training (hardware/UI)
// For interactive systems with physical controls:
iml.set_mode(nisps::IML<float>::Mode::Training);
iml.set_input(0, 0.3f);
iml.set_input(1, 0.7f);
iml.save_example(); // Stops inference
// ... user adjusts output controls ...
iml.set_output(0, 0.8f); // Or read from hardware
iml.save_example(); // Stores the input->output mapping
iml.set_mode(nisps::IML<float>::Mode::Inference);
API Reference
IML Constructor
nisps::IML<Float>(
size_t n_inputs, // Number of input parameters
size_t n_outputs, // Number of output parameters
std::vector<size_t> hidden_layers = {10, 10, 14}, // Hidden layer sizes
size_t max_iterations = 1000, // Training iterations
Float learning_rate = 1.0f, // Learning rate
Float convergence_threshold = 0.00001f // Stop training threshold
);
Input/Output
void set_input(size_t index, Float value); // Set single input (0-1 range)
void set_inputs(const Float* values, size_t count); // Set multiple inputs
void set_output(size_t index, Float value); // Set single output (for training)
void set_outputs(const Float* values, size_t count); // Set multiple outputs
const Float* get_outputs() const; // Get output array
void process(); // Run inference
Training
enum class Mode { Inference, Training };
void set_mode(Mode mode); // Switch modes
void add_example(const Float* in, size_t n_in, // Add training pair directly
const Float* out, size_t n_out);
void save_example(); // Interactive: store input->output pair
void clear_dataset(); // Clear training data
void randomise_weights(); // Randomize for exploration
Logging
void set_logger(LogFn fn); // Set callback for messages
// LogFn = void(*)(const char*)
Building the Tests
cd nisps-core
mkdir build && cd build
cmake ..
make
ctest --output-on-failure
Requirements
- C++20 compiler (GCC 10+, Clang 10+, MSVC 2019+)
- CMake 3.14+ (for building tests only)
Architecture
Core Components
- IML: High-level interactive ML interface
- MLP: Multi-layer perceptron (feedforward neural network)
- Dataset: Training data management
- Layer/Node: Neural network building blocks
- Loss: MSE and categorical cross-entropy functions
- Utils: Activation functions (sigmoid, ReLU, tanh, etc.)
Design Decisions
- Header-only: Simplifies integration, allows template specialization
- C++20: Enables
std::spanfor efficient array views - No SIMD: Portable code, relies on compiler auto-vectorization
- RMSProp optimizer: Fast convergence for interactive training
- Gradient clipping: Prevents numerical instability
Examples
See examples/simple_mapping.cpp for a complete working example that demonstrates:
- Untrained inference
- Programmatic training with
add_example() - Interactive training workflow with
save_example()+set_output()
See test/main.cpp for tests including convergence verification.
Origin
Extracted from MEMLNaut-NISPS - an embedded ML platform for audio synthesis on Raspberry Pi Pico.
Key changes from MEMLNaut-NISPS:
- Removed Arduino/RP2040 dependencies
- Removed audio synthesis code (use this to control your synth)
- Added proper namespacing
- Converted to header-only library
- Updated to modern C++20
License
Mozilla Public License Version 2.0
Original MLP code derived from David Alberto Nogueira's MLP project.
Contributing
This library is extracted from an active research project. Contributions welcome:
- Bug fixes
- Performance optimizations
- Example code
- Documentation improvements
Please keep the library dependency-free and platform-agnostic.
Citation
If you use this in research, please cite:
MEMLNaut-NISPS: Neural Interactive Shaping of Parameter Spaces
https://musicallyembodiedml.github.io/memlnaut/approaches/nisps
Support
- Issues: File at parent project (MEMLNaut-NISPS repo)
- Docs: https://musicallyembodiedml.github.io/memlnaut/
- Examples: See
examples/directory