Created
December 19, 2024 20:22
-
-
Save kmorrill/ac2e20613d45a7bea6e919f68f494eb6 to your computer and use it in GitHub Desktop.
Mozaic OP-XY Drum Generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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