Custom Transform Logic
Advanced topic: Building custom MIDI processing beyond built-in nodes
Overview
Documentation note
Neurode MIDI v2 treats NeuroScript 2.0 source as the canonical encoding for routing/transforms. Visual authoring may exist, but it compiles to NeuroScript (no TransformGraph/DAG runtime requirement).
Lua scripting is supported, but is non-canonical in v2.
While Neurode MIDI provides built-in routing/transformation primitives, you may need custom logic for:
- Algorithmic composition — Generative patterns, arpeggios, chord progressions
- Adaptive processing — Context-aware transformations based on musical analysis
- Hardware integration — Specialized controller mappings unique to your gear
- Experimental effects — Custom algorithms not covered by standard nodes
In v2, custom processing is expressed as NeuroScript 2.0 (canonical) and compiled off-thread into an immutable execution plan.
Approach 1: Neuroscript (Recommended for Simple Logic)
Neuroscript is a musician-focused DSL optimized for common MIDI operations:
When to Use
- Standard transforms (transpose, velocity scaling, filtering, remapping)
- Readable, maintainable scripts for musicians
- Fast prototyping without programming knowledge
Example: Custom Velocity Curve
# Compress loud notes, boost quiet notes
when vel > 100: vel scale 90..110
when vel < 40: vel scale 50..70
passExample: Conditional Layering
# Soft notes go to pad, loud notes to lead
when vel < 60: ch 1 -> ch 3
when vel >= 60: ch 1 -> ch 2
passLimitations
- No state between events (each event processed independently)
- No timing/delay (synchronous processing only)
- Fixed operations (no custom math/randomization)
Full Reference: Neuroscript Language
Approach 2: Lua (Advanced / Non-canonical)
Lua is available as a scripting engine for advanced, stateful logic.
Lua is non-canonical (v2)
If you are building presets meant to be portable/auditable, prefer NeuroScript. Lua is best treated as a power-user escape hatch and may have stricter limits in v2.
When to Use
- Stateful transforms where NeuroScript isn't enough yet (counters, probabilistic behaviour)
- Experimental logic you don’t want to encode as built-ins
- Prototyping an idea before it becomes a first-class NeuroScript feature
Example: Simple Velocity Accent
-- Boost note-on velocity slightly
if event.type == "noteOn" then
event.data2 = MIDI.clamp(event.data2 + 10)
end
return eventExample: Stateful Counter (Context Store)
-- Count note-on events in this script instance (local scope)
if event.type == "noteOn" then
local count = context.get("local", "count") or 0
count = count + 1
context.set("local", "count", count)
end
return eventReference: Lua API
Comparison: Choosing the Right Approach
| Feature | NeuroScript | Lua |
|---|---|---|
| Canonical | Yes | No (non-canonical) |
| Determinism | Strong (bounded plan) | Depends on engine limits |
| Ease of Use | Very easy | Medium |
| State Between Events | Limited (depends on v2 features) | Yes (via context) |
| Best For | Standard transforms, auditable presets | Advanced/custom logic |
Decision Matrix
Use Neuroscript if:
- Your logic fits standard operations (transpose, filter, velocity, remap)
- You want readable, maintainable scripts
- You don't need state or randomization
Use Lua if:
- You need stateful or probabilistic logic now
- You accept that Lua scripts may be less portable than NeuroScript
Best Practices
1. Start with Built-In Nodes
Before writing custom scripts, check if built-in nodes can achieve the same result:
Prefer built-in transforms (transpose, velocity clamp, channel remap, etc.) when they exist.
Built-in nodes are faster, more maintainable, and visually clearer in the graph.
2. Keep Scripts Small and Focused
Good (focused script):
# Single responsibility: boost quiet notes
when vel < 50: vel scale 60..80
passIf a script grows beyond ~10–15 lines, consider splitting it into separate routes/presets or refactoring into smaller, commented sections.
3. Test Scripts in Isolation
Use the Playground Simulator to test scripts before embedding in routes:
- Write script
- Test with sample MIDI events
- Verify output
- Apply the script to your route/preset
4. Profile Script Performance
Add Monitor nodes before/after script nodes to measure latency:
[Input] → [Monitor A] → [Script] → [Monitor B] → [Output]If latency exceeds 1ms consistently, consider:
- Simplifying logic
- Using built-in nodes
- Switching to Lua
5. Handle Edge Cases
MIDI events can be unpredictable — always validate:
-- Clamp notes after a transpose
if event.type == "noteOn" or event.type == "noteOff" then
event.data1 = MIDI.clamp(event.data1 + 12)
end
return event6. Comment Your Logic
Future you (or other musicians) will thank you:
-- Humanize velocity slightly (noteOn only)
if event.type == "noteOn" then
local delta = (math.random() * 10) - 5
event.data2 = MIDI.clamp(math.floor(event.data2 + delta))
end
return eventAdvanced Patterns
These patterns are often better expressed as NeuroScript once v2 scheduling/tags are implemented. Until then, Lua can be used for prototyping.
Limitations & Future Roadmap
Current Limitations
- No native Swift API: Cannot create custom node types in Swift (script nodes only)
- No async operations: Scripts run synchronously (no network, file I/O, setTimeout)
- No persistent storage: State resets when script reloads
- No inter-node communication: Nodes cannot share state
- No timing control: Cannot schedule future events within script
Future: Native Swift Extension API (Planned)
A future Swift API will enable:
- Custom node types: Build nodes with native performance
- Visual parameters: Expose controls in the graph UI
- Compile-time optimization: Swift nodes compiled with app
- Reusable components: Package and share custom nodes
When available, native nodes will be preferred over scripts for:
- Core transform operations
- Performance-critical paths
- Reusable, well-defined functionality
Scripts will remain ideal for:
- Prototyping and experimentation
- User-specific custom logic
- One-off transforms unique to a setup
Migration Path: Script → Native Node (Future)
Once the Swift extension API is available:
- Prototype in script (fast iteration, immediate testing)
- Validate with users (confirm behaviour is correct)
- Implement as native node (performance + reusability)
- Replace script node (keep script version for reference)
This workflow balances rapid prototyping with long-term performance.
Examples Gallery
For ready-to-run examples, use the in-app Script Engine “Examples” menu.
Next Steps
- Neuroscript Reference — Simple DSL for standard transforms
- Lua API — Lua scripting reference (non-canonical)
- NeuroScript routing model — Canonical compilation/execution model (no graph runtime)
Custom transforms are part of Neurode MIDI, a MIDI routing and transformation system for iOS and macOS.
