Skip to content

Instantly share code, notes, and snippets.

@kmorrill
Created December 19, 2024 20:22
Show Gist options
  • Save kmorrill/ac2e20613d45a7bea6e919f68f494eb6 to your computer and use it in GitHub Desktop.
Save kmorrill/ac2e20613d45a7bea6e919f68f494eb6 to your computer and use it in GitHub Desktop.
Mozaic OP-XY Drum Generator
@Description
OP-XY Drum Pattern Generator v2.0
A dynamic drum pattern generator for OP-XY creating musical 4-bar patterns.
Controls:
- Density: Overall number of hits (sparse to busy)
- Variation: Pattern unpredictability and dynamics
- Balance: Emphasis between core drums vs percussion
- Repetition: Pattern evolution over time
Creates musical rhythmic foundations with traditional kick/snare patterns
and structured hi-hat grooves. Additional percussion elements are added
thoughtfully based on density and variation settings.
@End
@OnLoad
SetShortName {OPXY}
ShowLayout 4 // Minimal layout with 4 knobs
// Initialize pattern state
currentTick = 0
isPlaying = NO
shouldRegenerate = YES
// Initialize drum note mapping
drumNotes[0] = 53 // F1: Kick A
drumNotes[1] = 54 // F#1: Kick B
drumNotes[2] = 55 // G1: Snare A
drumNotes[3] = 56 // G#1: Snare B
drumNotes[4] = 57 // A1: Rim
drumNotes[5] = 58 // A#1: Clap
drumNotes[6] = 59 // B1: Tambourine
drumNotes[7] = 60 // C2: Shaker
drumNotes[8] = 61 // C#2: Closed Hat A
drumNotes[9] = 62 // D2: Closed Hat B
drumNotes[10] = 63 // D#2: Open Hat
drumNotes[11] = 64 // E2: Clave
drumNotes[12] = 65 // F2: Low Tom
drumNotes[13] = 66 // F#2: Ride
drumNotes[14] = 67 // G2: Mid Tom
drumNotes[15] = 68 // G#2: Crash
drumNotes[16] = 69 // A2: High Tom
drumNotes[17] = 70 // A#2: Triangle
drumNotes[18] = 71 // B2: Low Conga
drumNotes[19] = 72 // C3: High Conga
drumNotes[20] = 73 // C#3: Cow Bell
drumNotes[21] = 74 // D3: Guiro
drumNotes[22] = 75 // D#3: Metal
drumNotes[23] = 76 // E3: Chi
// Basic pattern elements
kick = drumNotes[0]
snare = drumNotes[2]
closedHat = drumNotes[8]
openHat = drumNotes[10]
// Group indices in drumNotes array
coreStart = 0 // kicks and snares
coreEnd = 5 // through clap
hatsStart = 8 // closed/open hats
hatsEnd = 10
percStart = 6 // tambourine, shaker
percEnd = 7
tomsStart = 12 // toms and cymbals
tomsEnd = 16
exoticStart = 17 // congas through chi
exoticEnd = 23
// Initialize arrays for pattern
FillArray patternNotes, 0, 96
FillArray patternVels, 0, 96
FillArray patternDelays, 0, 96
// Control knob setup
LabelKnobs {OP-XY DRUM GENERATOR}
LabelKnob 0, {Density}
LabelKnob 1, {Variation}
LabelKnob 2, {Balance}
LabelKnob 3, {Repetition}
// Store previous knob values to detect changes
lastDensity = 64
lastVariation = 32
lastBalance = 64
lastRepetition = 32
SetKnobValue 0, lastDensity
SetKnobValue 1, lastVariation
SetKnobValue 2, lastBalance
SetKnobValue 3, lastRepetition
SetMetroPPQN 6 // 24 PPQN / 4 = 6 pulses per 16th note
Call @GeneratePattern
@End
@OnHostStart
isPlaying = YES
currentTick = 0
@End
@OnHostStop
isPlaying = NO
// Send all notes off
for i = 0 to 23
SendMIDINoteOff 0, drumNotes[i], 0
endfor
@End
@OnMetroPulse
if not isPlaying
Exit
endif
currentTick = CurrentMetroPulse % 96
// Check for knob changes
density = GetKnobValue 0
variation = GetKnobValue 1
balance = GetKnobValue 2
repetition = GetKnobValue 3
if density <> lastDensity or variation <> lastVariation or
balance <> lastBalance or repetition <> lastRepetition
lastDensity = density
lastVariation = variation
lastBalance = balance
lastRepetition = repetition
shouldRegenerate = YES
endif
// Regenerate if needed and at pattern start
if shouldRegenerate and currentTick = 0
Call @GeneratePattern
endif
// Play scheduled hits
note = patternNotes[currentTick]
if note > 0
velocity = patternVels[currentTick]
delay = patternDelays[currentTick]
SendMIDINoteOn 0, note, velocity, delay
SendMIDINoteOff 0, note, 0, delay + 50 // 50ms note length
endif
@End
@SelectInstrument
r = Random 0, 100
selectedNote = 0
if bal < 0.4 // Emphasize core sounds
if r < 85 // Higher probability for core sounds at low balance
if r < 60
selectedNote = drumNotes[Random coreStart, coreEnd]
else
selectedNote = drumNotes[Random hatsStart, hatsEnd]
endif
endif
elseif bal < 0.7 // Balanced
if r < 75 // Still favor core sounds but allow more variation
if r < 45
selectedNote = drumNotes[Random coreStart, coreEnd]
elseif r < 65
selectedNote = drumNotes[Random hatsStart, hatsEnd]
else
selectedNote = drumNotes[Random percStart, percEnd]
endif
endif
else // Emphasize percussion
if r < 65 // Allow more exotic sounds but maintain some core elements
if r < 25
selectedNote = drumNotes[Random coreStart, coreEnd]
elseif r < 40
selectedNote = drumNotes[Random hatsStart, hatsEnd]
elseif r < 55
selectedNote = drumNotes[Random percStart, percEnd]
else
selectedNote = drumNotes[Random exoticStart, exoticEnd]
endif
endif
endif
@End
@GeneratePattern
Log {Generating new pattern...}
dens = TranslateScale lastDensity, 0, 127, 0.1, 1.0
vari = TranslateScale lastVariation, 0, 127, 0, 1.0
bal = TranslateScale lastBalance, 0, 127, 0, 1.0
rep = TranslateScale lastRepetition, 0, 127, 0, 1.0
// Clear pattern
FillArray patternNotes, 0, 96
FillArray patternVels, 0, 96
FillArray patternDelays, 0, 96
// Generate basic kick pattern (on beats 1 and 3 plus variations)
for bar = 0 to 3
offset = bar * 24
// Strong kick on 1
patternNotes[offset] = kick
patternVels[offset] = 110 + (Random -5, 5)
// Kick on 3 (with probability based on density)
if (Random 0, 100) < (85 * dens)
patternNotes[offset + 12] = kick
patternVels[offset + 12] = 100 + (Random -5, 5)
endif
// Optional ghost kick before 3 (based on variation)
if vari > 0.5 and (Random 0, 100) < (30 * dens)
patternNotes[offset + 11] = kick
patternVels[offset + 11] = 70 + (Random -5, 5)
endif
endfor
// Generate basic snare pattern (on beats 2 and 4 plus ghost notes)
for bar = 0 to 3
offset = bar * 24
// Main snares on 2 and 4
patternNotes[offset + 6] = snare
patternVels[offset + 6] = 100 + (Random -5, 5)
patternNotes[offset + 18] = snare
patternVels[offset + 18] = 100 + (Random -5, 5)
// Ghost snares based on variation
if vari > 0.4
// Ghost note after 2
if (Random 0, 100) < (40 * dens)
patternNotes[offset + 8] = snare
patternVels[offset + 8] = 60 + (Random -5, 5)
endif
// Ghost note before 4
if (Random 0, 100) < (30 * dens)
patternNotes[offset + 17] = snare
patternVels[offset + 17] = 65 + (Random -5, 5)
endif
endif
endfor
// Generate structured hi-hat pattern
for tick = 0 to 95
if patternNotes[tick] = 0 // Don't place hats where kicks/snares are
beat = Div tick, 6
subbeat = tick % 6
// Core 8th note pattern
if subbeat = 0 or subbeat = 3
if (Random 0, 100) < (95 * dens) // Almost always on main 8ths
patternNotes[tick] = closedHat
// Accent on main beats
if subbeat = 0
patternVels[tick] = 90 + (Random -5, 5)
else
patternVels[tick] = 80 + (Random -5, 5)
endif
endif
// 16th note additions based on density
elseif (Random 0, 100) < (60 * dens)
patternNotes[tick] = closedHat
patternVels[tick] = 70 + (Random -5, 5)
endif
// Occasional open hats based on variation
if patternNotes[tick] = closedHat and vari > 0.3
if (Random 0, 100) < (15 * vari) // Rare open hats
if beat % 2 = 1 // Prefer offbeats for open hats
patternNotes[tick] = openHat
patternVels[tick] = patternVels[tick] + 10
endif
endif
endif
endif
endfor
// Add additional percussion elements thoughtfully
for tick = 0 to 95
if patternNotes[tick] = 0 // Only where no other elements exist
beat = Div tick, 6
subbeat = tick % 6
hitProb = 0
// Higher probability on off-beats and upbeats
if subbeat = 3 // Upbeats
hitProb = 40 * dens * vari
elseif beat % 2 = 1 // Off-beats
hitProb = 35 * dens * vari
else // Other positions
hitProb = 20 * dens * vari
endif
// Add percussion hit
if (Random 0, 100) < hitProb
Call @SelectInstrument
if selectedNote > 0 // Only if we got a valid note
patternNotes[tick] = selectedNote
// Velocity based on beat position
if subbeat = 0 // Main beats
patternVels[tick] = 90 + (Random -10, 10)
elseif subbeat = 3 // Upbeats
patternVels[tick] = 85 + (Random -10, 10)
else // Other positions
patternVels[tick] = 75 + (Random -10, 10)
endif
endif
endif
endif
endfor
// Add subtle timing variations based on variation control
if vari > 0.3
for tick = 0 to 95
if patternNotes[tick] > 0
// Smaller timing variations for core elements
if patternNotes[tick] = kick or patternNotes[tick] = snare
patternDelays[tick] = Random -3, 3
else
// Larger variations for other elements
patternDelays[tick] = Random -5, 5
endif
endif
endfor
endif
shouldRegenerate = NO
@End
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment