Complex Dependencies
Master advanced dependency patterns for sophisticated compositions with intricate note relationships.
Beyond Simple Chains
Basic dependencies link one note to another. Complex dependencies involve:
- Multiple inheritance paths
- Property-specific dependencies
- Conditional relationships
- Hierarchical structures
Multi-Property Dependencies
A single note can depend on different notes for different properties:
// Note 3 depends on Note 1 for frequency, Note 2 for timing
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(5, 4))
startTime: module.getNoteById(2).getVariable('startTime')
duration: module.getNoteById(2).getVariable('duration')Use Case: Harmony Following Melody
// Melody note (Note 1)
frequency: module.baseNote.getVariable('frequency').mul(new Fraction(3, 2))
startTime: new Fraction(0)
duration: new Fraction(1)
// Harmony note - follows melody timing but uses different pitch
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(5, 4))
startTime: module.getNoteById(1).getVariable('startTime') // Same timing
duration: module.getNoteById(1).getVariable('duration')
// Bass note - same timing as melody, octave below
frequency: module.getNoteById(1).getVariable('frequency').div(new Fraction(2))
startTime: module.getNoteById(1).getVariable('startTime')
duration: module.getNoteById(1).getVariable('duration')Branching Dependencies
Create tree structures where one note feeds multiple branches:
Root (Note 1)
/ | \
Note 2 Note 3 Note 4
| | |
Note 5 Note 6 Note 7Implementation
// Root (Note 1)
frequency: module.baseNote.getVariable('frequency')
startTime: new Fraction(0)
duration: new Fraction(4)
// Branch A: Perfect fifth up
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(3, 2))
startTime: module.getNoteById(1).getVariable('startTime')
duration: new Fraction(2)
// Branch B: Major third up
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(5, 4))
startTime: module.getNoteById(1).getVariable('startTime').add(new Fraction(2))
duration: new Fraction(2)
// Branch C: Octave up
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(2))
startTime: module.getNoteById(1).getVariable('startTime')
duration: new Fraction(4)Diamond Dependencies
A note can depend on multiple notes that share a common ancestor:
BaseNote
/ \
Note 1 Note 2
\ /
Note 3Implementation
// Note 1: Fifth up from base
frequency: module.baseNote.getVariable('frequency').mul(new Fraction(3, 2))
startTime: new Fraction(0)
duration: new Fraction(2)
// Note 2: Third up from base
frequency: module.baseNote.getVariable('frequency').mul(new Fraction(5, 4))
startTime: new Fraction(2)
duration: new Fraction(2)
// Note 3: Combines Note 1's timing with Note 2's pitch relationship
frequency: module.getNoteById(2).getVariable('frequency').mul(new Fraction(3, 2))
startTime: module.getNoteById(1).getVariable('startTime')
.add(module.getNoteById(1).getVariable('duration'))
duration: new Fraction(2)Cascading Property Changes
When you change a property at the top of a hierarchy, all dependent notes update:
// If BaseNote.frequency changes from 440 to 330:
// Note 1 (depends on BaseNote): 330 × 3/2 = 495 Hz
// Note 2 (depends on Note 1): 495 × 5/4 = 618.75 Hz
// Note 3 (depends on Note 2): 618.75 × 6/5 = 742.5 Hz
// ... entire chain updatesVisualizing the Cascade
- Select the root note
- Red lines show all dependents
- Edit the root's frequency
- Watch all connected notes update instantly
Tempo Hierarchies
Create sections with different tempos by establishing tempo-defining notes:
// BaseNote: tempo = 100 BPM (global default)
// Section A root (Note 10)
tempo: new Fraction(120) // Section A at 120 BPM
startTime: new Fraction(0)
duration: new Fraction(60).div(new Fraction(120)) // Uses own tempo
// Section A notes depend on Note 10 for tempo
startTime: module.getNoteById(10).getVariable('startTime')
.add(module.getNoteById(10).getVariable('duration'))
duration: new Fraction(60).div(module.findTempo(module.getNoteById(10)))
// Section B root (Note 20)
tempo: new Fraction(80) // Section B at 80 BPM
startTime: module.findMeasureLength(module.getNoteById(10)).mul(new Fraction(8)) // After 8 measures of Section A
duration: new Fraction(60).div(new Fraction(80))
// Section B notes depend on Note 20 for tempo
duration: new Fraction(60).div(module.findTempo(module.getNoteById(20)))Relative Timing Patterns
Call and Response
// Call (Note 1)
startTime: new Fraction(0)
duration: new Fraction(2)
// Response (Note 2) - starts after call with a gap
startTime: module.getNoteById(1).getVariable('startTime')
.add(module.getNoteById(1).getVariable('duration'))
.add(new Fraction(1, 2)) // Half-second gap
duration: module.getNoteById(1).getVariable('duration') // Same duration as callEcho Effect
// Original note
frequency: module.baseNote.getVariable('frequency')
startTime: new Fraction(0)
duration: new Fraction(1)
// Echo 1 (quieter, delayed)
frequency: module.getNoteById(1).getVariable('frequency')
startTime: module.getNoteById(1).getVariable('startTime').add(new Fraction(1, 4))
duration: module.getNoteById(1).getVariable('duration')
// Note: Volume would be handled by instrument/gain, not shown here
// Echo 2 (even more delayed)
frequency: module.getNoteById(1).getVariable('frequency')
startTime: module.getNoteById(1).getVariable('startTime').add(new Fraction(1, 2))
duration: module.getNoteById(1).getVariable('duration')Parallel Voice Leading
Create multiple voices that move together:
// Soprano (Note 1)
frequency: module.baseNote.getVariable('frequency').mul(new Fraction(2)) // 880 Hz
startTime: new Fraction(0)
duration: new Fraction(1)
// Alto - always a third below soprano
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(4, 5)) // Down major third
startTime: module.getNoteById(1).getVariable('startTime')
duration: module.getNoteById(1).getVariable('duration')
// Tenor - always a fifth below soprano
frequency: module.getNoteById(1).getVariable('frequency').mul(new Fraction(2, 3)) // Down perfect fifth
startTime: module.getNoteById(1).getVariable('startTime')
duration: module.getNoteById(1).getVariable('duration')
// Bass - always an octave below soprano
frequency: module.getNoteById(1).getVariable('frequency').div(new Fraction(2))
startTime: module.getNoteById(1).getVariable('startTime')
duration: module.getNoteById(1).getVariable('duration')
// When soprano changes, all voices update maintaining their intervalsSequential Pattern Generation
Rhythmic Sequence
// Beat 1
startTime: new Fraction(0)
duration: new Fraction(60).div(module.findTempo(module.baseNote))
// Each subsequent beat references previous
startTime: module.getNoteById(PREV).getVariable('startTime')
.add(module.getNoteById(PREV).getVariable('duration'))
duration: new Fraction(60).div(module.findTempo(module.baseNote))Melodic Sequence (Stepwise)
// Start note
frequency: module.baseNote.getVariable('frequency')
// Each subsequent note is a step higher
frequency: module.getNoteById(PREV).getVariable('frequency')
.mul(new Fraction(9, 8)) // Major second upDependency Graph Analysis
Viewing Dependencies
Click on any note to see:
- Blue lines: Notes this note depends on
- Red lines: Notes that depend on this note
Understanding Propagation
When Note 1 changes:
- All notes with blue lines to Note 1 are marked dirty
- Evaluation propagates through the dependency tree
- Only affected notes are recalculated (efficient!)
Cycle Prevention
RMT Compose automatically prevents circular dependencies:
// This would be rejected:
Note 1: depends on Note 2
Note 2: depends on Note 1 // ERROR: Circular dependencyPerformance Considerations
Deep Dependency Chains
Very deep chains (Note 1 → Note 2 → ... → Note 100) work but may have evaluation latency.
Tip: Consider flattening structures where possible:
// Instead of: Note 10 → Note 9 → ... → Note 1 → BaseNote
// Consider: All notes → BaseNote (flat)Wide Dependency Trees
Many notes depending on one note is efficient due to the inverted index lookup.
// This is fine:
100 notes all depending on BaseNote.frequencyDebugging Complex Dependencies
Trace the Chain
- Select the final note in your chain
- Follow blue lines back to the source
- Check each intermediate value
Verify Evaluation
- Change a root value
- Check that intermediate notes updated
- Verify final values are correct
Common Issues
- Wrong note ID: Double-check
getNoteById(N)numbers - Property typo: Ensure
getVariable('frequency')spelling - Missing dependency: Check that the referenced note exists
Best Practices
1. Document Your Structure
Keep a mental (or written) map of your dependency hierarchy.
2. Test Incrementally
Build complex structures step-by-step, testing each addition.
3. Use Meaningful IDs
When planning, assign note IDs purposefully:
- 1-10: Melody
- 11-20: Harmony
- 21-30: Bass
- etc.
4. Avoid Unnecessary Depth
Direct dependencies on common ancestors are more efficient than long chains.
Next Steps
- Module Library Creation - Save and reuse patterns
- Interval Exploration - Experiment with different intervals
- Dependency Graph Architecture - Technical details