Transform Pipeline (Legacy)
Legacy documentation
This page describes an early visual node-graph idea ("Transform Pipeline").
Neurode MIDI now uses Neuroscript v3 (Rust) 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)
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)
- Neuroscript: Fast (Rust-compiled 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).
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).
