Neuroscript Language Reference
Neuroscript is a musician-focused DSL for MIDI transformations. It uses a simple line-based syntax where each line represents a processing step applied to incoming MIDI events.
Design Philosophy
- Declarative: Describe what you want, not how to achieve it
- Line-based: One transformation per line, executed in order
- Permissive: Unmatched events pass through by default
- Readable: Syntax mirrors musical terminology (note, ch, transpose, vel)
- Safe: Compile-time validation prevents runtime errors
Basic Structure
# Comments start with #
keep note # Keep only note events
transpose +12 clamp # Shift up an octave, clamp out-of-range
pass # Explicitly pass remaining eventsEach line is a statement. Events flow top-to-bottom through the script. If an event doesn't match a filter, it passes to the next line.
Core Concepts
Event Flow
- MIDI events enter the script
- Each statement processes the event stream
- Statements can filter (keep/drop), transform (modify), or pass through
- Final events exit to the next node in the pipeline
Filtering vs. Transforming
- Filtering:
keep/dropselect which events proceed - Transforming:
transpose,vel scale,note mapmodify event data - Control flow:
when,pass,mutecontrol execution
Statement Types
Filtering: keep / drop
Select events by type, channel, note range, or CC number.
Syntax:
keep <selectors> [where <condition>]
drop <selectors> [where <condition>]Selectors:
note— Note On/Off messagescc— Control Change messagespc— Program Changebend— Pitch Bendaftertouch— Channel Aftertouchclock,start,stop,continue,realtime— System messagesall— Match everythingch <spec>— Channel filter (see Channel Spec below)note <range>— Note range filtercc <range>— CC number range filter
Examples:
keep note # Only note events
keep note, ch 1 # Notes on channel 1
keep note, ch 1..4 # Notes on channels 1-4
keep note C3..C5 # Notes from C3 to C5
drop cc 64..127 # Drop CC 64 and above
drop clock, start, stop # Drop all clock messagesChannel Specification
Channels can be specified as:
all— All 16 channels<n>— Single channel (1-16)<n>..<m>— Channel range (e.g.,ch 1..4)
Examples:
keep ch 1 # Channel 1 only
keep ch all # All channels
keep note, ch 1..8 # Notes on channels 1-8Transformations
transpose
Shift note pitches by semitones.
Syntax:
transpose <semitones> [mode]Modes:
clamp(default) — Clamp to MIDI range (0-127)wrap— Wrap around (128 → 0)drop— Drop out-of-range notes
Examples:
transpose +12 # Up one octave
transpose -7 clamp # Down 7 semitones, clamp
transpose +2 drop # Up 2 semitones, drop overflowoctave
Convenience shorthand for transpose by octaves (12 semitones).
Syntax:
octave <octaves> [mode]Examples:
octave +1 # Up one octave (same as transpose +12)
octave -2 clamp # Down two octaves, clampvel (velocity)
Modify note velocity in three ways:
scale — Map velocity to a range:
vel scale 50..100 # Scale velocity to 50-100 range
vel scale 80..127 # Boost to 80-127 rangefixed — Set constant velocity:
vel fixed 80 # All notes at velocity 80
vel fixed 100 # All notes at velocity 100clamp — Limit velocity range:
vel clamp 30..110 # Clamp between 30-110
vel clamp 64..127 # Remove quiet notesnote map
Remap individual notes (useful for scale corrections, alternate tunings).
Syntax:
note map {
<from> -> <to>
<from> -> <to>
...
}Shorthand (single mapping):
note C4 -> note D4Examples:
# Flatten scale tones for bluesy feel
note map {
E3 -> Eb3
E4 -> Eb4
A3 -> Bb3
A4 -> Bb4
}
# Single note mapping
note Bb3 -> note B3ch map (channel remap)
Remap MIDI channels.
Syntax:
ch <from> -> ch <to>Examples:
ch 2 -> ch 1 # Route channel 2 to channel 1
ch 10 -> ch 1 # Drums (ch 10) to melodic channelcc map (CC remap)
Remap CC controller numbers.
Syntax:
cc <from> -> cc <to>Examples:
cc 1 -> cc 74 # Mod wheel to filter cutoff
cc 11 -> cc 7 # Expression to volumeControl Flow
when
Conditional execution: apply action only when condition is true.
Syntax:
when <condition>: <action>Examples:
when vel > 100: vel clamp 80..100 # Tame loud notes
when note in C3..C4: transpose +12 # Shift low octave
when ch = 10: drop # Drop drum channelpass
Explicitly pass events through (optional, events pass by default).
passmute
Drop all remaining events (useful as final statement or in conditionals).
muteConditions (where / when)
Conditions use predicates and boolean operators.
Fields
type— Event type (note, cc, pc, bend, etc.)ch— Channel (1-16)note— Note number (0-127)vel— Velocity (0-127)cc— CC number (0-127)value— CC value (0-127)prog— Program number (0-127)bend— Pitch bend value
Operators
=,!=— Equality<,<=,>,>=— Comparisonin— Range/set membership
Boolean Logic
and— Both conditions must be trueor— Either condition can be truenot— Negate condition
Examples
# Keep only loud notes on channel 1
keep note where ch = 1 and vel > 90
# Drop quiet notes or CC
drop where (type = note and vel < 20) or type = cc
# Keep notes in specific range
keep note where note in 60..72
# Complex condition
when vel > 110 and ch in [1, 2, 3]: vel clamp 80..110Note Literals
Notes can be specified as:
C,C#,Db,D, ...B(middle C octave)C3,D#4,Bb-1(specific octaves)
Examples:
C4— Middle C (MIDI 60)A3— A below middle C (MIDI 57)Eb3— E-flat in octave 3F#5— F-sharp in octave 5
Complete Examples
Lead Zone with Boost
# Keep lead notes on channel 1, lift an octave, tame peaks
keep note, ch 1
transpose +12 clamp
vel clamp 50..110
passDrop Drums and CC Noise
# Clean mix: remove drums and unnecessary CC
drop note, ch 10 # Drop drum channel
drop cc 64..127 # Drop high CC numbers
passBlues Note Mapping
# Add bluesy flavor by flattening scale tones
note map {
E3 -> Eb3
E4 -> Eb4
A3 -> Bb3
A4 -> Bb4
}
passChannel Split with Transpose
# Route channels 1-4 up, channels 5-8 down
when ch in 1..4: transpose +12
when ch in 5..8: transpose -12
passMIDI Glue (Cleanup)
# Glue multiple tracks: remove clock spam, standardize CC
drop clock, start, stop, continue, realtime
cc 1 -> cc 74 # Mod wheel to filter
passConditional Velocity Scaling
# Boost quiet notes, tame loud ones
when vel < 50: vel scale 50..90
when vel > 110: vel clamp 80..110
passDynamic Layer Switching
# Loud notes get one sound, quiet notes another
when vel > 90: ch 1 -> ch 2
when vel <= 90: ch 1 -> ch 3
passComparison: Neuroscript vs. Lua
| Feature | Neuroscript | Lua |
|---|---|---|
| Syntax | Line-based, declarative | Procedural, imperative |
| Learning Curve | Low (musician-friendly) | Medium (programming knowledge) |
| Flexibility | Fixed MIDI operations | Arbitrary logic |
| Safety | Compile-time checks | Runtime errors possible |
| Performance | Optimized for MIDI | Interpreted overhead |
| Use Case | Common MIDI tasks (transpose, filter, remap) | Complex custom logic |
When to use Neuroscript:
- Standard MIDI operations (transpose, velocity scaling, filtering)
- Readable, maintainable scripts for musicians
- Fast prototyping without programming knowledge
When to use Lua:
- Complex stateful logic (arpeggios, sequencing)
- Custom algorithms not covered by Neuroscript
- Integration with external data or calculations
Error Handling
Neuroscript validates scripts at compile time before execution.
Common Errors
Out-of-range channel:
keep ch 17 # Error: Channel 17 is out of range (1-16)Invalid velocity:
vel fixed 150 # Error: Velocity 150 is out of range (0-127)Invalid note range:
keep note 128..140 # Error: Note range out of MIDI bounds (0-127)Empty selector:
keep # Error: Expected at least one selectorError Messages
Errors include line and column numbers:
3:6 Channel 17 is out of range (1-16).This means: Line 3, column 6 — check your channel specification.
Best Practices
1. Start Simple
Begin with basic filters, add complexity gradually:
# v1: Just filter
keep note, ch 1
# v2: Add transpose
keep note, ch 1
transpose +12
# v3: Add velocity control
keep note, ch 1
transpose +12
vel clamp 50..1102. Use Comments Liberally
Explain why, not just what:
# Bass zone: low keys only, boost quiet notes
keep note C1..C3
when vel < 60: vel scale 60..90
pass3. Explicit pass for Clarity
End scripts with pass to make intent clear:
keep note, ch 1
transpose +12
pass # All other events pass through4. Test Incrementally
Add one statement at a time, test with the MIDI visualizer:
keep note # Does this keep the right events?
# transpose +12 # Uncomment once filter is correct5. Prefer clamp Over drop
For live performance, clamping avoids unexpected silence:
transpose +12 clamp # Safer
# transpose +12 drop # Risky: high notes disappear6. Name Your Scripts
Save scripts with descriptive names:
bass_zone_boost.nslead_octave_up.nsdrum_channel_cleanup.ns
7. Keep Scripts Short
If a script exceeds 10-15 lines, consider refactoring into clearer sections (comments + focused when blocks), or splitting into multiple presets/routes.
# Prefer clarity within one script (canonical), or split by preset/route.If you use a visual editor, treat it as an authoring UI that compiles to NeuroScript (no graph runtime required).
Advanced Patterns
Layering (Split to Multiple Channels)
# Duplicate to channels 1 and 2 based on velocity
when vel > 90: ch 1 -> ch 2
passVelocity Zones
# Route to different synths based on dynamics
when vel < 60: ch 1 -> ch 3 # Soft layer
when vel >= 60 and vel < 100: ch 1 -> ch 2 # Medium layer
when vel >= 100: ch 1 -> ch 1 # Loud layer (original)
passNote Range Splits (Keyboard Zones)
# Bass on left, lead on right
when note < 60: ch 1 -> ch 2
when note >= 60: ch 1 -> ch 3
passCC Remapping for Different Gear
# Adapt mod wheel to different synth expectations
cc 1 -> cc 74 # Mod wheel to filter cutoff
cc 11 -> cc 7 # Expression to volume
passHumanize (Subtle Velocity Variation)
Not directly supported in Neuroscript — use Lua for randomization.
-- Lua alternative for humanization
if event.type == "noteOn" then
local delta = (math.random() * 10) - 5
event.data2 = MIDI.clamp(math.floor(event.data2 + delta))
end
return eventLimitations
No State Between Events
Each MIDI event is processed independently. For stateful logic (arpeggios, sequencing), use Lua scripting.
No Timing/Delay
Neuroscript processes events synchronously. For delays or timing effects, use multiple routes with separate timing nodes (future feature).
No Random/Math Functions
For random velocity, probability-based routing, or complex math, use Lua.
No MIDI Output Selection
Neuroscript transforms events within a single route. Route-level destination selection happens in the graph editor.
Grammar Reference (ANTLR)
For implementers: Neuroscript is defined by an ANTLR grammar at:
Sources/NeuroScriptKit/Grammar/NeuroScript.g4Key Productions:
script→statementListstatement→keep | drop | when | transpose | octave | vel* | *map | pass | muteselector→eventType | channel | note | ccexpr→orExpr → andExpr → notExpr → predicate
Next Steps
- Script Engine Overview — How scripting fits into v2
- Playground/Simulator — Test Neuroscript interactively
- Lua API — For complex custom logic beyond Neuroscript
Neuroscript is part of Neurode MIDI, a MIDI routing and transformation system for iOS and macOS.
