Code module - Gemini Gem

Hi all, I made this for my own use and it’s been error free for a few days so figured I’d share it.

https://gemini.google.com/gem/1rHBbsL4HRJRlDwdYez1_YQVovmZDIdAy?usp=sharing

this gem is loaded with available code documentation, as well as all of the factory preset code, plus some generated content meant to prevent frequent errors IN LLM generated code.

have fun, and remember to share anything cool you make! Feel free to report frequent errors (along with associated code) and I can try and remediate.

«13

Comments

  • edited March 26

    That was pretty cool. It took several iterations and starting over from scratch once to get anything that works. I finally got smart and stopped gemini from trying to fix broken attempts. After starting over it succeeded in making basically the equivalent of the bit reducer to use on LFOs, but also asked if I wanted to add a smoothing control. That led to another series of failures, and some working attempts that weren't what I liked. In the end I ended up with something more useful than my original request.

    So, not bad, but persistence required. The downside is I don't understand the code. The upside is now I can try to understand the code.

    // Linear Interpolator Stepper
    // 0 = Hard Steps, 1 = Perfect Straight Lines (Interpolated)
    
    // 1. Get the LFO signal and clamp it to safe range
    sig = clamp(input(audio, LFOIn), -1, 1)
    
    // 2. Parameters
    // Steps: 2 to 64
    numSteps = param(value, StepCount) * 62 + 2
    slew = param(value, SlewAmount)
    
    // 3. Scale and find indices
    scaled = sig * numSteps
    v1 = floor(scaled)
    
    // 4. Calculate the straight-line bridge (interpolation)
    // fract(scaled) is our "ramp" between the current and next step
    f = fract(scaled)
    interpolated = v1 + (f * slew)
    
    // 5. Output back to CV range
    out = interpolated / numSteps
    
    output(out, audio)
    
  • Argh! I had been using with consistent success (meaning no errors, not something totally revelatory).

  • edited March 26

    I didn't expect success right off. Part of me was hoping for a fail. I had to wipe the smirk off my face when it actually ended up doing a good job and solving some things it would have taken me hours to figure out.

  • Will be trying this, thanks @bcrichards.

    I’m kinda surprised that there don’t seem to have been any Code module creations shared on PatchStorage yet?

  • Certainly seems to work for me @bcrichards, it just built a physical modeling synth in seconds.

    Question: what’s the best way to get it to just add every feature it can instead of the obligatory ‘would you like me to add xyz’ which seems to appear at the end of every one of its posts?

  • It works but then repeatedly fails when trying to make things more complex. Gave up eventually.

    In such situations, where you’re going back and forth, copying and pasting code, it would be really helpful if there was a simple, quick way to select all text in the Code module’s text box. Worth considering @giku?

  • Vector morph w/sub oscillator


    #persample


    // --- INPUTS & PARAMETERS ---

    p = input(pitch, Pitch)

    g = input(gate, Gate)


    // Morphing & Timbre

    morph = param(value, Morph)

    richness = param(value, Richness)

    detune = param(value, Detune)


    // Sub & Saturation

    subLevel = param(value, SubLevel)

    drive = param(value, Drive)


    // Standard Synth Controls

    att = param(time, Attack)

    dec = param(time, Decay)

    cut = param(pitch, Cutoff)

    res = param(value, Resonance)


    // --- ENVELOPE & LFO ---

    ampEnv = envAD(g, att, dec)

    drift = oscSin(0.5) * 0.02


    // --- OSCILLATORS ---

    // Main Dual Stack

    osc1 = oscSin(p + drift)

    osc2 = oscSin(p * (1.0 + detune * 0.01))

    sig = (osc1 + osc2) * 0.5


    // Sub Oscillator (Exactly half the frequency of Pitch)

    subOsc = oscSin(p * 0.5)


    // --- VECTOR MORPHING ---

    folder = sin(sig * (1.0 + richness * 12.0))

    shaper = tanh(sig * (1.0 + richness * 20.0))

    sigMorphed = mix(folder, shaper, morph)


    // --- MIXING & FILTERING ---

    // Blend the Sub in before the filter so it gets shaped too

    combined = sigMorphed + (subOsc * subLevel)


    // Filter stage

    filtered = lpf(combined, cut, res)


    // Final Drive: Glue the Sub and Morph together

    // This prevents the sub from sounding "detached"

    out = tanh(filtered * (1.0 + drive * 3.0))


    // Apply Envelope

    finalOut = out * ampEnv


    output(finalOut, audio)

  • edited March 26

    On iPhone to the best of my knowledge it’s double tap&hold on a blank space (no text), try several times until the popup finally says select, move to the big menu, press select all then copy

    doesn’t matter if it’s on notes, or giku’s text windows, it the OS’s fault for sucking at text selection after so many versions still


    btw, looking forward to try your vector thing


  • Here are two I like so far.


    Pultec:

    #persample


    /* 

     Pultec EQP-1A Emulation

     Architecture: Transformers -> Parallel EQ Summing -> Asymmetrical Tube Amplifier

    */


    inputSig = input(audio)


    // — Parameters —

    // Parameter names are unified into single words to prevent parser errors.


    pLowFreq = param(pitch, LowFreq)  // LF Selector: 20, 30, 60, 100 Hz

    pLowBst = param(value, LowBoost) // LF Boost Amount

    pLowAtt = param(value, LowAtten) // LF Attenuate Amount


    pHiFreq = param(pitch, HiFreq)  // HF Boost Selector: 3k to 16k Hz

    pHiBW  = param(value, HiBandwidth)// HF Q: 0 = Broad, 1 = Sharp

    pHiBst = param(value, HiBoost)  // HF Boost Amount


    pHiAttF = param(pitch, HiAttFreq) // HF Atten Selector: 5k, 10k, 20k Hz

    pHiAtt = param(value, HiAtten)  // HF Attenuate Amount


    pDrive = param(value, TubeDrive) // Adds Asymmetrical Tube Saturation



    // — 1. Transformer / Input Stage —

    // Subtle roll-off at the extreme highs and lows (simulating analog hardware)

    x = hpf6(inputSig, 15)

    x = lpf6(x, 22000)



    // — 2. Filter Math (Parallel Processing) —


    // A. Low Shelf Boost

    // Pultec boosts up to ~14dB. A linear multiplier of 5.0 equals roughly +14dB.

    lowBoostSig = lpf6(x, pLowFreq) * (pLowBst * 5.0)


    // B. Low Shelf Cut (Attenuate)

    // THE PULTEC TRICK: The cut’s corner frequency is inherently shifted ~30% higher than the boost.

    // Subtracting a Low Pass Filter from the dry signal creates a Low Shelf cut.

    lowCutSig = lpf6(x, pLowFreq * 1.3) * (pLowAtt * 0.85)


    // C. High Peak Boost (Bell)

    // Convert Bandwidth (0-1) to SVF resonance (0-0.95 to avoid self-oscillation).

    res = pHiBW * 0.95

    hiBoostSig = bpf(x, pHiFreq, res) * (pHiBst * 5.0)


    // D. High Shelf Cut (Attenuate)

    // Subtracting a High Pass Filter from the dry signal creates a High Shelf cut.

    hiCutSig = hpf6(x, pHiAttF) * (pHiAtt * 0.85)



    // — 3. Parallel Summing —

    // Sum the dry signal with all parallel bands

    eq = x + lowBoostSig - lowCutSig + hiBoostSig - hiCutSig



    // — 4. Output Stage (Asymmetrical Tube Amplifier) —

    // Real vacuum tubes clip asymmetrically (the positive half of the waveform 

    // distorts differently than the negative half), generating warm even-order harmonics.


    driveSig = eq * (1.0 + pDrive * 2.0)


    posClip = tanh(driveSig)

    // Compress the negative half slightly more for asymmetry

    negClip = tanh(driveSig * 1.2) * 0.833 


    // Apply ternary logic for positive/negative halves

    tube = driveSig > 0.0 ? posClip : negClip


    // Blend the pure EQ and the Tube Character based on the Drive parameter

    out = mix(eq, tube, pDrive)


    output(out) 





    Glue Compressor

    #persample


    // 1. Inputs

    In = input(audio, AudioIn)


    // 2. Parameters (0.0 to 1.0 ranges)

    ThreshKnob = param(value, Threshold)

    RatioKnob = param(value, Ratio)

    AttackKnob = param(value, Attack)

    ReleaseKnob = param(value, Release)

    MakeupKnob = param(value, Makeup)

    ClipKnob = param(value, ClipAmount) 

    MixKnob = param(value, DryWet)


    // 3. Parameter Mapping

    // Threshold

    threshold = pow(ThreshKnob, 3.0) + 0.0001


    // Ratio (1:1 to 10:1)

    ratio = 1.0 + (RatioKnob * 9.0)


    // Attack Time

    att_sec = 0.0001 + (AttackKnob * 0.03)

    attCoef = 1.0 / (att_sec * sampleRate)


    // Release Time

    rel_sec = 0.05 + (ReleaseKnob * 1.15)

    relCoef = 1.0 / (rel_sec * sampleRate)


    // Makeup Gain

    makeup = 1.0 + (MakeupKnob * 14.8)



    // 4. Logarithmic VCA Sidechain

    InLevel = abs(In)

    InLog = log(InLevel + 0.000001)

    ThreshLog = log(threshold)


    // Calculate static Gain Reduction

    StaticGR = max(0.0, InLog - ThreshLog) * (1.0 - 1.0 / ratio)


    // Sidechain Envelope Smoothing

    coef = StaticGR > SmoothGR ? attCoef : relCoef

    SmoothGR = SmoothGR + (StaticGR - SmoothGR) * coef


    // 5. Apply Compression

    LinearGain = exp(-SmoothGR)

    CompSignal = In * LinearGain * makeup


    // 6. Variable Soft Clipping Stage

    // Calculate the drive into the soft clipper (ranges from 1x to 5x drive)

    Drive = 1.0 + (ClipKnob * 4.0)


    // Apply the hyperbolic tangent (tanh) soft clip. 

    // We divide by ‘Drive’ at the end to automatically compensate for the volume 

    // boost, ensuring it acts as a peak-tamer rather than a volume raiser.

    Saturated = tanh(CompSignal * Drive) / Drive


    // Morph between the perfectly clean digital signal and the analog soft-clipped signal

    GlueSignal = mix(CompSignal, Saturated, ClipKnob)


    // 7. Parallel Mix (Dry/Wet)

    Out = mix(In, GlueSignal, MixKnob)


    output(Out, audio)

  • Wow, this looks amazing! Can’t wait until I get home and give it a go

  • edited March 27

    This is so cool, thanks for sharing!

    After quite a couple of prompts, it made me this cool delay with saturation, digital degradation and filtering in the feedback loop.

    The initial settings are not very useful, maybe I figure out where to change them (hints welcome:).

    #persample


    // 1. Inputs and Parameters

    sigIn = input(audio, AudioInput)

    fbAmount = param(value, Feedback)

    beatDiv = param(value, BeatDivision)

    msOffset = param(value, MsOffset)

    lpCutoff = param(pitch, LowPass)

    hpCutoff = param(pitch, HighPass)

    preDrive = param(value, InputDrive)

    postDrive = param(value, FeedbackDrive)

    bitCrush = param(value, BitCrush)

    resample = param(value, Resample)

    dryWet = param(value, DryWet)


    // 2. Precise Timing Calculations

    // Tempo is provided by the Drambo host

    beatSec = 60 / tempo

    // Scale 0-1 param to 0-4 beats (e.g., 0.25 = 1 beat)

    targetBeats = beatDiv * 4

    // Offset scaled to 0.1 seconds (100ms max)

    offsetSec = msOffset * 0.1

    dTime = (beatSec * targetBeats) + offsetSec


    // 3. Input Drive (Limiter 1)

    // Tanh provides soft-clipping before entering the delay line

    drivenIn = tanh(sigIn * (1 + preDrive * 8))


    // 4. The Feedback Loop

    // delayedSig pulls the audio from the buffer based on dTime

    delayedSig = delay(fbSig, dTime)


    // --- LO-FI PROCESSING ---


    // A. Sample Rate Reduction (Resampling)

    // srCounter accumulates based on the Resample parameter

    srCounter = srCounter + (resample * 0.5)

    if (srCounter >= 1) {

      heldSample = delayedSig

      srCounter = 0

    }

    // If Resample is low, we use the clean signal; otherwise, the held one

    lofiSig = (resample > 0.01) ? heldSample : delayedSig


    // B. Bit Reduction (Quantization)

    // Reduces the vertical resolution of the waveform

    steps = 64 - (bitCrush * 62)

    lofiSig = floor(lofiSig * steps) / steps


    // C. Feedback Saturation & Filtering

    // postDrive adds gain inside the loop for cumulative distortion

    saturatedFb = tanh(lofiSig * (1 + postDrive * 8))

    f1 = lpf(saturatedFb, lpCutoff, 0.2)

    f2 = hpf(f1, hpCutoff, 0.1)


    // — FEEDBACK LIMITER (Limiter 2) —

    // Final safety check to prevent infinite volume buildup

    limitedFb = clamp(tanh(f2), -1, 1)


    // Sum Input + Processed Feedback into the loop variable

    fbSig = drivenIn + (limitedFb * fbAmount)


    // 5. Final Output

    out = mix(sigIn, fbSig, dryWet)

    output(out, audio)

  • What I would like to do, is put the code rack into a processor rack to give it more tweakable controls, but the code rack doesn’t produce (receive?) any signal once it’s in the rack and I’ve checked all the connections. Can I do anything about that,is it a known limitation or a bug?

  • edited March 27

    @tyslothrop1 My advice is get it working outside a rack first. Then if it doesn’t work inside a rack where you connect the inputs right, it might be a bug (of the rack module, which I think deserves more love)

    a project file always helps if you want feedback

  • Thanks for sharing your presets on PatchStorage @bcrichards, so much easier to download the presets than copying and pasting the code from here.

    @Intrepolicious Your Vector Morph is easily interesting enough to put on PatchStorage too!

  • Thanks, I think the problem is pasting the code module. If I put an empty code module into a rack and paste code into the module, it works:) It just doesn't if I paste the code module with code inside.

  • I have pasted code modules (with code) several times without issues, except not in a rack scenario. I’ll try that to see if I can reproduce.

    I have a vague feeling there are some quirks with the custom rack, that’s why I said it deserved some love and also suggested to try it outside the rack to remove it from the equation

  • I just had very quick test, and I can’t find the problem. The code module gets pasted correctly, of course I need to create inputs on the rack, and re-do the modulation connections but it seems to work

    what exactly doesn’t work for you?

  • Thanks for looking into it @pedro. I wasn’t talking about the (just) rack, but a processor rack. But it turns out the problem isn’t the rack, but pasting in general. Although a session restart fixes that. Here’s an unlisted video, in which I add a code module, select a preset and it works, then I cut and paste it, and it doesn’t. Then I restart the session and it works again. And afterwards I do the same inside an instrument rack. It was an entirely fresh session, and the same happens with code modules, that have fx in them.

    https://youtu.be/v6hiHkdLlN4

  • Hi everyone,

    really like the code module and getting nice stuff done, but I just can’t seem to get a code for a delay to work. Teally would like some help. Thanks!

  • edited March 31

    There’s two recipes at the end of the code docs that might help you, the karplus strong and the fb delay

    but what exactly is your problem or what are you trying to achieve?

  • @pedro

    thanks. I got a delay code module now. AI helped me and I understand it now.

  • Hi all, I’m trying to code a simple sinefold effect, but everything I try does not work. And I can code this easely in other apps. Anyone can provide me with a code?

    thanks!

  • edited April 5

    @Stanley I gave it a quick try, lots of room for improvement but looks close for starters


    // Wavefolder

    #persample

    in = input(audio, Input)

    fold = param(value, Fold)    // Adjusts the folding intensity (0 to 1)

    symm = param(value, Symmetry)  // Shifts the center to create asymmetric folding (0 to 1)

    gain = param(value, Gain)    // Input drive (0 to 1)

    // 1. Apply Gain to the input

    // We scale the gain so 0-1 param covers a useful range (up to 4x boost)

    drive = 1 + (gain * 3)

    sig = in * drive

    // 2. Apply Symmetry (DC Offset)

    // We shift the signal by up to 1.0 before folding

    offset = (symm * 2) - 1

    sig = sig + (offset * 0.5)

    // 3. Wavefolding Calculation

    // Based on the sinusoidal folding formula: out = sin(in * fold_amount)

    // We map the 'fold' parameter to a range that allows multiple folds

    foldAmount = 1 + (fold * 10)

    out = sin(sig * foldAmount)

    // 4. Post-fold Normalization

    // Wavefolding can reduce the perceived volume; we apply a slight 

    // makeup gain based on the fold amount to keep levels consistent

    makeup = 1 + (fold * 0.5)

    out = out * makeup

    // 5. Basic anti-aliasing

    out = lpf6(out, 15000)

    output(out, audio, Output)

  • @pedro thanks!

    I found out the link at the very start of this topic is the best way to go, but thank you so much.

  • edited April 6

    Use what works for you. Ben's Gem is a great help. I've used it before but now I just find it overkill sometimes not because there's something wrong with it, but because I explained that to gemini already (under my google account) so there's no need to repeat.


    For instance this is the prompt I used that got me something working at first try (did a bit of polishing later):

    ---

    I want you to write me a Drambo code module audio FX using the pseudo language described here:

    https://www.beepstreet.com/drambo-docs/code-module.html

    What I want to build is a wavefolder effect.

    Structure of the code should be like this:

    #persample // <--- my bad, this wouldn't be needed as there's no feedback

    in=input(audio, Input)

    // out = ...generate wavefolded output

    output(out, audio, Output)

    There should also be at least these parameters (they range from 0 to 1):

    fold=param(value, FoldAmount) // Adjusts how many times the wave is folded

    symm=param(value, Symmetry) // Allows different folding of the top and bottom parts of the wave, creating varied timbres

    but you can include more if you see fit

    Use ideas from here

    https://ccrma.stanford.edu/~jatin/ComplexNonlinearities/Wavefolder.html

    ---

  • Just a little krell patch, learned from @giku ‘s own “simple krell”


    But I want to break out of sound generation inside the code module, so I’m building an EOC self-repeating random envelope, that you just get going but then at each EOC outputs random: attack, decay, velocity, pitch, PC (how can I do this last one?)

  • edited April 8

    Pretty cool with a fb loop and an env follower, IMHO

    Better

Sign In or Register to comment.