Last active
November 24, 2025 22:34
-
-
Save 0gust1/98666bcde73846af69022ff26cc39e2d to your computer and use it in GitHub Desktop.
Renoise Pattrns script - ★ Melody Generator - "Exotic Scales + Renoise Scales" edition
This file contains hidden or 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
| -- ★ Melody Generator - "Exotic Scales + Renoise Scales" edition | |
| -- Similar to the `melody_synth_sparse_motif.lua` but with custom scale definitions | |
| -- | |
| -- --- | |
| -- - same motif-based composition approach as melody_synth_sparse_motif.lua | |
| -- - but with additional scales (not truly microtonal: 12-TET approximations) | |
| -- - supports all built-in Renoise scales | |
| -- | |
| -- SCALE ACCURACY CATEGORIES: | |
| -- (**Do not** trust blindly this indicator - maybe inaccurate) | |
| -- Each custom scale is prefixed with an accuracy indicator: | |
| -- • ✓ Compatible - Maps perfectly to 12-TET with no compromise | |
| -- • ~ Approximated - Minor tuning compromises (5-15¢ errors) but character preserved | |
| -- • ✗ Heavily Distorted - Significant errors (>20¢) that alter the scale's intended character | |
| -- | |
| -- AVAILABLE CUSTOM SCALES: | |
| -- | |
| -- WENDY CARLOS SCALES (12-TET APPROXIMATIONS): | |
| -- • ✗ Alpha - Carlos's first microtonal scale (~78¢/step → 100¢/step, heavily distorted) | |
| -- • ✗ Beta - More consonant variant (~63.8¢/step → varies, heavily distorted) | |
| -- | |
| -- AFRICAN SCALES: | |
| -- • ✓ Pygmy - Central African pentatonic scale used in Pygmy music | |
| -- • ✓ Mbira Dza Vadzimu - Zimbabwean shona mbira tuning (nyamaropa mode) | |
| -- | |
| -- ASIAN TRADITIONAL SCALES: | |
| -- • ✓ Hirajoshi - Japanese pentatonic with characteristic minor 2nd intervals | |
| -- • ✓ In Sen - Japanese pentatonic with suspended quality | |
| -- • ✓ Iwato - Japanese dark/mysterious, no P4/P5 (used in koto music) | |
| -- • ✓ Yo Scale - Japanese bright pentatonic (no 4th, like maj penta) | |
| -- • ✓ Kumoi - Japanese ethereal blend of minor/major (Sakura melody) | |
| -- • ✓ Pelog - Balinese gamelan 5-note with semitone clusters | |
| -- • ✓ Pelog Degung - Sundanese 7-note chromatic variant (more melodic freedom) | |
| -- | |
| -- MIDDLE EASTERN SCALES: | |
| -- • ~ Persian - Double harmonic with two augmented 2nds (exotic tension) | |
| -- | |
| -- INDIAN RAGAS: | |
| -- • ~ Marwa - Raga with augmented 4th (approximated) | |
| -- | |
| -- EUROPEAN EXOTIC: | |
| -- • ✓ Hungarian Major - Bright counterpart with augmented 2nds | |
| -- • ✓ Dorian b2 - Phrygian #6, dark but with major VI | |
| -- | |
| -- JAZZ/MODERN SCALES: | |
| -- • ✓ Bebop Dominant - Mixolydian + maj7 for chromatic approach (Parker) | |
| -- • ✓ Bebop Major - Major + #5, chromatic passing between 5-6 (I chord) | |
| -- • ✓ Bebop Dorian - Dorian + 3, chromatic pass for ii-V (Coltrane) | |
| -- • ✓ Bebop Minor - Natural minor + maj7 for i-IV progressions | |
| -- • ✓ Bebop Harmonic Minor - Harmonic minor + maj7 | |
| -- • ✓ Bebop Melodic Minor - Melodic minor + #5 | |
| -- • ✓ Mixolydian b6 - Hindu scale, Indian-influenced rock | |
| -- | |
| -- MESSIAEN MODES: | |
| -- • ✓ Messiaen Mode 3 - 9-tone mode (composed for 12-TET piano) | |
| -- • ✓ Messiaen Mode 4 - 8-tone mode with minor 2nd tetrachords | |
| -- • ✓ Messiaen Mode 5 - 6-tone mode, tritone-related hexatonic | |
| -- • ✓ Messiaen Mode 6 - 8-tone mode, whole-tone tetrachords | |
| -- • ✓ Messiaen Mode 7 - 10-tone asymmetric (composed for 12-TET piano) | |
| -- | |
| -- BLUES & FOLK: | |
| -- • ✓ Blues Heptatonic - Full 7-note blues with major tones | |
| -- | |
| -- MEDIEVAL/RENAISSANCE: | |
| -- • ~ Dorian Hexachord - 6-note medieval mode (approximated, historical tunings varied) | |
| -- | |
| -- BUILT-IN RENOISE SCALES: | |
| -- All standard Renoise scales are also available (major, minor, dorian, phrygian, etc.) | |
| -- These appear in the scale selector with the "Renoise:" prefix. | |
| -- | |
| -- HOW IT WORKS: | |
| -- Same motif-based algorithm as melody_synth_sparse_motif.lua, but: | |
| -- 1. Select from custom exotic scale definitions OR built-in Renoise scales | |
| -- 2. Each scale has unique interval patterns that create distinctive flavors | |
| -- 3. Some scales have fewer than 7 degrees, which affects melodic possibilities | |
| -- 4. Algorithm adapts to scale length automatically (pentatonic, heptatonic, octatonic, etc.) | |
| -- | |
| -- NOTE: Microtonal scales (Carlos, Bohlen-Pierce) are approximated to 12-TET semitones, | |
| -- as Renoise operates in standard MIDI pitch space. True microtonal implementation would | |
| -- require pitch bend messages. The scale() function supports max 11 intervals. | |
| -- | |
| -- CONTROLS: | |
| -- Scale Selection: | |
| -- • Scale - Choose from custom exotic or built-in Renoise scales | |
| -- • root_note - Root note (c, c#, d, etc.) | |
| -- • octave - Root octave (3-6) | |
| -- • range - Melodic range in semitones (7-36) | |
| -- | |
| -- Rhythm & Density (primary controls): | |
| -- • density - Note frequency (0.15-0.75, higher = more notes) | |
| -- • smooth_density - Smooth density transitions (boolean ; false = gated rhythm with sharp transitions, true = smooth/probabilistic) | |
| -- • rest_prob - Rest probability (0.0-0.6, higher = more silence) | |
| -- • motif_length - Motif length in 1/8 notes (4-16) | |
| -- | |
| -- Melodic Character: | |
| -- • step_motion - Stepwise vs leaps (0.2-0.9, higher = more stepwise) | |
| -- • direction - Phrase direction (0=descending, 0.5=balanced, 1=ascending) | |
| -- • variation - Variation amount (0.0-0.8, higher = more variation per repetition) | |
| -- | |
| -- Expression: | |
| -- • dynamics - Velocity range (0.1-0.7, higher = more dynamic variation) | |
| -- • accent_strength - Accent strength (0.0-0.6, higher = stronger beat accents) | |
| -- • humanize - Humanize timing (boolean, adds subtle timing variations) | |
| -- • phrase_reset_prob - Phrase reset probability (0.0-0.4, return to root for breathing) | |
| -- | |
| -- Seeds: | |
| -- • seed - Random seed (1-99999, for reproducible patterns) | |
| -- Custom scale definitions (intervals in semitones from root) | |
| -- | |
| -- SCALE ACCURACY CATEGORIES: | |
| -- ✓ Compatible: Scales that map perfectly to 12-TET with no compromise | |
| -- ~ Approximated: Minor tuning compromises (5-15¢ errors) but character preserved | |
| -- ✗ Heavily Distorted: Significant errors (>20¢) that alter the scale's intended character | |
| -- | |
| local EXOTIC_SCALES = { | |
| -- ===== WENDY CARLOS SCALES (12-TET APPROXIMATIONS) ===== | |
| -- ✗ Heavily Distorted: These microtonal scales lose significant character in 12-TET | |
| ["✗ Alpha"] = {0, 1, 3, 5, 6, 8, 10, 11}, -- ~78¢/step → 100¢/step (distorted) | |
| ["✗ Beta"] = {0, 1, 2, 4, 5, 7, 8, 10, 11}, -- ~63.8¢/step → varies (distorted) | |
| -- ===== AFRICAN SCALES ===== | |
| -- ✓ Compatible: These scales work well in 12-TET | |
| ["✓ Pygmy"] = {0, 1, 3, 6, 8}, -- Central African: pentatonic scale used in Pygmy polyphonic music | |
| ["✓ Mbira Dza Vadzimu"] = {0, 2, 3, 7, 9, 10}, -- Zimbabwean Shona: mbira tuning (nyamaropa mode, ancestral spirits music) | |
| -- ===== ASIAN TRADITIONAL SCALES ===== | |
| -- ✓ Compatible: These scales work well in 12-TET | |
| ["✓ Hirajoshi"] = {0, 2, 3, 7, 8}, -- Japanese: pentatonic with characteristic m2 intervals | |
| ["✓ In Sen"] = {0, 1, 5, 7, 10}, -- Japanese: pentatonic with suspended quality | |
| ["✓ Iwato"] = {0, 1, 5, 6, 10}, -- Japanese: dark/mysterious, no P4/P5 (used in koto music) | |
| ["✓ Yo Scale"] = {0, 2, 5, 7, 9}, -- Japanese: bright pentatonic (no 4th, like maj penta) | |
| ["✓ Kumoi"] = {0, 2, 3, 7, 9}, -- Japanese: ethereal blend of minor/major (Sakura melody) | |
| ["✓ Pelog"] = {0, 1, 3, 7, 8}, -- Balinese gamelan: 5-note with semitone clusters | |
| ["✓ Pelog Degung"] = {0, 1, 3, 6, 7, 8, 10}, -- Sundanese: 7-note chromatic variant (more melodic freedom) | |
| -- ===== MIDDLE EASTERN SCALES ===== | |
| -- ~ Approximated: Traditional tunings have microtonal nuances not captured here | |
| ["~ Persian"] = {0, 1, 4, 5, 6, 8, 11}, -- Double harmonic: two augmented 2nds (exotic tension) | |
| -- ===== INDIAN RAGAS ===== | |
| -- ~ Approximated: Traditional sruti divisions are more granular than 12-TET | |
| ["~ Marwa"] = {0, 1, 4, 6, 7, 9, 11}, -- Raga (approximated) | |
| -- ===== EUROPEAN EXOTIC ===== | |
| -- ✓ Compatible: These scales are designed for 12-TET | |
| ["✓ Hungarian Major"] = {0, 3, 4, 6, 7, 9, 10}, -- Bright counterpart: augmented 2nds with major tonality | |
| ["✓ Dorian b2"] = {0, 1, 3, 5, 7, 9, 10}, -- Phrygian #6: dark but with major VI | |
| -- ===== JAZZ/MODERN SCALES ===== | |
| -- ✓ Compatible: All designed for 12-TET equal temperament | |
| ["✓ Bebop Dominant"] = {0, 2, 4, 5, 7, 9, 10, 11}, -- Mixolydian + maj7: 8 notes for chromatic approach (Parker) | |
| ["✓ Bebop Major"] = {0, 2, 4, 5, 7, 8, 9, 11}, -- Major + #5: chromatic passing between 5-6 (I chord) | |
| ["✓ Bebop Dorian"] = {0, 2, 3, 4, 5, 7, 9, 10}, -- Dorian + 3: chromatic pass for ii-V (Coltrane) | |
| ["✓ Bebop Minor"] = {0, 2, 3, 5, 7, 9, 10, 11}, -- Natural minor + maj7: for i-IV progressions | |
| ["✓ Bebop Harmonic Minor"] = {0, 2, 3, 5, 7, 8, 10, 11}, -- Harmonic minor + maj7: chromatic approach | |
| ["✓ Bebop Melodic Minor"] = {0, 2, 3, 5, 7, 8, 9, 11}, -- Melodic minor + #5: chromatic passing | |
| ["✓ Mixolydian b6"] = {0, 2, 4, 5, 7, 8, 10}, -- Hindu scale: Indian-influenced rock/metal | |
| -- ===== MESSIAEN MODES ===== | |
| -- ✓ Compatible: Composed specifically for 12-TET piano | |
| ["✓ Messiaen Mode 3"] = {0, 2, 3, 4, 6, 7, 8, 10, 11}, -- 9-tone (compatible) | |
| ["✓ Messiaen Mode 4"] = {0, 1, 2, 5, 6, 7, 8, 11}, -- 8-tone: minor 2nd tetrachords | |
| ["✓ Messiaen Mode 5"] = {0, 1, 5, 6, 7, 11}, -- 6-tone: tritone-related hexatonic | |
| ["✓ Messiaen Mode 6"] = {0, 2, 4, 5, 6, 8, 10, 11}, -- 8-tone: whole-tone tetrachords | |
| ["✓ Messiaen Mode 7"] = {0, 1, 3, 4, 6, 7, 8, 10, 11}, -- 10-tone asymmetric (compatible) | |
| -- ===== BLUES & FOLK ===== | |
| -- ✓ Compatible: Designed for Western 12-TET instruments | |
| ["✓ Blues Heptatonic"] = {0, 2, 3, 4, 5, 7, 9}, -- 7-note blues: with major tones | |
| -- ===== MEDIEVAL/RENAISSANCE ===== | |
| -- ~ Approximated: Historical tunings varied; this is a 12-TET compromise | |
| ["~ Dorian Hexachord"] = {0, 2, 4, 5, 7, 9}, -- 6-note medieval (approximated) | |
| } | |
| -- Get scale names for parameter enum (custom + built-in) | |
| local function get_all_scale_names() | |
| local names = {} | |
| -- Add custom exotic scales with prefix | |
| for name, _ in pairs(EXOTIC_SCALES) do | |
| table.insert(names, "Custom: " .. name) | |
| end | |
| -- Add all built-in Renoise scales | |
| for _, name in ipairs(scale_names()) do | |
| table.insert(names, "Renoise: " .. name) | |
| end | |
| table.sort(names) | |
| return names | |
| end | |
| return pattern { | |
| unit = "1/8", -- Eighth notes for flexibility | |
| resolution = 1, | |
| parameter = { | |
| -- Seeds for consistency | |
| parameter.integer("seed", 42, {1, 99999}, "Random Seed"), | |
| -- Scale Selection (Custom + Built-in) | |
| parameter.enum("exotic_scale", "Custom: ✓ Hirajoshi", get_all_scale_names(), "Scale"), | |
| parameter.enum("root_note", "c", {"c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"}, "Root Note"), | |
| parameter.integer("octave", 4, {3, 6}, "Root Octave"), | |
| parameter.integer("range", 12, {7, 36}, "Melodic Range (semitones)"), | |
| -- Rhythm & Density (primary controls) | |
| parameter.number("density", 0.40, {0.15, 0.75}, "Density (note frequency)"), | |
| parameter.boolean("smooth_density", false, "Smooth Density (vs gated rhythm)"), | |
| parameter.number("rest_prob", 0.30, {0.0, 0.6}, "Rest Probability"), | |
| parameter.integer("motif_length", 8, {4, 16}, "Motif Length (in 1/8 notes)"), | |
| -- Melodic Character | |
| parameter.number("step_motion", 0.65, {0.2, 0.9}, "Step Motion (low=bigger leaps)"), | |
| parameter.number("direction", 0.5, {0.0, 1.0}, "Phrase Direction (0=down, 0.5=balanced, 1=up)"), | |
| parameter.number("variation", 0.35, {0.0, 0.8}, "Variation Amount"), | |
| -- Expression | |
| parameter.number("dynamics", 0.4, {0.1, 0.7}, "Velocity Range"), | |
| parameter.number("accent_strength", 0.25, {0.0, 0.6}, "Accent Strength"), | |
| parameter.boolean("humanize", true, "Humanize Timing"), | |
| parameter.number("phrase_reset_prob", 0.15, {0.0, 0.4}, "Phrase Reset (return to root)"), | |
| }, | |
| pulse = function(init_context) | |
| local rand = math.randomstate(init_context.parameter.seed --[[@as integer]]) | |
| return function(context) | |
| -- Read live parameter values | |
| local density = context.parameter.density --[[@as number]] | |
| local rest_prob = context.parameter.rest_prob --[[@as number]] | |
| local smooth_density = context.parameter.smooth_density --[[@as boolean]] | |
| -- Create sparse, irregular pulse with strategic rests | |
| local beat_position = context.pulse_step % 8 | |
| -- Emphasize certain beats (downbeats, phrase starts) | |
| local beat_strength = 0.5 | |
| if beat_position == 0 then | |
| beat_strength = 0.9 -- Strong downbeat | |
| elseif beat_position == 4 then | |
| beat_strength = 0.7 -- Mid-bar emphasis | |
| elseif beat_position == 2 or beat_position == 6 then | |
| beat_strength = 0.6 | |
| end | |
| -- Apply rest probability | |
| if rand() < rest_prob then | |
| return 0 | |
| end | |
| -- Combine density with beat strength | |
| local threshold = (1.0 - density) * 0.8 | |
| if smooth_density then | |
| -- Smooth probabilistic approach: gradual transition | |
| local play_probability = math.max(0, math.min(1, (beat_strength - threshold) / 0.5 + 0.5)) | |
| return rand() < play_probability and beat_strength or 0 | |
| else | |
| -- Hard threshold approach: gated rhythm with sharp transitions | |
| return beat_strength > threshold and beat_strength or 0 | |
| end | |
| end | |
| end, | |
| event = function(init_context) | |
| local rand = math.randomstate((init_context.parameter.seed --[[@as integer]]) + 1) | |
| local humanize_rand = math.randomstate((init_context.parameter.seed --[[@as integer]]) + 1000) | |
| -- State tracking (for realtime fiability) | |
| local current_degree = 1 -- Start on root | |
| local motif_notes = {} -- Store generated motif | |
| local motif_iteration = 0 -- Track which repetition we're on | |
| local last_scale_name = "" -- Track scale changes | |
| local last_root_note = "" -- Track root changes | |
| local last_motif_length = 0 -- Track motif length changes | |
| local last_octave = 0 -- Track octave changes | |
| return function(context) | |
| -- Early return for rests | |
| if context.pulse_value == 0 then | |
| return nil | |
| end | |
| -- Read live parameter values and create scale | |
| local scale_name = context.parameter.exotic_scale --[[@as string]] | |
| local root_note = context.parameter.root_note --[[@as string]] | |
| local octave = context.parameter.octave --[[@as integer]] | |
| local motif_length = context.parameter.motif_length --[[@as integer]] | |
| -- Detect scale, root, motif length, or octave change and reset motif | |
| if scale_name ~= last_scale_name or root_note ~= last_root_note or | |
| motif_length ~= last_motif_length or octave ~= last_octave then | |
| motif_notes = {} -- Clear motif | |
| motif_iteration = -1 -- Force regeneration | |
| current_degree = 1 -- Reset to root | |
| last_scale_name = scale_name | |
| last_root_note = root_note | |
| last_motif_length = motif_length | |
| last_octave = octave | |
| end | |
| local s | |
| local scale_length | |
| -- Check if it's a custom scale or built-in Renoise scale | |
| if scale_name:sub(1, 8) == "Custom: " then | |
| -- Custom scale: extract name and use interval table | |
| local custom_name = scale_name:sub(9) | |
| local scale_intervals = EXOTIC_SCALES[custom_name] | |
| local root = root_note .. octave | |
| s = scale(root, scale_intervals) | |
| scale_length = #scale_intervals | |
| else | |
| -- Built-in Renoise scale: extract name and use scale() with mode string | |
| local renoise_name = scale_name:sub(10) | |
| local root = root_note .. octave | |
| s = scale(root, renoise_name) | |
| scale_length = #s.notes | |
| end | |
| local step_motion = context.parameter.step_motion --[[@as number]] | |
| local direction_bias = context.parameter.direction --[[@as number]] | |
| local variation_amt = context.parameter.variation --[[@as number]] | |
| local range = context.parameter.range --[[@as integer]] | |
| local dynamics = context.parameter.dynamics --[[@as number]] | |
| local accent_str = context.parameter.accent_strength --[[@as number]] | |
| -- Generate weighted scale degree probabilities | |
| local function get_degree_weights(current) | |
| -- Create weights array for the actual scale length | |
| local weights = {} | |
| for i = 1, scale_length do | |
| -- Root and fifth (if exists) are strongest | |
| if i == 1 then | |
| weights[i] = 1.8 -- Root always strong | |
| elseif scale_length >= 5 and i == math.ceil(scale_length * 0.6) then | |
| weights[i] = 1.5 -- Approximate "fifth" position | |
| else | |
| weights[i] = 1.0 -- Equal weight for others | |
| end | |
| end | |
| -- Boost nearby degrees for stepwise motion | |
| if current > 1 then | |
| weights[current - 1] = weights[current - 1] * (1.0 + step_motion * 2.0) | |
| end | |
| if current < scale_length then | |
| weights[current + 1] = weights[current + 1] * (1.0 + step_motion * 2.0) | |
| end | |
| return weights | |
| end | |
| -- Select next degree based on weights and direction | |
| local function next_degree(current) | |
| local weights = get_degree_weights(current) | |
| -- Apply directional bias | |
| for i = 1, scale_length do | |
| if i > current then | |
| weights[i] = weights[i] * (0.5 + direction_bias) | |
| elseif i < current then | |
| weights[i] = weights[i] * (1.5 - direction_bias) | |
| end | |
| end | |
| -- Normalize and select | |
| local sum = 0 | |
| for i = 1, scale_length do | |
| sum = sum + weights[i] | |
| end | |
| local r = rand() * sum | |
| local cumulative = 0 | |
| for i = 1, scale_length do | |
| cumulative = cumulative + weights[i] | |
| if r <= cumulative then | |
| return i | |
| end | |
| end | |
| return current | |
| end | |
| -- Apply variation to a motif note | |
| local function vary_note(degree, variation_type) | |
| if rand() > variation_amt then | |
| return degree -- No variation | |
| end | |
| -- Different variation techniques | |
| if variation_type == "transpose" then | |
| -- Shift by a step | |
| local shift = rand() > 0.5 and 1 or -1 | |
| return math.max(1, math.min(scale_length, degree + shift)) | |
| elseif variation_type == "neighbor" then | |
| -- Neighbor tone ornament | |
| if rand() > 0.5 then | |
| return math.max(1, math.min(scale_length, degree + (rand() > 0.5 and 1 or -1))) | |
| end | |
| end | |
| return degree | |
| end | |
| local step_in_motif = context.pulse_step % motif_length | |
| local current_iteration = math.floor(context.pulse_step / motif_length) | |
| -- Generate new motif if we're starting fresh or iteration changed | |
| if current_iteration ~= motif_iteration then | |
| motif_iteration = current_iteration | |
| -- Decide on variation strategy for this iteration | |
| local var_type = ({"none", "transpose", "neighbor"})[math.floor(rand() * 3) + 1] | |
| -- Apply variations to existing motif or reset | |
| if motif_iteration > 0 and #motif_notes > 0 and rand() < variation_amt then | |
| -- Create variation of existing motif (make a copy) | |
| local new_motif = {} | |
| for i = 1, #motif_notes do | |
| if motif_notes[i] ~= nil then | |
| new_motif[i] = vary_note(motif_notes[i], var_type) | |
| end | |
| end | |
| motif_notes = new_motif | |
| end | |
| end | |
| -- Generate or retrieve motif note | |
| if motif_notes[step_in_motif + 1] == nil then | |
| -- Generate new note for this position | |
| local new_degree | |
| -- Check for phrase reset (return to root for breathing/cadence) | |
| local reset_prob = context.parameter.phrase_reset_prob --[[@as number]] | |
| if rand() < reset_prob then | |
| new_degree = 1 -- Reset to root | |
| -- Resolve to root at phrase end for natural cadence | |
| elseif step_in_motif == motif_length - 1 and rand() > 0.3 then | |
| new_degree = 1 -- Root | |
| else | |
| new_degree = next_degree(current_degree) | |
| end | |
| motif_notes[step_in_motif + 1] = new_degree | |
| current_degree = new_degree | |
| else | |
| -- Use stored motif note | |
| current_degree = motif_notes[step_in_motif + 1] | |
| end | |
| -- Create note from scale degree | |
| -- For custom scales, access notes directly from the scale.notes array | |
| -- since s:degree() only works with traditional 7-note scales | |
| local note_value = s.notes[current_degree] | |
| local n = note(note_value) | |
| -- Octave management - keep within specified range | |
| -- Calculate how many octaves upward we can shift (0 to max) | |
| local max_octave_shift = math.floor((range - 1) / 12) | |
| -- Apply a random octave variation within the allowed range | |
| if max_octave_shift >= 1 then | |
| local octave_shift = math.floor(rand() * (max_octave_shift + 1)) | |
| if octave_shift ~= 0 then | |
| n = n:transpose({octave_shift * 12}) | |
| end | |
| end | |
| -- Apply dynamics | |
| local base_velocity = 0.5 + (rand() - 0.5) * dynamics | |
| -- Add accents on certain beats | |
| local beat_pos = context.pulse_step % 8 | |
| if beat_pos == 0 or beat_pos == 4 then | |
| base_velocity = base_velocity + accent_str | |
| end | |
| base_velocity = math.max(0.3, math.min(1.0, base_velocity)) | |
| -- Humanize timing if enabled (applies before volume) | |
| if context.parameter.humanize --[[@as boolean]] then | |
| -- Small random timing variation (0-10% delay for natural feel) | |
| local timing_offset = humanize_rand() * 0.1 | |
| n = n:delay(timing_offset) | |
| end | |
| -- Apply volume | |
| n = n:volume(base_velocity) | |
| return n | |
| end | |
| end | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment