Created
September 1, 2026 01:23
-
-
Save R3V1Z3/8577c930d3317e4c3383d421f17d4573 to your computer and use it in GitHub Desktop.
SNES demo loop for Eatsbeats https://eatsbeats.app/?script=gist+8577c930d3317e4c3383d421f17d4573
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
| -- Eatsbeats Song File: "Eats SNES" | |
| -- Generated by Eatsbeats DAW | |
| return eatsbeats.song { | |
| version = "1.0", | |
| meta = { | |
| title = "Eats SNES", | |
| author = "Eatsbits", | |
| songKey = "C Major", | |
| bpm = 125.00, | |
| masterVolume = 0.85, | |
| isSongMode = false, | |
| isLooping = true, | |
| loopStartBar = 0, | |
| loopEndBar = 4, | |
| }, | |
| patterns = { | |
| { | |
| id = "p0", | |
| name = "Pattern A", | |
| lengthSteps = 16, | |
| tracks = { | |
| { | |
| id = "track_1787246564489", | |
| name = "Eats SFXR Generator", | |
| color = 0xffff3333, | |
| type = "luaScript", | |
| volume = 1.000, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "off", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- @name: SNES Sfxr | |
| -- @category: instrument | |
| local SNESSFX = {} | |
| function SNESSFX.init() | |
| Param.choice("SFXType", {"Laser", "Explosion", "Powerup", "Coin", "Jump", "Hurt", "Lose", "Button", "Warp", "Mutate", "Custom SNES"}, 0.0) | |
| Param.add("Seed", 1.0, 9999.0, 42.0, 1.0) | |
| Param.choice("Waveform", {"Sine", "Square", "Pulse 25%", "Pulse 12%", "Sawtooth", "Triangle", "Organ", "Strings", "Flute", "Slap Bass", "Chime", "Noise"}, 1.0) | |
| Param.add("Attack", 0.001, 0.5, 0.005) | |
| Param.add("Decay", 0.01, 2.0, 0.35) | |
| Param.add("Sustain", 0.0, 1.0, 0.2) | |
| Param.add("Release", 0.01, 2.0, 0.2) | |
| Param.add("PitchSweep", -2.0, 2.0, -0.45) | |
| Param.add("VibratoRate", 0.0, 20.0, 0.0) | |
| Param.add("VibratoDepth", 0.0, 1.0, 0.0) | |
| Param.add("EchoDelay", 16.0, 480.0, 120.0, 16.0) | |
| Param.add("EchoFeedback", 0.0, 0.95, 0.45) | |
| Param.add("EchoVolume", 0.0, 1.0, 0.35) | |
| Param.add("NoiseMix", 0.0, 1.0, 0.0) | |
| end | |
| function SNESSFX.oscillator(phase, waveIdx, noiseMix) | |
| local normPos = (phase / (2.0 * math.pi)) % 1.0 | |
| local w = math.floor(waveIdx or 1) | |
| local osc = 0.0 | |
| if w == 0 then | |
| osc = math.sin(phase) | |
| elseif w == 1 then | |
| osc = normPos < 0.5 and 1.0 or -1.0 | |
| elseif w == 2 then | |
| osc = normPos < 0.25 and 1.0 or -1.0 | |
| elseif w == 3 then | |
| osc = normPos < 0.125 and 1.0 or -1.0 | |
| elseif w == 4 then | |
| osc = (2.0 * normPos - 1.0) * 0.9 | |
| elseif w == 5 then | |
| osc = (4.0 * math.abs(normPos - 0.5) - 1.0) | |
| elseif w == 6 then | |
| osc = (math.sin(phase) + math.sin(phase * 2.0) * 0.5 + math.sin(phase * 4.0) * 0.25) * 0.57 | |
| elseif w == 7 then | |
| osc = (2.0 * normPos - 1.0) * 0.6 + (2.0 * ((normPos * 2.0) % 1.0) - 1.0) * 0.4 | |
| elseif w == 8 then | |
| osc = math.sin(phase) * 0.85 + math.sin(phase * 3.0) * 0.15 | |
| elseif w == 9 then | |
| osc = (math.sin(phase) + math.sin(phase * 3.0) * 0.4) * 0.75 | |
| elseif w == 10 then | |
| osc = (math.sin(phase) + math.sin(phase * 2.76) * 0.4 + math.sin(phase * 5.4) * 0.25) * 0.6 | |
| else | |
| osc = (math.random() * 2.0 - 1.0) * 0.8 | |
| end | |
| if (noiseMix or 0.0) > 0.01 then | |
| local n = (math.random() * 2.0 - 1.0) * noiseMix | |
| osc = osc * (1.0 - noiseMix) + n | |
| end | |
| return osc | |
| end | |
| function SNESSFX.envelope(time, a, d, s) | |
| local att = math.max(0.001, a or 0.005) | |
| local dec = math.max(0.01, d or 0.35) | |
| local sus = math.max(0.0, math.min(1.0, s or 0.2)) | |
| if time < att then | |
| return time / att | |
| else | |
| return math.max(0.0, sus + (1.0 - sus) * math.exp(-(time - att) * 4.0 / dec)) | |
| end | |
| end | |
| function SNESSFX.echo(signal, time, delayMs, feedback, vol) | |
| local dSec = math.max(0.016, (delayMs or 120.0) / 1000.0) | |
| local fb = math.max(0.0, math.min(0.95, feedback or 0.45)) | |
| local ev = math.max(0.0, math.min(1.0, vol or 0.35)) | |
| local echoSignal = 0.0 | |
| if time > dSec then | |
| local damp = math.exp(-(time - dSec) * 3.0) * fb | |
| echoSignal = signal * damp * 0.5 | |
| end | |
| return signal + echoSignal * ev | |
| end | |
| function SNESSFX.process(time, freq, note, params) | |
| local wIdx = params["Waveform"] or 1.0 | |
| local sweep = params["PitchSweep"] or -0.45 | |
| local a = params["Attack"] or 0.005 | |
| local d = params["Decay"] or 0.35 | |
| local s = params["Sustain"] or 0.2 | |
| local nMix = params["NoiseMix"] or 0.0 | |
| local eDelay = params["EchoDelay"] or 120.0 | |
| local eFdbk = params["EchoFeedback"] or 0.45 | |
| local eVol = params["EchoVolume"] or 0.35 | |
| -- Pitch sweep | |
| local sweepFreq = freq * math.max(0.05, 1.0 + sweep * math.min(1.5, time * 8.0)) | |
| local phase = 2.0 * math.pi * sweepFreq * time | |
| local rawVoice = SNESSFX.oscillator(phase, wIdx, nMix) | |
| local env = SNESSFX.envelope(time, a, d, s) | |
| local dryOut = rawVoice * env | |
| return math.tanh(SNESSFX.echo(dryOut, time, eDelay, eFdbk, eVol) * 1.3) | |
| end | |
| function SNESSFX.gui() | |
| return { | |
| panel = { | |
| title = "SNES Sfxr", | |
| subtitle = "16-Bit Procedural Sound Engine", | |
| background = "snes", | |
| accent = "#7B52AB", | |
| knobStyle = "snes", | |
| layout = { | |
| { | |
| type = "row", | |
| children = { | |
| { type = "listbox", param = "SFXType", label = "SFX Type", width = 160, height = 90 }, | |
| { type = "listbox", param = "Waveform", label = "Wavetable", width = 160, height = 90 }, | |
| { | |
| type = "column", | |
| children = { | |
| { type = "nixie", param = "Seed", label = "RNG SEED", width = 100 }, | |
| { type = "button", action = "randomize", label = "RANDOMIZE", width = 100, height = 32 } | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| type = "row", | |
| children = { | |
| { type = "knob", param = "Attack", label = "ATTACK", size = 48 }, | |
| { type = "knob", param = "Decay", label = "DECAY", size = 48 }, | |
| { type = "knob", param = "Sustain", label = "SUSTAIN", size = 48 }, | |
| { type = "knob", param = "Release", label = "RELEASE", size = 48 }, | |
| { type = "knob", param = "PitchSweep", label = "SWEEP", size = 48 }, | |
| { type = "knob", param = "VibratoRate", label = "VIB RATE", size = 48 }, | |
| { type = "knob", param = "VibratoDepth", label = "VIB DEPTH", size = 48 }, | |
| { type = "knob", param = "EchoDelay", label = "ECHO MS", size = 48 }, | |
| { type = "knob", param = "EchoVolume", label = "ECHO VOL", size = 48 }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| function SNESSFX.rack() | |
| return { | |
| rows = { | |
| { | |
| { id = "sfx_vco", title = "SFX GENERATOR VCO", hp = 16, row = 1, category = "VCO" }, | |
| { id = "sweep_env", title = "PITCH SWEEP ENV", hp = 14, row = 1, category = "MOD" }, | |
| }, | |
| { | |
| { id = "echo_fir", title = "8-TAP FIR ECHO", hp = 16, row = 2, category = "FX" }, | |
| { id = "master", title = "SFXR MASTER OUT", hp = 14, row = 2, category = "OUT" }, | |
| }, | |
| }, | |
| cables = { | |
| { from = "1:1:1", to = "1:0:0", color = "pitch" }, | |
| { from = "1:0:1", to = "2:0:0", color = "audio" }, | |
| { from = "2:0:1", to = "2:1:0", color = "audio" }, | |
| } | |
| } | |
| end | |
| return SNESSFX | |
| ]], | |
| luaParams = { | |
| ["SFXType"] = 4.0000, | |
| ["Seed"] = 6267.0000, | |
| ["Waveform"] = 1.0000, | |
| ["Attack"] = 0.0020, | |
| ["Decay"] = 0.1751, | |
| ["Sustain"] = 0.0000, | |
| ["Release"] = 0.0200, | |
| ["PitchSweep"] = 1.2180, | |
| ["VibratoRate"] = 0.0000, | |
| ["VibratoDepth"] = 0.0000, | |
| ["EchoDelay"] = 120.0000, | |
| ["EchoFeedback"] = 0.4500, | |
| ["EchoVolume"] = 0.0000, | |
| ["NoiseMix"] = 0.0000, | |
| }, | |
| notes = { | |
| { id = "1787246575927", pitch = 42, startStep = 2.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246602726", pitch = 24, startStep = 0.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246606780", pitch = 24, startStep = 4.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246607752", pitch = 24, startStep = 8.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246609128", pitch = 24, startStep = 12.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246622448", pitch = 42, startStep = 14.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "clip_track_1787246564489_0", | |
| name = "Drum", | |
| trackId = "track_1787246564489", | |
| startBar = 0, | |
| barLength = 4, | |
| loopLengthBars = 1, | |
| notes = { | |
| { id = "1787246575927", pitch = 42, startStep = 2.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246602726", pitch = 24, startStep = 0.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246606780", pitch = 24, startStep = 4.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246607752", pitch = 24, startStep = 8.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246609128", pitch = 24, startStep = 12.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246622448", pitch = 42, startStep = 14.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| id = "t_1787951348057", | |
| name = "Eats SFXR Snare", | |
| color = 0xff00ff66, | |
| type = "luaScript", | |
| volume = 1.000, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "off", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- @name: SNES Sfxr | |
| -- @category: instrument | |
| local SNESSFX = {} | |
| function SNESSFX.init() | |
| Param.choice("SFXType", {"Laser", "Explosion", "Powerup", "Coin", "Jump", "Hurt", "Lose", "Button", "Warp", "Mutate", "Custom SNES"}, 0.0) | |
| Param.add("Seed", 1.0, 9999.0, 42.0, 1.0) | |
| Param.choice("Waveform", {"Sine", "Square", "Pulse 25%", "Pulse 12%", "Sawtooth", "Triangle", "Organ", "Strings", "Flute", "Slap Bass", "Chime", "Noise"}, 1.0) | |
| Param.add("Attack", 0.001, 0.5, 0.005) | |
| Param.add("Decay", 0.01, 2.0, 0.35) | |
| Param.add("Sustain", 0.0, 1.0, 0.2) | |
| Param.add("Release", 0.01, 2.0, 0.2) | |
| Param.add("PitchSweep", -2.0, 2.0, -0.45) | |
| Param.add("VibratoRate", 0.0, 20.0, 0.0) | |
| Param.add("VibratoDepth", 0.0, 1.0, 0.0) | |
| Param.add("EchoDelay", 16.0, 480.0, 120.0, 16.0) | |
| Param.add("EchoFeedback", 0.0, 0.95, 0.45) | |
| Param.add("EchoVolume", 0.0, 1.0, 0.35) | |
| Param.add("NoiseMix", 0.0, 1.0, 0.0) | |
| end | |
| function SNESSFX.oscillator(phase, waveIdx, noiseMix) | |
| local normPos = (phase / (2.0 * math.pi)) % 1.0 | |
| local w = math.floor(waveIdx or 1) | |
| local osc = 0.0 | |
| if w == 0 then | |
| osc = math.sin(phase) | |
| elseif w == 1 then | |
| osc = normPos < 0.5 and 1.0 or -1.0 | |
| elseif w == 2 then | |
| osc = normPos < 0.25 and 1.0 or -1.0 | |
| elseif w == 3 then | |
| osc = normPos < 0.125 and 1.0 or -1.0 | |
| elseif w == 4 then | |
| osc = (2.0 * normPos - 1.0) * 0.9 | |
| elseif w == 5 then | |
| osc = (4.0 * math.abs(normPos - 0.5) - 1.0) | |
| elseif w == 6 then | |
| osc = (math.sin(phase) + math.sin(phase * 2.0) * 0.5 + math.sin(phase * 4.0) * 0.25) * 0.57 | |
| elseif w == 7 then | |
| osc = (2.0 * normPos - 1.0) * 0.6 + (2.0 * ((normPos * 2.0) % 1.0) - 1.0) * 0.4 | |
| elseif w == 8 then | |
| osc = math.sin(phase) * 0.85 + math.sin(phase * 3.0) * 0.15 | |
| elseif w == 9 then | |
| osc = (math.sin(phase) + math.sin(phase * 3.0) * 0.4) * 0.75 | |
| elseif w == 10 then | |
| osc = (math.sin(phase) + math.sin(phase * 2.76) * 0.4 + math.sin(phase * 5.4) * 0.25) * 0.6 | |
| else | |
| osc = (math.random() * 2.0 - 1.0) * 0.8 | |
| end | |
| if (noiseMix or 0.0) > 0.01 then | |
| local n = (math.random() * 2.0 - 1.0) * noiseMix | |
| osc = osc * (1.0 - noiseMix) + n | |
| end | |
| return osc | |
| end | |
| function SNESSFX.envelope(time, a, d, s) | |
| local att = math.max(0.001, a or 0.005) | |
| local dec = math.max(0.01, d or 0.35) | |
| local sus = math.max(0.0, math.min(1.0, s or 0.2)) | |
| if time < att then | |
| return time / att | |
| else | |
| return math.max(0.0, sus + (1.0 - sus) * math.exp(-(time - att) * 4.0 / dec)) | |
| end | |
| end | |
| function SNESSFX.echo(signal, time, delayMs, feedback, vol) | |
| local dSec = math.max(0.016, (delayMs or 120.0) / 1000.0) | |
| local fb = math.max(0.0, math.min(0.95, feedback or 0.45)) | |
| local ev = math.max(0.0, math.min(1.0, vol or 0.35)) | |
| local echoSignal = 0.0 | |
| if time > dSec then | |
| local damp = math.exp(-(time - dSec) * 3.0) * fb | |
| echoSignal = signal * damp * 0.5 | |
| end | |
| return signal + echoSignal * ev | |
| end | |
| function SNESSFX.process(time, freq, note, params) | |
| local wIdx = params["Waveform"] or 1.0 | |
| local sweep = params["PitchSweep"] or -0.45 | |
| local a = params["Attack"] or 0.005 | |
| local d = params["Decay"] or 0.35 | |
| local s = params["Sustain"] or 0.2 | |
| local nMix = params["NoiseMix"] or 0.0 | |
| local eDelay = params["EchoDelay"] or 120.0 | |
| local eFdbk = params["EchoFeedback"] or 0.45 | |
| local eVol = params["EchoVolume"] or 0.35 | |
| -- Pitch sweep | |
| local sweepFreq = freq * math.max(0.05, 1.0 + sweep * math.min(1.5, time * 8.0)) | |
| local phase = 2.0 * math.pi * sweepFreq * time | |
| local rawVoice = SNESSFX.oscillator(phase, wIdx, nMix) | |
| local env = SNESSFX.envelope(time, a, d, s) | |
| local dryOut = rawVoice * env | |
| return math.tanh(SNESSFX.echo(dryOut, time, eDelay, eFdbk, eVol) * 1.3) | |
| end | |
| function SNESSFX.gui() | |
| return { | |
| panel = { | |
| title = "SNES Sfxr", | |
| subtitle = "16-Bit Procedural Sound Engine", | |
| background = "snes", | |
| accent = "#7B52AB", | |
| knobStyle = "snes", | |
| layout = { | |
| { | |
| type = "row", | |
| children = { | |
| { type = "listbox", param = "SFXType", label = "SFX Type", width = 160, height = 90 }, | |
| { type = "listbox", param = "Waveform", label = "Wavetable", width = 160, height = 90 }, | |
| { | |
| type = "column", | |
| children = { | |
| { type = "nixie", param = "Seed", label = "RNG SEED", width = 100 }, | |
| { type = "button", action = "randomize", label = "RANDOMIZE", width = 100, height = 32 } | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| type = "row", | |
| children = { | |
| { type = "knob", param = "Attack", label = "ATTACK", size = 48 }, | |
| { type = "knob", param = "Decay", label = "DECAY", size = 48 }, | |
| { type = "knob", param = "Sustain", label = "SUSTAIN", size = 48 }, | |
| { type = "knob", param = "Release", label = "RELEASE", size = 48 }, | |
| { type = "knob", param = "PitchSweep", label = "SWEEP", size = 48 }, | |
| { type = "knob", param = "VibratoRate", label = "VIB RATE", size = 48 }, | |
| { type = "knob", param = "VibratoDepth", label = "VIB DEPTH", size = 48 }, | |
| { type = "knob", param = "EchoDelay", label = "ECHO MS", size = 48 }, | |
| { type = "knob", param = "EchoVolume", label = "ECHO VOL", size = 48 }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| function SNESSFX.rack() | |
| return { | |
| rows = { | |
| { | |
| { id = "sfx_vco", title = "SFX GENERATOR VCO", hp = 16, row = 1, category = "VCO" }, | |
| { id = "sweep_env", title = "PITCH SWEEP ENV", hp = 14, row = 1, category = "MOD" }, | |
| }, | |
| { | |
| { id = "echo_fir", title = "8-TAP FIR ECHO", hp = 16, row = 2, category = "FX" }, | |
| { id = "master", title = "SFXR MASTER OUT", hp = 14, row = 2, category = "OUT" }, | |
| }, | |
| }, | |
| cables = { | |
| { from = "1:1:1", to = "1:0:0", color = "pitch" }, | |
| { from = "1:0:1", to = "2:0:0", color = "audio" }, | |
| { from = "2:0:1", to = "2:1:0", color = "audio" }, | |
| } | |
| } | |
| end | |
| return SNESSFX | |
| ]], | |
| luaParams = { | |
| ["SFXType"] = 1.0000, | |
| ["Seed"] = 994.0000, | |
| ["Waveform"] = 5.0000, | |
| ["Attack"] = 0.0020, | |
| ["Decay"] = 0.6129, | |
| ["Sustain"] = 0.0000, | |
| ["Release"] = 0.1500, | |
| ["PitchSweep"] = -0.5287, | |
| ["VibratoRate"] = 0.0000, | |
| ["VibratoDepth"] = 0.0000, | |
| ["EchoDelay"] = 160.0000, | |
| ["EchoFeedback"] = 0.5500, | |
| ["EchoVolume"] = 0.4500, | |
| ["NoiseMix"] = 0.8159, | |
| ["SweepSpeed"] = 0.1600, | |
| ["ArpSpeed"] = 0.0500, | |
| }, | |
| notes = { | |
| { id = "1787951380831", pitch = 36, startStep = 4.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787951382317", pitch = 36, startStep = 12.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "clip_t_1787951348057_0", | |
| name = "Snare", | |
| trackId = "t_1787951348057", | |
| startBar = 0, | |
| barLength = 4, | |
| loopLengthBars = 1, | |
| notes = { | |
| { id = "1787951380831", pitch = 36, startStep = 4.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787951382317", pitch = 36, startStep = 12.00, durationSteps = 1.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| id = "track_1787246661243", | |
| name = "SNES Synth", | |
| color = 0xff3399ff, | |
| type = "luaScript", | |
| volume = 0.566, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "chord", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- @name: SNES Synth | |
| -- @category: instrument | |
| local SNESConsole = {} | |
| function SNESConsole.init() | |
| Param.choice("Waveform", {"Square", "Pulse 25%", "Pulse 12%", "Sawtooth", "Triangle", "Sine", "Organ", "Strings", "Flute", "Slap Bass", "Chime", "Noise"}, 0.0) | |
| Param.add("Attack", 0.001, 0.5, 0.005) | |
| Param.add("Decay", 0.01, 2.0, 0.3) | |
| Param.add("Sustain", 0.0, 1.0, 0.5) | |
| Param.add("Release", 0.01, 2.0, 0.25) | |
| Param.add("VibratoRate", 0.0, 20.0, 5.5) | |
| Param.add("VibratoDepth", 0.0, 1.0, 0.1) | |
| Param.add("EchoDelay", 16.0, 480.0, 140.0, 16.0) | |
| Param.add("EchoFeedback", 0.0, 0.9, 0.55) | |
| Param.add("EchoVolume", 0.0, 1.0, 0.4) | |
| end | |
| function SNESConsole.wavetable(phase, waveIdx) | |
| local normPos = (phase / (2.0 * math.pi)) % 1.0 | |
| local w = math.floor(waveIdx or 0) | |
| if w == 0 then | |
| local s = normPos < 0.5 and 1.0 or -1.0 | |
| return s * 0.9 | |
| elseif w == 1 then | |
| local s = normPos < 0.25 and 1.0 or -1.0 | |
| return s * 0.85 | |
| elseif w == 2 then | |
| local s = normPos < 0.125 and 1.0 or -1.0 | |
| return s * 0.85 | |
| elseif w == 3 then | |
| return (2.0 * normPos - 1.0) * 0.85 | |
| elseif w == 4 then | |
| local t = 4.0 * math.abs(normPos - 0.5) - 1.0 | |
| return t * 0.95 | |
| elseif w == 5 then | |
| return math.sin(phase) | |
| elseif w == 6 then | |
| local s1 = math.sin(phase) | |
| local s2 = math.sin(phase * 2.0) * 0.5 | |
| local s4 = math.sin(phase * 4.0) * 0.25 | |
| return (s1 + s2 + s4) * 0.57 | |
| elseif w == 7 then | |
| local saw1 = 2.0 * normPos - 1.0 | |
| local saw2 = 2.0 * ((normPos * 2.0) % 1.0) - 1.0 | |
| return (saw1 * 0.6 + saw2 * 0.4) * 0.9 | |
| elseif w == 8 then | |
| return math.sin(phase) * 0.85 + math.sin(phase * 3.0) * 0.15 | |
| elseif w == 9 then | |
| local b1 = math.sin(phase) | |
| local b2 = math.sin(phase * 3.0) * 0.4 | |
| return (b1 + b2) * 0.75 | |
| elseif w == 10 then | |
| local c1 = math.sin(phase) | |
| local c2 = math.sin(phase * 2.76) * 0.4 | |
| local c3 = math.sin(phase * 5.4) * 0.25 | |
| return (c1 + c2 + c3) * 0.6 | |
| else | |
| return (math.random() * 2.0 - 1.0) * 0.7 | |
| end | |
| end | |
| function SNESConsole.adsr(time, a, d, s, r) | |
| local att = math.max(0.001, a or 0.005) | |
| local dec = math.max(0.01, d or 0.3) | |
| local sus = math.max(0.0, math.min(1.0, s or 0.5)) | |
| if time < att then | |
| return time / att | |
| else | |
| local tDec = time - att | |
| return sus + (1.0 - sus) * math.exp(-tDec * 3.5 / dec) | |
| end | |
| end | |
| function SNESConsole.echo(signal, time, delayMs, feedback, vol) | |
| local dSec = math.max(0.016, (delayMs or 140.0) / 1000.0) | |
| local fb = math.max(0.0, math.min(0.95, feedback or 0.55)) | |
| local ev = math.max(0.0, math.min(1.0, vol or 0.4)) | |
| local echoSignal = 0.0 | |
| if time > dSec then | |
| local taps = { 0.34, 0.45, -0.12, 0.10 } | |
| for i = 1, 4 do | |
| local tTap = time - dSec * i | |
| if tTap > 0 then | |
| local damp = math.exp(-tTap * 2.5) * (fb ^ i) | |
| echoSignal = echoSignal + signal * damp * taps[i] | |
| end | |
| end | |
| end | |
| return signal + echoSignal * ev | |
| end | |
| function SNESConsole.process(time, freq, note, params) | |
| local wIdx = params["Waveform"] or 0.0 | |
| local a = params["Attack"] or 0.005 | |
| local d = params["Decay"] or 0.3 | |
| local s = params["Sustain"] or 0.5 | |
| local r = params["Release"] or 0.25 | |
| local vRate = params["VibratoRate"] or 5.5 | |
| local vDepth = params["VibratoDepth"] or 0.1 | |
| local eDelay = params["EchoDelay"] or 140.0 | |
| local eFdbk = params["EchoFeedback"] or 0.55 | |
| local eVol = params["EchoVolume"] or 0.4 | |
| -- 1. Vibrato Pitch Modulation | |
| local vibOffset = 0.0 | |
| if vDepth > 0.001 and vRate > 0.1 then | |
| vibOffset = math.sin(2.0 * math.pi * vRate * time) * (vDepth * 0.06) | |
| end | |
| local curFreq = freq * (1.0 + vibOffset) | |
| -- 2. Wavetable Synthesis with S-DSP Gaussian curve | |
| local phase = 2.0 * math.pi * curFreq * time | |
| local rawVoice = SNESConsole.wavetable(phase, wIdx) | |
| -- 3. ADSR / S-DSP Gain Envelope | |
| local env = SNESConsole.adsr(time, a, d, s, r) | |
| local dryOut = rawVoice * env | |
| -- 4. 8-Tap FIR Echo Reverb Emulation | |
| return math.tanh(SNESConsole.echo(dryOut, time, eDelay, eFdbk, eVol) * 1.2) | |
| end | |
| function SNESConsole.gui() | |
| return { | |
| panel = { | |
| title = "SNES Synth", | |
| subtitle = "16-Bit Polyphonic S-DSP Console Synthesizer", | |
| background = "snes", | |
| accent = "#7B52AB", | |
| knobStyle = "snes", | |
| layout = { | |
| { | |
| type = "row", | |
| children = { | |
| { type = "listbox", param = "Waveform", label = "Wavetable", width = 180, height = 90 }, | |
| { type = "knob", param = "Attack", label = "ATTACK", size = 48 }, | |
| { type = "knob", param = "Decay", label = "DECAY", size = 48 }, | |
| { type = "knob", param = "Sustain", label = "SUSTAIN", size = 48 }, | |
| { type = "knob", param = "Release", label = "RELEASE", size = 48 }, | |
| } | |
| }, | |
| { | |
| type = "row", | |
| children = { | |
| { type = "knob", param = "VibratoRate", label = "VIB RATE", size = 48 }, | |
| { type = "knob", param = "VibratoDepth", label = "VIB DEPTH", size = 48 }, | |
| { type = "knob", param = "EchoDelay", label = "ECHO MS", size = 48 }, | |
| { type = "knob", param = "EchoFeedback", label = "ECHO FDBK", size = 48 }, | |
| { type = "knob", param = "EchoVolume", label = "ECHO VOL", size = 48 }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| function SNESConsole.rack() | |
| return { | |
| rows = { | |
| -- ROW 1: Wavetable Sound Engine & ADSR | |
| { | |
| { id = "brr_vco", title = "BRR WAVETABLE VCO", hp = 16, row = 1, category = "VCO" }, | |
| { id = "snes_adsr", title = "ADSR / GAIN ENV", hp = 14, row = 1, category = "MOD" }, | |
| }, | |
| -- ROW 2: S-DSP Echo Processor & Output | |
| { | |
| { id = "echo", title = "8-TAP FIR ECHO", hp = 16, row = 2, category = "FX" }, | |
| { id = "master", title = "S-DSP MASTER OUT", hp = 14, row = 2, category = "OUT" }, | |
| }, | |
| }, | |
| cables = { | |
| { from = "1:0:2", to = "2:0:0", color = "audio" }, | |
| { from = "1:1:1", to = "2:0:1", color = "modulation" }, | |
| { from = "2:0:2", to = "2:1:0", color = "audio" }, | |
| } | |
| } | |
| end | |
| return SNESConsole | |
| ]], | |
| luaParams = { | |
| ["Waveform"] = 2.0000, | |
| ["Attack"] = 0.0050, | |
| ["Decay"] = 0.3000, | |
| ["Sustain"] = 0.5000, | |
| ["Release"] = 0.2500, | |
| ["VibratoRate"] = 5.5000, | |
| ["VibratoDepth"] = 0.1000, | |
| ["EchoDelay"] = 140.0000, | |
| ["EchoFeedback"] = 0.5500, | |
| ["EchoVolume"] = 0.4000, | |
| }, | |
| notes = { | |
| { id = "1787246833211", pitch = 48, startStep = 0.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246905089", pitch = 52, startStep = 0.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246915977", pitch = 57, startStep = 0.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247094431000_0", pitch = 48, startStep = 8.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247094431000_1", pitch = 52, startStep = 16.00, durationSteps = 16.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247094431000_2", pitch = 57, startStep = 16.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "clip_track_1787246661243_0", | |
| name = "SNES Synth", | |
| trackId = "track_1787246661243", | |
| startBar = 0, | |
| barLength = 4, | |
| loopLengthBars = 2, | |
| notes = { | |
| { id = "1787246833211", pitch = 48, startStep = 0.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246905089", pitch = 52, startStep = 0.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246915977", pitch = 57, startStep = 0.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247094431000_0", pitch = 48, startStep = 8.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247094431000_1", pitch = 52, startStep = 16.00, durationSteps = 16.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247094431000_2", pitch = 57, startStep = 16.00, durationSteps = 8.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| id = "track_1787246684146", | |
| name = "SNES Bass", | |
| color = 0xffffe600, | |
| type = "luaScript", | |
| volume = 0.837, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "bass", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- @name: SNES Synth | |
| -- @category: instrument | |
| local SNESConsole = {} | |
| function SNESConsole.init() | |
| Param.choice("Waveform", {"Square", "Pulse 25%", "Pulse 12%", "Sawtooth", "Triangle", "Sine", "Organ", "Strings", "Flute", "Slap Bass", "Chime", "Noise"}, 0.0) | |
| Param.add("Attack", 0.001, 0.5, 0.005) | |
| Param.add("Decay", 0.01, 2.0, 0.3) | |
| Param.add("Sustain", 0.0, 1.0, 0.5) | |
| Param.add("Release", 0.01, 2.0, 0.25) | |
| Param.add("VibratoRate", 0.0, 20.0, 5.5) | |
| Param.add("VibratoDepth", 0.0, 1.0, 0.1) | |
| Param.add("EchoDelay", 16.0, 480.0, 140.0, 16.0) | |
| Param.add("EchoFeedback", 0.0, 0.9, 0.55) | |
| Param.add("EchoVolume", 0.0, 1.0, 0.4) | |
| end | |
| function SNESConsole.wavetable(phase, waveIdx) | |
| local normPos = (phase / (2.0 * math.pi)) % 1.0 | |
| local w = math.floor(waveIdx or 0) | |
| if w == 0 then | |
| local s = normPos < 0.5 and 1.0 or -1.0 | |
| return s * 0.9 | |
| elseif w == 1 then | |
| local s = normPos < 0.25 and 1.0 or -1.0 | |
| return s * 0.85 | |
| elseif w == 2 then | |
| local s = normPos < 0.125 and 1.0 or -1.0 | |
| return s * 0.85 | |
| elseif w == 3 then | |
| return (2.0 * normPos - 1.0) * 0.85 | |
| elseif w == 4 then | |
| local t = 4.0 * math.abs(normPos - 0.5) - 1.0 | |
| return t * 0.95 | |
| elseif w == 5 then | |
| return math.sin(phase) | |
| elseif w == 6 then | |
| local s1 = math.sin(phase) | |
| local s2 = math.sin(phase * 2.0) * 0.5 | |
| local s4 = math.sin(phase * 4.0) * 0.25 | |
| return (s1 + s2 + s4) * 0.57 | |
| elseif w == 7 then | |
| local saw1 = 2.0 * normPos - 1.0 | |
| local saw2 = 2.0 * ((normPos * 2.0) % 1.0) - 1.0 | |
| return (saw1 * 0.6 + saw2 * 0.4) * 0.9 | |
| elseif w == 8 then | |
| return math.sin(phase) * 0.85 + math.sin(phase * 3.0) * 0.15 | |
| elseif w == 9 then | |
| local b1 = math.sin(phase) | |
| local b2 = math.sin(phase * 3.0) * 0.4 | |
| return (b1 + b2) * 0.75 | |
| elseif w == 10 then | |
| local c1 = math.sin(phase) | |
| local c2 = math.sin(phase * 2.76) * 0.4 | |
| local c3 = math.sin(phase * 5.4) * 0.25 | |
| return (c1 + c2 + c3) * 0.6 | |
| else | |
| return (math.random() * 2.0 - 1.0) * 0.7 | |
| end | |
| end | |
| function SNESConsole.adsr(time, a, d, s, r) | |
| local att = math.max(0.001, a or 0.005) | |
| local dec = math.max(0.01, d or 0.3) | |
| local sus = math.max(0.0, math.min(1.0, s or 0.5)) | |
| if time < att then | |
| return time / att | |
| else | |
| local tDec = time - att | |
| return sus + (1.0 - sus) * math.exp(-tDec * 3.5 / dec) | |
| end | |
| end | |
| function SNESConsole.echo(signal, time, delayMs, feedback, vol) | |
| local dSec = math.max(0.016, (delayMs or 140.0) / 1000.0) | |
| local fb = math.max(0.0, math.min(0.95, feedback or 0.55)) | |
| local ev = math.max(0.0, math.min(1.0, vol or 0.4)) | |
| local echoSignal = 0.0 | |
| if time > dSec then | |
| local taps = { 0.34, 0.45, -0.12, 0.10 } | |
| for i = 1, 4 do | |
| local tTap = time - dSec * i | |
| if tTap > 0 then | |
| local damp = math.exp(-tTap * 2.5) * (fb ^ i) | |
| echoSignal = echoSignal + signal * damp * taps[i] | |
| end | |
| end | |
| end | |
| return signal + echoSignal * ev | |
| end | |
| function SNESConsole.process(time, freq, note, params) | |
| local wIdx = params["Waveform"] or 0.0 | |
| local a = params["Attack"] or 0.005 | |
| local d = params["Decay"] or 0.3 | |
| local s = params["Sustain"] or 0.5 | |
| local r = params["Release"] or 0.25 | |
| local vRate = params["VibratoRate"] or 5.5 | |
| local vDepth = params["VibratoDepth"] or 0.1 | |
| local eDelay = params["EchoDelay"] or 140.0 | |
| local eFdbk = params["EchoFeedback"] or 0.55 | |
| local eVol = params["EchoVolume"] or 0.4 | |
| -- 1. Vibrato Pitch Modulation | |
| local vibOffset = 0.0 | |
| if vDepth > 0.001 and vRate > 0.1 then | |
| vibOffset = math.sin(2.0 * math.pi * vRate * time) * (vDepth * 0.06) | |
| end | |
| local curFreq = freq * (1.0 + vibOffset) | |
| -- 2. Wavetable Synthesis with S-DSP Gaussian curve | |
| local phase = 2.0 * math.pi * curFreq * time | |
| local rawVoice = SNESConsole.wavetable(phase, wIdx) | |
| -- 3. ADSR / S-DSP Gain Envelope | |
| local env = SNESConsole.adsr(time, a, d, s, r) | |
| local dryOut = rawVoice * env | |
| -- 4. 8-Tap FIR Echo Reverb Emulation | |
| return math.tanh(SNESConsole.echo(dryOut, time, eDelay, eFdbk, eVol) * 1.2) | |
| end | |
| function SNESConsole.gui() | |
| return { | |
| panel = { | |
| title = "SNES Synth", | |
| subtitle = "16-Bit Polyphonic S-DSP Console Synthesizer", | |
| background = "snes", | |
| accent = "#7B52AB", | |
| knobStyle = "snes", | |
| layout = { | |
| { | |
| type = "row", | |
| children = { | |
| { type = "listbox", param = "Waveform", label = "Wavetable", width = 180, height = 90 }, | |
| { type = "knob", param = "Attack", label = "ATTACK", size = 48 }, | |
| { type = "knob", param = "Decay", label = "DECAY", size = 48 }, | |
| { type = "knob", param = "Sustain", label = "SUSTAIN", size = 48 }, | |
| { type = "knob", param = "Release", label = "RELEASE", size = 48 }, | |
| } | |
| }, | |
| { | |
| type = "row", | |
| children = { | |
| { type = "knob", param = "VibratoRate", label = "VIB RATE", size = 48 }, | |
| { type = "knob", param = "VibratoDepth", label = "VIB DEPTH", size = 48 }, | |
| { type = "knob", param = "EchoDelay", label = "ECHO MS", size = 48 }, | |
| { type = "knob", param = "EchoFeedback", label = "ECHO FDBK", size = 48 }, | |
| { type = "knob", param = "EchoVolume", label = "ECHO VOL", size = 48 }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| function SNESConsole.rack() | |
| return { | |
| rows = { | |
| -- ROW 1: Wavetable Sound Engine & ADSR | |
| { | |
| { id = "brr_vco", title = "BRR WAVETABLE VCO", hp = 16, row = 1, category = "VCO" }, | |
| { id = "snes_adsr", title = "ADSR / GAIN ENV", hp = 14, row = 1, category = "MOD" }, | |
| }, | |
| -- ROW 2: S-DSP Echo Processor & Output | |
| { | |
| { id = "echo", title = "8-TAP FIR ECHO", hp = 16, row = 2, category = "FX" }, | |
| { id = "master", title = "S-DSP MASTER OUT", hp = 14, row = 2, category = "OUT" }, | |
| }, | |
| }, | |
| cables = { | |
| { from = "1:0:2", to = "2:0:0", color = "audio" }, | |
| { from = "1:1:1", to = "2:0:1", color = "modulation" }, | |
| { from = "2:0:2", to = "2:1:0", color = "audio" }, | |
| } | |
| } | |
| end | |
| return SNESConsole | |
| ]], | |
| luaParams = { | |
| ["Waveform"] = 9.0000, | |
| ["Attack"] = 0.0050, | |
| ["Decay"] = 0.3000, | |
| ["Sustain"] = 0.5000, | |
| ["Release"] = 0.2500, | |
| ["VibratoRate"] = 5.5000, | |
| ["VibratoDepth"] = 0.1000, | |
| ["EchoDelay"] = 140.0000, | |
| ["EchoFeedback"] = 0.5500, | |
| ["EchoVolume"] = 0.4000, | |
| }, | |
| notes = { | |
| { id = "1787246863084", pitch = 36, startStep = 0.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246947301", pitch = 36, startStep = 4.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246951816", pitch = 36, startStep = 8.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246957784", pitch = 36, startStep = 12.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_0", pitch = 36, startStep = 6.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_1", pitch = 36, startStep = 2.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_2", pitch = 36, startStep = 14.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_3", pitch = 36, startStep = 10.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_0", pitch = 36, startStep = 16.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_1", pitch = 36, startStep = 20.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_2", pitch = 36, startStep = 24.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_3", pitch = 36, startStep = 28.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_4", pitch = 36, startStep = 22.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_5", pitch = 36, startStep = 18.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_6", pitch = 36, startStep = 30.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_7", pitch = 36, startStep = 26.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "clip_track_1787246684146_0", | |
| name = "SNES Bass", | |
| trackId = "track_1787246684146", | |
| startBar = 0, | |
| barLength = 4, | |
| loopLengthBars = 2, | |
| notes = { | |
| { id = "1787246863084", pitch = 36, startStep = 0.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246947301", pitch = 36, startStep = 4.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246951816", pitch = 36, startStep = 8.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "1787246957784", pitch = 36, startStep = 12.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_0", pitch = 36, startStep = 6.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_1", pitch = 36, startStep = 2.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_2", pitch = 36, startStep = 14.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787246987166000_3", pitch = 36, startStep = 10.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_0", pitch = 36, startStep = 16.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_1", pitch = 36, startStep = 20.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_2", pitch = 36, startStep = 24.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_3", pitch = 36, startStep = 28.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_4", pitch = 36, startStep = 22.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_5", pitch = 36, startStep = 18.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_6", pitch = 36, startStep = 30.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "p_1787247066161000_7", pitch = 36, startStep = 26.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| }, | |
| { | |
| id = "p1", | |
| name = "Pattern B", | |
| lengthSteps = 32, | |
| tracks = { | |
| { | |
| id = "p1_k", | |
| name = "Eats Kick", | |
| color = 0xffff007a, | |
| type = "luaScript", | |
| volume = 0.950, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "off", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- --- Procedural Sub Kick Drum Script (Lua) --- | |
| local ProceduralKick = {} | |
| function ProceduralKick.init() | |
| Param.add("StartFreq", 100.0, 300.0, 160.0) | |
| Param.add("EndFreq", 30.0, 60.0, 42.0) | |
| Param.add("PitchDecay", 0.01, 0.1, 0.035) | |
| Param.add("AmpDecay", 0.1, 0.6, 0.35) | |
| Param.add("Click", 0.0, 1.0, 0.0) | |
| end | |
| function ProceduralKick.process(time, freq, note, params) | |
| local startF = params["StartFreq"] or 160.0 | |
| local endF = params["EndFreq"] or 42.0 | |
| local pDecay = params["PitchDecay"] or 0.035 | |
| local aDecay = params["AmpDecay"] or 0.35 | |
| local click = params["Click"] or 0.0 | |
| -- Pitch sweep envelope | |
| local curFreq = endF + (startF - endF) * math.exp(-time / math.max(0.005, pDecay)) | |
| local phase = 2.0 * math.pi * curFreq * time | |
| local subSine = math.sin(phase) | |
| -- Transient click | |
| local clickTransient = (math.random() * 2.0 - 1.0) * math.exp(-time * 150.0) * click | |
| -- Amplitude envelope decaying to near zero by time = aDecay | |
| local env = math.exp(-time * 5.0 / math.max(0.01, aDecay)) | |
| local rawOutput = (subSine * 0.85 + clickTransient * 0.15) * env | |
| -- Smooth fade toward edge of kick duration to guarantee clean drop to silence | |
| local maxDur = math.max(0.1, aDecay) | |
| local fadeStart = maxDur - 0.04 | |
| local edgeFade = 1.0 | |
| if time > fadeStart then | |
| local norm = math.max(0.0, math.min(1.0, (maxDur - time) / 0.04)) | |
| edgeFade = 0.5 * (1.0 - math.cos(math.pi * norm)) | |
| end | |
| if time >= maxDur then edgeFade = 0.0 end | |
| return math.tanh(rawOutput * edgeFade * 1.3) | |
| end | |
| return ProceduralKick | |
| ]], | |
| luaParams = { | |
| ["StartFreq"] = 160.0000, | |
| ["EndFreq"] = 42.0000, | |
| ["PitchDecay"] = 0.0350, | |
| ["AmpDecay"] = 0.3500, | |
| ["Click"] = 0.0000, | |
| }, | |
| steps = { | |
| [1] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [2] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [3] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [4] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [5] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [6] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [7] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| [8] = { pitch = 36, velocity = 0.95, isSlide = false, isAccent = true }, | |
| }, | |
| notes = { | |
| { id = "k_0", pitch = 36, startStep = 0.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_4", pitch = 36, startStep = 4.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_8", pitch = 36, startStep = 8.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_12", pitch = 36, startStep = 12.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_16", pitch = 36, startStep = 16.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_20", pitch = 36, startStep = 20.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_24", pitch = 36, startStep = 24.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_28", pitch = 36, startStep = 28.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "c_k1", | |
| name = "Kick Beat A", | |
| trackId = "t_kick", | |
| startBar = 0, | |
| barLength = 4, | |
| notes = { | |
| { id = "k_0", pitch = 36, startStep = 0.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_4", pitch = 36, startStep = 4.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_8", pitch = 36, startStep = 8.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_12", pitch = 36, startStep = 12.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_16", pitch = 36, startStep = 16.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_20", pitch = 36, startStep = 20.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_24", pitch = 36, startStep = 24.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "k_28", pitch = 36, startStep = 28.00, durationSteps = 1.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| id = "p1_s", | |
| name = "Eats Snare", | |
| color = 0xffff8c00, | |
| type = "luaScript", | |
| volume = 0.850, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "off", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- --- Procedural Snare Drum Script (Lua) --- | |
| local ProceduralSnare = {} | |
| function ProceduralSnare.init() | |
| Param.add("ToneFreq", 100.0, 300.0, 185.0) | |
| Param.add("Snappy", 0.0, 1.0, 0.65) | |
| Param.add("Decay", 0.05, 0.5, 0.1) | |
| end | |
| function ProceduralSnare.process(time, freq, note, params) | |
| local toneFreq = params["ToneFreq"] or 185.0 | |
| local snappy = params["Snappy"] or 0.65 | |
| local decay = params["Decay"] or 0.1 | |
| -- Body tone (pitch sweep) | |
| local sweepFreq = toneFreq * math.exp(-time * 40.0) | |
| local body = math.sin(2.0 * math.pi * sweepFreq * time) * math.exp(-time * 25.0) | |
| -- Snare noise wires | |
| local noise = (math.random() * 2.0 - 1.0) * math.exp(-time / decay) | |
| local filteredNoise = DSP.highpass(noise, 1500.0, 1.0) | |
| local output = body * (1.0 - snappy) + filteredNoise * snappy | |
| return math.tanh(output * 1.2) | |
| end | |
| return ProceduralSnare | |
| ]], | |
| luaParams = { | |
| ["ToneFreq"] = 185.0000, | |
| ["Snappy"] = 0.6500, | |
| ["Decay"] = 0.1000, | |
| }, | |
| steps = { | |
| [1] = { pitch = 38, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [2] = { pitch = 38, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [3] = { pitch = 38, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [4] = { pitch = 38, velocity = 0.90, isSlide = false, isAccent = true }, | |
| }, | |
| notes = { | |
| { id = "s_4", pitch = 38, startStep = 4.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "s_12", pitch = 38, startStep = 12.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "s_20", pitch = 38, startStep = 20.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "s_28", pitch = 38, startStep = 28.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "c_s1", | |
| name = "Snare Pattern", | |
| trackId = "t_snare", | |
| startBar = 0, | |
| barLength = 4, | |
| notes = { | |
| { id = "s_4", pitch = 38, startStep = 4.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "s_12", pitch = 38, startStep = 12.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "s_20", pitch = 38, startStep = 20.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "s_28", pitch = 38, startStep = 28.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| id = "p1_h", | |
| name = "Eats Hats", | |
| color = 0xffffb700, | |
| type = "luaScript", | |
| volume = 0.750, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "off", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- --- Procedural Hi-Hat Synth Script (Lua) --- | |
| local ProceduralHiHat = {} | |
| function ProceduralHiHat.init() | |
| Param.add("Cutoff", 4000.0, 14000.0, 8500.0) | |
| Param.add("Decay", 0.01, 0.4, 0.05) | |
| Param.add("Metallic", 0.0, 1.0, 0.15) | |
| end | |
| function ProceduralHiHat.process(time, freq, note, params) | |
| local cutoff = params["Cutoff"] or 8500.0 | |
| local decay = params["Decay"] or 0.05 | |
| local metallic = params["Metallic"] or 0.15 | |
| local env = math.exp(-time / math.max(0.005, decay)) | |
| -- Metallic ring harmonics (6 detuned square waves for TR-style metallic sheen) | |
| local ring1 = math.sin(2.0 * math.pi * 205.0 * time) > 0 and 1.0 or -1.0 | |
| local ring2 = math.sin(2.0 * math.pi * 305.0 * time) > 0 and 1.0 or -1.0 | |
| local ring3 = math.sin(2.0 * math.pi * 365.0 * time) > 0 and 1.0 or -1.0 | |
| local ring4 = math.sin(2.0 * math.pi * 396.0 * time) > 0 and 1.0 or -1.0 | |
| local ring5 = math.sin(2.0 * math.pi * 434.0 * time) > 0 and 1.0 or -1.0 | |
| local ring6 = math.sin(2.0 * math.pi * 700.0 * time) > 0 and 1.0 or -1.0 | |
| local metallicRing = (ring1 + ring2 + ring3 + ring4 + ring5 + ring6) / 6.0 | |
| -- White noise generator (primary hi-hat sound source) | |
| local noise = (math.random() * 2.0 - 1.0) | |
| local rawSignal = noise * (1.0 - metallic * 0.4) + metallicRing * (metallic * 0.4) | |
| -- High-pass filtered noise sibilance | |
| local filtered = DSP.highpass(rawSignal, cutoff, 1.2) | |
| return filtered * env * 0.75 | |
| end | |
| return ProceduralHiHat | |
| ]], | |
| luaParams = { | |
| ["Cutoff"] = 8500.0000, | |
| ["Decay"] = 0.0500, | |
| ["Metallic"] = 0.1500, | |
| }, | |
| steps = { | |
| [1] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [2] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [3] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [4] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [5] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [6] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [7] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [8] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [9] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [10] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [11] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [12] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [13] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [14] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| [15] = { pitch = 42, velocity = 0.60, isSlide = false, isAccent = false }, | |
| [16] = { pitch = 42, velocity = 0.90, isSlide = false, isAccent = true }, | |
| }, | |
| notes = { | |
| { id = "h_0", pitch = 42, startStep = 0.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_2", pitch = 42, startStep = 2.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_4", pitch = 42, startStep = 4.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_6", pitch = 42, startStep = 6.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_8", pitch = 42, startStep = 8.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_10", pitch = 42, startStep = 10.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_12", pitch = 42, startStep = 12.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_14", pitch = 42, startStep = 14.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_16", pitch = 42, startStep = 16.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_18", pitch = 42, startStep = 18.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_20", pitch = 42, startStep = 20.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_22", pitch = 42, startStep = 22.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| }, | |
| clips = { | |
| { | |
| id = "clip_p1_h_0", | |
| name = "Eats Hats Clip", | |
| trackId = "p1_h", | |
| startBar = 0, | |
| barLength = 2, | |
| luaScriptCode = [[ | |
| -- --- Procedural Hi-Hat Synth Script (Lua) --- | |
| local ProceduralHiHat = {} | |
| function ProceduralHiHat.init() | |
| Param.add("Cutoff", 4000.0, 14000.0, 8500.0) | |
| Param.add("Decay", 0.01, 0.4, 0.05) | |
| Param.add("Metallic", 0.0, 1.0, 0.15) | |
| end | |
| function ProceduralHiHat.process(time, freq, note, params) | |
| local cutoff = params["Cutoff"] or 8500.0 | |
| local decay = params["Decay"] or 0.05 | |
| local metallic = params["Metallic"] or 0.15 | |
| local env = math.exp(-time / math.max(0.005, decay)) | |
| -- Metallic ring harmonics (6 detuned square waves for TR-style metallic sheen) | |
| local ring1 = math.sin(2.0 * math.pi * 205.0 * time) > 0 and 1.0 or -1.0 | |
| local ring2 = math.sin(2.0 * math.pi * 305.0 * time) > 0 and 1.0 or -1.0 | |
| local ring3 = math.sin(2.0 * math.pi * 365.0 * time) > 0 and 1.0 or -1.0 | |
| local ring4 = math.sin(2.0 * math.pi * 396.0 * time) > 0 and 1.0 or -1.0 | |
| local ring5 = math.sin(2.0 * math.pi * 434.0 * time) > 0 and 1.0 or -1.0 | |
| local ring6 = math.sin(2.0 * math.pi * 700.0 * time) > 0 and 1.0 or -1.0 | |
| local metallicRing = (ring1 + ring2 + ring3 + ring4 + ring5 + ring6) / 6.0 | |
| -- White noise generator (primary hi-hat sound source) | |
| local noise = (math.random() * 2.0 - 1.0) | |
| local rawSignal = noise * (1.0 - metallic * 0.4) + metallicRing * (metallic * 0.4) | |
| -- High-pass filtered noise sibilance | |
| local filtered = DSP.highpass(rawSignal, cutoff, 1.2) | |
| return filtered * env * 0.75 | |
| end | |
| return ProceduralHiHat | |
| ]], | |
| luaParams = { ["Cutoff"] = 8500.0000, ["Decay"] = 0.0500, ["Metallic"] = 0.1500 }, | |
| notes = { | |
| { id = "h_0", pitch = 42, startStep = 0.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_2", pitch = 42, startStep = 2.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_4", pitch = 42, startStep = 4.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_6", pitch = 42, startStep = 6.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_8", pitch = 42, startStep = 8.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_10", pitch = 42, startStep = 10.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_12", pitch = 42, startStep = 12.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_14", pitch = 42, startStep = 14.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_16", pitch = 42, startStep = 16.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_18", pitch = 42, startStep = 18.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "h_20", pitch = 42, startStep = 20.00, durationSteps = 1.00, velocity = 0.60, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| { id = "h_22", pitch = 42, startStep = 22.00, durationSteps = 1.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = false }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| id = "p1_w", | |
| name = "Eats 303", | |
| color = 0xff00ff66, | |
| type = "luaScript", | |
| volume = 0.900, | |
| pan = 0.000, | |
| isMuted = false, | |
| isSoloed = false, | |
| chordFollowMode = "off", | |
| sampleName = "kick", | |
| synthWaveform = "sawtooth", | |
| cutoff = 3000.0, | |
| resonance = 1.00, | |
| attack = 0.010, | |
| release = 0.300, | |
| trackerColumns = 4, | |
| activeView = "pianoRoll", | |
| luaScriptCode = [[ | |
| -- --- JC-303 Roland TB-303 Acid Engine (Lua) --- | |
| local Acid303 = {} | |
| function Acid303.init() | |
| Param.add("Waveform", 0.0, 1.0, 0.0) -- 0 = Saw, 1 = Square | |
| Param.add("Cutoff", 100.0, 6500.0, 1600.0) | |
| Param.add("Resonance", 0.5, 16.0, 8.0) | |
| Param.add("EnvMod", 0.0, 1.0, 0.75) | |
| Param.add("Decay", 0.05, 1.2, 0.28) | |
| Param.add("Accent", 0.0, 1.0, 0.6) | |
| Param.add("Slide", 0.0, 1.0, 0.4) | |
| Param.add("Overdrive", 0.0, 1.0, 0.3) | |
| end | |
| function Acid303.process(time, freq, note, params, targetNote, isSlide, isAccent) | |
| local waveType = params["Waveform"] or 0.0 | |
| local cutoff = params["Cutoff"] or 1600.0 | |
| local res = params["Resonance"] or 8.0 | |
| local envMod = params["EnvMod"] or 0.75 | |
| local decay = params["Decay"] or 0.28 | |
| local accent = params["Accent"] or 0.6 | |
| local drive = params["Overdrive"] or 0.3 | |
| local slideParam = params["Slide"] or 0.4 | |
| -- Pitch glide / Portamento logic for JC-303 continuous monophonic voice | |
| local currentFreq = freq | |
| if targetNote and targetNote > 0 then | |
| local targetFreq = 440.0 * (2.0 ^ ((targetNote - 69) / 12.0)) | |
| currentFreq = targetFreq + (freq - targetFreq) * math.exp(-time / 0.060) | |
| elseif isSlide or slideParam > 0.5 then | |
| local targetFreq = targetNote and (440.0 * (2.0 ^ ((targetNote - 69) / 12.0))) or freq | |
| currentFreq = targetFreq + (freq - targetFreq) * math.exp(-time / 0.060) | |
| end | |
| -- JC-303 Oscillators: Leaky Integrator Sawtooth & Differentiated Square | |
| local phase = time * currentFreq | |
| local normPhase = phase - math.floor(phase) | |
| local sawRaw = 2.0 * normPhase - 1.0 | |
| local sawHP = sawRaw - 0.85 * math.exp(-time * 12.0) | |
| local sqrRaw = normPhase < 0.46 and 0.75 or -0.75 | |
| local osc = waveType < 0.5 and sawHP or sqrRaw | |
| -- Dynamic Accent & VCF Envelope Decay Dynamics | |
| local hasAccent = isAccent or (accent > 0.7 and not isSlide) | |
| local envBoost = hasAccent and (1.0 + accent * 1.1) or 1.0 | |
| local envDecay = decay / (hasAccent and (1.0 + accent * 0.9) or 1.0) | |
| local env = math.exp(-time / envDecay) | |
| local accentPulse = hasAccent and (accent * 0.4 * math.exp(-time / 0.035)) or 0.0 | |
| -- 24dB 4-Pole Diode Ladder Filter simulation with feedback saturation | |
| local modCutoff = cutoff + (envMod * (env + accentPulse) * 6500.0 * envBoost) | |
| local filtered = DSP.lowpass(osc, modCutoff, res) | |
| -- Post-VCF 150Hz 1-Pole High-Pass filter & overdrive saturation | |
| local highpassed = filtered * 0.98 | |
| local output = highpassed * (hasAccent and 1.35 or 1.0) | |
| if drive > 0.05 then | |
| output = math.tanh(output * (1.0 + drive * 4.0)) | |
| end | |
| return output | |
| end | |
| return Acid303 | |
| ]], | |
| luaParams = { | |
| ["Waveform"] = 0.0000, | |
| ["Cutoff"] = 1800.0000, | |
| ["Resonance"] = 8.0000, | |
| ["EnvMod"] = 0.7500, | |
| ["Decay"] = 0.2800, | |
| ["Accent"] = 0.6000, | |
| ["Slide"] = 0.4000, | |
| ["Overdrive"] = 0.3000, | |
| }, | |
| steps = { | |
| [1] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [2] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [3] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [4] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [5] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [6] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [7] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [8] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [9] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [10] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [11] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [12] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [13] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [14] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [15] = { pitch = 36, velocity = 0.85, isSlide = false, isAccent = true }, | |
| [16] = { pitch = 42, velocity = 0.85, isSlide = false, isAccent = true }, | |
| }, | |
| notes = { | |
| { id = "n1", pitch = 36, startStep = 0.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n2", pitch = 36, startStep = 2.00, durationSteps = 1.00, velocity = 0.80, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n3", pitch = 48, startStep = 3.00, durationSteps = 1.00, velocity = 1.00, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n4", pitch = 39, startStep = 4.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n5", pitch = 41, startStep = 6.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n6", pitch = 43, startStep = 8.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n7", pitch = 36, startStep = 10.00, durationSteps = 2.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n8", pitch = 48, startStep = 12.00, durationSteps = 2.00, velocity = 1.00, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n9", pitch = 46, startStep = 14.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| clips = { | |
| { | |
| id = "c_w1", | |
| name = "Acid 303 Riff", | |
| trackId = "t_lua_303", | |
| startBar = 0, | |
| barLength = 4, | |
| notes = { | |
| { id = "n1", pitch = 36, startStep = 0.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n2", pitch = 36, startStep = 2.00, durationSteps = 1.00, velocity = 0.80, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n3", pitch = 48, startStep = 3.00, durationSteps = 1.00, velocity = 1.00, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n4", pitch = 39, startStep = 4.00, durationSteps = 2.00, velocity = 0.85, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n5", pitch = 41, startStep = 6.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n6", pitch = 43, startStep = 8.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n7", pitch = 36, startStep = 10.00, durationSteps = 2.00, velocity = 0.95, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n8", pitch = 48, startStep = 12.00, durationSteps = 2.00, velocity = 1.00, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| { id = "n9", pitch = 46, startStep = 14.00, durationSteps = 2.00, velocity = 0.90, column = 0, effectCommand = "00", isSlide = false, isAccent = true }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| }, | |
| }, | |
| arranger = { | |
| activePatternIndex = 0, | |
| items = { | |
| { patternId = "p0", startBar = 0, barLength = 4 }, | |
| { patternId = "p1", startBar = 4, barLength = 4 }, | |
| } | |
| }, | |
| chordTrack = { | |
| { id = "chord_1787947628767_0", startBar = 0, barLength = 1.0, rootPitchClass = 0, quality = "minor" }, | |
| { id = "chord_1787947628767_1", startBar = 1, barLength = 1.0, rootPitchClass = 8, quality = "major" }, | |
| { id = "chord_1787947628767_2", startBar = 2, barLength = 1.0, rootPitchClass = 10, quality = "major" }, | |
| { id = "chord_1787947628767_3", startBar = 3, barLength = 1.0, rootPitchClass = 0, quality = "minor" }, | |
| }, | |
| masterFx = { | |
| { id = "fx_1788056360682_luaFX", name = "Eats-Spectrum", type = "luaFX", enabled = true, mix = 1.00, params = { ["Gain"] = 1.0000, ["Decay"] = 0.6000, ["Mode"] = 0.0000 }, presetId = "eats_spectrum", luaScriptCode = [[ | |
| -- @name: Eats-Spectrum | |
| -- @category: audioFx | |
| -- @description: 16-band graphic spectrum analyzer visualizer with peak hold caps | |
| local Spectrum = {} | |
| function Spectrum.init() | |
| Param.add("Gain", 0.2, 4.0, 1.0, 0.1) | |
| Param.add("Decay", 0.1, 2.0, 0.6, 0.05) | |
| Param.choice("Mode", {"16-Band Bar", "8-Band Chunky", "Peak Hold", "Smooth Curve"}, 0.0) | |
| end | |
| function Spectrum.process(input_l, input_r, params) | |
| return input_l, input_r | |
| end | |
| function Spectrum.gui() | |
| return { | |
| panel = { | |
| title = "Eats-Spectrum", | |
| subtitle = "16-Band Real-Time Spectrum Analyzer", | |
| background = "dark", | |
| accent = "#00E5FF", | |
| layout = { | |
| { | |
| type = "canvas", | |
| mode = "spectrum", | |
| width = 340, | |
| height = 180 | |
| }, | |
| { | |
| type = "row", | |
| children = { | |
| { type = "knob", param = "Gain", label = "GAIN", unit = "x" }, | |
| { type = "knob", param = "Decay", label = "DECAY", unit = "s" }, | |
| { type = "listbox", param = "Mode", label = "METER MODE", width = 120, height = 70 } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| function Spectrum.rack() | |
| return { | |
| rows = { | |
| { | |
| { id = "in_lr", title = "AUDIO INPUT L/R", hp = 14, row = 1, category = "OUT" }, | |
| { id = "spectrum_fft", title = "16-BAND FFT ANALYZER", hp = 16, row = 1, category = "FX" }, | |
| }, | |
| { | |
| { id = "thru_out", title = "THRU AUDIO OUT", hp = 14, row = 2, category = "OUT" }, | |
| }, | |
| }, | |
| cables = { | |
| { from = "1:0:1", to = "1:1:0", color = "audio" }, | |
| { from = "1:0:1", to = "2:0:0", color = "audio" }, | |
| } | |
| } | |
| end | |
| return Spectrum | |
| ]], luaParams = { ["Gain"] = 1.0000, ["Decay"] = 0.6000, ["Mode"] = 0.0000 } }, | |
| { id = "fx_1788056364837_luaFX", name = "Eats-Scope", type = "luaFX", enabled = true, mix = 1.00, params = { ["Timebase"] = 1.0000, ["Gain"] = 1.0000, ["GlowColor"] = 0.0000 }, presetId = "eats_scope", luaScriptCode = [[ | |
| -- @name: Eats-Scope | |
| -- @category: audioFx | |
| -- @description: Glowing neon vector oscilloscope and waveform monitor visualizer | |
| local Scope = {} | |
| function Scope.init() | |
| Param.add("Timebase", 0.1, 2.0, 1.0, 0.05) | |
| Param.add("Gain", 0.1, 3.0, 1.0, 0.1) | |
| Param.choice("GlowColor", {"Neon Mint", "Cyber Cyan", "Laser Red", "Gold Solar", "Purple Dream"}, 0.0) | |
| end | |
| function Scope.process(input_l, input_r, params) | |
| return input_l, input_r | |
| end | |
| function Scope.gui() | |
| return { | |
| panel = { | |
| title = "Eats-Scope", | |
| subtitle = "Vector Waveform Oscilloscope Visualizer", | |
| background = "dark", | |
| accent = "#00FF9D", | |
| layout = { | |
| { | |
| type = "canvas", | |
| mode = "vector", | |
| width = 340, | |
| height = 180 | |
| }, | |
| { | |
| type = "row", | |
| children = { | |
| { type = "knob", param = "Timebase", label = "TIMEBASE", unit = "x" }, | |
| { type = "knob", param = "Gain", label = "GAIN", unit = "x" }, | |
| { type = "listbox", param = "GlowColor", label = "BEAM COLOR", width = 120, height = 70 } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| function Scope.rack() | |
| return { | |
| rows = { | |
| { | |
| { id = "in_lr", title = "AUDIO INPUT L/R", hp = 14, row = 1, category = "OUT" }, | |
| { id = "vector_scope", title = "VECTOR OSCILLOSCOPE", hp = 16, row = 1, category = "FX" }, | |
| }, | |
| { | |
| { id = "thru_out", title = "THRU AUDIO OUT", hp = 14, row = 2, category = "OUT" }, | |
| }, | |
| }, | |
| cables = { | |
| { from = "1:0:1", to = "1:1:0", color = "audio" }, | |
| { from = "1:0:1", to = "2:0:0", color = "audio" }, | |
| } | |
| } | |
| end | |
| return Scope | |
| ]], luaParams = { ["Timebase"] = 1.0000, ["Gain"] = 1.0000, ["GlowColor"] = 0.0000 } }, | |
| }, | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment