Transform Pipeline (Legacy) โ
Legacy documentation
This page describes an early visual node-graph idea ("Transform Pipeline").
Neurode MIDI v2 uses NeuroScript 2.0 source as the canonical encoding for routing/transforms.
- Compilation happens off realtime threads into an immutable plan.
- Plan activation is an atomic swap; failures fall back to pass-through and the UI shows the compile error.
- There is no TransformGraph/DAG runtime requirement.
If you arrived here from other docs, treat the remainder of this page as historical.
Overview โ
In the legacy design, instead of simple linear routing (Source โ Destination), the Transform Pipeline allowed sophisticated processing chains:
[MIDI In] โ [Filter] โ [Transpose] โ [Split] โ [Output 1]
โโโโโโโโ [Output 2]Each node processes MIDI events, and connections (edges) define the flow between nodes.
Core Concepts โ
Nodes โ
Nodes are processing units. Each node has:
- Type: Determines behaviour (filter, transform, script, etc.)
- Inputs/Outputs: Connection points (ports)
- Parameters: Configuration specific to the node type
- Enabled State: Can be bypassed without deletion
Connections (Edges) โ
Connections link nodes together:
- Source: Output port of one node
- Target: Input port of another node
- One input per port: Each input can only receive from one source
- Multiple outputs: An output can fan out to many nodes
Graphs โ
A graph is a complete pipeline:
- Collection of nodes
- Set of connections
- Metadata (name, creation date, etc.)
Node Categories โ
1. Input/Output โ
MIDI Input โ Entry point for incoming MIDI events
- Inputs: None
- Outputs: 1 (all events)
- Use: Automatically created for each route
MIDI Output โ Exit point sending events to destination
- Inputs: 1 (events to send)
- Outputs: None
- Use: Automatically created for each route
Virtual Input/Output โ For internal routing
- Planned feature for complex multi-route setups
2. Filters โ
Filters select which events pass through:
Note Filter โ Pass/block note events
- Parameters: Note range (e.g., C3-C5), pass/block mode
- Use: Isolate specific keyboard zones
Channel Filter โ Pass/block by MIDI channel
- Parameters: Channels 1-16 selection, pass/block mode
- Use: Route specific channels differently
Velocity Filter โ Filter by note velocity
- Parameters: Velocity range (0-127), pass/block mode
- Use: Separate loud/soft playing layers
CC Filter โ Filter Control Change messages
- Parameters: CC number range, pass/block mode
- Use: Remove unwanted CC data
Message Type Filter โ Filter by event type
- Parameters: Note, CC, Program Change, Pitch Bend, etc.
- Use: Drop all CC, keep only notes
Realtime Filter โ Filter clock and system messages
- Parameters: Clock, Start, Stop, Continue
- Use: Clean up clock spam
3. Transforms โ
Transforms modify event data:
Transpose โ Shift note pitch
- Parameters: Semitones (+/- 127), clamp/wrap/drop mode
- Use: Octave shifts, key changes
Octave Shift โ Convenience wrapper for transpose by octaves
- Parameters: Octaves (+/- 10), mode
- Use: Same as transpose, but in octave units
Note Map โ Remap individual notes
- Parameters: List of note pairs (from โ to)
- Use: Scale corrections, alternate tunings
Velocity Curve โ Apply curve to velocity
- Parameters: Curve type (linear, exponential, logarithmic, custom)
- Use: Shape dynamics naturally
Velocity Scale โ Map velocity to range
- Parameters: Min/max output range
- Use: Boost quiet notes, tame loud ones
Fixed Velocity โ Set constant velocity
- Parameters: Fixed value (0-127)
- Use: Remove dynamics entirely
Channel Remap โ Change MIDI channel
- Parameters: From channel, to channel
- Use: Route drums to melodic channel, etc.
CC Remap โ Change CC number
- Parameters: From CC, to CC
- Use: Adapt controllers for different synths
CC Value Scale/Curve โ Modify CC values
- Parameters: Range or curve
- Use: Adjust modulation response
4. Generators โ
Generators create additional events:
Chord โ Add chord notes (planned)
- Parameters: Chord type, voicing
- Use: Harmonize single-note input
Harmonizer โ Add harmonies (planned)
- Parameters: Interval, scale
- Use: Automatic vocal-style harmonies
Arpeggiator โ Generate arpeggios (planned)
- Parameters: Pattern, tempo
- Use: Convert chords to arpeggios
Echo โ Delayed repetitions (planned)
- Parameters: Delay time, feedback, decay
- Use: MIDI delay effect
Strum โ Humanize chord timing (planned)
- Parameters: Strum speed, direction
- Use: Guitar-like chord rolls
5. Logic โ
Logic nodes control event flow:
Split โ Duplicate events to multiple outputs
- Inputs: 1
- Outputs: Multiple (configurable)
- Use: Send to multiple processing chains
Merge โ Combine events from multiple inputs
- Inputs: Multiple (configurable)
- Outputs: 1
- Use: Recombine split paths
Switch โ Route to one of several outputs
- Parameters: Active output index, control source
- Use: Dynamic routing based on condition
Gate โ Pass/block events dynamically
- Parameters: Open/closed state, control source
- Use: Mute/unmute sections
Conditional โ Route based on event properties
- Parameters: Condition expression
- Use: "If velocity > 90, go left; else go right"
6. Utility โ
Utility nodes provide debugging and optimization:
Monitor โ Observe event stream
- Parameters: Display mode
- Use: Debugging, visualizing flow
Latency โ Measure/compensate latency (planned)
- Parameters: Compensation amount
- Use: Align timing across routes
Quantize โ Snap note timing (planned)
- Parameters: Grid resolution
- Use: Tighten timing to grid
Humanize โ Add timing/velocity variation (planned)
- Parameters: Amount, randomness
- Use: Natural feel
Panic โ Send all-notes-off
- Parameters: Trigger source
- Use: Emergency stop
7. Script Nodes โ
Script nodes run custom code:
Neuroscript โ Line-based DSL
- Parameters: Neuroscript code
- Use: Standard MIDI operations (see Neuroscript Reference)
Lua โ Lightweight scripting
- Parameters: Lua code
- Use: Performance-critical custom code
Building a Pipeline โ
Step 1: Start with a Template โ
Every route begins with a default pipeline:
[MIDI Input] โ [MIDI Output]This is a passthrough โ events flow directly from source to destination.
Step 2: Insert Nodes โ
Add nodes between input and output:
- Open the Transform Pipeline editor for the route
- Click Add Node (+ button)
- Select node type from category
- Place node on canvas
- Connect by dragging from output port to input port
Example: Add transpose node:
[MIDI Input] โ [Transpose] โ [MIDI Output]Step 3: Configure Parameters โ
Select the node and adjust parameters:
- Transpose: Set semitones to +12 (one octave up)
- Mode: Choose "clamp" (safe for live performance)
Step 4: Test with Monitor โ
Add a Monitor node to observe event flow:
[MIDI Input] โ [Monitor] โ [Transpose] โ [MIDI Output]Monitor shows events passing through in real-time.
Step 5: Add Complexity โ
Insert more nodes, create branches:
[MIDI Input] โ [Channel Filter: CH 1] โ [Transpose +12] โ [Output 1]
[Channel Filter: CH 2] โ [Transpose -12] โ [Output 2]This splits channel 1 (up octave) and channel 2 (down octave) to different destinations.
Execution Model โ
Topological Sort โ
Graphs execute in topological order โ nodes are processed based on dependencies, not visual position:
A โ B โ D
A โ C โ DExecution order: A โ (B, C in parallel) โ D
Pipeline Execution Flow โ
graph LR
A[MIDI Input] --> B[Note Filter]
B --> C[Transpose +12]
C --> D[Velocity Scale]
D --> E[Script Node]
E --> F[MIDI Output]
style A fill:#10b981,stroke:#059669,stroke-width:2px,color:#fff
style B fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#fff
style C fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style D fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style E fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style F fill:#3b82f6,stroke:#2563eb,stroke-width:2px,color:#fffNode Types: Input (green), Filters (orange), Transforms (purple), Scripts (pink), Output (blue). Events flow left-to-right through the pipeline.
Cycle Detection โ
Graphs cannot contain cycles โ feedback loops are invalid:
A โ B โ C
โ โ
โโโโโThis would create infinite loop and is rejected at validation.
Event Flow โ
- MIDI event arrives at MIDI Input node
- Event is passed to connected nodes
- Each node processes and optionally transforms the event
- Processed event(s) continue to next nodes
- Final events exit through MIDI Output node
Key: Nodes can:
- Drop events (filters blocking)
- Pass unchanged (transparent)
- Transform (modify properties)
- Generate (create additional events)
Parallel Branches โ
When a node outputs to multiple connections, events are duplicated:
[Input] โ [Split] โ [Transpose +12] โ [Output 1]
โ [Transpose -12] โ [Output 2]Same event processed twice, each output gets independent copy.
Branching Pipeline Visualization โ
graph TD
A[MIDI Input] --> B[Split Node]
B --> C[Transpose +12]
B --> D[Transpose -12]
C --> E[Velocity Scale 80-127]
D --> F[Velocity Scale 40-80]
E --> G[Output 1: Lead Synth]
F --> H[Output 2: Bass Synth]
style A fill:#10b981,stroke:#059669,stroke-width:2px,color:#fff
style B fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#fff
style C fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style D fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style E fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style F fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style G fill:#3b82f6,stroke:#2563eb,stroke-width:2px,color:#fff
style H fill:#3b82f6,stroke:#2563eb,stroke-width:2px,color:#fffSplit Node duplicates events to both branches. Each branch applies independent transforms before reaching separate outputs.
Example Pipelines โ
1. Lead Zone with Boost โ
Goal: Keep notes on channel 1, transpose up, tame loud notes.
[MIDI Input]
โ
[Channel Filter: CH 1]
โ
[Transpose: +12 clamp]
โ
[Velocity Curve: Compress]
โ
[MIDI Output]Neuroscript Equivalent:
keep note, ch 1
transpose +12 clamp
vel clamp 50..110
pass2. Keyboard Split (Bass + Lead) โ
Goal: Low keys โ bass synth, high keys โ lead synth.
[MIDI Input]
โ
[Split]
โโโ [Note Filter: C1-B3] โ [Channel Remap: CH 1โ2] โ [Output: Bass]
โโโ [Note Filter: C4-C7] โ [Channel Remap: CH 1โ3] โ [Output: Lead]3. Velocity Layer Routing โ
Goal: Soft notes to pad, loud notes to lead.
[MIDI Input]
โ
[Split]
โโโ [Velocity Filter: 0-70] โ [Output: Pad Synth]
โโโ [Velocity Filter: 71-127] โ [Output: Lead Synth]4. MIDI Cleanup (Remove Clock) โ
Goal: Strip clock messages, standardize CC.
[MIDI Input]
โ
[Realtime Filter: Block Clock/Start/Stop]
โ
[CC Remap: 1โ74]
โ
[MIDI Output]5. Dynamic Harmonizer (Future) โ
Goal: Add harmony based on velocity.
[MIDI Input]
โ
[Split]
โโโ [Pass Through] โ [Merge] โ [Output]
โโโ [Conditional: vel > 90]
โ
[Harmonizer: +7 semitones] โ [Merge]When velocity exceeds 90, add harmony a fifth above.
Linearization โ
Some graphs can be linearized โ represented as a simple ordered list of steps (no branching).
Linear Graph โ
[Input] โ [A] โ [B] โ [C] โ [Output]This linearizes to: [A, B, C] โ simple sequential processing.
Non-Linear Graph (Branching) โ
[Input] โ [Split] โ [A] โ [Merge] โ [Output]
โ [B] โโโโโโThis cannot linearize โ requires full graph execution.
Why Linearization Matters โ
- Simple Editor: Linear graphs can use the simple transform list UI
- Performance: Linear execution is slightly faster
- Debugging: Easier to understand sequential flow
Preference: Keep graphs linear when possible. Use branching only when necessary.
Performance Considerations โ
Node Count โ
- Target: <10 nodes per route for real-time performance
- Max: 50 nodes supported, but latency increases
Script Node Costs โ
- Neuroscript: Fast (compiled)
- Lua: Fast (lightweight interpreter)
Recommendation: Prefer built-in nodes over scripting when available.
Monitor Node Impact โ
Monitor nodes add minimal overhead but should be disabled in production:
- Enable for debugging
- Disable for live performance
Parallel Branches โ
Branches execute sequentially, not truly parallel:
[Split] โ [A (10ฮผs)] โ [Merge]
โ [B (5ฮผs)] โโโTotal: ~15ฮผs, not 10ฮผs.
Keep branch processing lightweight.
Graph Validation โ
Graphs are validated before execution:
Must Have โ
- At least one MIDI Input node
- At least one MIDI Output node
- All nodes connected (no orphans)
Cannot Have โ
- Cycles (A โ B โ A)
- Disconnected subgraphs (islands)
- Invalid connections (type mismatches)
Validation errors appear in the editor with specific messages and highlights.
Debugging Pipelines โ
1. Use Monitor Nodes โ
Insert Monitor nodes at key points to observe event flow:
[Input] โ [Monitor: "After Filter"] โ [Filter] โ [Monitor: "After Transform"] โ [Output]2. Enable/Disable Nodes โ
Toggle enabled state to isolate issues:
- Disable suspect node โ Does problem persist?
- If yes, issue is elsewhere
- If no, issue is in that node
3. Check Event Visualizer โ
The global Event Visualizer shows final output events โ use it to confirm pipeline results.
4. Simplify โ
If pipeline misbehaves:
- Remove all nodes except input/output (passthrough)
- Add nodes back one at a time
- Test after each addition
- Isolate the failing node
5. Compare to Neuroscript โ
If you have a working Neuroscript version, create equivalent graph and compare:
- Neuroscript is easier to debug (line-by-line)
- Graph is more flexible but harder to trace
Presets โ
Pipelines can be saved as Presets and reused:
Saving โ
- Create a working pipeline
- Click Save as Preset
- Name it (e.g., "Bass Zone Boost")
- Optionally add description/tags
Loading โ
- Open route's Transform Pipeline editor
- Click Load Preset
- Select from library
- Pipeline is replaced
Sharing (Future) โ
Presets will be exportable/importable for sharing with other users.
Best Practices โ
1. Keep It Simple โ
Start with the simplest pipeline that works. Add complexity only when needed.
Good:
[Input] โ [Transpose] โ [Output]Overkill (for simple transpose):
[Input] โ [Split] โ [Transpose A] โ [Merge] โ [Output]
โ [Transpose B] โโโโโโ2. Name Your Nodes โ
Default names like "Transpose 1" become confusing in complex graphs:
- Bad: "Transpose 1", "Transpose 2"
- Good: "Bass Up Octave", "Lead Down Fifth"
3. Use Comments (Future) โ
Once annotation support is added, document why a node exists:
- "Clamps velocity because XYZ synth clips above 110"
- "Removes CC 64 which causes stuck sustain on ABC hardware"
4. Modular Design โ
Break complex pipelines into multiple routes instead of one giant graph:
- Route 1: Input โ Cleanup โ Virtual Out
- Route 2: Virtual In โ Processing โ Final Out
Easier to debug and reuse.
5. Test Incrementally โ
Build pipelines one node at a time:
- Add node
- Test immediately
- Confirm correct behaviour
- Proceed to next node
Don't build entire graph then debug โ you won't know where the issue is.
6. Monitor Key Points โ
Place Monitor nodes at:
- After filters (confirm events are filtered correctly)
- Before scripts (see input to custom code)
- Before output (final event check)
7. Disable, Don't Delete โ
When experimenting, disable nodes instead of deleting:
- Preserve your work
- Easy to re-enable and compare
- No need to remember parameters
Limitations โ
No Feedback Loops โ
Graphs cannot contain cycles:
A โ B โ C
โ โ
โโโโโ โ InvalidFor delay effects, use dedicated Echo node (planned).
No Timing Control โ
Nodes process events immediately โ no scheduling or delays within graph.
For timing effects:
- Use multiple routes with routing-level timing (future)
- Use scripting with internal state (limited)
No Conditional Branching (Yet) โ
Current Split node duplicates to all outputs. Conditional node (planned) will enable:
[Input] โ [Conditional: vel > 90] โ [Output A]
โ [Output B (else)]No External State โ
Nodes cannot share state between processing:
- Each event processed independently
- No "remember last note" or cross-event logic (except in scripts)
Advanced: Custom Node Types โ
Currently, custom nodes are not supported via Swift API.
For custom logic, use Script nodes (Neuroscript, Lua).
Future: Custom Swift-based node types with native performance.
Comparison: Pipeline vs. Simple Routing โ
| Feature | Simple Routing | Transform Pipeline |
|---|---|---|
| Ease of Use | Easy | Medium |
| Flexibility | Limited | High |
| Branching | No | Yes |
| Visual | List-based | Node graph |
| Performance | Fastest | Very fast |
| Best For | Basic routes | Complex processing |
When to use Simple Routing:
- Straightforward MIDI passthrough
- Single transform (transpose, channel remap)
When to use Transform Pipeline:
- Multiple transforms in sequence
- Branching/splitting
- Conditional logic
- Debugging complex processing
Next Steps โ
- Transform Types Reference โ Complete catalog of node types
- Neuroscript Language โ Script node reference
- Performance Optimization โ Keep pipelines efficient
This legacy Transform Pipeline concept was an early exploration for Neurode MIDI (iOS/macOS).
