From 88d7868e12d9f60bfeba2462a8d981bd67bf2aa7 Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 20:25:20 -0500 Subject: [PATCH 01/66] document some math funcs --- packages/core/pattern.mjs | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index c5b7953c9..0776aa5a1 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1156,12 +1156,61 @@ const COMPOSERS = { * @tags math */ div: [numeralArgs((a, b) => a / b)], + /** + * + * Divides each number by the given factor. + * @name div + * @memberof Pattern + * @tags math + */ mod: [numeralArgs(_mod)], + /** + * + * Raises each number to the power of the given factor + * @name pow + * @memberof Pattern + * @tags math + */ pow: [numeralArgs(Math.pow)], + /** + * + * Preforms a bitwise `and` on each number and the given factor + * @name band + * @memberof Pattern + * @tags math + */ band: [numeralArgs((a, b) => a & b)], + /** + * + * Preforms a bitwise `or` on each number and the given factor + * @name bor + * @memberof Pattern + * @tags math + */ bor: [numeralArgs((a, b) => a | b)], + /** + * + * Preforms a bitwise `xor` on each number and the given factor + * @name bxor + * @memberof Pattern + * @tags math + */ bxor: [numeralArgs((a, b) => a ^ b)], + /** + * + * Preforms a bitwise `left shift` on each number by the given factor + * @name blshift + * @memberof Pattern + * @tags math + */ blshift: [numeralArgs((a, b) => a << b)], + /** + * + * Preforms a bitwise `right shift` on each number by the given factor + * @name brshift + * @memberof Pattern + * @tags math + */ brshift: [numeralArgs((a, b) => a >> b)], // TODO - force numerical comparison if both look like numbers? From 4f7a0f76d3b60f8bed1bca726608824e9be428e0 Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 20:49:37 -0500 Subject: [PATCH 02/66] make getFreq and getFrequency synonyms and add doc --- packages/core/util.mjs | 41 ++- undocumented.json | 732 ++++++++++++++++++++--------------------- 2 files changed, 390 insertions(+), 383 deletions(-) diff --git a/packages/core/util.mjs b/packages/core/util.mjs index c38c8e05f..15adedebc 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -70,17 +70,6 @@ export const getEventOffsetMs = (targetTimeSeconds, currentTimeSeconds) => { return (targetTimeSeconds - currentTimeSeconds) * 1000; }; -/** - * @deprecated does not appear to be referenced or invoked anywhere in the codebase - * @noAutocomplete - */ -export const getFreq = (noteOrMidi) => { - if (typeof noteOrMidi === 'number') { - return midiToFreq(noteOrMidi); - } - return midiToFreq(noteToMidi(noteOrMidi)); -}; - const pcs = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; /** * @deprecated only used in workshop (first-notes) @@ -130,25 +119,47 @@ export const getPlayableNoteValue = (hap) => { return note; }; +/** + * Given a hap, returns the frequency of the note or freq, if defined + * Given a note, returns the frequency of that note + * `getFreq('c3')` + * Given a midi note number, returns the frequency of that midi note + * `getFreq('c3')` + * + * @name getFrequency + * @synonyms getFreq + * @param {Hap | note | number} + * @returns {Number} + */ export const getFrequency = (hap) => { - let { value, context } = hap; + let { value, context } = + typeof hap == 'object' + ? hap + : // default: interpret value as midi note. + { value: hap, context: { type: 'note' } }; // if value is number => interpret as midi number as long as its not marked as frequency if (typeof value === 'object') { if (value.freq) { return value.freq; } - return getFreq(value.note || value.n || value.value); + let v = value.note || value.n || value.value; + if (typeof v === 'number') { + return midiToFreq(v); + } + return midiToFreq(noteToMidi(v)); } if (typeof value === 'number' && context.type !== 'frequency') { - value = midiToFreq(hap.value); + value = midiToFreq(value); } else if (typeof value === 'string' && isNote(value)) { - value = midiToFreq(noteToMidi(hap.value)); + value = midiToFreq(noteToMidi(value)); } else if (typeof value !== 'number') { throw new Error('not a note or frequency: ' + value); } return value; }; +export const getFreq = (hap) => getFrequency(hap); + // rotate array by n steps (to the left) export const rotate = (arr, n) => arr.slice(n).concat(arr.slice(0, n)); diff --git a/undocumented.json b/undocumented.json index 3f5a3be30..f59eb2039 100644 --- a/undocumented.json +++ b/undocumented.json @@ -8,12 +8,19 @@ ], "/packages/codemirror/autocomplete.mjs": [ "Autocomplete", + "bankCompletions", + "getSynonymDoc", "strudelAutocomplete", "isAutoCompletionEnabled" ], - "/packages/codemirror/tooltip.mjs": [ - "strudelTooltip", - "isTooltipEnabled" + "/packages/codemirror/basicSetup.mjs": [ + "basicSetup", + "minimalSetup" + ], + "/packages/codemirror/block_utilities.mjs": [ + "getBlockRegions", + "getBlockAt", + "evalBlock" ], "/packages/codemirror/flash.mjs": [ "setFlash", @@ -24,15 +31,29 @@ "/packages/codemirror/highlight.mjs": [ "setMiniLocations", "showMiniLocations", + "displayMiniLocations", "updateMiniLocations", "highlightMiniLocations", + "getMiniLocations", + "getActiveMiniLocations", "highlightExtension", "isPatternHighlightingEnabled" ], "/packages/codemirror/keybindings.mjs": [ "keybindings" ], - "/packages/codemirror/themes/strudel-theme.mjs": [], + "/packages/codemirror/labelJump.mjs": [ + "jumpToCharacter" + ], + "/packages/codemirror/slider.mjs": [ + "acorn parse error: SyntaxError: undefined" + ], + "/packages/codemirror/themes/theme-helper.mjs": [ + "createTheme" + ], + "/packages/codemirror/themes/strudel-theme.mjs": [ + "settings" + ], "/packages/codemirror/themes/bluescreen.mjs": [ "settings" ], @@ -48,7 +69,103 @@ "/packages/codemirror/themes/algoboy.mjs": [ "settings" ], - "/packages/codemirror/themes/terminal.mjs": [ + "/packages/codemirror/themes/CutiePi.mjs": [ + "settings" + ], + "/packages/codemirror/themes/sonic-pink.mjs": [ + "settings" + ], + "/packages/codemirror/themes/red-text.mjs": [ + "settings" + ], + "/packages/codemirror/themes/green-text.mjs": [ + "settings" + ], + "/packages/codemirror/themes/archBtw.mjs": [ + "settings" + ], + "/packages/codemirror/themes/fruitDaw.mjs": [ + "settings" + ], + "/packages/codemirror/themes/bluescreenlight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/androidstudio.mjs": [ + "settings" + ], + "/packages/codemirror/themes/atomone.mjs": [ + "settings" + ], + "/packages/codemirror/themes/aura.mjs": [ + "settings" + ], + "/packages/codemirror/themes/darcula.mjs": [ + "settings" + ], + "/packages/codemirror/themes/dracula.mjs": [ + "settings" + ], + "/packages/codemirror/themes/duotoneDark.mjs": [ + "settings" + ], + "/packages/codemirror/themes/eclipse.mjs": [ + "settings" + ], + "/packages/codemirror/themes/githubDark.mjs": [ + "settings" + ], + "/packages/codemirror/themes/githubLight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/gruvboxDark.mjs": [ + "settings" + ], + "/packages/codemirror/themes/gruvboxLight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/materialDark.mjs": [ + "settings" + ], + "/packages/codemirror/themes/materialLight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/nord.mjs": [ + "settings" + ], + "/packages/codemirror/themes/monokai.mjs": [ + "settings" + ], + "/packages/codemirror/themes/solarizedDark.mjs": [ + "settings" + ], + "/packages/codemirror/themes/solarizedLight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/sublime.mjs": [ + "settings" + ], + "/packages/codemirror/themes/tokyoNight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/tokioNightStorm.mjs": [ + "settings" + ], + "/packages/codemirror/themes/tokyoNightDay.mjs": [ + "settings" + ], + "/packages/codemirror/themes/vscodeDark.mjs": [ + "settings" + ], + "/packages/codemirror/themes/vscodeLight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/xcodeLight.mjs": [ + "settings" + ], + "/packages/codemirror/themes/bbedit.mjs": [ + "settings" + ], + "/packages/codemirror/themes/noctisLilac.mjs": [ "settings" ], "/packages/codemirror/themes.mjs": [ @@ -60,50 +177,34 @@ "initTheme", "activateTheme" ], - "/packages/codemirror/slider.mjs": [ - "acorn parse error: SyntaxError: undefined" + "/packages/codemirror/tooltip.mjs": [ + "strudelTooltip", + "isTooltipEnabled" ], "/packages/codemirror/widget.mjs": [ - "addWidget", - "updateWidgets", - "setWidget", - "BlockWidget", - "widgetPlugin", - "registerWidget" + "acorn parse error: SyntaxError: undefined" ], "/packages/codemirror/codemirror.mjs": [ - "defaultSettings", - "codemirrorSettings", - "initEditor", - "StrudelMirror" + "acorn parse error: SyntaxError: undefined" ], "/packages/codemirror/index.mjs": [], - "/packages/core/fraction.mjs": [ - "gcd", - "lcm" - ], - "/packages/core/timespan.mjs": [ - "TimeSpan" - ], - "/packages/core/hap.mjs": [ - "Hap" - ], - "/packages/core/state.mjs": [ - "State" - ], "/packages/core/logger.mjs": [ "logKey", + "errorLogger", "logger" ], "/packages/core/util.mjs": [ "isNoteWithOctave", "isNote", "tokenizeNote", + "getAccidentalsOffset", "noteToMidi", "midiToFreq", "freqToMidi", "valueToMidi", + "getEventOffsetMs", "_mod", + "averageArray", "nanFallback", "getSoundIndex", "getPlayableNoteValue", @@ -122,13 +223,37 @@ "fractionalArgs", "splitAt", "zipWith", + "pairs", "clamp", "sol2note", + "uniq", + "uniqsort", + "uniqsortr", "unicodeToBase64", "base64ToUnicode", "code2hash", "hash2code", - "objectMap" + "objectMap", + "cycleToSeconds", + "ClockCollator", + "getPerformanceTimeSeconds", + "keyAlias", + "getCurrentKeyboardState", + "stringifyValues" + ], + "/packages/core/fraction.mjs": [ + "gcd", + "lcm", + "isFraction" + ], + "/packages/core/timespan.mjs": [ + "TimeSpan" + ], + "/packages/core/hap.mjs": [ + "Hap" + ], + "/packages/core/state.mjs": [ + "State" ], "/packages/core/value.mjs": [ "unionWithObj", @@ -137,292 +262,77 @@ "map" ], "/packages/core/drawLine.mjs": [], - "/packages/core/pattern.mjs": [ - "setStringParser", - "polyrhythm", - "pr", - "pm", - "nothing", - "isPattern", - "reify", - "stackLeft", - "stackRight", - "stackCentre", - "stackBy", - "fastcat", - "_polymeterListSteps", - "set", - "keep", - "keepif", - "mod", - "pow", - "band", - "bor", - "bxor", - "blshift", - "brshift", - "lt", - "gt", - "lte", - "gte", - "eq", - "eqt", - "ne", - "net", - "and", - "or", - "func", - "compressSpan", - "compressspan", - "fastgap", - "focusSpan", - "focusspan", - "density", - "sparsity", - "zoomArc", - "zoomarc", - "inv", - "juxby", - "echowith", - "stutWith", - "stutwith", - "iterback", - "slowchunk", - "slowChunk", - "chunkback", - "fastchunk", - "bypass", - "hsla", - "hsl", - "loopat", - "loopatcps" - ], - "/packages/core/controls.mjs": [ - "createParam", - "sound", - "src", - "att", - "fmi", - "fmrelease", - "fmvelocity", - "analyze", - "fft", - "dec", - "sus", - "rel", - "hold", - "bandf", - "bp", - "bandq", - "loopb", - "loope", - "ch", - "phaserrate", - "phasr", - "ph", - "phs", - "phc", - "phd", - "phasdp", - "cutoff", - "ctf", - "lp", - "lpe", - "hpe", - "bpe", - "lpa", - "hpa", - "bpa", - "lpd", - "hpd", - "bpd", - "lps", - "hps", - "bps", - "lpr", - "hpr", - "bpr", - "fanchor", - "vibrato", - "v", - "vmod", - "hcutoff", - "hp", - "hresonance", - "resonance", - "delayfb", - "dfb", - "delayt", - "dt", - "lock", - "det", - "fadeTime", - "fadeOutTime", - "fadeInTime", - "patt", - "pdec", - "psustain", - "psus", - "prel", - "gate", - "gat", - "activeLabel", - "degree", - "mtranspose", - "ctranspose", - "harmonic", - "stepsPerOctave", - "octaveR", - "nudge", - "overgain", - "overshape", - "panspan", - "pansplay", - "panwidth", - "panorient", - "rate", - "slide", - "semitone", - "voice", - "chord", - "dictionary", - "dict", - "anchor", - "offset", - "octaves", - "mode", - "rlp", - "rdim", - "rfade", - "ir", - "size", - "sz", - "rsize", - "dist", - "compressorKnee", - "compressorRatio", - "compressorAttack", - "compressorRelease", - "waveloss", - "density", - "expression", - "sustainpedal", - "tremolodepth", - "tremdp", - "tremolorate", - "tremr", - "fshift", - "fshiftnote", - "fshiftphase", - "triode", - "krush", - "kcutoff", - "octer", - "octersub", - "octersubsub", - "ring", - "ringf", - "ringdf", - "freeze", - "xsdelay", - "tsdelay", - "real", - "imag", - "enhance", - "partials", - "comb", - "smear", - "scram", - "binshift", - "hbrick", - "lbrick", - "midichan", - "control", - "ccn", - "ccv", - "polyTouch", - "midibend", - "miditouch", - "ctlNum", - "frameRate", - "frames", - "hours", - "midicmd", - "minutes", - "progNum", - "seconds", - "songPtr", - "uid", - "val", - "cps", - "legato", - "dur", - "zrand", - "curve", - "deltaSlide", - "pitchJump", - "pitchJumpTime", - "lfo", - "repeatTime", - "znoise", - "zmod", - "zcrush", - "zdelay", - "tremolo", - "zzfx", - "colour", - "createParams", - "ad", - "ds", - "ar" - ], - "/packages/core/euclid.mjs": [ - "bjork", - "euclidrot" - ], - "/packages/core/zyklus.mjs": [], - "/packages/core/signal.mjs": [ - "steady", - "signal", - "isaw", - "isaw2", - "saw2", - "sine2", - "cosine2", - "square2", - "tri2", - "time", - "_brandBy", - "_irand", - "pickSqueeze", - "pickmodSqueeze", - "__chooseWith", - "randcat", - "wrandcat", - "perlinWith", - "degradeByWith" - ], - "/packages/core/speak.mjs": [ - "speak" - ], "/packages/core/evaluate.mjs": [ + "strudelScope", + "userDefinedKeys", "evalScope", "evaluate" ], + "/packages/core/pattern.mjs": [ + "acorn parse error: SyntaxError: undefined" + ], + "/packages/core/controls.mjs": [ + "acorn parse error: SyntaxError: undefined" + ], + "/packages/core/euclid.mjs": [ + "bjorklund", + "bjork", + "euclidrot", + "eish" + ], + "/packages/core/zyklus.mjs": [], + "/packages/core/cyclist.mjs": [ + "Cyclist" + ], + "/packages/core/impure.mjs": [ + "reset_state", + "reset_timelines", + "timeline" + ], + "/packages/core/pick.mjs": [ + "pickSqueeze", + "pickmodSqueeze" + ], "/packages/core/neocyclist.mjs": [ "NeoCyclist" ], - "/packages/core/cyclist.mjs": [ - "Cyclist" - ], - "/packages/core/time.mjs": [ + "/packages/core/schedulerState.mjs": [ "getTime", - "setTime" + "setTime", + "setCpsFunc", + "getCps", + "setPattern", + "getPattern", + "setTriggerFunc", + "getTriggerFunc", + "setIsStarted", + "getIsStarted" ], "/packages/core/repl.mjs": [ "repl", "getTrigger" ], + "/packages/core/signal.mjs": [ + "steady", + "signal", + "mouseY", + "mouseX", + "getRandsAtTime", + "randrun", + "_brandBy", + "_irand", + "__chooseWith", + "chooseIn", + "chooseOut", + "randcat", + "wrandcat", + "degradeByWith", + "_keyDown", + "perCycle" + ], + "/packages/core/speak.mjs": [ + "speak" + ], "/packages/core/ui.mjs": [ "backgroundImage", "cleanupUi" @@ -440,13 +350,19 @@ "isTauri" ], "/packages/desktopbridge/midibridge.mjs": [], - "/packages/desktopbridge/oscbridge.mjs": [], + "/packages/desktopbridge/oscbridge.mjs": [ + "oscTriggerTauri" + ], "/packages/desktopbridge/index.mjs": [], "/packages/draw/draw.mjs": [ "getDrawContext", "cleanupDraw", + "cleanupDrawContext", "Framer", - "Drawer" + "Drawer", + "getComputedPropertyValue", + "getTheme", + "setTheme" ], "/packages/draw/animate.mjs": [ "x", @@ -467,16 +383,98 @@ "convertHexToNumber" ], "/packages/draw/pianoroll.mjs": [ + "__pianoroll", "getDrawOptions", "getPunchcardPainter", "drawPianoroll" ], "/packages/draw/spiral.mjs": [], + "/packages/draw/pitchwheel.mjs": [], "/packages/draw/index.mjs": [], + "/packages/edo/edoscale.mjs": [ + "EdoScale" + ], + "/packages/edo/ratios.mjs": [], + "/packages/edo/intervals.mjs": [ + "Intervals" + ], + "/packages/edo/pitches.mjs": [ + "Pitches" + ], + "/packages/edo/edo.mjs": [], + "/packages/edo/index.mjs": [ + "packageName" + ], + "/packages/superdough/audioContext.mjs": [ + "setDefaultAudioContext", + "setAudioContext", + "getAudioContext", + "getAudioContextCurrentTime" + ], + "/packages/superdough/logger.mjs": [ + "errorLogger", + "logger", + "setLogger" + ], + "/packages/superdough/noise.mjs": [ + "getNoiseBuffer", + "getNoiseOscillator", + "getNoiseMix" + ], + "/packages/superdough/nodePools.mjs": [ + "isPoolable", + "releaseNodeToPool", + "getNodeFromPool" + ], + "/packages/superdough/util.mjs": [ + "tokenizeNote", + "getAccidentalsOffset", + "noteToMidi", + "midiToFreq", + "clamp", + "freqToMidi", + "valueToMidi", + "nanFallback", + "_mod", + "getSoundIndex", + "cycleToSeconds", + "secondsToCycle", + "getCommonSampleInfo", + "getBaseURL" + ], + "/packages/superdough/helpers.mjs": [ + "noises", + "gainNode", + "effectSend", + "getWorklet", + "getParamADSR", + "getEnvelope", + "getLfo", + "getCompressor", + "getADSRValues", + "getParamLfo", + "applyParameterModulators", + "createFilter", + "drywet", + "getPitchEnvelope", + "getVibratoOscillator", + "scheduleAtTime", + "webAudioTimeout", + "applyFM", + "distortionAlgorithms", + "getDistortionAlgorithm", + "getDistortion", + "getFrequencyFromValue", + "onceEnded", + "releaseAudioNode", + "cleanupOnEnd" + ], + "/packages/midi/util.mjs": [], + "/packages/midi/input.mjs": [ + "acorn parse error: SyntaxError: undefined" + ], "/packages/midi/midi.mjs": [ - "WebMidi", - "enableWebMidi", - "midin" + "acorn parse error: SyntaxError: undefined" ], "/packages/midi/index.mjs": [], "/packages/mini/krill-parser.js": [], @@ -502,6 +500,7 @@ "/packages/repl/index.mjs": [], "/packages/soundfonts/gm.mjs": [], "/packages/soundfonts/fontloader.mjs": [ + "setSoundfontUrl", "getFontBufferSource", "getFontPitch", "registerSoundfonts" @@ -521,67 +520,40 @@ "/packages/superdough/vowel.mjs": [ "vowelFormant" ], - "/packages/superdough/logger.mjs": [ - "logger", - "setLogger" + "/packages/superdough/superdoughdata.mjs": [ + "getSuperdoughControlTargets" ], - "/packages/superdough/util.mjs": [ - "tokenizeNote", - "noteToMidi", - "midiToFreq", - "clamp", - "freqToMidi", - "valueToMidi", - "nanFallback", - "_mod", - "getSoundIndex" - ], - "/packages/superdough/helpers.mjs": [ - "gainNode", - "getParamADSR", - "getCompressor", - "getADSRValues", - "createFilter", - "drywet", - "getPitchEnvelope", - "getVibratoOscillator", - "webAudioTimeout", - "applyFM" + "/packages/superdough/modulators.mjs": [ + "connectLFO", + "connectEnvelope", + "connectBusModulator" ], "/packages/superdough/sampler.mjs": [ "getCachedBuffer", + "getDuration", + "getSampleInfo", + "getSampleBuffer", "getSampleBufferSource", "loadBuffer", "reverseBuffer", "getLoadedBuffer", "processSampleMap", "registerSamplesPrefix", - "onTriggerSample" + "fetchSampleMap", + "onTriggerSample", + "registerSampleSource" + ], + "/packages/superdough/superdoughoutput.mjs": [ + "acorn parse error: SyntaxError: undefined" + ], + "/packages/superdough/wavetable.mjs": [ + "Warpmode", + "resetSeenKeys", + "registerWaveTable", + "onTriggerSynth" ], "/packages/superdough/superdough.mjs": [ - "soundMap", - "registerSound", - "getSound", - "resetLoadedSounds", - "setDefaultAudioContext", - "getAudioContext", - "getWorklet", - "initAudio", - "initAudioOnFirstClick", - "initializeAudioOutput", - "connectToDestination", - "panic", - "analysers", - "analysersData", - "getAnalyserById", - "getAnalyzerData", - "resetGlobalEffects", - "superdough", - "superdoughTrigger" - ], - "/packages/superdough/noise.mjs": [ - "getNoiseOscillator", - "getNoiseMix" + "acorn parse error: SyntaxError: undefined" ], "/packages/superdough/synth.mjs": [ "registerSynthSounds", @@ -622,7 +594,11 @@ "Step", "Note" ], - "/packages/tonal/tonal.mjs": [], + "/packages/tonal/tonal.mjs": [ + "trans", + "scaleTrans", + "strans" + ], "/packages/tonal/ireal.mjs": [ "simple", "complex" @@ -639,31 +615,51 @@ "packageName" ], "/packages/transpiler/transpiler.mjs": [ - "registerWidgetType", - "transpiler", - "getWidgetID" + "registerLanguage", + "getLanguages", + "registerTranspilerPlugin", + "getPlugins", + "transpiler" + ], + "/packages/transpiler/plugin-kabelsalat.mjs": [ + "genExprSource", + "extractPatternPlaceholders" + ], + "/packages/transpiler/plugin-mini.mjs": [ + "acorn parse error: SyntaxError: undefined" + ], + "/packages/transpiler/plugin-sample.mjs": [ + "withAwait" + ], + "/packages/transpiler/plugin-widgets.mjs": [ + "acorn parse error: SyntaxError: undefined" ], "/packages/transpiler/index.mjs": [ "evaluate" ], + "/packages/webaudio/supradough.mjs": [ + "fetchSampleMap", + "doughsamples" + ], "/packages/webaudio/webaudio.mjs": [ "webaudioOutput", + "renderPatternAudio", "webaudioRepl" ], "/packages/webaudio/scope.mjs": [ "drawTimeScope", "drawFrequencyScope" ], + "/packages/webaudio/spectrum.mjs": [], "/packages/webaudio/index.mjs": [], + "/packages/xen/tunejs.js": [], "/packages/xen/xen.mjs": [ "edo", - "xen", - "tuning" - ], - "/packages/xen/tunejs.js": [], - "/packages/xen/tune.mjs": [ - "tune" + "ftrans", + "fTrans", + "fTranspose" ], + "/packages/xen/tune.mjs": [], "/packages/xen/index.mjs": [], "/index.mjs": [] } From c2572f4faddc1afb3bcf4eb4bda7524a4a57088b Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 21:00:26 -0500 Subject: [PATCH 03/66] deprecate chooseIn --- packages/core/signal.mjs | 9 +++++++-- packages/mondough/mondough.mjs | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 4398e2ab7..42e841f6e 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -569,9 +569,14 @@ export const chooseInWith = (pat, xs) => { */ export const choose = (...xs) => chooseWith(rand, xs); -// todo: doc +/** + * Has no difference with `choose` + * + * @name chooseIn + * @deprecated + * @returns {Pattern} + */ export const chooseIn = (...xs) => chooseInWith(rand, xs); -export const chooseOut = choose; /** * Chooses from the given list of values (or patterns of values), according diff --git a/packages/mondough/mondough.mjs b/packages/mondough/mondough.mjs index a230c3cb9..a043adb83 100644 --- a/packages/mondough/mondough.mjs +++ b/packages/mondough/mondough.mjs @@ -46,7 +46,6 @@ lib[':'] = tail; lib['..'] = range; lib['def'] = () => silence; lib['or'] = (...children) => chooseIn(...children); // always has structure but is cyclewise.. e.g. "s oh*8.dec[.04 | .5]" -//lib['or'] = (...children) => chooseOut(...children); // "s oh*8.dec[.04 | .5]" is better but "dec[.04 | .5].s oh*8" has no struct function evaluator(node, scope) { const { type } = node; From 10b0e300578ab5efca62880e50a215bcb603a55b Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 22:13:46 -0500 Subject: [PATCH 04/66] a bunch more docs --- packages/core/pattern.mjs | 4 +- packages/core/schedulerState.mjs | 11 + packages/core/signal.mjs | 22 +- packages/core/util.mjs | 1 + test/__snapshots__/examples.test.mjs.snap | 330 ++++++++++++++++++++++ 5 files changed, 365 insertions(+), 3 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 0776aa5a1..ed5ff9cd4 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1074,7 +1074,7 @@ const COMPOSERS = { * @param {Pattern} pat * @returns {Pattern} * @memberof Pattern - * @tags internal, combiners + * @tags internals, combiners * @example * // because input pattern has `s` set, * // it overrides the "sine" declared earlier @@ -1099,7 +1099,7 @@ const COMPOSERS = { * @param {Pattern} pat * @memberof Pattern * @returns {Pattern} - * @tags internal, combiners + * @tags internals, combiners * @example * // notes, already defined, will stay "c a f e", * // while "s", not defined, will be set to "piano" diff --git a/packages/core/schedulerState.mjs b/packages/core/schedulerState.mjs index 14b4c9054..7c39fb373 100644 --- a/packages/core/schedulerState.mjs +++ b/packages/core/schedulerState.mjs @@ -9,6 +9,12 @@ let cpsFunc; let pattern; let triggerFunc; let isStarted; + +/** + * @name getTime + * @tags internals + * @returns {Number} the current cycle time as a number + */ export function getTime() { if (!time) { throw new Error('no time set! use setTime to define a time source'); @@ -24,6 +30,11 @@ export function setCpsFunc(func) { cpsFunc = func; } +/** + * @name getCps + * @tags internals + * @returns {Number} the current cycles per second + */ export function getCps() { return cpsFunc?.(); } diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 42e841f6e..7e0dfbbc3 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -15,6 +15,24 @@ export function steady(value) { return new Pattern((state) => [new Hap(undefined, state.span, value)]); } +/** + * Creates a signal given by the input function + * + * @name signal + * @param {*} func + * @returns {Pattern} signal + * @tags generators + * @example + * // sawtooth signal definition + * signal(t=>t%1).segment(16).range(30,40).note() + * @example + * signal(t=>Math.pow(t,2)%1).range(40, 60).add(5).segment(16) + * .off(0, x=>x.add(7)) + * .note() + * .s("gm_electric_guitar_jazz,gm_epiano") + * .gain(1.7) + * .pianoroll() + */ export const signal = (func) => { const query = (state) => [new Hap(undefined, state.span, func(state.span.begin, state.controls))]; return new Pattern(query); @@ -172,6 +190,7 @@ export const time = signal(id); /** * The mouse's x position value ranges from 0 to 1. * @name mousex + * @synonyms mouseX * @return {Pattern} * @tags external_io * @example @@ -182,6 +201,7 @@ export const time = signal(id); /** * The mouse's y position value ranges from 0 to 1. * @name mousey + * @synonyms mouseY * @return {Pattern} * @tags external_io * @example @@ -571,7 +591,7 @@ export const choose = (...xs) => chooseWith(rand, xs); /** * Has no difference with `choose` - * + * * @name chooseIn * @deprecated * @returns {Pattern} diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 15adedebc..2f499adae 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -127,6 +127,7 @@ export const getPlayableNoteValue = (hap) => { * `getFreq('c3')` * * @name getFrequency + * @tags pitch * @synonyms getFreq * @param {Hap | note | number} * @returns {Number} diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 458904baf..2aaa80a51 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -11639,6 +11639,336 @@ exports[`runs examples > example "shuffle" example index 1 1`] = ` ] `; +exports[`runs examples > example "signal" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:30 ]", + "[ 1/16 → 1/8 | note:30.625 ]", + "[ 1/8 → 3/16 | note:31.25 ]", + "[ 3/16 → 1/4 | note:31.875 ]", + "[ 1/4 → 5/16 | note:32.5 ]", + "[ 5/16 → 3/8 | note:33.125 ]", + "[ 3/8 → 7/16 | note:33.75 ]", + "[ 7/16 → 1/2 | note:34.375 ]", + "[ 1/2 → 9/16 | note:35 ]", + "[ 9/16 → 5/8 | note:35.625 ]", + "[ 5/8 → 11/16 | note:36.25 ]", + "[ 11/16 → 3/4 | note:36.875 ]", + "[ 3/4 → 13/16 | note:37.5 ]", + "[ 13/16 → 7/8 | note:38.125 ]", + "[ 7/8 → 15/16 | note:38.75 ]", + "[ 15/16 → 1/1 | note:39.375 ]", + "[ 1/1 → 17/16 | note:30 ]", + "[ 17/16 → 9/8 | note:30.625 ]", + "[ 9/8 → 19/16 | note:31.25 ]", + "[ 19/16 → 5/4 | note:31.875 ]", + "[ 5/4 → 21/16 | note:32.5 ]", + "[ 21/16 → 11/8 | note:33.125 ]", + "[ 11/8 → 23/16 | note:33.75 ]", + "[ 23/16 → 3/2 | note:34.375 ]", + "[ 3/2 → 25/16 | note:35 ]", + "[ 25/16 → 13/8 | note:35.625 ]", + "[ 13/8 → 27/16 | note:36.25 ]", + "[ 27/16 → 7/4 | note:36.875 ]", + "[ 7/4 → 29/16 | note:37.5 ]", + "[ 29/16 → 15/8 | note:38.125 ]", + "[ 15/8 → 31/16 | note:38.75 ]", + "[ 31/16 → 2/1 | note:39.375 ]", + "[ 2/1 → 33/16 | note:30 ]", + "[ 33/16 → 17/8 | note:30.625 ]", + "[ 17/8 → 35/16 | note:31.25 ]", + "[ 35/16 → 9/4 | note:31.875 ]", + "[ 9/4 → 37/16 | note:32.5 ]", + "[ 37/16 → 19/8 | note:33.125 ]", + "[ 19/8 → 39/16 | note:33.75 ]", + "[ 39/16 → 5/2 | note:34.375 ]", + "[ 5/2 → 41/16 | note:35 ]", + "[ 41/16 → 21/8 | note:35.625 ]", + "[ 21/8 → 43/16 | note:36.25 ]", + "[ 43/16 → 11/4 | note:36.875 ]", + "[ 11/4 → 45/16 | note:37.5 ]", + "[ 45/16 → 23/8 | note:38.125 ]", + "[ 23/8 → 47/16 | note:38.75 ]", + "[ 47/16 → 3/1 | note:39.375 ]", + "[ 3/1 → 49/16 | note:30 ]", + "[ 49/16 → 25/8 | note:30.625 ]", + "[ 25/8 → 51/16 | note:31.25 ]", + "[ 51/16 → 13/4 | note:31.875 ]", + "[ 13/4 → 53/16 | note:32.5 ]", + "[ 53/16 → 27/8 | note:33.125 ]", + "[ 27/8 → 55/16 | note:33.75 ]", + "[ 55/16 → 7/2 | note:34.375 ]", + "[ 7/2 → 57/16 | note:35 ]", + "[ 57/16 → 29/8 | note:35.625 ]", + "[ 29/8 → 59/16 | note:36.25 ]", + "[ 59/16 → 15/4 | note:36.875 ]", + "[ 15/4 → 61/16 | note:37.5 ]", + "[ 61/16 → 31/8 | note:38.125 ]", + "[ 31/8 → 63/16 | note:38.75 ]", + "[ 63/16 → 4/1 | note:39.375 ]", +] +`; + +exports[`runs examples > example "signal" example index 1 1`] = ` +[ + "[ 0/1 → 1/16 | note:45 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 0/1 → 1/16 | note:45 s:gm_epiano gain:1.7 ]", + "[ 0/1 → 1/16 | note:52 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 0/1 → 1/16 | note:52 s:gm_epiano gain:1.7 ]", + "[ 1/16 → 1/8 | note:45.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/16 → 1/8 | note:45.078125 s:gm_epiano gain:1.7 ]", + "[ 1/16 → 1/8 | note:52.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/16 → 1/8 | note:52.078125 s:gm_epiano gain:1.7 ]", + "[ 1/8 → 3/16 | note:45.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/8 → 3/16 | note:45.3125 s:gm_epiano gain:1.7 ]", + "[ 1/8 → 3/16 | note:52.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/8 → 3/16 | note:52.3125 s:gm_epiano gain:1.7 ]", + "[ 3/16 → 1/4 | note:45.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/16 → 1/4 | note:45.703125 s:gm_epiano gain:1.7 ]", + "[ 3/16 → 1/4 | note:52.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/16 → 1/4 | note:52.703125 s:gm_epiano gain:1.7 ]", + "[ 1/4 → 5/16 | note:46.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/4 → 5/16 | note:46.25 s:gm_epiano gain:1.7 ]", + "[ 1/4 → 5/16 | note:53.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/4 → 5/16 | note:53.25 s:gm_epiano gain:1.7 ]", + "[ 5/16 → 3/8 | note:46.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/16 → 3/8 | note:46.953125 s:gm_epiano gain:1.7 ]", + "[ 5/16 → 3/8 | note:53.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/16 → 3/8 | note:53.953125 s:gm_epiano gain:1.7 ]", + "[ 3/8 → 7/16 | note:47.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/8 → 7/16 | note:47.8125 s:gm_epiano gain:1.7 ]", + "[ 3/8 → 7/16 | note:54.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/8 → 7/16 | note:54.8125 s:gm_epiano gain:1.7 ]", + "[ 7/16 → 1/2 | note:48.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/16 → 1/2 | note:48.828125 s:gm_epiano gain:1.7 ]", + "[ 7/16 → 1/2 | note:55.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/16 → 1/2 | note:55.828125 s:gm_epiano gain:1.7 ]", + "[ 1/2 → 9/16 | note:50 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/2 → 9/16 | note:50 s:gm_epiano gain:1.7 ]", + "[ 1/2 → 9/16 | note:57 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/2 → 9/16 | note:57 s:gm_epiano gain:1.7 ]", + "[ 9/16 → 5/8 | note:51.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 9/16 → 5/8 | note:51.328125 s:gm_epiano gain:1.7 ]", + "[ 9/16 → 5/8 | note:58.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 9/16 → 5/8 | note:58.328125 s:gm_epiano gain:1.7 ]", + "[ 5/8 → 11/16 | note:52.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/8 → 11/16 | note:52.8125 s:gm_epiano gain:1.7 ]", + "[ 5/8 → 11/16 | note:59.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/8 → 11/16 | note:59.8125 s:gm_epiano gain:1.7 ]", + "[ 11/16 → 3/4 | note:54.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 11/16 → 3/4 | note:54.453125 s:gm_epiano gain:1.7 ]", + "[ 11/16 → 3/4 | note:61.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 11/16 → 3/4 | note:61.453125 s:gm_epiano gain:1.7 ]", + "[ 3/4 → 13/16 | note:56.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/4 → 13/16 | note:56.25 s:gm_epiano gain:1.7 ]", + "[ 3/4 → 13/16 | note:63.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/4 → 13/16 | note:63.25 s:gm_epiano gain:1.7 ]", + "[ 13/16 → 7/8 | note:58.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 13/16 → 7/8 | note:58.203125 s:gm_epiano gain:1.7 ]", + "[ 13/16 → 7/8 | note:65.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 13/16 → 7/8 | note:65.203125 s:gm_epiano gain:1.7 ]", + "[ 7/8 → 15/16 | note:60.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/8 → 15/16 | note:60.3125 s:gm_epiano gain:1.7 ]", + "[ 7/8 → 15/16 | note:67.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/8 → 15/16 | note:67.3125 s:gm_epiano gain:1.7 ]", + "[ 15/16 → 1/1 | note:62.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 15/16 → 1/1 | note:62.578125 s:gm_epiano gain:1.7 ]", + "[ 15/16 → 1/1 | note:69.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 15/16 → 1/1 | note:69.578125 s:gm_epiano gain:1.7 ]", + "[ 1/1 → 17/16 | note:45 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/1 → 17/16 | note:45 s:gm_epiano gain:1.7 ]", + "[ 1/1 → 17/16 | note:52 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 1/1 → 17/16 | note:52 s:gm_epiano gain:1.7 ]", + "[ 17/16 → 9/8 | note:47.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 17/16 → 9/8 | note:47.578125 s:gm_epiano gain:1.7 ]", + "[ 17/16 → 9/8 | note:54.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 17/16 → 9/8 | note:54.578125 s:gm_epiano gain:1.7 ]", + "[ 9/8 → 19/16 | note:50.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 9/8 → 19/16 | note:50.3125 s:gm_epiano gain:1.7 ]", + "[ 9/8 → 19/16 | note:57.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 9/8 → 19/16 | note:57.3125 s:gm_epiano gain:1.7 ]", + "[ 19/16 → 5/4 | note:53.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 19/16 → 5/4 | note:53.203125 s:gm_epiano gain:1.7 ]", + "[ 19/16 → 5/4 | note:60.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 19/16 → 5/4 | note:60.203125 s:gm_epiano gain:1.7 ]", + "[ 5/4 → 21/16 | note:56.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/4 → 21/16 | note:56.25 s:gm_epiano gain:1.7 ]", + "[ 5/4 → 21/16 | note:63.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/4 → 21/16 | note:63.25 s:gm_epiano gain:1.7 ]", + "[ 21/16 → 11/8 | note:59.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 21/16 → 11/8 | note:59.453125 s:gm_epiano gain:1.7 ]", + "[ 21/16 → 11/8 | note:66.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 21/16 → 11/8 | note:66.453125 s:gm_epiano gain:1.7 ]", + "[ 11/8 → 23/16 | note:62.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 11/8 → 23/16 | note:62.8125 s:gm_epiano gain:1.7 ]", + "[ 11/8 → 23/16 | note:69.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 11/8 → 23/16 | note:69.8125 s:gm_epiano gain:1.7 ]", + "[ 23/16 → 3/2 | note:46.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 23/16 → 3/2 | note:46.328125 s:gm_epiano gain:1.7 ]", + "[ 23/16 → 3/2 | note:53.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 23/16 → 3/2 | note:53.328125 s:gm_epiano gain:1.7 ]", + "[ 3/2 → 25/16 | note:50 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/2 → 25/16 | note:50 s:gm_epiano gain:1.7 ]", + "[ 3/2 → 25/16 | note:57 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/2 → 25/16 | note:57 s:gm_epiano gain:1.7 ]", + "[ 25/16 → 13/8 | note:53.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 25/16 → 13/8 | note:53.828125 s:gm_epiano gain:1.7 ]", + "[ 25/16 → 13/8 | note:60.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 25/16 → 13/8 | note:60.828125 s:gm_epiano gain:1.7 ]", + "[ 13/8 → 27/16 | note:57.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 13/8 → 27/16 | note:57.8125 s:gm_epiano gain:1.7 ]", + "[ 13/8 → 27/16 | note:64.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 13/8 → 27/16 | note:64.8125 s:gm_epiano gain:1.7 ]", + "[ 27/16 → 7/4 | note:61.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 27/16 → 7/4 | note:61.953125 s:gm_epiano gain:1.7 ]", + "[ 27/16 → 7/4 | note:68.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 27/16 → 7/4 | note:68.953125 s:gm_epiano gain:1.7 ]", + "[ 7/4 → 29/16 | note:46.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/4 → 29/16 | note:46.25 s:gm_epiano gain:1.7 ]", + "[ 7/4 → 29/16 | note:53.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/4 → 29/16 | note:53.25 s:gm_epiano gain:1.7 ]", + "[ 29/16 → 15/8 | note:50.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 29/16 → 15/8 | note:50.703125 s:gm_epiano gain:1.7 ]", + "[ 29/16 → 15/8 | note:57.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 29/16 → 15/8 | note:57.703125 s:gm_epiano gain:1.7 ]", + "[ 15/8 → 31/16 | note:55.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 15/8 → 31/16 | note:55.3125 s:gm_epiano gain:1.7 ]", + "[ 15/8 → 31/16 | note:62.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 15/8 → 31/16 | note:62.3125 s:gm_epiano gain:1.7 ]", + "[ 31/16 → 2/1 | note:60.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 31/16 → 2/1 | note:60.078125 s:gm_epiano gain:1.7 ]", + "[ 31/16 → 2/1 | note:67.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 31/16 → 2/1 | note:67.078125 s:gm_epiano gain:1.7 ]", + "[ 2/1 → 33/16 | note:45 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 2/1 → 33/16 | note:45 s:gm_epiano gain:1.7 ]", + "[ 2/1 → 33/16 | note:52 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 2/1 → 33/16 | note:52 s:gm_epiano gain:1.7 ]", + "[ 33/16 → 17/8 | note:50.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 33/16 → 17/8 | note:50.078125 s:gm_epiano gain:1.7 ]", + "[ 33/16 → 17/8 | note:57.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 33/16 → 17/8 | note:57.078125 s:gm_epiano gain:1.7 ]", + "[ 17/8 → 35/16 | note:55.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 17/8 → 35/16 | note:55.3125 s:gm_epiano gain:1.7 ]", + "[ 17/8 → 35/16 | note:62.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 17/8 → 35/16 | note:62.3125 s:gm_epiano gain:1.7 ]", + "[ 35/16 → 9/4 | note:60.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 35/16 → 9/4 | note:60.703125 s:gm_epiano gain:1.7 ]", + "[ 35/16 → 9/4 | note:67.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 35/16 → 9/4 | note:67.703125 s:gm_epiano gain:1.7 ]", + "[ 9/4 → 37/16 | note:46.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 9/4 → 37/16 | note:46.25 s:gm_epiano gain:1.7 ]", + "[ 9/4 → 37/16 | note:53.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 9/4 → 37/16 | note:53.25 s:gm_epiano gain:1.7 ]", + "[ 37/16 → 19/8 | note:51.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 37/16 → 19/8 | note:51.953125 s:gm_epiano gain:1.7 ]", + "[ 37/16 → 19/8 | note:58.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 37/16 → 19/8 | note:58.953125 s:gm_epiano gain:1.7 ]", + "[ 19/8 → 39/16 | note:57.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 19/8 → 39/16 | note:57.8125 s:gm_epiano gain:1.7 ]", + "[ 19/8 → 39/16 | note:64.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 19/8 → 39/16 | note:64.8125 s:gm_epiano gain:1.7 ]", + "[ 39/16 → 5/2 | note:63.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 39/16 → 5/2 | note:63.828125 s:gm_epiano gain:1.7 ]", + "[ 39/16 → 5/2 | note:70.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 39/16 → 5/2 | note:70.828125 s:gm_epiano gain:1.7 ]", + "[ 5/2 → 41/16 | note:50 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/2 → 41/16 | note:50 s:gm_epiano gain:1.7 ]", + "[ 5/2 → 41/16 | note:57 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 5/2 → 41/16 | note:57 s:gm_epiano gain:1.7 ]", + "[ 41/16 → 21/8 | note:56.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 41/16 → 21/8 | note:56.328125 s:gm_epiano gain:1.7 ]", + "[ 41/16 → 21/8 | note:63.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 41/16 → 21/8 | note:63.328125 s:gm_epiano gain:1.7 ]", + "[ 21/8 → 43/16 | note:62.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 21/8 → 43/16 | note:62.8125 s:gm_epiano gain:1.7 ]", + "[ 21/8 → 43/16 | note:69.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 21/8 → 43/16 | note:69.8125 s:gm_epiano gain:1.7 ]", + "[ 43/16 → 11/4 | note:49.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 43/16 → 11/4 | note:49.453125 s:gm_epiano gain:1.7 ]", + "[ 43/16 → 11/4 | note:56.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 43/16 → 11/4 | note:56.453125 s:gm_epiano gain:1.7 ]", + "[ 11/4 → 45/16 | note:56.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 11/4 → 45/16 | note:56.25 s:gm_epiano gain:1.7 ]", + "[ 11/4 → 45/16 | note:63.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 11/4 → 45/16 | note:63.25 s:gm_epiano gain:1.7 ]", + "[ 45/16 → 23/8 | note:63.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 45/16 → 23/8 | note:63.203125 s:gm_epiano gain:1.7 ]", + "[ 45/16 → 23/8 | note:70.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 45/16 → 23/8 | note:70.203125 s:gm_epiano gain:1.7 ]", + "[ 23/8 → 47/16 | note:50.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 23/8 → 47/16 | note:50.3125 s:gm_epiano gain:1.7 ]", + "[ 23/8 → 47/16 | note:57.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 23/8 → 47/16 | note:57.3125 s:gm_epiano gain:1.7 ]", + "[ 47/16 → 3/1 | note:57.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 47/16 → 3/1 | note:57.578125 s:gm_epiano gain:1.7 ]", + "[ 47/16 → 3/1 | note:64.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 47/16 → 3/1 | note:64.578125 s:gm_epiano gain:1.7 ]", + "[ 3/1 → 49/16 | note:45 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/1 → 49/16 | note:45 s:gm_epiano gain:1.7 ]", + "[ 3/1 → 49/16 | note:52 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 3/1 → 49/16 | note:52 s:gm_epiano gain:1.7 ]", + "[ 49/16 → 25/8 | note:52.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 49/16 → 25/8 | note:52.578125 s:gm_epiano gain:1.7 ]", + "[ 49/16 → 25/8 | note:59.578125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 49/16 → 25/8 | note:59.578125 s:gm_epiano gain:1.7 ]", + "[ 25/8 → 51/16 | note:60.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 25/8 → 51/16 | note:60.3125 s:gm_epiano gain:1.7 ]", + "[ 25/8 → 51/16 | note:67.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 25/8 → 51/16 | note:67.3125 s:gm_epiano gain:1.7 ]", + "[ 51/16 → 13/4 | note:48.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 51/16 → 13/4 | note:48.203125 s:gm_epiano gain:1.7 ]", + "[ 51/16 → 13/4 | note:55.203125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 51/16 → 13/4 | note:55.203125 s:gm_epiano gain:1.7 ]", + "[ 13/4 → 53/16 | note:56.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 13/4 → 53/16 | note:56.25 s:gm_epiano gain:1.7 ]", + "[ 13/4 → 53/16 | note:63.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 13/4 → 53/16 | note:63.25 s:gm_epiano gain:1.7 ]", + "[ 53/16 → 27/8 | note:64.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 53/16 → 27/8 | note:64.453125 s:gm_epiano gain:1.7 ]", + "[ 53/16 → 27/8 | note:71.453125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 53/16 → 27/8 | note:71.453125 s:gm_epiano gain:1.7 ]", + "[ 27/8 → 55/16 | note:52.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 27/8 → 55/16 | note:52.8125 s:gm_epiano gain:1.7 ]", + "[ 27/8 → 55/16 | note:59.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 27/8 → 55/16 | note:59.8125 s:gm_epiano gain:1.7 ]", + "[ 55/16 → 7/2 | note:61.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 55/16 → 7/2 | note:61.328125 s:gm_epiano gain:1.7 ]", + "[ 55/16 → 7/2 | note:68.328125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 55/16 → 7/2 | note:68.328125 s:gm_epiano gain:1.7 ]", + "[ 7/2 → 57/16 | note:50 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/2 → 57/16 | note:50 s:gm_epiano gain:1.7 ]", + "[ 7/2 → 57/16 | note:57 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 7/2 → 57/16 | note:57 s:gm_epiano gain:1.7 ]", + "[ 57/16 → 29/8 | note:58.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 57/16 → 29/8 | note:58.828125 s:gm_epiano gain:1.7 ]", + "[ 57/16 → 29/8 | note:65.828125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 57/16 → 29/8 | note:65.828125 s:gm_epiano gain:1.7 ]", + "[ 29/8 → 59/16 | note:47.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 29/8 → 59/16 | note:47.8125 s:gm_epiano gain:1.7 ]", + "[ 29/8 → 59/16 | note:54.8125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 29/8 → 59/16 | note:54.8125 s:gm_epiano gain:1.7 ]", + "[ 59/16 → 15/4 | note:56.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 59/16 → 15/4 | note:56.953125 s:gm_epiano gain:1.7 ]", + "[ 59/16 → 15/4 | note:63.953125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 59/16 → 15/4 | note:63.953125 s:gm_epiano gain:1.7 ]", + "[ 15/4 → 61/16 | note:46.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 15/4 → 61/16 | note:46.25 s:gm_epiano gain:1.7 ]", + "[ 15/4 → 61/16 | note:53.25 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 15/4 → 61/16 | note:53.25 s:gm_epiano gain:1.7 ]", + "[ 61/16 → 31/8 | note:55.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 61/16 → 31/8 | note:55.703125 s:gm_epiano gain:1.7 ]", + "[ 61/16 → 31/8 | note:62.703125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 61/16 → 31/8 | note:62.703125 s:gm_epiano gain:1.7 ]", + "[ 31/8 → 63/16 | note:45.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 31/8 → 63/16 | note:45.3125 s:gm_epiano gain:1.7 ]", + "[ 31/8 → 63/16 | note:52.3125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 31/8 → 63/16 | note:52.3125 s:gm_epiano gain:1.7 ]", + "[ 63/16 → 4/1 | note:55.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 63/16 → 4/1 | note:55.078125 s:gm_epiano gain:1.7 ]", + "[ 63/16 → 4/1 | note:62.078125 s:gm_electric_guitar_jazz gain:1.7 ]", + "[ 63/16 → 4/1 | note:62.078125 s:gm_epiano gain:1.7 ]", +] +`; + exports[`runs examples > example "silence" example index 0 1`] = `[]`; exports[`runs examples > example "sine" example index 0 1`] = ` From d4ec7e9a55e44111966f47f7c0d42194eb08269a Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 23:15:45 -0500 Subject: [PATCH 05/66] modify degradeByWith to be more user friendly, add doc --- packages/core/signal.mjs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 7e0dfbbc3..2a688e5b3 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import { Hap } from './hap.mjs'; -import { Pattern, fastcat, pure, register, reify, silence, stack, sequenceP } from './pattern.mjs'; +import { Pattern, fastcat, pure, register, reify, silence, stack, sequenceP, isPattern } from './pattern.mjs'; import Fraction from './fraction.mjs'; import { id, keyAlias, getCurrentKeyboardState } from './util.mjs'; @@ -742,11 +742,32 @@ export const perlin = signal((t, controls) => _perlin(t, controls.randSeed)); */ export const berlin = signal((t, controls) => _berlin(t, controls.randSeed)); +/** + * `degradeByWith` is like `degradeBy`, but with more granular control. + * + * `degradeBy(x)` is essentially `degradeByWith(rand, x)` + * + * This allows you to do things other than with `rand`, see examples below + * + * @name degradeByWith + * @param {Pattern|number} test + * @param {Pattern|number} testAgainst + * @memberof Pattern + * @tags temporal + * @returns {Pattern} + * @example + * note("c a f e").degradeByWith(sine, .1) + * @example + * note("c a f e").degradeByWith(perlin, .5) + * @example + * note("c a f e").degradeByWith(sine, cosine) + */ export const degradeByWith = register( 'degradeByWith', - (withPat, x, pat) => pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)), - true, - true, + (withPat, x, pat) => { + if (!isPattern(withPat)) withPat = pure(withPat) + return pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)); + }, ); /** From 787adfb20f5301e6398c4751c395ac54378bcb0c Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 23:16:15 -0500 Subject: [PATCH 06/66] format and snapshot --- packages/core/signal.mjs | 23 +++++++++------------ test/__snapshots__/examples.test.mjs.snap | 25 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 2a688e5b3..9bd13d102 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -743,12 +743,12 @@ export const perlin = signal((t, controls) => _perlin(t, controls.randSeed)); export const berlin = signal((t, controls) => _berlin(t, controls.randSeed)); /** - * `degradeByWith` is like `degradeBy`, but with more granular control. - * + * `degradeByWith` is like `degradeBy`, but with more granular control. + * * `degradeBy(x)` is essentially `degradeByWith(rand, x)` - * - * This allows you to do things other than with `rand`, see examples below - * + * + * This allows you to do things other than with `rand`, see examples below + * * @name degradeByWith * @param {Pattern|number} test * @param {Pattern|number} testAgainst @@ -759,16 +759,13 @@ export const berlin = signal((t, controls) => _berlin(t, controls.randSeed)); * note("c a f e").degradeByWith(sine, .1) * @example * note("c a f e").degradeByWith(perlin, .5) - * @example + * @example * note("c a f e").degradeByWith(sine, cosine) */ -export const degradeByWith = register( - 'degradeByWith', - (withPat, x, pat) => { - if (!isPattern(withPat)) withPat = pure(withPat) - return pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)); - }, -); +export const degradeByWith = register('degradeByWith', (withPat, x, pat) => { + if (!isPattern(withPat)) withPat = pure(withPat); + return pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)); +}); /** * Randomly removes events from the pattern by a given amount. diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 2aaa80a51..af6ede9fe 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2977,6 +2977,31 @@ exports[`runs examples > example "degradeBy" example index 2 1`] = ` ] `; +exports[`runs examples > example "degradeByWith" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c ]", + "[ 1/4 → 1/2 | note:a ]", + "[ 1/2 → 3/4 | note:f ]", + "[ 3/4 → 1/1 | note:e ]", + "[ 1/1 → 5/4 | note:c ]", + "[ 5/4 → 3/2 | note:a ]", + "[ 3/2 → 7/4 | note:f ]", + "[ 7/4 → 2/1 | note:e ]", + "[ 2/1 → 9/4 | note:c ]", + "[ 9/4 → 5/2 | note:a ]", + "[ 5/2 → 11/4 | note:f ]", + "[ 11/4 → 3/1 | note:e ]", + "[ 3/1 → 13/4 | note:c ]", + "[ 13/4 → 7/2 | note:a ]", + "[ 7/2 → 15/4 | note:f ]", + "[ 15/4 → 4/1 | note:e ]", +] +`; + +exports[`runs examples > example "degradeByWith" example index 1 1`] = `[]`; + +exports[`runs examples > example "degradeByWith" example index 2 1`] = `[]`; + exports[`runs examples > example "delay" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd delay:0 ]", From a76a23a43d7f193de375d870068c820de9467217 Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 21 Feb 2026 23:37:00 -0500 Subject: [PATCH 07/66] replace a missing sample with an existing one in example --- packages/core/controls.mjs | 2 +- test/__snapshots__/examples.test.mjs.snap | 96 +++++++++++------------ 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 7971f1ed9..d22167c0c 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -118,7 +118,7 @@ export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound'); * @param {number | Pattern} position Position in the wavetable from 0 to 1 * @synonyms wavetablePosition * @example - * s("squelch").bank("wt_digital").seg(8).note("F1").wt("0 0.25 0.5 0.75 1") + * s("wt_digital").seg(8).note("F1").wt("0 0.25 0.5 0.75 1") */ export const { wt, wavetablePosition } = registerControl('wt', 'wavetablePosition'); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index af6ede9fe..70519695f 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -14798,54 +14798,54 @@ exports[`runs examples > example "withValue" example index 0 1`] = ` exports[`runs examples > example "wt" example index 0 1`] = ` [ - "[ 0/1 → 1/8 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ (1/8 → 1/5) ⇝ 1/4 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ 1/8 ⇜ (1/5 → 1/4) | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 1/4 → 3/8 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ (3/8 → 2/5) ⇝ 1/2 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 3/8 ⇜ (2/5 → 1/2) | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ (1/2 → 3/5) ⇝ 5/8 | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ 1/2 ⇜ (3/5 → 5/8) | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 5/8 → 3/4 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ (3/4 → 4/5) ⇝ 7/8 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 3/4 ⇜ (4/5 → 7/8) | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 7/8 → 1/1 | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 1/1 → 9/8 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ (9/8 → 6/5) ⇝ 5/4 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ 9/8 ⇜ (6/5 → 5/4) | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 5/4 → 11/8 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ (11/8 → 7/5) ⇝ 3/2 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 11/8 ⇜ (7/5 → 3/2) | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ (3/2 → 8/5) ⇝ 13/8 | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ 3/2 ⇜ (8/5 → 13/8) | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 13/8 → 7/4 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ (7/4 → 9/5) ⇝ 15/8 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 7/4 ⇜ (9/5 → 15/8) | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 15/8 → 2/1 | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 2/1 → 17/8 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ (17/8 → 11/5) ⇝ 9/4 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ 17/8 ⇜ (11/5 → 9/4) | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 9/4 → 19/8 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ (19/8 → 12/5) ⇝ 5/2 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 19/8 ⇜ (12/5 → 5/2) | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ (5/2 → 13/5) ⇝ 21/8 | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ 5/2 ⇜ (13/5 → 21/8) | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 21/8 → 11/4 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ (11/4 → 14/5) ⇝ 23/8 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 11/4 ⇜ (14/5 → 23/8) | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 23/8 → 3/1 | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 3/1 → 25/8 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ (25/8 → 16/5) ⇝ 13/4 | s:squelch bank:wt_digital note:F1 wt:0 ]", - "[ 25/8 ⇜ (16/5 → 13/4) | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 13/4 → 27/8 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ (27/8 → 17/5) ⇝ 7/2 | s:squelch bank:wt_digital note:F1 wt:0.25 ]", - "[ 27/8 ⇜ (17/5 → 7/2) | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ (7/2 → 18/5) ⇝ 29/8 | s:squelch bank:wt_digital note:F1 wt:0.5 ]", - "[ 7/2 ⇜ (18/5 → 29/8) | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 29/8 → 15/4 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ (15/4 → 19/5) ⇝ 31/8 | s:squelch bank:wt_digital note:F1 wt:0.75 ]", - "[ 15/4 ⇜ (19/5 → 31/8) | s:squelch bank:wt_digital note:F1 wt:1 ]", - "[ 31/8 → 4/1 | s:squelch bank:wt_digital note:F1 wt:1 ]", + "[ 0/1 → 1/8 | s:wt_digital note:F1 wt:0 ]", + "[ (1/8 → 1/5) ⇝ 1/4 | s:wt_digital note:F1 wt:0 ]", + "[ 1/8 ⇜ (1/5 → 1/4) | s:wt_digital note:F1 wt:0.25 ]", + "[ 1/4 → 3/8 | s:wt_digital note:F1 wt:0.25 ]", + "[ (3/8 → 2/5) ⇝ 1/2 | s:wt_digital note:F1 wt:0.25 ]", + "[ 3/8 ⇜ (2/5 → 1/2) | s:wt_digital note:F1 wt:0.5 ]", + "[ (1/2 → 3/5) ⇝ 5/8 | s:wt_digital note:F1 wt:0.5 ]", + "[ 1/2 ⇜ (3/5 → 5/8) | s:wt_digital note:F1 wt:0.75 ]", + "[ 5/8 → 3/4 | s:wt_digital note:F1 wt:0.75 ]", + "[ (3/4 → 4/5) ⇝ 7/8 | s:wt_digital note:F1 wt:0.75 ]", + "[ 3/4 ⇜ (4/5 → 7/8) | s:wt_digital note:F1 wt:1 ]", + "[ 7/8 → 1/1 | s:wt_digital note:F1 wt:1 ]", + "[ 1/1 → 9/8 | s:wt_digital note:F1 wt:0 ]", + "[ (9/8 → 6/5) ⇝ 5/4 | s:wt_digital note:F1 wt:0 ]", + "[ 9/8 ⇜ (6/5 → 5/4) | s:wt_digital note:F1 wt:0.25 ]", + "[ 5/4 → 11/8 | s:wt_digital note:F1 wt:0.25 ]", + "[ (11/8 → 7/5) ⇝ 3/2 | s:wt_digital note:F1 wt:0.25 ]", + "[ 11/8 ⇜ (7/5 → 3/2) | s:wt_digital note:F1 wt:0.5 ]", + "[ (3/2 → 8/5) ⇝ 13/8 | s:wt_digital note:F1 wt:0.5 ]", + "[ 3/2 ⇜ (8/5 → 13/8) | s:wt_digital note:F1 wt:0.75 ]", + "[ 13/8 → 7/4 | s:wt_digital note:F1 wt:0.75 ]", + "[ (7/4 → 9/5) ⇝ 15/8 | s:wt_digital note:F1 wt:0.75 ]", + "[ 7/4 ⇜ (9/5 → 15/8) | s:wt_digital note:F1 wt:1 ]", + "[ 15/8 → 2/1 | s:wt_digital note:F1 wt:1 ]", + "[ 2/1 → 17/8 | s:wt_digital note:F1 wt:0 ]", + "[ (17/8 → 11/5) ⇝ 9/4 | s:wt_digital note:F1 wt:0 ]", + "[ 17/8 ⇜ (11/5 → 9/4) | s:wt_digital note:F1 wt:0.25 ]", + "[ 9/4 → 19/8 | s:wt_digital note:F1 wt:0.25 ]", + "[ (19/8 → 12/5) ⇝ 5/2 | s:wt_digital note:F1 wt:0.25 ]", + "[ 19/8 ⇜ (12/5 → 5/2) | s:wt_digital note:F1 wt:0.5 ]", + "[ (5/2 → 13/5) ⇝ 21/8 | s:wt_digital note:F1 wt:0.5 ]", + "[ 5/2 ⇜ (13/5 → 21/8) | s:wt_digital note:F1 wt:0.75 ]", + "[ 21/8 → 11/4 | s:wt_digital note:F1 wt:0.75 ]", + "[ (11/4 → 14/5) ⇝ 23/8 | s:wt_digital note:F1 wt:0.75 ]", + "[ 11/4 ⇜ (14/5 → 23/8) | s:wt_digital note:F1 wt:1 ]", + "[ 23/8 → 3/1 | s:wt_digital note:F1 wt:1 ]", + "[ 3/1 → 25/8 | s:wt_digital note:F1 wt:0 ]", + "[ (25/8 → 16/5) ⇝ 13/4 | s:wt_digital note:F1 wt:0 ]", + "[ 25/8 ⇜ (16/5 → 13/4) | s:wt_digital note:F1 wt:0.25 ]", + "[ 13/4 → 27/8 | s:wt_digital note:F1 wt:0.25 ]", + "[ (27/8 → 17/5) ⇝ 7/2 | s:wt_digital note:F1 wt:0.25 ]", + "[ 27/8 ⇜ (17/5 → 7/2) | s:wt_digital note:F1 wt:0.5 ]", + "[ (7/2 → 18/5) ⇝ 29/8 | s:wt_digital note:F1 wt:0.5 ]", + "[ 7/2 ⇜ (18/5 → 29/8) | s:wt_digital note:F1 wt:0.75 ]", + "[ 29/8 → 15/4 | s:wt_digital note:F1 wt:0.75 ]", + "[ (15/4 → 19/5) ⇝ 31/8 | s:wt_digital note:F1 wt:0.75 ]", + "[ 15/4 ⇜ (19/5 → 31/8) | s:wt_digital note:F1 wt:1 ]", + "[ 31/8 → 4/1 | s:wt_digital note:F1 wt:1 ]", ] `; From 53cce1d97b27e1723efcfaa3c7131918ccec4288 Mon Sep 17 00:00:00 2001 From: tyow Date: Sun, 22 Feb 2026 13:35:10 -0500 Subject: [PATCH 08/66] revert undocumented.json --- undocumented.json | 720 +++++++++++++++++++++++----------------------- 1 file changed, 362 insertions(+), 358 deletions(-) diff --git a/undocumented.json b/undocumented.json index f59eb2039..3f5a3be30 100644 --- a/undocumented.json +++ b/undocumented.json @@ -8,19 +8,12 @@ ], "/packages/codemirror/autocomplete.mjs": [ "Autocomplete", - "bankCompletions", - "getSynonymDoc", "strudelAutocomplete", "isAutoCompletionEnabled" ], - "/packages/codemirror/basicSetup.mjs": [ - "basicSetup", - "minimalSetup" - ], - "/packages/codemirror/block_utilities.mjs": [ - "getBlockRegions", - "getBlockAt", - "evalBlock" + "/packages/codemirror/tooltip.mjs": [ + "strudelTooltip", + "isTooltipEnabled" ], "/packages/codemirror/flash.mjs": [ "setFlash", @@ -31,29 +24,15 @@ "/packages/codemirror/highlight.mjs": [ "setMiniLocations", "showMiniLocations", - "displayMiniLocations", "updateMiniLocations", "highlightMiniLocations", - "getMiniLocations", - "getActiveMiniLocations", "highlightExtension", "isPatternHighlightingEnabled" ], "/packages/codemirror/keybindings.mjs": [ "keybindings" ], - "/packages/codemirror/labelJump.mjs": [ - "jumpToCharacter" - ], - "/packages/codemirror/slider.mjs": [ - "acorn parse error: SyntaxError: undefined" - ], - "/packages/codemirror/themes/theme-helper.mjs": [ - "createTheme" - ], - "/packages/codemirror/themes/strudel-theme.mjs": [ - "settings" - ], + "/packages/codemirror/themes/strudel-theme.mjs": [], "/packages/codemirror/themes/bluescreen.mjs": [ "settings" ], @@ -69,103 +48,7 @@ "/packages/codemirror/themes/algoboy.mjs": [ "settings" ], - "/packages/codemirror/themes/CutiePi.mjs": [ - "settings" - ], - "/packages/codemirror/themes/sonic-pink.mjs": [ - "settings" - ], - "/packages/codemirror/themes/red-text.mjs": [ - "settings" - ], - "/packages/codemirror/themes/green-text.mjs": [ - "settings" - ], - "/packages/codemirror/themes/archBtw.mjs": [ - "settings" - ], - "/packages/codemirror/themes/fruitDaw.mjs": [ - "settings" - ], - "/packages/codemirror/themes/bluescreenlight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/androidstudio.mjs": [ - "settings" - ], - "/packages/codemirror/themes/atomone.mjs": [ - "settings" - ], - "/packages/codemirror/themes/aura.mjs": [ - "settings" - ], - "/packages/codemirror/themes/darcula.mjs": [ - "settings" - ], - "/packages/codemirror/themes/dracula.mjs": [ - "settings" - ], - "/packages/codemirror/themes/duotoneDark.mjs": [ - "settings" - ], - "/packages/codemirror/themes/eclipse.mjs": [ - "settings" - ], - "/packages/codemirror/themes/githubDark.mjs": [ - "settings" - ], - "/packages/codemirror/themes/githubLight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/gruvboxDark.mjs": [ - "settings" - ], - "/packages/codemirror/themes/gruvboxLight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/materialDark.mjs": [ - "settings" - ], - "/packages/codemirror/themes/materialLight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/nord.mjs": [ - "settings" - ], - "/packages/codemirror/themes/monokai.mjs": [ - "settings" - ], - "/packages/codemirror/themes/solarizedDark.mjs": [ - "settings" - ], - "/packages/codemirror/themes/solarizedLight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/sublime.mjs": [ - "settings" - ], - "/packages/codemirror/themes/tokyoNight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/tokioNightStorm.mjs": [ - "settings" - ], - "/packages/codemirror/themes/tokyoNightDay.mjs": [ - "settings" - ], - "/packages/codemirror/themes/vscodeDark.mjs": [ - "settings" - ], - "/packages/codemirror/themes/vscodeLight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/xcodeLight.mjs": [ - "settings" - ], - "/packages/codemirror/themes/bbedit.mjs": [ - "settings" - ], - "/packages/codemirror/themes/noctisLilac.mjs": [ + "/packages/codemirror/themes/terminal.mjs": [ "settings" ], "/packages/codemirror/themes.mjs": [ @@ -177,34 +60,50 @@ "initTheme", "activateTheme" ], - "/packages/codemirror/tooltip.mjs": [ - "strudelTooltip", - "isTooltipEnabled" + "/packages/codemirror/slider.mjs": [ + "acorn parse error: SyntaxError: undefined" ], "/packages/codemirror/widget.mjs": [ - "acorn parse error: SyntaxError: undefined" + "addWidget", + "updateWidgets", + "setWidget", + "BlockWidget", + "widgetPlugin", + "registerWidget" ], "/packages/codemirror/codemirror.mjs": [ - "acorn parse error: SyntaxError: undefined" + "defaultSettings", + "codemirrorSettings", + "initEditor", + "StrudelMirror" ], "/packages/codemirror/index.mjs": [], + "/packages/core/fraction.mjs": [ + "gcd", + "lcm" + ], + "/packages/core/timespan.mjs": [ + "TimeSpan" + ], + "/packages/core/hap.mjs": [ + "Hap" + ], + "/packages/core/state.mjs": [ + "State" + ], "/packages/core/logger.mjs": [ "logKey", - "errorLogger", "logger" ], "/packages/core/util.mjs": [ "isNoteWithOctave", "isNote", "tokenizeNote", - "getAccidentalsOffset", "noteToMidi", "midiToFreq", "freqToMidi", "valueToMidi", - "getEventOffsetMs", "_mod", - "averageArray", "nanFallback", "getSoundIndex", "getPlayableNoteValue", @@ -223,37 +122,13 @@ "fractionalArgs", "splitAt", "zipWith", - "pairs", "clamp", "sol2note", - "uniq", - "uniqsort", - "uniqsortr", "unicodeToBase64", "base64ToUnicode", "code2hash", "hash2code", - "objectMap", - "cycleToSeconds", - "ClockCollator", - "getPerformanceTimeSeconds", - "keyAlias", - "getCurrentKeyboardState", - "stringifyValues" - ], - "/packages/core/fraction.mjs": [ - "gcd", - "lcm", - "isFraction" - ], - "/packages/core/timespan.mjs": [ - "TimeSpan" - ], - "/packages/core/hap.mjs": [ - "Hap" - ], - "/packages/core/state.mjs": [ - "State" + "objectMap" ], "/packages/core/value.mjs": [ "unionWithObj", @@ -262,77 +137,292 @@ "map" ], "/packages/core/drawLine.mjs": [], - "/packages/core/evaluate.mjs": [ - "strudelScope", - "userDefinedKeys", - "evalScope", - "evaluate" - ], "/packages/core/pattern.mjs": [ - "acorn parse error: SyntaxError: undefined" + "setStringParser", + "polyrhythm", + "pr", + "pm", + "nothing", + "isPattern", + "reify", + "stackLeft", + "stackRight", + "stackCentre", + "stackBy", + "fastcat", + "_polymeterListSteps", + "set", + "keep", + "keepif", + "mod", + "pow", + "band", + "bor", + "bxor", + "blshift", + "brshift", + "lt", + "gt", + "lte", + "gte", + "eq", + "eqt", + "ne", + "net", + "and", + "or", + "func", + "compressSpan", + "compressspan", + "fastgap", + "focusSpan", + "focusspan", + "density", + "sparsity", + "zoomArc", + "zoomarc", + "inv", + "juxby", + "echowith", + "stutWith", + "stutwith", + "iterback", + "slowchunk", + "slowChunk", + "chunkback", + "fastchunk", + "bypass", + "hsla", + "hsl", + "loopat", + "loopatcps" ], "/packages/core/controls.mjs": [ - "acorn parse error: SyntaxError: undefined" + "createParam", + "sound", + "src", + "att", + "fmi", + "fmrelease", + "fmvelocity", + "analyze", + "fft", + "dec", + "sus", + "rel", + "hold", + "bandf", + "bp", + "bandq", + "loopb", + "loope", + "ch", + "phaserrate", + "phasr", + "ph", + "phs", + "phc", + "phd", + "phasdp", + "cutoff", + "ctf", + "lp", + "lpe", + "hpe", + "bpe", + "lpa", + "hpa", + "bpa", + "lpd", + "hpd", + "bpd", + "lps", + "hps", + "bps", + "lpr", + "hpr", + "bpr", + "fanchor", + "vibrato", + "v", + "vmod", + "hcutoff", + "hp", + "hresonance", + "resonance", + "delayfb", + "dfb", + "delayt", + "dt", + "lock", + "det", + "fadeTime", + "fadeOutTime", + "fadeInTime", + "patt", + "pdec", + "psustain", + "psus", + "prel", + "gate", + "gat", + "activeLabel", + "degree", + "mtranspose", + "ctranspose", + "harmonic", + "stepsPerOctave", + "octaveR", + "nudge", + "overgain", + "overshape", + "panspan", + "pansplay", + "panwidth", + "panorient", + "rate", + "slide", + "semitone", + "voice", + "chord", + "dictionary", + "dict", + "anchor", + "offset", + "octaves", + "mode", + "rlp", + "rdim", + "rfade", + "ir", + "size", + "sz", + "rsize", + "dist", + "compressorKnee", + "compressorRatio", + "compressorAttack", + "compressorRelease", + "waveloss", + "density", + "expression", + "sustainpedal", + "tremolodepth", + "tremdp", + "tremolorate", + "tremr", + "fshift", + "fshiftnote", + "fshiftphase", + "triode", + "krush", + "kcutoff", + "octer", + "octersub", + "octersubsub", + "ring", + "ringf", + "ringdf", + "freeze", + "xsdelay", + "tsdelay", + "real", + "imag", + "enhance", + "partials", + "comb", + "smear", + "scram", + "binshift", + "hbrick", + "lbrick", + "midichan", + "control", + "ccn", + "ccv", + "polyTouch", + "midibend", + "miditouch", + "ctlNum", + "frameRate", + "frames", + "hours", + "midicmd", + "minutes", + "progNum", + "seconds", + "songPtr", + "uid", + "val", + "cps", + "legato", + "dur", + "zrand", + "curve", + "deltaSlide", + "pitchJump", + "pitchJumpTime", + "lfo", + "repeatTime", + "znoise", + "zmod", + "zcrush", + "zdelay", + "tremolo", + "zzfx", + "colour", + "createParams", + "ad", + "ds", + "ar" ], "/packages/core/euclid.mjs": [ - "bjorklund", "bjork", - "euclidrot", - "eish" + "euclidrot" ], "/packages/core/zyklus.mjs": [], - "/packages/core/cyclist.mjs": [ - "Cyclist" - ], - "/packages/core/impure.mjs": [ - "reset_state", - "reset_timelines", - "timeline" - ], - "/packages/core/pick.mjs": [ + "/packages/core/signal.mjs": [ + "steady", + "signal", + "isaw", + "isaw2", + "saw2", + "sine2", + "cosine2", + "square2", + "tri2", + "time", + "_brandBy", + "_irand", "pickSqueeze", - "pickmodSqueeze" + "pickmodSqueeze", + "__chooseWith", + "randcat", + "wrandcat", + "perlinWith", + "degradeByWith" + ], + "/packages/core/speak.mjs": [ + "speak" + ], + "/packages/core/evaluate.mjs": [ + "evalScope", + "evaluate" ], "/packages/core/neocyclist.mjs": [ "NeoCyclist" ], - "/packages/core/schedulerState.mjs": [ + "/packages/core/cyclist.mjs": [ + "Cyclist" + ], + "/packages/core/time.mjs": [ "getTime", - "setTime", - "setCpsFunc", - "getCps", - "setPattern", - "getPattern", - "setTriggerFunc", - "getTriggerFunc", - "setIsStarted", - "getIsStarted" + "setTime" ], "/packages/core/repl.mjs": [ "repl", "getTrigger" ], - "/packages/core/signal.mjs": [ - "steady", - "signal", - "mouseY", - "mouseX", - "getRandsAtTime", - "randrun", - "_brandBy", - "_irand", - "__chooseWith", - "chooseIn", - "chooseOut", - "randcat", - "wrandcat", - "degradeByWith", - "_keyDown", - "perCycle" - ], - "/packages/core/speak.mjs": [ - "speak" - ], "/packages/core/ui.mjs": [ "backgroundImage", "cleanupUi" @@ -350,19 +440,13 @@ "isTauri" ], "/packages/desktopbridge/midibridge.mjs": [], - "/packages/desktopbridge/oscbridge.mjs": [ - "oscTriggerTauri" - ], + "/packages/desktopbridge/oscbridge.mjs": [], "/packages/desktopbridge/index.mjs": [], "/packages/draw/draw.mjs": [ "getDrawContext", "cleanupDraw", - "cleanupDrawContext", "Framer", - "Drawer", - "getComputedPropertyValue", - "getTheme", - "setTheme" + "Drawer" ], "/packages/draw/animate.mjs": [ "x", @@ -383,98 +467,16 @@ "convertHexToNumber" ], "/packages/draw/pianoroll.mjs": [ - "__pianoroll", "getDrawOptions", "getPunchcardPainter", "drawPianoroll" ], "/packages/draw/spiral.mjs": [], - "/packages/draw/pitchwheel.mjs": [], "/packages/draw/index.mjs": [], - "/packages/edo/edoscale.mjs": [ - "EdoScale" - ], - "/packages/edo/ratios.mjs": [], - "/packages/edo/intervals.mjs": [ - "Intervals" - ], - "/packages/edo/pitches.mjs": [ - "Pitches" - ], - "/packages/edo/edo.mjs": [], - "/packages/edo/index.mjs": [ - "packageName" - ], - "/packages/superdough/audioContext.mjs": [ - "setDefaultAudioContext", - "setAudioContext", - "getAudioContext", - "getAudioContextCurrentTime" - ], - "/packages/superdough/logger.mjs": [ - "errorLogger", - "logger", - "setLogger" - ], - "/packages/superdough/noise.mjs": [ - "getNoiseBuffer", - "getNoiseOscillator", - "getNoiseMix" - ], - "/packages/superdough/nodePools.mjs": [ - "isPoolable", - "releaseNodeToPool", - "getNodeFromPool" - ], - "/packages/superdough/util.mjs": [ - "tokenizeNote", - "getAccidentalsOffset", - "noteToMidi", - "midiToFreq", - "clamp", - "freqToMidi", - "valueToMidi", - "nanFallback", - "_mod", - "getSoundIndex", - "cycleToSeconds", - "secondsToCycle", - "getCommonSampleInfo", - "getBaseURL" - ], - "/packages/superdough/helpers.mjs": [ - "noises", - "gainNode", - "effectSend", - "getWorklet", - "getParamADSR", - "getEnvelope", - "getLfo", - "getCompressor", - "getADSRValues", - "getParamLfo", - "applyParameterModulators", - "createFilter", - "drywet", - "getPitchEnvelope", - "getVibratoOscillator", - "scheduleAtTime", - "webAudioTimeout", - "applyFM", - "distortionAlgorithms", - "getDistortionAlgorithm", - "getDistortion", - "getFrequencyFromValue", - "onceEnded", - "releaseAudioNode", - "cleanupOnEnd" - ], - "/packages/midi/util.mjs": [], - "/packages/midi/input.mjs": [ - "acorn parse error: SyntaxError: undefined" - ], "/packages/midi/midi.mjs": [ - "acorn parse error: SyntaxError: undefined" + "WebMidi", + "enableWebMidi", + "midin" ], "/packages/midi/index.mjs": [], "/packages/mini/krill-parser.js": [], @@ -500,7 +502,6 @@ "/packages/repl/index.mjs": [], "/packages/soundfonts/gm.mjs": [], "/packages/soundfonts/fontloader.mjs": [ - "setSoundfontUrl", "getFontBufferSource", "getFontPitch", "registerSoundfonts" @@ -520,40 +521,67 @@ "/packages/superdough/vowel.mjs": [ "vowelFormant" ], - "/packages/superdough/superdoughdata.mjs": [ - "getSuperdoughControlTargets" + "/packages/superdough/logger.mjs": [ + "logger", + "setLogger" ], - "/packages/superdough/modulators.mjs": [ - "connectLFO", - "connectEnvelope", - "connectBusModulator" + "/packages/superdough/util.mjs": [ + "tokenizeNote", + "noteToMidi", + "midiToFreq", + "clamp", + "freqToMidi", + "valueToMidi", + "nanFallback", + "_mod", + "getSoundIndex" + ], + "/packages/superdough/helpers.mjs": [ + "gainNode", + "getParamADSR", + "getCompressor", + "getADSRValues", + "createFilter", + "drywet", + "getPitchEnvelope", + "getVibratoOscillator", + "webAudioTimeout", + "applyFM" ], "/packages/superdough/sampler.mjs": [ "getCachedBuffer", - "getDuration", - "getSampleInfo", - "getSampleBuffer", "getSampleBufferSource", "loadBuffer", "reverseBuffer", "getLoadedBuffer", "processSampleMap", "registerSamplesPrefix", - "fetchSampleMap", - "onTriggerSample", - "registerSampleSource" - ], - "/packages/superdough/superdoughoutput.mjs": [ - "acorn parse error: SyntaxError: undefined" - ], - "/packages/superdough/wavetable.mjs": [ - "Warpmode", - "resetSeenKeys", - "registerWaveTable", - "onTriggerSynth" + "onTriggerSample" ], "/packages/superdough/superdough.mjs": [ - "acorn parse error: SyntaxError: undefined" + "soundMap", + "registerSound", + "getSound", + "resetLoadedSounds", + "setDefaultAudioContext", + "getAudioContext", + "getWorklet", + "initAudio", + "initAudioOnFirstClick", + "initializeAudioOutput", + "connectToDestination", + "panic", + "analysers", + "analysersData", + "getAnalyserById", + "getAnalyzerData", + "resetGlobalEffects", + "superdough", + "superdoughTrigger" + ], + "/packages/superdough/noise.mjs": [ + "getNoiseOscillator", + "getNoiseMix" ], "/packages/superdough/synth.mjs": [ "registerSynthSounds", @@ -594,11 +622,7 @@ "Step", "Note" ], - "/packages/tonal/tonal.mjs": [ - "trans", - "scaleTrans", - "strans" - ], + "/packages/tonal/tonal.mjs": [], "/packages/tonal/ireal.mjs": [ "simple", "complex" @@ -615,51 +639,31 @@ "packageName" ], "/packages/transpiler/transpiler.mjs": [ - "registerLanguage", - "getLanguages", - "registerTranspilerPlugin", - "getPlugins", - "transpiler" - ], - "/packages/transpiler/plugin-kabelsalat.mjs": [ - "genExprSource", - "extractPatternPlaceholders" - ], - "/packages/transpiler/plugin-mini.mjs": [ - "acorn parse error: SyntaxError: undefined" - ], - "/packages/transpiler/plugin-sample.mjs": [ - "withAwait" - ], - "/packages/transpiler/plugin-widgets.mjs": [ - "acorn parse error: SyntaxError: undefined" + "registerWidgetType", + "transpiler", + "getWidgetID" ], "/packages/transpiler/index.mjs": [ "evaluate" ], - "/packages/webaudio/supradough.mjs": [ - "fetchSampleMap", - "doughsamples" - ], "/packages/webaudio/webaudio.mjs": [ "webaudioOutput", - "renderPatternAudio", "webaudioRepl" ], "/packages/webaudio/scope.mjs": [ "drawTimeScope", "drawFrequencyScope" ], - "/packages/webaudio/spectrum.mjs": [], "/packages/webaudio/index.mjs": [], - "/packages/xen/tunejs.js": [], "/packages/xen/xen.mjs": [ "edo", - "ftrans", - "fTrans", - "fTranspose" + "xen", + "tuning" + ], + "/packages/xen/tunejs.js": [], + "/packages/xen/tune.mjs": [ + "tune" ], - "/packages/xen/tune.mjs": [], "/packages/xen/index.mjs": [], "/index.mjs": [] } From dfb2335b1c6e023dae5f977e269748a7da760ec2 Mon Sep 17 00:00:00 2001 From: tyow Date: Sun, 22 Feb 2026 17:28:32 -0500 Subject: [PATCH 09/66] document stackRight, Left, Centre/Center --- packages/core/pattern.mjs | 50 +++++++ packages/core/test/pattern.test.mjs | 3 +- test/__snapshots__/examples.test.mjs.snap | 171 ++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index ed5ff9cd4..054aade24 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1519,6 +1519,15 @@ function _stackWith(func, pats) { return stack(...func(steps, pats)); } +/** + * The input patterns are aligned to the left, with silence padded to the shorter pattern(s) in the final steps if there's a stepcount disparity. See also `stackRight` and `stackCentre/stackCenter` + * + * @name stackLeft + * @tags temporal, stepwise + * @return {Pattern} + * @example + * stackLeft("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + */ export function stackLeft(...pats) { return _stackWith( (steps, pats) => pats.map((pat) => (pat._steps.eq(steps) ? pat : stepcat(pat, gap(steps.sub(pat._steps))))), @@ -1526,6 +1535,15 @@ export function stackLeft(...pats) { ); } +/** + * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCentre/stackCenter` + * + * @name stackRight + * @tags temporal, stepwise + * @return {Pattern} + * @example + * stackRight("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + */ export function stackRight(...pats) { return _stackWith( (steps, pats) => pats.map((pat) => (pat._steps.eq(steps) ? pat : stepcat(gap(steps.sub(pat._steps)), pat))), @@ -1533,6 +1551,15 @@ export function stackRight(...pats) { ); } +/** + * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCentre/stackCenter` + * @name stackCentre + * @tags temporal, stepwise + * @synonyms stackCenter + * @return {Pattern} + * @example + * stackCentre("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + */ export function stackCentre(...pats) { return _stackWith( (steps, pats) => @@ -1547,6 +1574,10 @@ export function stackCentre(...pats) { ); } +export function stackCenter(pats) { + return stackCentre(pats); +} + export function stackBy(by, ...pats) { const [left, ...right] = pats.map((pat) => pat._steps); const steps = left.maximum(...right); @@ -2922,10 +2953,29 @@ export const { ribbon, rib } = register(['ribbon', 'rib'], (offset, cycles, pat) pat.early(offset).restart(pure(1).slow(cycles)), ); +/** + * Sets the pattern's color to the given [hsla](https://www.w3schools.com/htmL/html_colors_hsl.asp) + * + * @name hsla + * @param {Number} hue (0 - 360) + * @param {Number} saturation percentage (0 - 100) + * @param {Number} lightness percentage (0 - 100) + * @param {Number} alpha (transparency) (0.0 - 1.0) + * @tags visualization + */ export const hsla = register('hsla', (h, s, l, a, pat) => { return pat.color(`hsla(${h}turn,${s * 100}%,${l * 100}%,${a})`); }); +/** + * Sets the pattern's color to the given [hsl](https://www.w3schools.com/htmL/html_colors_hsl.asp) + * + * @name hsl + * @param {Number} hue (0 - 360) + * @param {Number} saturation percentage (0 - 100) + * @param {Number} lightness percentage (0 - 100) + * @tags visualization + */ export const hsl = register('hsl', (h, s, l, pat) => { return pat.color(`hsl(${h}turn,${s * 100}%,${l * 100}%)`); }); diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index aaa14bd27..4442aa4a7 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -55,7 +55,7 @@ import { expand, } from '../index.mjs'; -import { log, logValues } from '../pattern.mjs'; +import { log, logValues, stackCenter } from '../pattern.mjs'; import { steady } from '../signal.mjs'; @@ -1166,6 +1166,7 @@ describe('Pattern', () => { expect(stackRight(fastcat(0, 1, 2), fastcat(3, 4))._steps).toStrictEqual(Fraction(3)); // maybe this should double when they are either all even or all odd expect(stackCentre(fastcat(0, 1, 2), fastcat(3, 4))._steps).toStrictEqual(Fraction(3)); + expect(stackCenter(fastcat(0, 1, 2), fastcat(3, 4))._steps).toStrictEqual(Fraction(3)); expect(fastcat(0, 1).ply(3)._steps).toStrictEqual(Fraction(6)); expect(fastcat(0, 1).setSteps(undefined).ply(3)._steps).toStrictEqual(undefined); expect(fastcat(0, 1).fast(3)._steps).toStrictEqual(Fraction(2)); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 70519695f..622aa1a16 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -12694,6 +12694,177 @@ exports[`runs examples > example "stack" example index 1 1`] = ` ] `; +exports[`runs examples > example "stackCentre" example index 0 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 1/10 → 3/10 | note:C3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 3/10 → 1/2 | note:D3 ]", + "[ 3/10 → 1/2 | note:F3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 1/2 → 7/10 | note:Eb3 ]", + "[ 1/2 → 7/10 | note:G3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 7/10 → 9/10 | note:F3 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 11/10 → 13/10 | note:C3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 13/10 → 3/2 | note:D3 ]", + "[ 13/10 → 3/2 | note:F3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 3/2 → 17/10 | note:Eb3 ]", + "[ 3/2 → 17/10 | note:G3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 17/10 → 19/10 | note:F3 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 21/10 → 23/10 | note:C3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 23/10 → 5/2 | note:D3 ]", + "[ 23/10 → 5/2 | note:F3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 5/2 → 27/10 | note:Eb3 ]", + "[ 5/2 → 27/10 | note:G3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 27/10 → 29/10 | note:F3 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 31/10 → 33/10 | note:C3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 33/10 → 7/2 | note:D3 ]", + "[ 33/10 → 7/2 | note:F3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 7/2 → 37/10 | note:Eb3 ]", + "[ 7/2 → 37/10 | note:G3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 37/10 → 39/10 | note:F3 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + +exports[`runs examples > example "stackLeft" example index 0 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 0/1 → 1/5 | note:C3 ]", + "[ 0/1 → 1/5 | note:F3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:D3 ]", + "[ 1/5 → 2/5 | note:G3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 2/5 → 3/5 | note:Eb3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 3/5 → 4/5 | note:F3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 1/1 → 6/5 | note:C3 ]", + "[ 1/1 → 6/5 | note:F3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:D3 ]", + "[ 6/5 → 7/5 | note:G3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 7/5 → 8/5 | note:Eb3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 8/5 → 9/5 | note:F3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 2/1 → 11/5 | note:C3 ]", + "[ 2/1 → 11/5 | note:F3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:D3 ]", + "[ 11/5 → 12/5 | note:G3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 12/5 → 13/5 | note:Eb3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 13/5 → 14/5 | note:F3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 3/1 → 16/5 | note:C3 ]", + "[ 3/1 → 16/5 | note:F3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:D3 ]", + "[ 16/5 → 17/5 | note:G3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 17/5 → 18/5 | note:Eb3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 18/5 → 19/5 | note:F3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + +exports[`runs examples > example "stackRight" example index 0 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:C3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 2/5 → 3/5 | note:D3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 3/5 → 4/5 | note:Eb3 ]", + "[ 3/5 → 4/5 | note:F3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 4/5 → 1/1 | note:F3 ]", + "[ 4/5 → 1/1 | note:G3 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:C3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 7/5 → 8/5 | note:D3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 8/5 → 9/5 | note:Eb3 ]", + "[ 8/5 → 9/5 | note:F3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 9/5 → 2/1 | note:F3 ]", + "[ 9/5 → 2/1 | note:G3 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:C3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 12/5 → 13/5 | note:D3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 13/5 → 14/5 | note:Eb3 ]", + "[ 13/5 → 14/5 | note:F3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 14/5 → 3/1 | note:F3 ]", + "[ 14/5 → 3/1 | note:G3 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:C3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 17/5 → 18/5 | note:D3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 18/5 → 19/5 | note:Eb3 ]", + "[ 18/5 → 19/5 | note:F3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 19/5 → 4/1 | note:F3 ]", + "[ 19/5 → 4/1 | note:G3 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + exports[`runs examples > example "stepalt" example index 0 1`] = ` [ "[ 0/1 → 1/5 | s:bd ]", From 58e6b0543f56c4afca4186cf02265560e0ee27e0 Mon Sep 17 00:00:00 2001 From: tyow Date: Sun, 22 Feb 2026 17:37:57 -0500 Subject: [PATCH 10/66] add stackRepeat (already supported, just not exposed to the user) --- packages/core/pattern.mjs | 18 +++++- test/__snapshots__/examples.test.mjs.snap | 73 +++++++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 054aade24..65804a262 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1523,7 +1523,7 @@ function _stackWith(func, pats) { * The input patterns are aligned to the left, with silence padded to the shorter pattern(s) in the final steps if there's a stepcount disparity. See also `stackRight` and `stackCentre/stackCenter` * * @name stackLeft - * @tags temporal, stepwise + * @tags temporal, stepwise, combiners * @return {Pattern} * @example * stackLeft("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() @@ -1539,7 +1539,7 @@ export function stackLeft(...pats) { * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCentre/stackCenter` * * @name stackRight - * @tags temporal, stepwise + * @tags temporal, stepwise, combiners * @return {Pattern} * @example * stackRight("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() @@ -1554,7 +1554,7 @@ export function stackRight(...pats) { /** * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCentre/stackCenter` * @name stackCentre - * @tags temporal, stepwise + * @tags temporal, stepwise, combiners * @synonyms stackCenter * @return {Pattern} * @example @@ -1578,6 +1578,18 @@ export function stackCenter(pats) { return stackCentre(pats); } +/** + * Shorthand for `pm(patterns).pace(m)` where m is the max step count in the input patterns + * @name stackRepeat + * @tags temporal, stepwise, combiners + * @return {Pattern} + * @example + * stackRepeat("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + */ +export function stackRepeat(...pats) { + return stackBy(reify('repeat'), ...pats); +} + export function stackBy(by, ...pats) { const [left, ...right] = pats.map((pat) => pat._steps); const steps = left.maximum(...right); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 622aa1a16..a4d4e2536 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -12808,6 +12808,79 @@ exports[`runs examples > example "stackLeft" example index 0 1`] = ` ] `; +exports[`runs examples > example "stackRepeat" example index 0 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 0/1 → 1/5 | note:C3 ]", + "[ 0/1 → 1/5 | note:F3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:D3 ]", + "[ 1/5 → 2/5 | note:G3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 2/5 → 3/5 | note:Eb3 ]", + "[ 2/5 → 3/5 | note:F3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 3/5 → 4/5 | note:F3 ]", + "[ 3/5 → 4/5 | note:G3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 4/5 → 1/1 | note:C3 ]", + "[ 4/5 → 1/1 | note:F3 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 1/1 → 6/5 | note:D3 ]", + "[ 1/1 → 6/5 | note:G3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:Eb3 ]", + "[ 6/5 → 7/5 | note:F3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 7/5 → 8/5 | note:F3 ]", + "[ 7/5 → 8/5 | note:G3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 8/5 → 9/5 | note:C3 ]", + "[ 8/5 → 9/5 | note:F3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 9/5 → 2/1 | note:D3 ]", + "[ 9/5 → 2/1 | note:G3 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 2/1 → 11/5 | note:Eb3 ]", + "[ 2/1 → 11/5 | note:F3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:F3 ]", + "[ 11/5 → 12/5 | note:G3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 12/5 → 13/5 | note:C3 ]", + "[ 12/5 → 13/5 | note:F3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 13/5 → 14/5 | note:D3 ]", + "[ 13/5 → 14/5 | note:G3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 14/5 → 3/1 | note:Eb3 ]", + "[ 14/5 → 3/1 | note:F3 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 3/1 → 16/5 | note:F3 ]", + "[ 3/1 → 16/5 | note:G3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:C3 ]", + "[ 16/5 → 17/5 | note:F3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 17/5 → 18/5 | note:D3 ]", + "[ 17/5 → 18/5 | note:G3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 18/5 → 19/5 | note:Eb3 ]", + "[ 18/5 → 19/5 | note:F3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 19/5 → 4/1 | note:F3 ]", + "[ 19/5 → 4/1 | note:G3 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + exports[`runs examples > example "stackRight" example index 0 1`] = ` [ "[ 0/1 → 1/15 | note:G3 ]", From 4d0a4902ab848ea41f1ea8fc6aecd65bcbb6bf47 Mon Sep 17 00:00:00 2001 From: tyow Date: Sun, 22 Feb 2026 17:42:09 -0500 Subject: [PATCH 11/66] enable stack variants to be chained --- packages/core/pattern.mjs | 28 +++ test/__snapshots__/examples.test.mjs.snap | 244 ++++++++++++++++++++++ 2 files changed, 272 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 65804a262..f666dda87 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -850,6 +850,22 @@ export class Pattern { return stack(this, ...pats); } + stackLeft(...pats) { + return stackLeft(this, ...pats); + } + + stackRight(...pats) { + return stackRight(this, ...pats); + } + + stackCentre(...pats) { + return stackCentre(this, ...pats); + } + + stackRepeat(...pats) { + return stackRepeat(this, ...pats); + } + sequence(...pats) { return sequence(this, ...pats); } @@ -1527,6 +1543,9 @@ function _stackWith(func, pats) { * @return {Pattern} * @example * stackLeft("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + * @example + * // chainable + * "0 1 2 3".stackLeft("3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() */ export function stackLeft(...pats) { return _stackWith( @@ -1543,6 +1562,9 @@ export function stackLeft(...pats) { * @return {Pattern} * @example * stackRight("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + * @example + * // chainable + * "0 1 2 3".stackRight("3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() */ export function stackRight(...pats) { return _stackWith( @@ -1559,6 +1581,9 @@ export function stackRight(...pats) { * @return {Pattern} * @example * stackCentre("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + * @example + * // chainable + * "0 1 2 3".stackCentre("3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() */ export function stackCentre(...pats) { return _stackWith( @@ -1585,6 +1610,9 @@ export function stackCenter(pats) { * @return {Pattern} * @example * stackRepeat("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + * @example + * // chainable + * "0 1 2 3".stackRepeat("3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() */ export function stackRepeat(...pats) { return stackBy(reify('repeat'), ...pats); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index a4d4e2536..c4d616cee 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -12751,6 +12751,63 @@ exports[`runs examples > example "stackCentre" example index 0 1`] = ` ] `; +exports[`runs examples > example "stackCentre" example index 1 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 1/10 → 3/10 | note:C3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 3/10 → 1/2 | note:D3 ]", + "[ 3/10 → 1/2 | note:F3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 1/2 → 7/10 | note:Eb3 ]", + "[ 1/2 → 7/10 | note:G3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 7/10 → 9/10 | note:F3 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 11/10 → 13/10 | note:C3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 13/10 → 3/2 | note:D3 ]", + "[ 13/10 → 3/2 | note:F3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 3/2 → 17/10 | note:Eb3 ]", + "[ 3/2 → 17/10 | note:G3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 17/10 → 19/10 | note:F3 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 21/10 → 23/10 | note:C3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 23/10 → 5/2 | note:D3 ]", + "[ 23/10 → 5/2 | note:F3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 5/2 → 27/10 | note:Eb3 ]", + "[ 5/2 → 27/10 | note:G3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 27/10 → 29/10 | note:F3 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 31/10 → 33/10 | note:C3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 33/10 → 7/2 | note:D3 ]", + "[ 33/10 → 7/2 | note:F3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 7/2 → 37/10 | note:Eb3 ]", + "[ 7/2 → 37/10 | note:G3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 37/10 → 39/10 | note:F3 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + exports[`runs examples > example "stackLeft" example index 0 1`] = ` [ "[ 0/1 → 1/15 | note:G3 ]", @@ -12808,6 +12865,63 @@ exports[`runs examples > example "stackLeft" example index 0 1`] = ` ] `; +exports[`runs examples > example "stackLeft" example index 1 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 0/1 → 1/5 | note:C3 ]", + "[ 0/1 → 1/5 | note:F3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:D3 ]", + "[ 1/5 → 2/5 | note:G3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 2/5 → 3/5 | note:Eb3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 3/5 → 4/5 | note:F3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 1/1 → 6/5 | note:C3 ]", + "[ 1/1 → 6/5 | note:F3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:D3 ]", + "[ 6/5 → 7/5 | note:G3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 7/5 → 8/5 | note:Eb3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 8/5 → 9/5 | note:F3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 2/1 → 11/5 | note:C3 ]", + "[ 2/1 → 11/5 | note:F3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:D3 ]", + "[ 11/5 → 12/5 | note:G3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 12/5 → 13/5 | note:Eb3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 13/5 → 14/5 | note:F3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 3/1 → 16/5 | note:C3 ]", + "[ 3/1 → 16/5 | note:F3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:D3 ]", + "[ 16/5 → 17/5 | note:G3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 17/5 → 18/5 | note:Eb3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 18/5 → 19/5 | note:F3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + exports[`runs examples > example "stackRepeat" example index 0 1`] = ` [ "[ 0/1 → 1/15 | note:G3 ]", @@ -12881,6 +12995,79 @@ exports[`runs examples > example "stackRepeat" example index 0 1`] = ` ] `; +exports[`runs examples > example "stackRepeat" example index 1 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 0/1 → 1/5 | note:C3 ]", + "[ 0/1 → 1/5 | note:F3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:D3 ]", + "[ 1/5 → 2/5 | note:G3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 2/5 → 3/5 | note:Eb3 ]", + "[ 2/5 → 3/5 | note:F3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 3/5 → 4/5 | note:F3 ]", + "[ 3/5 → 4/5 | note:G3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 4/5 → 1/1 | note:C3 ]", + "[ 4/5 → 1/1 | note:F3 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 1/1 → 6/5 | note:D3 ]", + "[ 1/1 → 6/5 | note:G3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:Eb3 ]", + "[ 6/5 → 7/5 | note:F3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 7/5 → 8/5 | note:F3 ]", + "[ 7/5 → 8/5 | note:G3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 8/5 → 9/5 | note:C3 ]", + "[ 8/5 → 9/5 | note:F3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 9/5 → 2/1 | note:D3 ]", + "[ 9/5 → 2/1 | note:G3 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 2/1 → 11/5 | note:Eb3 ]", + "[ 2/1 → 11/5 | note:F3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:F3 ]", + "[ 11/5 → 12/5 | note:G3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 12/5 → 13/5 | note:C3 ]", + "[ 12/5 → 13/5 | note:F3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 13/5 → 14/5 | note:D3 ]", + "[ 13/5 → 14/5 | note:G3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 14/5 → 3/1 | note:Eb3 ]", + "[ 14/5 → 3/1 | note:F3 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 3/1 → 16/5 | note:F3 ]", + "[ 3/1 → 16/5 | note:G3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:C3 ]", + "[ 16/5 → 17/5 | note:F3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 17/5 → 18/5 | note:D3 ]", + "[ 17/5 → 18/5 | note:G3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 18/5 → 19/5 | note:Eb3 ]", + "[ 18/5 → 19/5 | note:F3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 19/5 → 4/1 | note:F3 ]", + "[ 19/5 → 4/1 | note:G3 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + exports[`runs examples > example "stackRight" example index 0 1`] = ` [ "[ 0/1 → 1/15 | note:G3 ]", @@ -12938,6 +13125,63 @@ exports[`runs examples > example "stackRight" example index 0 1`] = ` ] `; +exports[`runs examples > example "stackRight" example index 1 1`] = ` +[ + "[ 0/1 → 1/15 | note:G3 ]", + "[ 1/15 → 2/15 | note:Ab3 ]", + "[ 2/15 → 1/5 | note:Bb3 ]", + "[ 1/5 → 2/5 | note:C3 ]", + "[ 1/5 → 2/5 | note:C4 ]", + "[ 2/5 → 3/5 | note:D3 ]", + "[ 2/5 → 3/5 | note:D4 ]", + "[ 3/5 → 4/5 | note:Eb3 ]", + "[ 3/5 → 4/5 | note:F3 ]", + "[ 3/5 → 4/5 | note:Eb4 ]", + "[ 4/5 → 1/1 | note:F3 ]", + "[ 4/5 → 1/1 | note:G3 ]", + "[ 4/5 → 1/1 | note:F4 ]", + "[ 1/1 → 16/15 | note:G3 ]", + "[ 16/15 → 17/15 | note:Ab3 ]", + "[ 17/15 → 6/5 | note:Bb3 ]", + "[ 6/5 → 7/5 | note:C3 ]", + "[ 6/5 → 7/5 | note:C4 ]", + "[ 7/5 → 8/5 | note:D3 ]", + "[ 7/5 → 8/5 | note:D4 ]", + "[ 8/5 → 9/5 | note:Eb3 ]", + "[ 8/5 → 9/5 | note:F3 ]", + "[ 8/5 → 9/5 | note:Eb4 ]", + "[ 9/5 → 2/1 | note:F3 ]", + "[ 9/5 → 2/1 | note:G3 ]", + "[ 9/5 → 2/1 | note:F4 ]", + "[ 2/1 → 31/15 | note:G3 ]", + "[ 31/15 → 32/15 | note:Ab3 ]", + "[ 32/15 → 11/5 | note:Bb3 ]", + "[ 11/5 → 12/5 | note:C3 ]", + "[ 11/5 → 12/5 | note:C4 ]", + "[ 12/5 → 13/5 | note:D3 ]", + "[ 12/5 → 13/5 | note:D4 ]", + "[ 13/5 → 14/5 | note:Eb3 ]", + "[ 13/5 → 14/5 | note:F3 ]", + "[ 13/5 → 14/5 | note:Eb4 ]", + "[ 14/5 → 3/1 | note:F3 ]", + "[ 14/5 → 3/1 | note:G3 ]", + "[ 14/5 → 3/1 | note:F4 ]", + "[ 3/1 → 46/15 | note:G3 ]", + "[ 46/15 → 47/15 | note:Ab3 ]", + "[ 47/15 → 16/5 | note:Bb3 ]", + "[ 16/5 → 17/5 | note:C3 ]", + "[ 16/5 → 17/5 | note:C4 ]", + "[ 17/5 → 18/5 | note:D3 ]", + "[ 17/5 → 18/5 | note:D4 ]", + "[ 18/5 → 19/5 | note:Eb3 ]", + "[ 18/5 → 19/5 | note:F3 ]", + "[ 18/5 → 19/5 | note:Eb4 ]", + "[ 19/5 → 4/1 | note:F3 ]", + "[ 19/5 → 4/1 | note:G3 ]", + "[ 19/5 → 4/1 | note:F4 ]", +] +`; + exports[`runs examples > example "stepalt" example index 0 1`] = ` [ "[ 0/1 → 1/5 | s:bd ]", From aedd7de2df0c659091467f1324c7e59c64e42b1e Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 23 Feb 2026 17:00:29 -0500 Subject: [PATCH 12/66] getDur doc fix --- missingExamples | 71 +++++++++++++++++++++++ packages/superdough/sampler.mjs | 5 +- test/__snapshots__/examples.test.mjs.snap | 9 +++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 missingExamples diff --git a/missingExamples b/missingExamples new file mode 100644 index 000000000..e14c770ff --- /dev/null +++ b/missingExamples @@ -0,0 +1,71 @@ +(manually filled out) + +aliasBank +appBoth, appLeft, appRight, appWhole +applyGradualLowpass +applyHannWindow +asNumber +bpdc +bpdepth +brak +bpsync +ccn +ccv +channel +chebyshev +chooseInWith +chyx +computeMagnitudes +createCC +crossfade +csoundm +cubic +findPeaks +fmrelease +fold +generateGraph +generateReverb +getFrequency (rendered weird rn) +hard +hpdc +hprate +hpshape +hpskew +hpsync +inhabitmod +lpshape +lpskew +parray +pickAndRename +pickmod +pickmodF +pickmodOut +pickmodReset +pickOut +pickReset +pickRestart +sequenceP +sinefold +slider +soundAlias +source +tables +time +warpattack +warpdc +warpdecay +warpdepth +warpenv +warprate +warpshape +warpskew +warpsustain +warpsync +withHap +withHaps +withContext +withHapSpan +withHapTime +within +withLoc +withSeed \ No newline at end of file diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index d45e1050b..c4b39b469 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -36,8 +36,9 @@ function humanFileSize(bytes, si) { * * Note: `must` be called with await, otherwise you'll get a pending Promise object. * - * @name getDuration,getDur - * @tag samples + * @name getDuration + * @synonym getDur + * @tags samples * @param {string} sampleName * @param {number} (optional) n * diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index c4d616cee..13498f734 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5340,6 +5340,15 @@ exports[`runs examples > example "getDur" example index 0 1`] = ` ] `; +exports[`runs examples > example "getDuration" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:sax cps:null ]", + "[ 1/1 → 2/1 | s:sax cps:null ]", + "[ 2/1 → 3/1 | s:sax cps:null ]", + "[ 3/1 → 4/1 | s:sax cps:null ]", +] +`; + exports[`runs examples > example "grow" example index 0 1`] = ` [ "[ 0/1 → 1/10 | s:tha bank:mridangam ]", From 70213a26ae0730a0608bcc6f4ba50c02e97d15e0 Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 23 Feb 2026 17:21:27 -0500 Subject: [PATCH 13/66] added some examples to things --- packages/core/pattern.mjs | 4 +++ packages/core/pick.mjs | 2 ++ packages/core/util.mjs | 8 +++++- test/__snapshots__/examples.test.mjs.snap | 30 ++++++++++++++++------- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index f666dda87..318847752 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2532,8 +2532,12 @@ export const off = register('off', function (time_pat, func, pat) { * Returns a new pattern where every other cycle is played once, twice as * fast, and offset in time by one quarter of a cycle. Creates a kind of * breakbeat feel. + * @name brak * @tags temporal * @returns Pattern + * @memberof Pattern + * @example + * s("bd hh!2 rim").brak() */ export const brak = register('brak', function (pat) { return pat.when(slowcat(false, true), (x) => fastcat(x, silence)._late(0.25)); diff --git a/packages/core/pick.mjs b/packages/core/pick.mjs index 3974c695b..35bd5fc2c 100644 --- a/packages/core/pick.mjs +++ b/packages/core/pick.mjs @@ -104,6 +104,8 @@ export const pickmodF = register('pickmodF', function (pickPattern, lookup, pat) * @param {Pattern} pat * @param {*} xs * @returns {Pattern} + * @example + * note("<0*4 1 2*2 3*3>".pickOut(["g a", "e f", "f g f g" , "g c d"])) */ export const pickOut = register('pickOut', function (lookup, pat) { return _pick(lookup, pat, false).outerJoin(); diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 2f499adae..65baf724e 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -121,16 +121,22 @@ export const getPlayableNoteValue = (hap) => { /** * Given a hap, returns the frequency of the note or freq, if defined + * * Given a note, returns the frequency of that note * `getFreq('c3')` + * * Given a midi note number, returns the frequency of that midi note * `getFreq('c3')` * * @name getFrequency * @tags pitch * @synonyms getFreq - * @param {Hap | note | number} + * @param {Hap | note | number} input * @returns {Number} + * @example + * i("4 8 9 10 - - 5 7 9 11 - -").tune("tranh3") + * .mul(getFreq('c3')) + * .freq().clip(.5).room(1) */ export const getFrequency = (hap) => { let { value, context } = diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 13498f734..163d5e8b3 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1798,6 +1798,27 @@ exports[`runs examples > example "bpsustain" example index 0 1`] = ` ] `; +exports[`runs examples > example "brak" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | s:bd ]", + "[ 1/4 → 1/2 | s:hh ]", + "[ 1/2 → 3/4 | s:hh ]", + "[ 3/4 → 1/1 | s:rim ]", + "[ 5/4 → 11/8 | s:bd ]", + "[ 11/8 → 3/2 | s:hh ]", + "[ 3/2 → 13/8 | s:hh ]", + "[ 13/8 → 7/4 | s:rim ]", + "[ 2/1 → 9/4 | s:bd ]", + "[ 9/4 → 5/2 | s:hh ]", + "[ 5/2 → 11/4 | s:hh ]", + "[ 11/4 → 3/1 | s:rim ]", + "[ 13/4 → 27/8 | s:bd ]", + "[ 27/8 → 7/2 | s:hh ]", + "[ 7/2 → 29/8 | s:hh ]", + "[ 29/8 → 15/4 | s:rim ]", +] +`; + exports[`runs examples > example "brand" example index 0 1`] = ` [ "[ 0/1 → 1/10 | s:hh pan:true ]", @@ -5331,15 +5352,6 @@ exports[`runs examples > example "gain" example index 0 1`] = ` exports[`runs examples > example "gap" example index 0 1`] = `[]`; -exports[`runs examples > example "getDur" example index 0 1`] = ` -[ - "[ 0/1 → 1/1 | s:sax cps:null ]", - "[ 1/1 → 2/1 | s:sax cps:null ]", - "[ 2/1 → 3/1 | s:sax cps:null ]", - "[ 3/1 → 4/1 | s:sax cps:null ]", -] -`; - exports[`runs examples > example "getDuration" example index 0 1`] = ` [ "[ 0/1 → 1/1 | s:sax cps:null ]", From e5a08091f1632b97438af1e7331eb6e2059e6f55 Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 23 Feb 2026 17:21:57 -0500 Subject: [PATCH 14/66] format --- packages/core/util.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 65baf724e..6a8936e55 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -121,10 +121,10 @@ export const getPlayableNoteValue = (hap) => { /** * Given a hap, returns the frequency of the note or freq, if defined - * + * * Given a note, returns the frequency of that note * `getFreq('c3')` - * + * * Given a midi note number, returns the frequency of that midi note * `getFreq('c3')` * From 44ec79182ede1d1aa297e149f8c4c8e15e3414f4 Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 23 Feb 2026 20:06:38 -0500 Subject: [PATCH 15/66] added examples to withHap and withHaps --- packages/core/pattern.mjs | 21 ++++ test/__snapshots__/examples.test.mjs.snap | 113 ++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 318847752..f88c4159a 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -508,6 +508,16 @@ export class Pattern { * @param {Function} func * @returns Pattern * @noAutocomplete + * @example + * note("[a b, c d] a b c d").s("gm_piano").withHaps(x=>{ + * if (x.length > 1) { + * x.map(hap=>{ + * // select gm_piano:1 sample + * hap.value.n = 1 + * }) + * } + * return x + * }) */ withHaps(func) { const result = new Pattern((state) => func(this.query(state), state)); @@ -521,6 +531,17 @@ export class Pattern { * @param {Function} func * @returns Pattern * @noAutocomplete + * @example + * note("c a f e").withHap(x=>{ + * if (x.value.note == 'c') { + * x.value.s = 'gm_piano' + * } + * else { + * x.value.s = 'gm_piano' + * x.value.n = 2 + * } + * return x + * }) */ withHap(func) { return this.withHaps((haps) => haps.map(func)); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 163d5e8b3..551f80cd0 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5361,6 +5361,43 @@ exports[`runs examples > example "getDuration" example index 0 1`] = ` ] `; +exports[`runs examples > example "getFrequency" example index 0 1`] = ` +[ + "[ 0/1 → 1/12 | freq:{i:4} clip:0.5 room:1 ]", + "[ 1/12 → 1/6 | freq:{i:8} clip:0.5 room:1 ]", + "[ 1/6 → 1/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 1/4 → 1/3 | freq:{i:10} clip:0.5 room:1 ]", + "[ 1/2 → 7/12 | freq:{i:5} clip:0.5 room:1 ]", + "[ 7/12 → 2/3 | freq:{i:7} clip:0.5 room:1 ]", + "[ 2/3 → 3/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 3/4 → 5/6 | freq:{i:11} clip:0.5 room:1 ]", + "[ 1/1 → 13/12 | freq:{i:4} clip:0.5 room:1 ]", + "[ 13/12 → 7/6 | freq:{i:8} clip:0.5 room:1 ]", + "[ 7/6 → 5/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 5/4 → 4/3 | freq:{i:10} clip:0.5 room:1 ]", + "[ 3/2 → 19/12 | freq:{i:5} clip:0.5 room:1 ]", + "[ 19/12 → 5/3 | freq:{i:7} clip:0.5 room:1 ]", + "[ 5/3 → 7/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 7/4 → 11/6 | freq:{i:11} clip:0.5 room:1 ]", + "[ 2/1 → 25/12 | freq:{i:4} clip:0.5 room:1 ]", + "[ 25/12 → 13/6 | freq:{i:8} clip:0.5 room:1 ]", + "[ 13/6 → 9/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 9/4 → 7/3 | freq:{i:10} clip:0.5 room:1 ]", + "[ 5/2 → 31/12 | freq:{i:5} clip:0.5 room:1 ]", + "[ 31/12 → 8/3 | freq:{i:7} clip:0.5 room:1 ]", + "[ 8/3 → 11/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 11/4 → 17/6 | freq:{i:11} clip:0.5 room:1 ]", + "[ 3/1 → 37/12 | freq:{i:4} clip:0.5 room:1 ]", + "[ 37/12 → 19/6 | freq:{i:8} clip:0.5 room:1 ]", + "[ 19/6 → 13/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 13/4 → 10/3 | freq:{i:10} clip:0.5 room:1 ]", + "[ 7/2 → 43/12 | freq:{i:5} clip:0.5 room:1 ]", + "[ 43/12 → 11/3 | freq:{i:7} clip:0.5 room:1 ]", + "[ 11/3 → 15/4 | freq:{i:9} clip:0.5 room:1 ]", + "[ 15/4 → 23/6 | freq:{i:11} clip:0.5 room:1 ]", +] +`; + exports[`runs examples > example "grow" example index 0 1`] = ` [ "[ 0/1 → 1/10 | s:tha bank:mridangam ]", @@ -9150,6 +9187,24 @@ exports[`runs examples > example "pickF" example index 2 1`] = ` ] `; +exports[`runs examples > example "pickOut" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:g ]", + "[ 1/4 → 1/2 | note:g ]", + "[ 1/2 → 3/4 | note:a ]", + "[ 3/4 → 1/1 | note:a ]", + "[ (1/1 → 3/2) ⇝ 2/1 | note:e ]", + "[ 1/1 ⇜ (3/2 → 2/1) | note:f ]", + "[ (2/1 → 9/4) ⇝ 5/2 | note:f ]", + "[ 2/1 ⇜ (9/4 → 5/2) | note:g ]", + "[ (5/2 → 11/4) ⇝ 3/1 | note:f ]", + "[ 5/2 ⇜ (11/4 → 3/1) | note:g ]", + "[ 3/1 → 10/3 | note:g ]", + "[ 10/3 → 11/3 | note:c ]", + "[ 11/3 → 4/1 | note:d ]", +] +`; + exports[`runs examples > example "pickmodRestart" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C3 s:piano ]", @@ -15288,6 +15343,64 @@ exports[`runs examples > example "withBase" example index 1 1`] = ` ] `; +exports[`runs examples > example "withHap" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c s:gm_piano ]", + "[ 1/4 → 1/2 | note:a s:gm_piano n:2 ]", + "[ 1/2 → 3/4 | note:f s:gm_piano n:2 ]", + "[ 3/4 → 1/1 | note:e s:gm_piano n:2 ]", + "[ 1/1 → 5/4 | note:c s:gm_piano ]", + "[ 5/4 → 3/2 | note:a s:gm_piano n:2 ]", + "[ 3/2 → 7/4 | note:f s:gm_piano n:2 ]", + "[ 7/4 → 2/1 | note:e s:gm_piano n:2 ]", + "[ 2/1 → 9/4 | note:c s:gm_piano ]", + "[ 9/4 → 5/2 | note:a s:gm_piano n:2 ]", + "[ 5/2 → 11/4 | note:f s:gm_piano n:2 ]", + "[ 11/4 → 3/1 | note:e s:gm_piano n:2 ]", + "[ 3/1 → 13/4 | note:c s:gm_piano ]", + "[ 13/4 → 7/2 | note:a s:gm_piano n:2 ]", + "[ 7/2 → 15/4 | note:f s:gm_piano n:2 ]", + "[ 15/4 → 4/1 | note:e s:gm_piano n:2 ]", +] +`; + +exports[`runs examples > example "withHaps" example index 0 1`] = ` +[ + "[ 0/1 → 1/10 | note:a s:gm_piano n:1 ]", + "[ 0/1 → 1/10 | note:c s:gm_piano n:1 ]", + "[ 1/10 → 1/5 | note:b s:gm_piano n:1 ]", + "[ 1/10 → 1/5 | note:d s:gm_piano n:1 ]", + "[ 1/5 → 2/5 | note:a s:gm_piano n:1 ]", + "[ 2/5 → 3/5 | note:b s:gm_piano n:1 ]", + "[ 3/5 → 4/5 | note:c s:gm_piano n:1 ]", + "[ 4/5 → 1/1 | note:d s:gm_piano n:1 ]", + "[ 1/1 → 11/10 | note:a s:gm_piano n:1 ]", + "[ 1/1 → 11/10 | note:c s:gm_piano n:1 ]", + "[ 11/10 → 6/5 | note:b s:gm_piano n:1 ]", + "[ 11/10 → 6/5 | note:d s:gm_piano n:1 ]", + "[ 6/5 → 7/5 | note:a s:gm_piano n:1 ]", + "[ 7/5 → 8/5 | note:b s:gm_piano n:1 ]", + "[ 8/5 → 9/5 | note:c s:gm_piano n:1 ]", + "[ 9/5 → 2/1 | note:d s:gm_piano n:1 ]", + "[ 2/1 → 21/10 | note:a s:gm_piano n:1 ]", + "[ 2/1 → 21/10 | note:c s:gm_piano n:1 ]", + "[ 21/10 → 11/5 | note:b s:gm_piano n:1 ]", + "[ 21/10 → 11/5 | note:d s:gm_piano n:1 ]", + "[ 11/5 → 12/5 | note:a s:gm_piano n:1 ]", + "[ 12/5 → 13/5 | note:b s:gm_piano n:1 ]", + "[ 13/5 → 14/5 | note:c s:gm_piano n:1 ]", + "[ 14/5 → 3/1 | note:d s:gm_piano n:1 ]", + "[ 3/1 → 31/10 | note:a s:gm_piano n:1 ]", + "[ 3/1 → 31/10 | note:c s:gm_piano n:1 ]", + "[ 31/10 → 16/5 | note:b s:gm_piano n:1 ]", + "[ 31/10 → 16/5 | note:d s:gm_piano n:1 ]", + "[ 16/5 → 17/5 | note:a s:gm_piano n:1 ]", + "[ 17/5 → 18/5 | note:b s:gm_piano n:1 ]", + "[ 18/5 → 19/5 | note:c s:gm_piano n:1 ]", + "[ 19/5 → 4/1 | note:d s:gm_piano n:1 ]", +] +`; + exports[`runs examples > example "withValue" example index 0 1`] = ` [ "[ 0/1 → 1/3 | 10 ]", From 37d052a655fd038c0437854c5d62256e85b81f91 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 24 Feb 2026 01:51:38 -0500 Subject: [PATCH 16/66] Change "tidal" to "strudel" in docs --- website/src/pages/technical-manual/patterns.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/technical-manual/patterns.mdx b/website/src/pages/technical-manual/patterns.mdx index 49ede526c..eadaf8786 100644 --- a/website/src/pages/technical-manual/patterns.mdx +++ b/website/src/pages/technical-manual/patterns.mdx @@ -8,7 +8,7 @@ import { JsDoc } from '../../docs/JsDoc'; # Patterns -Patterns are the essence of Tidal. Its patterns are abstract entities that represent flows of time as functions, adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can output a set of events that happen within that time span. It depends on the structure of the Pattern how the events are located in time. +Patterns are the essence of Strudel (and Tidal). Its patterns are abstract entities that represent flows of time as functions, adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can output a set of events that happen within that time span. It depends on the structure of the Pattern how the events are located in time. From now on, this process of generating events from a time span will be called **querying**. Example: From 36a456eeb4ffe40063f5ffbbbd86c0fff55f2686 Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 28 Feb 2026 14:43:04 -0500 Subject: [PATCH 17/66] Update replicate and extend examples to show difference --- packages/core/pattern.mjs | 9 +++------ test/__snapshots__/examples.test.mjs.snap | 10 +++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index f88c4159a..594aab7a8 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3396,7 +3396,7 @@ export const drop = stepRegister('drop', function (i, pat) { * @tags stepwise * @example * stepcat( - * sound("bd bd - cp").extend(2), + * sound("bd bd - ").extend(2), * sound("bd - sd -") * ).pace(8) */ @@ -3407,15 +3407,12 @@ export const extend = stepRegister('extend', function (factor, pat) { /** * *Experimental* * - * `replicate` is similar to `fast` in that it increases its density, but it also increases the step count - * accordingly. So `stepcat("a b".replicate(2), "c d")` would be the same as `"a b a b c d"`, whereas - * `stepcat("a b".fast(2), "c d")` would be the same as `"[a b] [a b] c d"`. + * `replicate` is similar to `extend`, except that `replicate` will "replicate" the thing being extended. This will only differ from `extend` when the input pattern differs from cycle to cycle. * - * TODO: find out how this function differs from extend * @tags stepwise * @example * stepcat( - * sound("bd bd - cp").replicate(2), + * sound("bd bd - ").replicate(2), * sound("bd - sd -") * ).pace(8) */ diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 551f80cd0..dbf94bf76 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -4371,7 +4371,7 @@ exports[`runs examples > example "extend" example index 0 1`] = ` "[ 3/8 → 1/2 | s:cp ]", "[ 1/2 → 5/8 | s:bd ]", "[ 5/8 → 3/4 | s:bd ]", - "[ 7/8 → 1/1 | s:cp ]", + "[ 7/8 → 1/1 | s:hh ]", "[ 1/1 → 9/8 | s:bd ]", "[ 5/4 → 11/8 | s:sd ]", "[ 3/2 → 13/8 | s:bd ]", @@ -4379,7 +4379,7 @@ exports[`runs examples > example "extend" example index 0 1`] = ` "[ 15/8 → 2/1 | s:cp ]", "[ 2/1 → 17/8 | s:bd ]", "[ 17/8 → 9/4 | s:bd ]", - "[ 19/8 → 5/2 | s:cp ]", + "[ 19/8 → 5/2 | s:hh ]", "[ 5/2 → 21/8 | s:bd ]", "[ 11/4 → 23/8 | s:sd ]", "[ 3/1 → 25/8 | s:bd ]", @@ -4387,7 +4387,7 @@ exports[`runs examples > example "extend" example index 0 1`] = ` "[ 27/8 → 7/2 | s:cp ]", "[ 7/2 → 29/8 | s:bd ]", "[ 29/8 → 15/4 | s:bd ]", - "[ 31/8 → 4/1 | s:cp ]", + "[ 31/8 → 4/1 | s:hh ]", ] `; @@ -10148,10 +10148,10 @@ exports[`runs examples > example "replicate" example index 0 1`] = ` "[ 5/4 → 11/8 | s:sd ]", "[ 3/2 → 13/8 | s:bd ]", "[ 13/8 → 7/4 | s:bd ]", - "[ 15/8 → 2/1 | s:cp ]", + "[ 15/8 → 2/1 | s:hh ]", "[ 2/1 → 17/8 | s:bd ]", "[ 17/8 → 9/4 | s:bd ]", - "[ 19/8 → 5/2 | s:cp ]", + "[ 19/8 → 5/2 | s:hh ]", "[ 5/2 → 21/8 | s:bd ]", "[ 11/4 → 23/8 | s:sd ]", "[ 3/1 → 25/8 | s:bd ]", From ee4b8fa315a0c7a36905e838650c2511085895eb Mon Sep 17 00:00:00 2001 From: tyow Date: Sat, 28 Feb 2026 14:46:01 -0500 Subject: [PATCH 18/66] cleanup --- missingExamples | 71 ------------------------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 missingExamples diff --git a/missingExamples b/missingExamples deleted file mode 100644 index e14c770ff..000000000 --- a/missingExamples +++ /dev/null @@ -1,71 +0,0 @@ -(manually filled out) - -aliasBank -appBoth, appLeft, appRight, appWhole -applyGradualLowpass -applyHannWindow -asNumber -bpdc -bpdepth -brak -bpsync -ccn -ccv -channel -chebyshev -chooseInWith -chyx -computeMagnitudes -createCC -crossfade -csoundm -cubic -findPeaks -fmrelease -fold -generateGraph -generateReverb -getFrequency (rendered weird rn) -hard -hpdc -hprate -hpshape -hpskew -hpsync -inhabitmod -lpshape -lpskew -parray -pickAndRename -pickmod -pickmodF -pickmodOut -pickmodReset -pickOut -pickReset -pickRestart -sequenceP -sinefold -slider -soundAlias -source -tables -time -warpattack -warpdc -warpdecay -warpdepth -warpenv -warprate -warpshape -warpskew -warpsustain -warpsync -withHap -withHaps -withContext -withHapSpan -withHapTime -within -withLoc -withSeed \ No newline at end of file From 1ef39a2ea7ed3ea706c96bf2a9e9ba97daecb0ab Mon Sep 17 00:00:00 2001 From: tyow Date: Sun, 1 Mar 2026 12:42:13 -0500 Subject: [PATCH 19/66] small fixes --- website/src/pages/learn/signals.mdx | 8 ++++++++ website/src/pages/technical-manual/patterns.mdx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/website/src/pages/learn/signals.mdx b/website/src/pages/learn/signals.mdx index 527e42bef..501135b29 100644 --- a/website/src/pages/learn/signals.mdx +++ b/website/src/pages/learn/signals.mdx @@ -55,6 +55,14 @@ There is also `saw2`, `sine2`, `cosine2`, `tri2`, `square2` and `rand2` which ha +## time + + + +## creating your own signal + + + ## mouseX diff --git a/website/src/pages/technical-manual/patterns.mdx b/website/src/pages/technical-manual/patterns.mdx index eadaf8786..da97b2157 100644 --- a/website/src/pages/technical-manual/patterns.mdx +++ b/website/src/pages/technical-manual/patterns.mdx @@ -8,7 +8,7 @@ import { JsDoc } from '../../docs/JsDoc'; # Patterns -Patterns are the essence of Strudel (and Tidal). Its patterns are abstract entities that represent flows of time as functions, adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can output a set of events that happen within that time span. It depends on the structure of the Pattern how the events are located in time. +Patterns are the essence of Uzu languages such as Strudel and Tidal. Its patterns are abstract entities that represent flows of time as functions, adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can output a set of events that happen within that time span. It depends on the structure of the Pattern how the events are located in time. From now on, this process of generating events from a time span will be called **querying**. Example: From e502a3c3e5c5d8b8477be8e86d9fe6a27b615ea4 Mon Sep 17 00:00:00 2001 From: tyow Date: Sun, 8 Mar 2026 19:06:49 -0400 Subject: [PATCH 20/66] change bus tags s.t. it appears in reference --- packages/core/controls.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index d22167c0c..ac0b26c7c 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2226,7 +2226,7 @@ export const { orbit } = registerControl('orbit', 'o'); * otherPat.bmod(..) (to modulate another pattern with the bus) * * @name bus - * @tags superdirt + * @tags superdough * @param {number | Pattern} number */ export const { bus } = registerControl('bus'); @@ -2235,7 +2235,7 @@ export const { bus } = registerControl('bus'); * Postgain multiplier prior to sending the signal to the audio bus. * * @name busgain - * @tags superdirt + * @tags superdough * @synonyms bgain * @param {number | Pattern} number */ From 814510f637175e80f65c540ac64ebef40b3e085f Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 9 Mar 2026 19:32:28 -0400 Subject: [PATCH 21/66] init --- packages/xen/xen.mjs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 828cab6ea..6e67a1e4e 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { register, _mod, parseNumeral, removeUndefineds } from '@strudel/core'; +import { register, _mod, parseNumeral, removeUndefineds, Pattern } from '@strudel/core'; import Tune from './tunejs.js'; // returns a list of frequency ratios for given edo scale @@ -97,15 +97,15 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); */ export const xen = register('xen', function (scaleNameOrRatios, pat) { - return pat.withHaps((haps) => { - haps = haps.map((hap) => { + const query = state => { + let haps = pat.withHap((hap)=> { let hVal = hap.value; const isObject = typeof hVal === 'object'; if (!isObject) { throw new Error(`Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`); } const { i, ...otherValues } = hVal; - const scale = getXenScale(scaleNameOrRatios); + const scale = getXenScale(scaleNameOrRatios.query(state)[0].value); let freq = xenOffset(scale, parseNumeral(hVal.i)); // 10 is somewhat arbitrary freq = trimFreq(freq); @@ -113,10 +113,11 @@ export const xen = register('xen', function (scaleNameOrRatios, pat) { return isEdo(scaleNameOrRatios) ? hap.setContext({ ...hap.context, edoSize: scaleNameOrRatios.match(/^([1-9]+[0-9]*)edo$/)[1] }) : hap; - }); + }).query(state); return removeUndefineds(haps); - }); -}); + }; + return new Pattern(query) +}, false); /** * Assumes pattern of frequencies tuned to some `base` frequency, such as the output of `xen` From 02e0c43f77e03d440b1f3641aff7be7c801bf791 Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 9 Mar 2026 19:39:27 -0400 Subject: [PATCH 22/66] thoughts --- packages/xen/xen.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 6e67a1e4e..cb45d8069 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -106,6 +106,9 @@ export const xen = register('xen', function (scaleNameOrRatios, pat) { } const { i, ...otherValues } = hVal; const scale = getXenScale(scaleNameOrRatios.query(state)[0].value); + // scale probably needs to not be turned into pure vals + // maintain hap functionality so highlighting works + let freq = xenOffset(scale, parseNumeral(hVal.i)); // 10 is somewhat arbitrary freq = trimFreq(freq); From a161d5a5d3ccbd7fc526e99d0396eb98cd12acb9 Mon Sep 17 00:00:00 2001 From: tyow Date: Mon, 9 Mar 2026 19:44:36 -0400 Subject: [PATCH 23/66] comment in jisc3 --- packages/xen/xen.mjs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index cb45d8069..2282ea7f0 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -61,6 +61,21 @@ function xenOffset(xenScale, offset, index = 0) { const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); +// const JIsc3 = register('JIsc3', (input) => { +// let k = input + +// return new Pattern((state) => { +// let res = k.query(state) +// res = parray(k.query(state).map(x=>{ +// let c = x.context +// x = eval(x.value.join('/')) +// let r = reify(x).setContext(c) +// return r +// })).query(state) +// return res +// }) +// }) + // accepts a scale name such as 31edo, and a pattern // pattern expected to follow format such that a value can be mapped // to an edostep within the scale. Returns the pattern with From 3554a544d638f4cc7752741b1eeb658cb1e29bcf Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 00:18:34 -0400 Subject: [PATCH 24/66] erroring out --- packages/xen/xen.mjs | 62 +++++++++++------- test/__snapshots__/examples.test.mjs.snap | 80 +++++++++++------------ 2 files changed, 80 insertions(+), 62 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 2282ea7f0..759e488f7 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { register, _mod, parseNumeral, removeUndefineds, Pattern } from '@strudel/core'; +import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, reify } from '@strudel/core'; import Tune from './tunejs.js'; // returns a list of frequency ratios for given edo scale @@ -61,20 +61,7 @@ function xenOffset(xenScale, offset, index = 0) { const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); -// const JIsc3 = register('JIsc3', (input) => { -// let k = input - -// return new Pattern((state) => { -// let res = k.query(state) -// res = parray(k.query(state).map(x=>{ -// let c = x.context -// x = eval(x.value.join('/')) -// let r = reify(x).setContext(c) -// return r -// })).query(state) -// return res -// }) -// }) + // accepts a scale name such as 31edo, and a pattern // pattern expected to follow format such that a value can be mapped @@ -112,24 +99,55 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); */ export const xen = register('xen', function (scaleNameOrRatios, pat) { + const JIsc3 = (input) => { + let k = input + + return new Pattern((state) => { + let res = k.query(state) + res = parray(k.query(state).map(x => { + let c = x.context + x = eval(x.value.join('/')) + let r = reify(x).setContext(c) + return r + })).query(state) + return res + }) + } + const query = state => { - let haps = pat.withHap((hap)=> { + let haps = pat.withHap((hap) => { let hVal = hap.value; const isObject = typeof hVal === 'object'; if (!isObject) { throw new Error(`Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`); } const { i, ...otherValues } = hVal; - const scale = getXenScale(scaleNameOrRatios.query(state)[0].value); - // scale probably needs to not be turned into pure vals - // maintain hap functionality so highlighting works - + // const scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); + let scale + let edo = false + if (scaleNameOrRatios.__pure != undefined) { + scale = getXenScale(scaleNameOrRatios.__pure) + } else { + let sc = scaleNameOrRatios.splitQueries().query(state); + let nameMaybe = sc[0].value + if (typeof sc[0].value == 'string') { + scale = getXenScale(sc[0].value) + if (isEdo(nameMaybe)) { + edo = nameMaybe.match(/^([1-9]+[0-9]*)edo$/)[1] + } + hap = hap.setContext(hap.combineContext(sc[0])) + } else { + // we can do [0] here because JIsc3 uses parray + scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); + hap = hap.setContext(hap.combineContext(scaleNameOrRatios.query(state)[i])) + } + } let freq = xenOffset(scale, parseNumeral(hVal.i)); // 10 is somewhat arbitrary freq = trimFreq(freq); hap.value = { ...otherValues, freq }; - return isEdo(scaleNameOrRatios) - ? hap.setContext({ ...hap.context, edoSize: scaleNameOrRatios.match(/^([1-9]+[0-9]*)edo$/)[1] }) + return edo + ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; }).query(state); return removeUndefineds(haps); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 458904baf..54ab8a879 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5841,29 +5841,29 @@ exports[`runs examples > example "i" example index 0 1`] = ` "[ 3/4 → 7/8 | freq:505.4272762 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", "[ 1/1 → 9/8 | freq:220 ]", - "[ 9/8 → 5/4 | freq:235.7901618 ]", - "[ 5/4 → 11/8 | freq:252.7136381 ]", - "[ 11/8 → 3/2 | freq:270.8517709 ]", - "[ 3/2 → 13/8 | freq:290.2917404 ]", - "[ 13/8 → 7/4 | freq:311.1269837 ]", - "[ 7/4 → 15/8 | freq:333.4576446 ]", - "[ 15/8 → 2/1 | freq:357.3910544 ]", + "[ 9/8 → 5/4 | freq:252.7136381 ]", + "[ 5/4 → 11/8 | freq:290.2917404 ]", + "[ 11/8 → 3/2 | freq:333.4576446 ]", + "[ 3/2 → 13/8 | freq:383.0422479 ]", + "[ 13/8 → 7/4 | freq:440 ]", + "[ 7/4 → 15/8 | freq:505.4272762 ]", + "[ 15/8 → 2/1 | freq:580.5834807 ]", "[ 2/1 → 17/8 | freq:220 ]", - "[ 17/8 → 9/4 | freq:230.404707 ]", - "[ 9/4 → 19/8 | freq:241.3014955 ]", - "[ 19/8 → 5/2 | freq:252.7136381 ]", - "[ 5/2 → 21/8 | freq:264.6655079 ]", - "[ 21/8 → 11/4 | freq:277.182631 ]", - "[ 11/4 → 23/8 | freq:290.2917404 ]", - "[ 23/8 → 3/1 | freq:304.0208336 ]", + "[ 17/8 → 9/4 | freq:252.7136381 ]", + "[ 9/4 → 19/8 | freq:290.2917404 ]", + "[ 19/8 → 5/2 | freq:333.4576446 ]", + "[ 5/2 → 21/8 | freq:383.0422479 ]", + "[ 21/8 → 11/4 | freq:440 ]", + "[ 11/4 → 23/8 | freq:505.4272762 ]", + "[ 23/8 → 3/1 | freq:580.5834807 ]", "[ 3/1 → 25/8 | freq:220 ]", - "[ 25/8 → 13/4 | freq:275 ]", - "[ 13/4 → 27/8 | freq:293.3333333 ]", - "[ 27/8 → 7/2 | freq:330 ]", - "[ 7/2 → 29/8 | freq:352 ]", + "[ 25/8 → 13/4 | freq:252.7136381 ]", + "[ 13/4 → 27/8 | freq:290.2917404 ]", + "[ 27/8 → 7/2 | freq:333.4576446 ]", + "[ 7/2 → 29/8 | freq:383.0422479 ]", "[ 29/8 → 15/4 | freq:440 ]", - "[ 15/4 → 31/8 | freq:550 ]", - "[ 31/8 → 4/1 | freq:586.6666667 ]", + "[ 15/4 → 31/8 | freq:505.4272762 ]", + "[ 31/8 → 4/1 | freq:580.5834807 ]", ] `; @@ -14637,29 +14637,29 @@ exports[`runs examples > example "xen" example index 3 1`] = ` "[ 3/4 → 7/8 | freq:505.4272762 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", "[ 1/1 → 9/8 | freq:220 ]", - "[ 9/8 → 5/4 | freq:235.7901618 ]", - "[ 5/4 → 11/8 | freq:252.7136381 ]", - "[ 11/8 → 3/2 | freq:270.8517709 ]", - "[ 3/2 → 13/8 | freq:290.2917404 ]", - "[ 13/8 → 7/4 | freq:311.1269837 ]", - "[ 7/4 → 15/8 | freq:333.4576446 ]", - "[ 15/8 → 2/1 | freq:357.3910544 ]", + "[ 9/8 → 5/4 | freq:252.7136381 ]", + "[ 5/4 → 11/8 | freq:290.2917404 ]", + "[ 11/8 → 3/2 | freq:333.4576446 ]", + "[ 3/2 → 13/8 | freq:383.0422479 ]", + "[ 13/8 → 7/4 | freq:440 ]", + "[ 7/4 → 15/8 | freq:505.4272762 ]", + "[ 15/8 → 2/1 | freq:580.5834807 ]", "[ 2/1 → 17/8 | freq:220 ]", - "[ 17/8 → 9/4 | freq:230.404707 ]", - "[ 9/4 → 19/8 | freq:241.3014955 ]", - "[ 19/8 → 5/2 | freq:252.7136381 ]", - "[ 5/2 → 21/8 | freq:264.6655079 ]", - "[ 21/8 → 11/4 | freq:277.182631 ]", - "[ 11/4 → 23/8 | freq:290.2917404 ]", - "[ 23/8 → 3/1 | freq:304.0208336 ]", + "[ 17/8 → 9/4 | freq:252.7136381 ]", + "[ 9/4 → 19/8 | freq:290.2917404 ]", + "[ 19/8 → 5/2 | freq:333.4576446 ]", + "[ 5/2 → 21/8 | freq:383.0422479 ]", + "[ 21/8 → 11/4 | freq:440 ]", + "[ 11/4 → 23/8 | freq:505.4272762 ]", + "[ 23/8 → 3/1 | freq:580.5834807 ]", "[ 3/1 → 25/8 | freq:220 ]", - "[ 25/8 → 13/4 | freq:275 ]", - "[ 13/4 → 27/8 | freq:293.3333333 ]", - "[ 27/8 → 7/2 | freq:330 ]", - "[ 7/2 → 29/8 | freq:352 ]", + "[ 25/8 → 13/4 | freq:252.7136381 ]", + "[ 13/4 → 27/8 | freq:290.2917404 ]", + "[ 27/8 → 7/2 | freq:333.4576446 ]", + "[ 7/2 → 29/8 | freq:383.0422479 ]", "[ 29/8 → 15/4 | freq:440 ]", - "[ 15/4 → 31/8 | freq:550 ]", - "[ 31/8 → 4/1 | freq:586.6666667 ]", + "[ 15/4 → 31/8 | freq:505.4272762 ]", + "[ 31/8 → 4/1 | freq:580.5834807 ]", ] `; From 7510cb715f0a15732c7834a069e6862567fad468 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 00:41:23 -0400 Subject: [PATCH 25/66] fixed prev bug. next: support stack --- packages/xen/xen.mjs | 8 +-- test/__snapshots__/examples.test.mjs.snap | 80 +++++++++++------------ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 759e488f7..237eed039 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, reify } from '@strudel/core'; +import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, reify, State } from '@strudel/core'; import Tune from './tunejs.js'; // returns a list of frequency ratios for given edo scale @@ -122,13 +122,13 @@ export const xen = register('xen', function (scaleNameOrRatios, pat) { throw new Error(`Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`); } const { i, ...otherValues } = hVal; - // const scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); let scale let edo = false if (scaleNameOrRatios.__pure != undefined) { scale = getXenScale(scaleNameOrRatios.__pure) } else { - let sc = scaleNameOrRatios.splitQueries().query(state); + let st = new State(state.span.intersection(hap.part)) + let sc = scaleNameOrRatios.splitQueries().query(st); let nameMaybe = sc[0].value if (typeof sc[0].value == 'string') { scale = getXenScale(sc[0].value) @@ -139,7 +139,7 @@ export const xen = register('xen', function (scaleNameOrRatios, pat) { } else { // we can do [0] here because JIsc3 uses parray scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); - hap = hap.setContext(hap.combineContext(scaleNameOrRatios.query(state)[i])) + hap = hap.setContext(hap.combineContext(scaleNameOrRatios.query(st)[i])) } } let freq = xenOffset(scale, parseNumeral(hVal.i)); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 54ab8a879..458904baf 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5841,29 +5841,29 @@ exports[`runs examples > example "i" example index 0 1`] = ` "[ 3/4 → 7/8 | freq:505.4272762 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", "[ 1/1 → 9/8 | freq:220 ]", - "[ 9/8 → 5/4 | freq:252.7136381 ]", - "[ 5/4 → 11/8 | freq:290.2917404 ]", - "[ 11/8 → 3/2 | freq:333.4576446 ]", - "[ 3/2 → 13/8 | freq:383.0422479 ]", - "[ 13/8 → 7/4 | freq:440 ]", - "[ 7/4 → 15/8 | freq:505.4272762 ]", - "[ 15/8 → 2/1 | freq:580.5834807 ]", + "[ 9/8 → 5/4 | freq:235.7901618 ]", + "[ 5/4 → 11/8 | freq:252.7136381 ]", + "[ 11/8 → 3/2 | freq:270.8517709 ]", + "[ 3/2 → 13/8 | freq:290.2917404 ]", + "[ 13/8 → 7/4 | freq:311.1269837 ]", + "[ 7/4 → 15/8 | freq:333.4576446 ]", + "[ 15/8 → 2/1 | freq:357.3910544 ]", "[ 2/1 → 17/8 | freq:220 ]", - "[ 17/8 → 9/4 | freq:252.7136381 ]", - "[ 9/4 → 19/8 | freq:290.2917404 ]", - "[ 19/8 → 5/2 | freq:333.4576446 ]", - "[ 5/2 → 21/8 | freq:383.0422479 ]", - "[ 21/8 → 11/4 | freq:440 ]", - "[ 11/4 → 23/8 | freq:505.4272762 ]", - "[ 23/8 → 3/1 | freq:580.5834807 ]", + "[ 17/8 → 9/4 | freq:230.404707 ]", + "[ 9/4 → 19/8 | freq:241.3014955 ]", + "[ 19/8 → 5/2 | freq:252.7136381 ]", + "[ 5/2 → 21/8 | freq:264.6655079 ]", + "[ 21/8 → 11/4 | freq:277.182631 ]", + "[ 11/4 → 23/8 | freq:290.2917404 ]", + "[ 23/8 → 3/1 | freq:304.0208336 ]", "[ 3/1 → 25/8 | freq:220 ]", - "[ 25/8 → 13/4 | freq:252.7136381 ]", - "[ 13/4 → 27/8 | freq:290.2917404 ]", - "[ 27/8 → 7/2 | freq:333.4576446 ]", - "[ 7/2 → 29/8 | freq:383.0422479 ]", + "[ 25/8 → 13/4 | freq:275 ]", + "[ 13/4 → 27/8 | freq:293.3333333 ]", + "[ 27/8 → 7/2 | freq:330 ]", + "[ 7/2 → 29/8 | freq:352 ]", "[ 29/8 → 15/4 | freq:440 ]", - "[ 15/4 → 31/8 | freq:505.4272762 ]", - "[ 31/8 → 4/1 | freq:580.5834807 ]", + "[ 15/4 → 31/8 | freq:550 ]", + "[ 31/8 → 4/1 | freq:586.6666667 ]", ] `; @@ -14637,29 +14637,29 @@ exports[`runs examples > example "xen" example index 3 1`] = ` "[ 3/4 → 7/8 | freq:505.4272762 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", "[ 1/1 → 9/8 | freq:220 ]", - "[ 9/8 → 5/4 | freq:252.7136381 ]", - "[ 5/4 → 11/8 | freq:290.2917404 ]", - "[ 11/8 → 3/2 | freq:333.4576446 ]", - "[ 3/2 → 13/8 | freq:383.0422479 ]", - "[ 13/8 → 7/4 | freq:440 ]", - "[ 7/4 → 15/8 | freq:505.4272762 ]", - "[ 15/8 → 2/1 | freq:580.5834807 ]", + "[ 9/8 → 5/4 | freq:235.7901618 ]", + "[ 5/4 → 11/8 | freq:252.7136381 ]", + "[ 11/8 → 3/2 | freq:270.8517709 ]", + "[ 3/2 → 13/8 | freq:290.2917404 ]", + "[ 13/8 → 7/4 | freq:311.1269837 ]", + "[ 7/4 → 15/8 | freq:333.4576446 ]", + "[ 15/8 → 2/1 | freq:357.3910544 ]", "[ 2/1 → 17/8 | freq:220 ]", - "[ 17/8 → 9/4 | freq:252.7136381 ]", - "[ 9/4 → 19/8 | freq:290.2917404 ]", - "[ 19/8 → 5/2 | freq:333.4576446 ]", - "[ 5/2 → 21/8 | freq:383.0422479 ]", - "[ 21/8 → 11/4 | freq:440 ]", - "[ 11/4 → 23/8 | freq:505.4272762 ]", - "[ 23/8 → 3/1 | freq:580.5834807 ]", + "[ 17/8 → 9/4 | freq:230.404707 ]", + "[ 9/4 → 19/8 | freq:241.3014955 ]", + "[ 19/8 → 5/2 | freq:252.7136381 ]", + "[ 5/2 → 21/8 | freq:264.6655079 ]", + "[ 21/8 → 11/4 | freq:277.182631 ]", + "[ 11/4 → 23/8 | freq:290.2917404 ]", + "[ 23/8 → 3/1 | freq:304.0208336 ]", "[ 3/1 → 25/8 | freq:220 ]", - "[ 25/8 → 13/4 | freq:252.7136381 ]", - "[ 13/4 → 27/8 | freq:290.2917404 ]", - "[ 27/8 → 7/2 | freq:333.4576446 ]", - "[ 7/2 → 29/8 | freq:383.0422479 ]", + "[ 25/8 → 13/4 | freq:275 ]", + "[ 13/4 → 27/8 | freq:293.3333333 ]", + "[ 27/8 → 7/2 | freq:330 ]", + "[ 7/2 → 29/8 | freq:352 ]", "[ 29/8 → 15/4 | freq:440 ]", - "[ 15/4 → 31/8 | freq:505.4272762 ]", - "[ 31/8 → 4/1 | freq:580.5834807 ]", + "[ 15/4 → 31/8 | freq:550 ]", + "[ 31/8 → 4/1 | freq:586.6666667 ]", ] `; From a1ccb9c0bb311c4815ea0fd2361e0891c0a45099 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 14:39:25 -0400 Subject: [PATCH 26/66] working stack for named scales --- packages/xen/xen.mjs | 149 ++++++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 237eed039..3d304b706 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, reify, State } from '@strudel/core'; +import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, reify, State, flatten } from '@strudel/core'; import Tune from './tunejs.js'; // returns a list of frequency ratios for given edo scale @@ -61,8 +61,6 @@ function xenOffset(xenScale, offset, index = 0) { const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); - - // accepts a scale name such as 31edo, and a pattern // pattern expected to follow format such that a value can be mapped // to an edostep within the scale. Returns the pattern with @@ -98,62 +96,99 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); * i("0 1 2 3 4 5 6 7").xen("<5edo 10edo 15edo hexany15>") */ -export const xen = register('xen', function (scaleNameOrRatios, pat) { - const JIsc3 = (input) => { - let k = input +export const xen = register( + 'xen', + function (scaleNameOrRatios, pat) { + const JIsc3 = (input) => { + let k = input; - return new Pattern((state) => { - let res = k.query(state) - res = parray(k.query(state).map(x => { - let c = x.context - x = eval(x.value.join('/')) - let r = reify(x).setContext(c) - return r - })).query(state) - return res - }) - } + return new Pattern((state) => { + let res = k.query(state); + res = parray( + k.query(state).map((x) => { + let c = x.context; + x = eval(x.value.join('/')); + let r = reify(x).setContext(c); + return r; + }), + ).query(state); + return res; + }); + }; - const query = state => { - let haps = pat.withHap((hap) => { - let hVal = hap.value; - const isObject = typeof hVal === 'object'; - if (!isObject) { - throw new Error(`Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`); - } - const { i, ...otherValues } = hVal; - let scale - let edo = false - if (scaleNameOrRatios.__pure != undefined) { - scale = getXenScale(scaleNameOrRatios.__pure) - } else { - let st = new State(state.span.intersection(hap.part)) - let sc = scaleNameOrRatios.splitQueries().query(st); - let nameMaybe = sc[0].value - if (typeof sc[0].value == 'string') { - scale = getXenScale(sc[0].value) - if (isEdo(nameMaybe)) { - edo = nameMaybe.match(/^([1-9]+[0-9]*)edo$/)[1] - } - hap = hap.setContext(hap.combineContext(sc[0])) - } else { - // we can do [0] here because JIsc3 uses parray - scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); - hap = hap.setContext(hap.combineContext(scaleNameOrRatios.query(st)[i])) - } - } - let freq = xenOffset(scale, parseNumeral(hVal.i)); - // 10 is somewhat arbitrary - freq = trimFreq(freq); - hap.value = { ...otherValues, freq }; - return edo - ? hap.setContext({ ...hap.context, edoSize: edo }) - : hap; - }).query(state); - return removeUndefineds(haps); - }; - return new Pattern(query) -}, false); + // How do we use curry to solve the stack problem + /* + Register does this by effectively saying + are there multiple args? + If so, split them into two arrays + left has 1, right has rest + join left and right: + + result = join(right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn))); + + where mapFn applies the registered function 'func'. + ---- + When we're in xen but not in query, then we already have access to `scaleNameOrRatios` + Could we run query for each? (could that be 'func' in this case?) + ---- + I don't think so, I think it makes more sense to do it within the query, but call an innerfunc on each arg + */ + const query = (state) => { + let args = scaleNameOrRatios.query(state); + + let k = args.map((arg) => { + let haps = pat + .withHap((hap) => { + let hVal = hap.value; + const isObject = typeof hVal === 'object'; + if (!isObject) { + throw new Error( + `Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`, + ); + } + const { i, ...otherValues } = hVal; + let scale; + let edo = false; + if (scaleNameOrRatios.__pure != undefined) { + scale = getXenScale(scaleNameOrRatios.__pure); + } else { + let st = new State(state.span.intersection(hap.part)); + let sc = arg; + let nameMaybe = sc.value; + if (typeof sc.value == 'string') { + scale = getXenScale(nameMaybe); + if (isEdo(nameMaybe)) { + edo = nameMaybe.match(/^([1-9]+[0-9]*)edo$/)[1]; + } + hap = hap.setContext(hap.combineContext(sc)); + } else { + // we can do [0] here because JIsc3 uses parray + scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); + hap = hap.setContext(hap.combineContext(scaleNameOrRatios.query(st)[i])); + } + } + let freq = xenOffset(scale, parseNumeral(hVal.i)); + // 10 is somewhat arbitrary + freq = trimFreq(freq); + hap.value = { ...otherValues, freq }; + return edo ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; + }) + .query(state); + return removeUndefineds(haps); + }); + console.log(k) + return flatten(k) + }; + + /* + perhaps instead of + return new Pattern(query) + we could do a join on scaleNameOrRatios which creates multiple patterns and then joins them + */ + return new Pattern(query); + }, + false, +); /** * Assumes pattern of frequencies tuned to some `base` frequency, such as the output of `xen` From 3e5c945e1259bae02102a8b5246fbb8f3666a3d3 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 15:00:39 -0400 Subject: [PATCH 27/66] added thoughts. --- packages/xen/xen.mjs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 3d304b706..248d2739d 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -132,6 +132,23 @@ export const xen = register( Could we run query for each? (could that be 'func' in this case?) ---- I don't think so, I think it makes more sense to do it within the query, but call an innerfunc on each arg + ---- + Okay, working for strings. + + How do you tell the different between these two inputs? + + i("0 1").xen(` + 1:1, + [13:12, 5:4], + 2:1 + `).piano().log() + + i("0 1").xen(` + 1:1, + 13:12, + 5:4, + 2:1 + `).piano().log() */ const query = (state) => { let args = scaleNameOrRatios.query(state); From 7c7da3e783ea486af797511a6b1daec017a7512e Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 18:01:56 -0400 Subject: [PATCH 28/66] progress --- packages/xen/xen.mjs | 72 ++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 248d2739d..ec18819ca 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -149,11 +149,44 @@ export const xen = register( 5:4, 2:1 `).piano().log() - */ - const query = (state) => { - let args = scaleNameOrRatios.query(state); - let k = args.map((arg) => { + + If it's not, we can just do: + xen([ + "1:1", + "[13:12, 5:4]", + ... + ]) + + ---- + + okay I got the above working and now I need to rejigger this one to work with the other stuff + + i("0 1 2 3").xen("hexany15, [sev-elev hexany23]").s("sin") + + */ + + const JIsc2 = (input) => { + let k = parray(input) + + return new Pattern((state) => { + let res = k.withValue(x => x.map(y => eval(y.join('/')))).query(state) + return res + }) + } + + const query = (state) => { + let input = scaleNameOrRatios.query(state); + + let scPat = input[0].value + let scs + if (typeof scPat == 'string') { + scs = [scPat] + } else { + scs = JIsc2(scPat).query(state) + } + + let k = scs.map(sc => { let haps = pat .withHap((hap) => { let hVal = hap.value; @@ -166,22 +199,17 @@ export const xen = register( const { i, ...otherValues } = hVal; let scale; let edo = false; - if (scaleNameOrRatios.__pure != undefined) { - scale = getXenScale(scaleNameOrRatios.__pure); + let st = new State(state.span.intersection(hap.part)); + if (typeof sc == 'string') { + scale = getXenScale(sc); + if (isEdo(sc)) { + edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; + } + hap = hap.setContext(hap.combineContext(sc)); } else { - let st = new State(state.span.intersection(hap.part)); - let sc = arg; - let nameMaybe = sc.value; - if (typeof sc.value == 'string') { - scale = getXenScale(nameMaybe); - if (isEdo(nameMaybe)) { - edo = nameMaybe.match(/^([1-9]+[0-9]*)edo$/)[1]; - } - hap = hap.setContext(hap.combineContext(sc)); - } else { - // we can do [0] here because JIsc3 uses parray - scale = getXenScale(JIsc3(scaleNameOrRatios).query(state)[0].value); - hap = hap.setContext(hap.combineContext(scaleNameOrRatios.query(st)[i])); + scale = getXenScale(sc.value); + for (let h of scPat[i].query(state)) { + hap = hap.setContext(hap.combineContext(h)); } } let freq = xenOffset(scale, parseNumeral(hVal.i)); @@ -191,9 +219,9 @@ export const xen = register( return edo ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; }) .query(state); - return removeUndefineds(haps); - }); - console.log(k) + // return removeUndefineds(haps); + return haps + }) return flatten(k) }; From f769b9c7a50d67e7adf985e7f6526273ba137194 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 18:11:34 -0400 Subject: [PATCH 29/66] It looks good in repl and bad on test --- packages/xen/xen.mjs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index ec18819ca..8ac524c8d 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -178,15 +178,17 @@ export const xen = register( const query = (state) => { let input = scaleNameOrRatios.query(state); + if (input.length == 0) return [] + let scPat = input[0].value let scs if (typeof scPat == 'string') { - scs = [scPat] + scs = input.map(x=>x.value) } else { scs = JIsc2(scPat).query(state) } - let k = scs.map(sc => { + let k = scs.map((sc, ith) => { let haps = pat .withHap((hap) => { let hVal = hap.value; @@ -205,7 +207,7 @@ export const xen = register( if (isEdo(sc)) { edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; } - hap = hap.setContext(hap.combineContext(sc)); + hap = hap.setContext(hap.combineContext(input[ith])); } else { scale = getXenScale(sc.value); for (let h of scPat[i].query(state)) { @@ -219,8 +221,8 @@ export const xen = register( return edo ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; }) .query(state); - // return removeUndefineds(haps); - return haps + return removeUndefineds(haps); + // return haps }) return flatten(k) }; @@ -230,7 +232,9 @@ export const xen = register( return new Pattern(query) we could do a join on scaleNameOrRatios which creates multiple patterns and then joins them */ - return new Pattern(query); + let res = new Pattern(query) + + return res; }, false, ); From 25bf6856aedcba1ec1a9b9ebb8a6dae437a018ff Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 18:25:01 -0400 Subject: [PATCH 30/66] format, snapshot --- packages/xen/xen.mjs | 98 ++---- test/__snapshots__/examples.test.mjs.snap | 365 +++++++++++++++++++--- 2 files changed, 349 insertions(+), 114 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 8ac524c8d..a5cd72488 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -67,6 +67,16 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); // values mapped to the frequencies associated with the given edosteps // scaleNameOrRatios: string || number[], steps?: number +// old, currently unsupported +// * @example +// * // You can also use xen with frequency ratios. +// * // This is equivalent to the above: +// * i("0 1 2").xen([ +// * Math.pow(2, 0/31), +// * Math.pow(2, 8/31), +// * Math.pow(2, 18/31), +// * ]).piano() + /** * Assumes a numerical pattern of scale steps, and a scale. Scales accepted are all preset scale names of `tune`, arbitrary edos such as 31edo, or an array of frequency ratios. Assumes scales repeat at octave (2/1). Returns a new pattern with all values mapped to their associated frequency, assuming a base frequency of 220hz. * @@ -79,14 +89,6 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); * // A minor triad in 31edo: * i("0 8 18").xen("31edo").piano() * @example - * // You can also use xen with frequency ratios. - * // This is equivalent to the above: - * i("0 1 2").xen([ - * Math.pow(2, 0/31), - * Math.pow(2, 8/31), - * Math.pow(2, 18/31), - * ]).piano() - * @example * // xen also supports all scale names that * // tune does: * i("0 1 2 3 4 5").xen("hexany15") @@ -116,76 +118,26 @@ export const xen = register( }); }; - // How do we use curry to solve the stack problem - /* - Register does this by effectively saying - are there multiple args? - If so, split them into two arrays - left has 1, right has rest - join left and right: - - result = join(right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn))); - - where mapFn applies the registered function 'func'. - ---- - When we're in xen but not in query, then we already have access to `scaleNameOrRatios` - Could we run query for each? (could that be 'func' in this case?) - ---- - I don't think so, I think it makes more sense to do it within the query, but call an innerfunc on each arg - ---- - Okay, working for strings. - - How do you tell the different between these two inputs? - - i("0 1").xen(` - 1:1, - [13:12, 5:4], - 2:1 - `).piano().log() - - i("0 1").xen(` - 1:1, - 13:12, - 5:4, - 2:1 - `).piano().log() - - - If it's not, we can just do: - xen([ - "1:1", - "[13:12, 5:4]", - ... - ]) - - ---- - - okay I got the above working and now I need to rejigger this one to work with the other stuff - - i("0 1 2 3").xen("hexany15, [sev-elev hexany23]").s("sin") - - */ - const JIsc2 = (input) => { - let k = parray(input) + let k = parray(input); return new Pattern((state) => { - let res = k.withValue(x => x.map(y => eval(y.join('/')))).query(state) - return res - }) - } + let res = k.withValue((x) => x.map((y) => eval(y.join('/')))).query(state); + return res; + }); + }; const query = (state) => { let input = scaleNameOrRatios.query(state); - - if (input.length == 0) return [] - let scPat = input[0].value - let scs + if (input.length == 0) return []; + + let scPat = input[0].value; + let scs; if (typeof scPat == 'string') { - scs = input.map(x=>x.value) + scs = input.map((x) => x.value); } else { - scs = JIsc2(scPat).query(state) + scs = JIsc2(scPat).query(state); } let k = scs.map((sc, ith) => { @@ -223,8 +175,8 @@ export const xen = register( .query(state); return removeUndefineds(haps); // return haps - }) - return flatten(k) + }); + return flatten(k); }; /* @@ -232,8 +184,8 @@ export const xen = register( return new Pattern(query) we could do a join on scaleNameOrRatios which creates multiple patterns and then joins them */ - let res = new Pattern(query) - + let res = new Pattern(query); + return res; }, false, diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 458904baf..cc8a73c89 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5832,37 +5832,133 @@ exports[`runs examples > example "hush" example index 0 1`] = ` exports[`runs examples > example "i" example index 0 1`] = ` [ + "[ 0/1 → 1/8 | freq:220 ]", + "[ 0/1 → 1/8 | freq:220 ]", + "[ 0/1 → 1/8 | freq:220 ]", "[ 0/1 → 1/8 | freq:220 ]", "[ 1/8 → 1/4 | freq:252.7136381 ]", + "[ 1/8 → 1/4 | freq:235.7901618 ]", + "[ 1/8 → 1/4 | freq:230.404707 ]", + "[ 1/8 → 1/4 | freq:275 ]", "[ 1/4 → 3/8 | freq:290.2917404 ]", + "[ 1/4 → 3/8 | freq:252.7136381 ]", + "[ 1/4 → 3/8 | freq:241.3014955 ]", + "[ 1/4 → 3/8 | freq:293.3333333 ]", "[ 3/8 → 1/2 | freq:333.4576446 ]", + "[ 3/8 → 1/2 | freq:270.8517709 ]", + "[ 3/8 → 1/2 | freq:252.7136381 ]", + "[ 3/8 → 1/2 | freq:330 ]", "[ 1/2 → 5/8 | freq:383.0422479 ]", + "[ 1/2 → 5/8 | freq:290.2917404 ]", + "[ 1/2 → 5/8 | freq:264.6655079 ]", + "[ 1/2 → 5/8 | freq:352 ]", + "[ 5/8 → 3/4 | freq:440 ]", + "[ 5/8 → 3/4 | freq:311.1269837 ]", + "[ 5/8 → 3/4 | freq:277.182631 ]", "[ 5/8 → 3/4 | freq:440 ]", "[ 3/4 → 7/8 | freq:505.4272762 ]", + "[ 3/4 → 7/8 | freq:333.4576446 ]", + "[ 3/4 → 7/8 | freq:290.2917404 ]", + "[ 3/4 → 7/8 | freq:550 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", + "[ 7/8 → 1/1 | freq:357.3910544 ]", + "[ 7/8 → 1/1 | freq:304.0208336 ]", + "[ 7/8 → 1/1 | freq:586.6666667 ]", "[ 1/1 → 9/8 | freq:220 ]", + "[ 1/1 → 9/8 | freq:220 ]", + "[ 1/1 → 9/8 | freq:220 ]", + "[ 1/1 → 9/8 | freq:220 ]", + "[ 9/8 → 5/4 | freq:252.7136381 ]", "[ 9/8 → 5/4 | freq:235.7901618 ]", + "[ 9/8 → 5/4 | freq:230.404707 ]", + "[ 9/8 → 5/4 | freq:275 ]", + "[ 5/4 → 11/8 | freq:290.2917404 ]", "[ 5/4 → 11/8 | freq:252.7136381 ]", + "[ 5/4 → 11/8 | freq:241.3014955 ]", + "[ 5/4 → 11/8 | freq:293.3333333 ]", + "[ 11/8 → 3/2 | freq:333.4576446 ]", "[ 11/8 → 3/2 | freq:270.8517709 ]", + "[ 11/8 → 3/2 | freq:252.7136381 ]", + "[ 11/8 → 3/2 | freq:330 ]", + "[ 3/2 → 13/8 | freq:383.0422479 ]", "[ 3/2 → 13/8 | freq:290.2917404 ]", + "[ 3/2 → 13/8 | freq:264.6655079 ]", + "[ 3/2 → 13/8 | freq:352 ]", + "[ 13/8 → 7/4 | freq:440 ]", "[ 13/8 → 7/4 | freq:311.1269837 ]", + "[ 13/8 → 7/4 | freq:277.182631 ]", + "[ 13/8 → 7/4 | freq:440 ]", + "[ 7/4 → 15/8 | freq:505.4272762 ]", "[ 7/4 → 15/8 | freq:333.4576446 ]", + "[ 7/4 → 15/8 | freq:290.2917404 ]", + "[ 7/4 → 15/8 | freq:550 ]", + "[ 15/8 → 2/1 | freq:580.5834807 ]", "[ 15/8 → 2/1 | freq:357.3910544 ]", + "[ 15/8 → 2/1 | freq:304.0208336 ]", + "[ 15/8 → 2/1 | freq:586.6666667 ]", "[ 2/1 → 17/8 | freq:220 ]", + "[ 2/1 → 17/8 | freq:220 ]", + "[ 2/1 → 17/8 | freq:220 ]", + "[ 2/1 → 17/8 | freq:220 ]", + "[ 17/8 → 9/4 | freq:252.7136381 ]", + "[ 17/8 → 9/4 | freq:235.7901618 ]", "[ 17/8 → 9/4 | freq:230.404707 ]", + "[ 17/8 → 9/4 | freq:275 ]", + "[ 9/4 → 19/8 | freq:290.2917404 ]", + "[ 9/4 → 19/8 | freq:252.7136381 ]", "[ 9/4 → 19/8 | freq:241.3014955 ]", + "[ 9/4 → 19/8 | freq:293.3333333 ]", + "[ 19/8 → 5/2 | freq:333.4576446 ]", + "[ 19/8 → 5/2 | freq:270.8517709 ]", "[ 19/8 → 5/2 | freq:252.7136381 ]", + "[ 19/8 → 5/2 | freq:330 ]", + "[ 5/2 → 21/8 | freq:383.0422479 ]", + "[ 5/2 → 21/8 | freq:290.2917404 ]", "[ 5/2 → 21/8 | freq:264.6655079 ]", + "[ 5/2 → 21/8 | freq:352 ]", + "[ 21/8 → 11/4 | freq:440 ]", + "[ 21/8 → 11/4 | freq:311.1269837 ]", "[ 21/8 → 11/4 | freq:277.182631 ]", + "[ 21/8 → 11/4 | freq:440 ]", + "[ 11/4 → 23/8 | freq:505.4272762 ]", + "[ 11/4 → 23/8 | freq:333.4576446 ]", "[ 11/4 → 23/8 | freq:290.2917404 ]", + "[ 11/4 → 23/8 | freq:550 ]", + "[ 23/8 → 3/1 | freq:580.5834807 ]", + "[ 23/8 → 3/1 | freq:357.3910544 ]", "[ 23/8 → 3/1 | freq:304.0208336 ]", + "[ 23/8 → 3/1 | freq:586.6666667 ]", "[ 3/1 → 25/8 | freq:220 ]", + "[ 3/1 → 25/8 | freq:220 ]", + "[ 3/1 → 25/8 | freq:220 ]", + "[ 3/1 → 25/8 | freq:220 ]", + "[ 25/8 → 13/4 | freq:252.7136381 ]", + "[ 25/8 → 13/4 | freq:235.7901618 ]", + "[ 25/8 → 13/4 | freq:230.404707 ]", "[ 25/8 → 13/4 | freq:275 ]", + "[ 13/4 → 27/8 | freq:290.2917404 ]", + "[ 13/4 → 27/8 | freq:252.7136381 ]", + "[ 13/4 → 27/8 | freq:241.3014955 ]", "[ 13/4 → 27/8 | freq:293.3333333 ]", + "[ 27/8 → 7/2 | freq:333.4576446 ]", + "[ 27/8 → 7/2 | freq:270.8517709 ]", + "[ 27/8 → 7/2 | freq:252.7136381 ]", "[ 27/8 → 7/2 | freq:330 ]", + "[ 7/2 → 29/8 | freq:383.0422479 ]", + "[ 7/2 → 29/8 | freq:290.2917404 ]", + "[ 7/2 → 29/8 | freq:264.6655079 ]", "[ 7/2 → 29/8 | freq:352 ]", "[ 29/8 → 15/4 | freq:440 ]", + "[ 29/8 → 15/4 | freq:311.1269837 ]", + "[ 29/8 → 15/4 | freq:277.182631 ]", + "[ 29/8 → 15/4 | freq:440 ]", + "[ 15/4 → 31/8 | freq:505.4272762 ]", + "[ 15/4 → 31/8 | freq:333.4576446 ]", + "[ 15/4 → 31/8 | freq:290.2917404 ]", "[ 15/4 → 31/8 | freq:550 ]", + "[ 31/8 → 4/1 | freq:580.5834807 ]", + "[ 31/8 → 4/1 | freq:357.3910544 ]", + "[ 31/8 → 4/1 | freq:304.0208336 ]", "[ 31/8 → 4/1 | freq:586.6666667 ]", ] `; @@ -14566,99 +14662,286 @@ exports[`runs examples > example "wtphaserand" example index 0 1`] = ` exports[`runs examples > example "xen" example index 0 1`] = ` [ "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", ] `; exports[`runs examples > example "xen" example index 1 1`] = ` [ - "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 0/1 → 1/6 | freq:220 ]", + "[ 0/1 → 1/6 | freq:220 ]", + "[ 0/1 → 1/6 | freq:220 ]", + "[ 0/1 → 1/6 | freq:220 ]", + "[ 1/6 → 1/3 | freq:275 ]", + "[ 1/6 → 1/3 | freq:275 ]", + "[ 1/6 → 1/3 | freq:275 ]", + "[ 1/6 → 1/3 | freq:275 ]", + "[ 1/3 → 1/2 | freq:293.3333333 ]", + "[ 1/3 → 1/2 | freq:293.3333333 ]", + "[ 1/3 → 1/2 | freq:293.3333333 ]", + "[ 1/3 → 1/2 | freq:293.3333333 ]", + "[ 1/2 → 2/3 | freq:330 ]", + "[ 1/2 → 2/3 | freq:330 ]", + "[ 1/2 → 2/3 | freq:330 ]", + "[ 1/2 → 2/3 | freq:330 ]", + "[ 2/3 → 5/6 | freq:352 ]", + "[ 2/3 → 5/6 | freq:352 ]", + "[ 2/3 → 5/6 | freq:352 ]", + "[ 2/3 → 5/6 | freq:352 ]", + "[ 5/6 → 1/1 | freq:440 ]", + "[ 5/6 → 1/1 | freq:440 ]", + "[ 5/6 → 1/1 | freq:440 ]", + "[ 5/6 → 1/1 | freq:440 ]", + "[ 1/1 → 7/6 | freq:220 ]", + "[ 1/1 → 7/6 | freq:220 ]", + "[ 1/1 → 7/6 | freq:220 ]", + "[ 1/1 → 7/6 | freq:220 ]", + "[ 7/6 → 4/3 | freq:275 ]", + "[ 7/6 → 4/3 | freq:275 ]", + "[ 7/6 → 4/3 | freq:275 ]", + "[ 7/6 → 4/3 | freq:275 ]", + "[ 4/3 → 3/2 | freq:293.3333333 ]", + "[ 4/3 → 3/2 | freq:293.3333333 ]", + "[ 4/3 → 3/2 | freq:293.3333333 ]", + "[ 4/3 → 3/2 | freq:293.3333333 ]", + "[ 3/2 → 5/3 | freq:330 ]", + "[ 3/2 → 5/3 | freq:330 ]", + "[ 3/2 → 5/3 | freq:330 ]", + "[ 3/2 → 5/3 | freq:330 ]", + "[ 5/3 → 11/6 | freq:352 ]", + "[ 5/3 → 11/6 | freq:352 ]", + "[ 5/3 → 11/6 | freq:352 ]", + "[ 5/3 → 11/6 | freq:352 ]", + "[ 11/6 → 2/1 | freq:440 ]", + "[ 11/6 → 2/1 | freq:440 ]", + "[ 11/6 → 2/1 | freq:440 ]", + "[ 11/6 → 2/1 | freq:440 ]", + "[ 2/1 → 13/6 | freq:220 ]", + "[ 2/1 → 13/6 | freq:220 ]", + "[ 2/1 → 13/6 | freq:220 ]", + "[ 2/1 → 13/6 | freq:220 ]", + "[ 13/6 → 7/3 | freq:275 ]", + "[ 13/6 → 7/3 | freq:275 ]", + "[ 13/6 → 7/3 | freq:275 ]", + "[ 13/6 → 7/3 | freq:275 ]", + "[ 7/3 → 5/2 | freq:293.3333333 ]", + "[ 7/3 → 5/2 | freq:293.3333333 ]", + "[ 7/3 → 5/2 | freq:293.3333333 ]", + "[ 7/3 → 5/2 | freq:293.3333333 ]", + "[ 5/2 → 8/3 | freq:330 ]", + "[ 5/2 → 8/3 | freq:330 ]", + "[ 5/2 → 8/3 | freq:330 ]", + "[ 5/2 → 8/3 | freq:330 ]", + "[ 8/3 → 17/6 | freq:352 ]", + "[ 8/3 → 17/6 | freq:352 ]", + "[ 8/3 → 17/6 | freq:352 ]", + "[ 8/3 → 17/6 | freq:352 ]", + "[ 17/6 → 3/1 | freq:440 ]", + "[ 17/6 → 3/1 | freq:440 ]", + "[ 17/6 → 3/1 | freq:440 ]", + "[ 17/6 → 3/1 | freq:440 ]", + "[ 3/1 → 19/6 | freq:220 ]", + "[ 3/1 → 19/6 | freq:220 ]", + "[ 3/1 → 19/6 | freq:220 ]", + "[ 3/1 → 19/6 | freq:220 ]", + "[ 19/6 → 10/3 | freq:275 ]", + "[ 19/6 → 10/3 | freq:275 ]", + "[ 19/6 → 10/3 | freq:275 ]", + "[ 19/6 → 10/3 | freq:275 ]", + "[ 10/3 → 7/2 | freq:293.3333333 ]", + "[ 10/3 → 7/2 | freq:293.3333333 ]", + "[ 10/3 → 7/2 | freq:293.3333333 ]", + "[ 10/3 → 7/2 | freq:293.3333333 ]", + "[ 7/2 → 11/3 | freq:330 ]", + "[ 7/2 → 11/3 | freq:330 ]", + "[ 7/2 → 11/3 | freq:330 ]", + "[ 7/2 → 11/3 | freq:330 ]", + "[ 11/3 → 23/6 | freq:352 ]", + "[ 11/3 → 23/6 | freq:352 ]", + "[ 11/3 → 23/6 | freq:352 ]", + "[ 11/3 → 23/6 | freq:352 ]", + "[ 23/6 → 4/1 | freq:440 ]", + "[ 23/6 → 4/1 | freq:440 ]", + "[ 23/6 → 4/1 | freq:440 ]", + "[ 23/6 → 4/1 | freq:440 ]", ] `; exports[`runs examples > example "xen" example index 2 1`] = ` [ - "[ 0/1 → 1/6 | freq:220 ]", - "[ 1/6 → 1/3 | freq:275 ]", - "[ 1/3 → 1/2 | freq:293.3333333 ]", - "[ 1/2 → 2/3 | freq:330 ]", - "[ 2/3 → 5/6 | freq:352 ]", - "[ 5/6 → 1/1 | freq:440 ]", - "[ 1/1 → 7/6 | freq:220 ]", - "[ 7/6 → 4/3 | freq:275 ]", - "[ 4/3 → 3/2 | freq:293.3333333 ]", - "[ 3/2 → 5/3 | freq:330 ]", - "[ 5/3 → 11/6 | freq:352 ]", - "[ 11/6 → 2/1 | freq:440 ]", - "[ 2/1 → 13/6 | freq:220 ]", - "[ 13/6 → 7/3 | freq:275 ]", - "[ 7/3 → 5/2 | freq:293.3333333 ]", - "[ 5/2 → 8/3 | freq:330 ]", - "[ 8/3 → 17/6 | freq:352 ]", - "[ 17/6 → 3/1 | freq:440 ]", - "[ 3/1 → 19/6 | freq:220 ]", - "[ 19/6 → 10/3 | freq:275 ]", - "[ 10/3 → 7/2 | freq:293.3333333 ]", - "[ 7/2 → 11/3 | freq:330 ]", - "[ 11/3 → 23/6 | freq:352 ]", - "[ 23/6 → 4/1 | freq:440 ]", -] -`; - -exports[`runs examples > example "xen" example index 3 1`] = ` -[ + "[ 0/1 → 1/8 | freq:220 ]", + "[ 0/1 → 1/8 | freq:220 ]", + "[ 0/1 → 1/8 | freq:220 ]", "[ 0/1 → 1/8 | freq:220 ]", "[ 1/8 → 1/4 | freq:252.7136381 ]", + "[ 1/8 → 1/4 | freq:235.7901618 ]", + "[ 1/8 → 1/4 | freq:230.404707 ]", + "[ 1/8 → 1/4 | freq:275 ]", "[ 1/4 → 3/8 | freq:290.2917404 ]", + "[ 1/4 → 3/8 | freq:252.7136381 ]", + "[ 1/4 → 3/8 | freq:241.3014955 ]", + "[ 1/4 → 3/8 | freq:293.3333333 ]", "[ 3/8 → 1/2 | freq:333.4576446 ]", + "[ 3/8 → 1/2 | freq:270.8517709 ]", + "[ 3/8 → 1/2 | freq:252.7136381 ]", + "[ 3/8 → 1/2 | freq:330 ]", "[ 1/2 → 5/8 | freq:383.0422479 ]", + "[ 1/2 → 5/8 | freq:290.2917404 ]", + "[ 1/2 → 5/8 | freq:264.6655079 ]", + "[ 1/2 → 5/8 | freq:352 ]", + "[ 5/8 → 3/4 | freq:440 ]", + "[ 5/8 → 3/4 | freq:311.1269837 ]", + "[ 5/8 → 3/4 | freq:277.182631 ]", "[ 5/8 → 3/4 | freq:440 ]", "[ 3/4 → 7/8 | freq:505.4272762 ]", + "[ 3/4 → 7/8 | freq:333.4576446 ]", + "[ 3/4 → 7/8 | freq:290.2917404 ]", + "[ 3/4 → 7/8 | freq:550 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", + "[ 7/8 → 1/1 | freq:357.3910544 ]", + "[ 7/8 → 1/1 | freq:304.0208336 ]", + "[ 7/8 → 1/1 | freq:586.6666667 ]", "[ 1/1 → 9/8 | freq:220 ]", + "[ 1/1 → 9/8 | freq:220 ]", + "[ 1/1 → 9/8 | freq:220 ]", + "[ 1/1 → 9/8 | freq:220 ]", + "[ 9/8 → 5/4 | freq:252.7136381 ]", "[ 9/8 → 5/4 | freq:235.7901618 ]", + "[ 9/8 → 5/4 | freq:230.404707 ]", + "[ 9/8 → 5/4 | freq:275 ]", + "[ 5/4 → 11/8 | freq:290.2917404 ]", "[ 5/4 → 11/8 | freq:252.7136381 ]", + "[ 5/4 → 11/8 | freq:241.3014955 ]", + "[ 5/4 → 11/8 | freq:293.3333333 ]", + "[ 11/8 → 3/2 | freq:333.4576446 ]", "[ 11/8 → 3/2 | freq:270.8517709 ]", + "[ 11/8 → 3/2 | freq:252.7136381 ]", + "[ 11/8 → 3/2 | freq:330 ]", + "[ 3/2 → 13/8 | freq:383.0422479 ]", "[ 3/2 → 13/8 | freq:290.2917404 ]", + "[ 3/2 → 13/8 | freq:264.6655079 ]", + "[ 3/2 → 13/8 | freq:352 ]", + "[ 13/8 → 7/4 | freq:440 ]", "[ 13/8 → 7/4 | freq:311.1269837 ]", + "[ 13/8 → 7/4 | freq:277.182631 ]", + "[ 13/8 → 7/4 | freq:440 ]", + "[ 7/4 → 15/8 | freq:505.4272762 ]", "[ 7/4 → 15/8 | freq:333.4576446 ]", + "[ 7/4 → 15/8 | freq:290.2917404 ]", + "[ 7/4 → 15/8 | freq:550 ]", + "[ 15/8 → 2/1 | freq:580.5834807 ]", "[ 15/8 → 2/1 | freq:357.3910544 ]", + "[ 15/8 → 2/1 | freq:304.0208336 ]", + "[ 15/8 → 2/1 | freq:586.6666667 ]", "[ 2/1 → 17/8 | freq:220 ]", + "[ 2/1 → 17/8 | freq:220 ]", + "[ 2/1 → 17/8 | freq:220 ]", + "[ 2/1 → 17/8 | freq:220 ]", + "[ 17/8 → 9/4 | freq:252.7136381 ]", + "[ 17/8 → 9/4 | freq:235.7901618 ]", "[ 17/8 → 9/4 | freq:230.404707 ]", + "[ 17/8 → 9/4 | freq:275 ]", + "[ 9/4 → 19/8 | freq:290.2917404 ]", + "[ 9/4 → 19/8 | freq:252.7136381 ]", "[ 9/4 → 19/8 | freq:241.3014955 ]", + "[ 9/4 → 19/8 | freq:293.3333333 ]", + "[ 19/8 → 5/2 | freq:333.4576446 ]", + "[ 19/8 → 5/2 | freq:270.8517709 ]", "[ 19/8 → 5/2 | freq:252.7136381 ]", + "[ 19/8 → 5/2 | freq:330 ]", + "[ 5/2 → 21/8 | freq:383.0422479 ]", + "[ 5/2 → 21/8 | freq:290.2917404 ]", "[ 5/2 → 21/8 | freq:264.6655079 ]", + "[ 5/2 → 21/8 | freq:352 ]", + "[ 21/8 → 11/4 | freq:440 ]", + "[ 21/8 → 11/4 | freq:311.1269837 ]", "[ 21/8 → 11/4 | freq:277.182631 ]", + "[ 21/8 → 11/4 | freq:440 ]", + "[ 11/4 → 23/8 | freq:505.4272762 ]", + "[ 11/4 → 23/8 | freq:333.4576446 ]", "[ 11/4 → 23/8 | freq:290.2917404 ]", + "[ 11/4 → 23/8 | freq:550 ]", + "[ 23/8 → 3/1 | freq:580.5834807 ]", + "[ 23/8 → 3/1 | freq:357.3910544 ]", "[ 23/8 → 3/1 | freq:304.0208336 ]", + "[ 23/8 → 3/1 | freq:586.6666667 ]", "[ 3/1 → 25/8 | freq:220 ]", + "[ 3/1 → 25/8 | freq:220 ]", + "[ 3/1 → 25/8 | freq:220 ]", + "[ 3/1 → 25/8 | freq:220 ]", + "[ 25/8 → 13/4 | freq:252.7136381 ]", + "[ 25/8 → 13/4 | freq:235.7901618 ]", + "[ 25/8 → 13/4 | freq:230.404707 ]", "[ 25/8 → 13/4 | freq:275 ]", + "[ 13/4 → 27/8 | freq:290.2917404 ]", + "[ 13/4 → 27/8 | freq:252.7136381 ]", + "[ 13/4 → 27/8 | freq:241.3014955 ]", "[ 13/4 → 27/8 | freq:293.3333333 ]", + "[ 27/8 → 7/2 | freq:333.4576446 ]", + "[ 27/8 → 7/2 | freq:270.8517709 ]", + "[ 27/8 → 7/2 | freq:252.7136381 ]", "[ 27/8 → 7/2 | freq:330 ]", + "[ 7/2 → 29/8 | freq:383.0422479 ]", + "[ 7/2 → 29/8 | freq:290.2917404 ]", + "[ 7/2 → 29/8 | freq:264.6655079 ]", "[ 7/2 → 29/8 | freq:352 ]", "[ 29/8 → 15/4 | freq:440 ]", + "[ 29/8 → 15/4 | freq:311.1269837 ]", + "[ 29/8 → 15/4 | freq:277.182631 ]", + "[ 29/8 → 15/4 | freq:440 ]", + "[ 15/4 → 31/8 | freq:505.4272762 ]", + "[ 15/4 → 31/8 | freq:333.4576446 ]", + "[ 15/4 → 31/8 | freq:290.2917404 ]", "[ 15/4 → 31/8 | freq:550 ]", + "[ 31/8 → 4/1 | freq:580.5834807 ]", + "[ 31/8 → 4/1 | freq:357.3910544 ]", + "[ 31/8 → 4/1 | freq:304.0208336 ]", "[ 31/8 → 4/1 | freq:586.6666667 ]", ] `; From fa4d060ed2dd2c92d5ded595992c85b7477c3255 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 18:25:44 -0400 Subject: [PATCH 31/66] add todo --- packages/xen/xen.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index a5cd72488..400d61362 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -68,6 +68,7 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); // scaleNameOrRatios: string || number[], steps?: number // old, currently unsupported +// TODO support // * @example // * // You can also use xen with frequency ratios. // * // This is equivalent to the above: From c0a43a20dc9fcaee18dd39fa77b3805f3140d938 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 19:02:25 -0400 Subject: [PATCH 32/66] complete parity with current implementation --- packages/xen/xen.mjs | 39 +++++++---------------- test/__snapshots__/examples.test.mjs.snap | 19 ++++++++++- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 400d61362..20817c3a6 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -69,14 +69,6 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); // old, currently unsupported // TODO support -// * @example -// * // You can also use xen with frequency ratios. -// * // This is equivalent to the above: -// * i("0 1 2").xen([ -// * Math.pow(2, 0/31), -// * Math.pow(2, 8/31), -// * Math.pow(2, 18/31), -// * ]).piano() /** * Assumes a numerical pattern of scale steps, and a scale. Scales accepted are all preset scale names of `tune`, arbitrary edos such as 31edo, or an array of frequency ratios. Assumes scales repeat at octave (2/1). Returns a new pattern with all values mapped to their associated frequency, assuming a base frequency of 220hz. @@ -90,6 +82,14 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); * // A minor triad in 31edo: * i("0 8 18").xen("31edo").piano() * @example + * // You can also use xen with frequency ratios. + * // This is equivalent to the above: + * i("0 1 2").xen([ + * Math.pow(2, 0/31), + * Math.pow(2, 8/31), + * Math.pow(2, 18/31), + * ]).piano() + * @example * // xen also supports all scale names that * // tune does: * i("0 1 2 3 4 5").xen("hexany15") @@ -102,23 +102,6 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); export const xen = register( 'xen', function (scaleNameOrRatios, pat) { - const JIsc3 = (input) => { - let k = input; - - return new Pattern((state) => { - let res = k.query(state); - res = parray( - k.query(state).map((x) => { - let c = x.context; - x = eval(x.value.join('/')); - let r = reify(x).setContext(c); - return r; - }), - ).query(state); - return res; - }); - }; - const JIsc2 = (input) => { let k = parray(input); @@ -137,6 +120,8 @@ export const xen = register( let scs; if (typeof scPat == 'string') { scs = input.map((x) => x.value); + } else if (scPat.every((x) => typeof x == 'number')) { + scs = [scPat]; } else { scs = JIsc2(scPat).query(state); } @@ -154,13 +139,14 @@ export const xen = register( const { i, ...otherValues } = hVal; let scale; let edo = false; - let st = new State(state.span.intersection(hap.part)); if (typeof sc == 'string') { scale = getXenScale(sc); if (isEdo(sc)) { edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; } hap = hap.setContext(hap.combineContext(input[ith])); + } else if (sc.every((x) => typeof x == 'number')) { + scale = getXenScale(sc); } else { scale = getXenScale(sc.value); for (let h of scPat[i].query(state)) { @@ -175,7 +161,6 @@ export const xen = register( }) .query(state); return removeUndefineds(haps); - // return haps }); return flatten(k); }; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index cc8a73c89..379ef4ada 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -14713,6 +14713,23 @@ exports[`runs examples > example "xen" example index 0 1`] = ` `; exports[`runs examples > example "xen" example index 1 1`] = ` +[ + "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", +] +`; + +exports[`runs examples > example "xen" example index 2 1`] = ` [ "[ 0/1 → 1/6 | freq:220 ]", "[ 0/1 → 1/6 | freq:220 ]", @@ -14813,7 +14830,7 @@ exports[`runs examples > example "xen" example index 1 1`] = ` ] `; -exports[`runs examples > example "xen" example index 2 1`] = ` +exports[`runs examples > example "xen" example index 3 1`] = ` [ "[ 0/1 → 1/8 | freq:220 ]", "[ 0/1 → 1/8 | freq:220 ]", From c901ffb37f86fee6e891e7e5f70b611e9f5856e6 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 19:24:59 -0400 Subject: [PATCH 33/66] example, deprecate tune, swap xen.mdx sections --- packages/xen/tune.mjs | 3 + packages/xen/xen.mjs | 11 +- test/__snapshots__/examples.test.mjs.snap | 263 +++++++++++++++++++++- website/src/pages/learn/xen.mdx | 12 +- 4 files changed, 281 insertions(+), 8 deletions(-) diff --git a/packages/xen/tune.mjs b/packages/xen/tune.mjs index a070515b3..b17ad3292 100644 --- a/packages/xen/tune.mjs +++ b/packages/xen/tune.mjs @@ -8,11 +8,14 @@ import Tune from './tunejs.js'; import { register } from '@strudel/core'; /** + * Deprecated. + * * Assumes pattern contains numerical scale degrees on the `i` control (see examples below). Accepts a scale name or list of frequencies (see all available names at the link on the reference). Returns a new pattern with all values mapped to a frequency ratio. Similar to `xen`. * @name tune * @returns Pattern * @memberof Pattern * @param {(string | number[] )} scale + * @deprecated * @example * i("0 1 2 3 4 5").tune("hexany15").mul("220").freq() * @example diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 20817c3a6..c717c7984 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -90,6 +90,15 @@ const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); * Math.pow(2, 18/31), * ]).piano() * @example + * // However, with the above you won't get highlighting of the scale indices, + * // instead you could do something like: + * i("[<2 ->, 0 1 <- 2> 3]*2").xen([ + * "1:1", // tonic + * "5:4", // Major 3rd + * "[3:2 4:3]", // Perfect 5th, 4th + * "5:3" // Major 6th + * ]).s("tri") + * @example * // xen also supports all scale names that * // tune does: * i("0 1 2 3 4 5").xen("hexany15") @@ -145,7 +154,7 @@ export const xen = register( edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; } hap = hap.setContext(hap.combineContext(input[ith])); - } else if (sc.every((x) => typeof x == 'number')) { + } else if (Array.isArray(sc) && sc.every((x) => typeof x == 'number')) { scale = getXenScale(sc); } else { scale = getXenScale(sc.value); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 379ef4ada..cf14e6a29 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -14730,6 +14730,267 @@ exports[`runs examples > example "xen" example index 1 1`] = ` `; exports[`runs examples > example "xen" example index 2 1`] = ` +[ + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/8 | freq:220 s:tri ]", + "[ 0/1 → 1/2 | freq:330 s:tri ]", + "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", + "[ 0/1 → 1/2 | freq:330 s:tri ]", + "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", + "[ 0/1 → 1/2 | freq:330 s:tri ]", + "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", + "[ 0/1 → 1/2 | freq:330 s:tri ]", + "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 1/8 → 1/4 | freq:275 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 1/2 → 5/8 | freq:220 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 5/8 → 3/4 | freq:275 s:tri ]", + "[ 3/4 → 7/8 | freq:330 s:tri ]", + "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", + "[ 3/4 → 7/8 | freq:330 s:tri ]", + "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", + "[ 3/4 → 7/8 | freq:330 s:tri ]", + "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", + "[ 3/4 → 7/8 | freq:330 s:tri ]", + "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 9/8 | freq:220 s:tri ]", + "[ 1/1 → 3/2 | freq:330 s:tri ]", + "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", + "[ 1/1 → 3/2 | freq:330 s:tri ]", + "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", + "[ 1/1 → 3/2 | freq:330 s:tri ]", + "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", + "[ 1/1 → 3/2 | freq:330 s:tri ]", + "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 9/8 → 5/4 | freq:275 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 3/2 → 13/8 | freq:220 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 13/8 → 7/4 | freq:275 s:tri ]", + "[ 7/4 → 15/8 | freq:330 s:tri ]", + "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", + "[ 7/4 → 15/8 | freq:330 s:tri ]", + "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", + "[ 7/4 → 15/8 | freq:330 s:tri ]", + "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", + "[ 7/4 → 15/8 | freq:330 s:tri ]", + "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 17/8 | freq:220 s:tri ]", + "[ 2/1 → 5/2 | freq:330 s:tri ]", + "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", + "[ 2/1 → 5/2 | freq:330 s:tri ]", + "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", + "[ 2/1 → 5/2 | freq:330 s:tri ]", + "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", + "[ 2/1 → 5/2 | freq:330 s:tri ]", + "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 17/8 → 9/4 | freq:275 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 5/2 → 21/8 | freq:220 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 21/8 → 11/4 | freq:275 s:tri ]", + "[ 11/4 → 23/8 | freq:330 s:tri ]", + "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", + "[ 11/4 → 23/8 | freq:330 s:tri ]", + "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", + "[ 11/4 → 23/8 | freq:330 s:tri ]", + "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", + "[ 11/4 → 23/8 | freq:330 s:tri ]", + "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 25/8 | freq:220 s:tri ]", + "[ 3/1 → 7/2 | freq:330 s:tri ]", + "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", + "[ 3/1 → 7/2 | freq:330 s:tri ]", + "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", + "[ 3/1 → 7/2 | freq:330 s:tri ]", + "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", + "[ 3/1 → 7/2 | freq:330 s:tri ]", + "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 25/8 → 13/4 | freq:275 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 7/2 → 29/8 | freq:220 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 29/8 → 15/4 | freq:275 s:tri ]", + "[ 15/4 → 31/8 | freq:330 s:tri ]", + "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", + "[ 15/4 → 31/8 | freq:330 s:tri ]", + "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", + "[ 15/4 → 31/8 | freq:330 s:tri ]", + "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", + "[ 15/4 → 31/8 | freq:330 s:tri ]", + "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", + "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", +] +`; + +exports[`runs examples > example "xen" example index 3 1`] = ` [ "[ 0/1 → 1/6 | freq:220 ]", "[ 0/1 → 1/6 | freq:220 ]", @@ -14830,7 +15091,7 @@ exports[`runs examples > example "xen" example index 2 1`] = ` ] `; -exports[`runs examples > example "xen" example index 3 1`] = ` +exports[`runs examples > example "xen" example index 4 1`] = ` [ "[ 0/1 → 1/8 | freq:220 ]", "[ 0/1 → 1/8 | freq:220 ]", diff --git a/website/src/pages/learn/xen.mdx b/website/src/pages/learn/xen.mdx index 144a40f1a..3e6a5b12f 100644 --- a/website/src/pages/learn/xen.mdx +++ b/website/src/pages/learn/xen.mdx @@ -12,6 +12,12 @@ import { JsDoc } from '../../docs/JsDoc'; These functions allow the use of scales other than your typical chromatic 12 based ones. +### xen(scaleOrRatios) + +{/* TODO add explanation of EDO to documentation */} + + + ### tune(scale) {/* TODO (maybe): combine jsdoc things in tune.mjs with here */} @@ -110,9 +116,3 @@ You can also give tune a list of frequencies to use as the scale: 523.2511306012 ]).mul(220).freq();`} /> - -### xen(scaleOrRatios) - -{/* TODO add explanation of EDO to documentation */} - - From b586d8c4c4bd53287f894310c483c528bcbf8472 Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 10 Mar 2026 19:39:08 -0400 Subject: [PATCH 34/66] add notes on missing functionality --- packages/xen/xen.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index c717c7984..8a5e242dc 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -120,6 +120,20 @@ export const xen = register( }); }; + // TODO fix the following: + /* + let p1 = i("0 1").xen("hexany15 hexany23") + let p2 = i("[0 1]").xen([ + "[1:1 2:1]", // tonic + "5:4", // Major 3rd + ]).s("tri") + + let p3 = i("[0 1]").xen([ + "[<1:1 2:1>]", // tonic + "5:4", // Major 3rd + ]).s("tri") + */ + const query = (state) => { let input = scaleNameOrRatios.query(state); From beee2bc921ec25a440d9e056e8c7c67cf6e52746 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 13:53:24 -0400 Subject: [PATCH 35/66] refactor --- packages/xen/xen.mjs | 189 +++++--- test/__snapshots__/examples.test.mjs.snap | 565 +--------------------- 2 files changed, 146 insertions(+), 608 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 8a5e242dc..7dc4501c8 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -4,7 +4,18 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, reify, State, flatten } from '@strudel/core'; +import { + register, + _mod, + parseNumeral, + removeUndefineds, + Pattern, + parray, + reify, + State, + flatten, + Hap, +} from '@strudel/core'; import Tune from './tunejs.js'; // returns a list of frequency ratios for given edo scale @@ -135,67 +146,133 @@ export const xen = register( */ const query = (state) => { - let input = scaleNameOrRatios.query(state); + // let input = scaleNameOrRatios.query(state); - if (input.length == 0) return []; + // if (input.length == 0) return []; - let scPat = input[0].value; - let scs; - if (typeof scPat == 'string') { - scs = input.map((x) => x.value); - } else if (scPat.every((x) => typeof x == 'number')) { - scs = [scPat]; - } else { - scs = JIsc2(scPat).query(state); - } + // let scPat = input[0].value; + // let scs; + // if (typeof scPat == 'string') { + // scs = input.map((x) => x.value); + // } else if (scPat.every((x) => typeof x == 'number')) { + // scs = [scPat]; + // } else { + // scs = JIsc2(scPat).query(state); + // } - let k = scs.map((sc, ith) => { - let haps = pat - .withHap((hap) => { - let hVal = hap.value; - const isObject = typeof hVal === 'object'; - if (!isObject) { - throw new Error( - `Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`, - ); - } - const { i, ...otherValues } = hVal; - let scale; - let edo = false; - if (typeof sc == 'string') { - scale = getXenScale(sc); - if (isEdo(sc)) { - edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; - } - hap = hap.setContext(hap.combineContext(input[ith])); - } else if (Array.isArray(sc) && sc.every((x) => typeof x == 'number')) { - scale = getXenScale(sc); - } else { - scale = getXenScale(sc.value); - for (let h of scPat[i].query(state)) { - hap = hap.setContext(hap.combineContext(h)); - } - } - let freq = xenOffset(scale, parseNumeral(hVal.i)); - // 10 is somewhat arbitrary + // outside should be haps + // which then queries the scales using their hap timespans + + // input[0].value.forEach((inp, ith) => { + // let smth = inp.query(state) + // let scs = JIsc2(inp).query(state) + + // let k = scs.map((sc, jth) => { + let haps = pat.query(state); + let resHaps = []; + + haps.forEach((hap) => { + let hVal = hap.value; + const isObject = typeof hVal === 'object'; + if (!isObject) { + throw new Error( + `Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`, + ); + } + const { i, ...otherValues } = hVal; + let edo = false; + let st = new State(hap.wholeOrPart().intersection(state.span), state.controls); + + // Format: An array of values + // Each value is a pattern, representing an index on the scale + let sc = scaleNameOrRatios.query(st); + // Check to see if input is attempting to be a named scale + let isStr = typeof sc[0].value == 'string'; + if (!isStr) { + sc = sc[0].value; + } + + if (!isStr) { + let scaleByIndices = []; + for (let j = 0; j < sc.length; j++) { + scaleByIndices[j] = sc[j].query(st); + } + /* + Format: An array of 1 or more Haps, each Hap has an array of values of + one or more scale index definitions (contains whole scale) + Assumption: inner value array should be same length between each + */ + let sc2 = JIsc2(sc).query(st); + + for (let j = 0; j < scaleByIndices[i].length; j++) { + let scale = getXenScale(sc2[j].value); + let freq = xenOffset(scale, parseNumeral(i)); + let newHap = hap; freq = trimFreq(freq); - hap.value = { ...otherValues, freq }; - return edo ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; - }) - .query(state); - return removeUndefineds(haps); + newHap.value = { ...otherValues, freq }; + newHap = newHap.setContext(newHap.combineContext(scaleByIndices[i][j])); + resHaps.push(newHap); + } + } else { + // scale is named scale(s) + for (let j = 0; j < sc.length; j++) { + let scale = getXenScale(sc[j].value); + let freq = xenOffset(scale, parseNumeral(i)); + let newHap = hap; + freq = trimFreq(freq); + newHap.value = { ...otherValues, freq }; + newHap = newHap.setContext(newHap.combineContext(sc[j])); + resHaps.push(newHap); + } + } + + /* + We need to know: + - How many Haps to make, given a scale index (i value) from a Hap from pat (sc can tell us) + - The whole scale, so the frequency can be set (sc2 can tell us) + - if sc2.length > 1, how do we deal? + - The frequency of the ith element of the scale, given a Hap (from pat) with i value of ith + + The whole scale definitions in sc2 can be indexed by the index of Hap within sc: + + */ + + // if (typeof sc == 'string') { + // scale = getXenScale(sc); + // if (isEdo(sc)) { + // edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; + // } + // hap = hap.setContext(hap.combineContext(input[ith])); + // } else if (Array.isArray(sc) && sc.every((x) => typeof x == 'number')) { + // scale = getXenScale(sc); + // } else { + // scale = getXenScale(sc.value); + // for (let h of scPat[_mod(i, scale.length)].query(state)) { + // let ts = hap.wholeOrPart().intersection(h.wholeOrPart()); + // if (ts == undefined) return undefined; + // hap = hap.withSpan((x) => ts); + // hap = hap.setContext(hap.combineContext(h)); + // } + // } + // let freq = xenOffset(scale, parseNumeral(i)); + // // 10 is somewhat arbitrary + // freq = trimFreq(freq); + // hap.value = { ...otherValues, freq }; + return edo ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; }); - return flatten(k); + + // }); + // return flatten(k); + // }) + + /* + perhaps instead of + return new Pattern(query) + we could do a join on scaleNameOrRatios which creates multiple patterns and then joins them + */ + return resHaps; }; - - /* - perhaps instead of - return new Pattern(query) - we could do a join on scaleNameOrRatios which creates multiple patterns and then joins them - */ - let res = new Pattern(query); - - return res; + return new Pattern(query); }, false, ); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index cf14e6a29..26bbdfe7a 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5084,18 +5084,18 @@ exports[`runs examples > example "ftranspose" example index 0 1`] = ` exports[`runs examples > example "ftranspose" example index 1 1`] = ` [ - "[ 0/1 → 1/3 | freq:263.0921203 ]", - "[ 1/3 → 2/3 | freq:314.6248353 ]", - "[ 2/3 → 1/1 | freq:393.4589706 ]", - "[ 1/1 → 4/3 | freq:183.965981 ]", - "[ 4/3 → 5/3 | freq:220 ]", - "[ 5/3 → 2/1 | freq:275.1244143 ]", - "[ 2/1 → 7/3 | freq:263.0921203 ]", - "[ 7/3 → 8/3 | freq:314.6248353 ]", - "[ 8/3 → 3/1 | freq:393.4589706 ]", - "[ 3/1 → 10/3 | freq:183.965981 ]", - "[ 10/3 → 11/3 | freq:220 ]", - "[ 11/3 → 4/1 | freq:275.1244143 ]", + "[ 0/1 → 1/3 | freq:349.2282314 ]", + "[ 1/3 → 2/3 | freq:417.6327085 ]", + "[ 2/3 → 1/1 | freq:522.2770651 ]", + "[ 1/1 → 4/3 | freq:138.5913155 ]", + "[ 4/3 → 5/3 | freq:165.7376502 ]", + "[ 5/3 → 2/1 | freq:207.2657906 ]", + "[ 2/1 → 7/3 | freq:349.2282314 ]", + "[ 7/3 → 8/3 | freq:417.6327085 ]", + "[ 8/3 → 3/1 | freq:522.2770651 ]", + "[ 3/1 → 10/3 | freq:138.5913155 ]", + "[ 10/3 → 11/3 | freq:165.7376502 ]", + "[ 11/3 → 4/1 | freq:207.2657906 ]", ] `; @@ -5832,133 +5832,37 @@ exports[`runs examples > example "hush" example index 0 1`] = ` exports[`runs examples > example "i" example index 0 1`] = ` [ - "[ 0/1 → 1/8 | freq:220 ]", - "[ 0/1 → 1/8 | freq:220 ]", - "[ 0/1 → 1/8 | freq:220 ]", "[ 0/1 → 1/8 | freq:220 ]", "[ 1/8 → 1/4 | freq:252.7136381 ]", - "[ 1/8 → 1/4 | freq:235.7901618 ]", - "[ 1/8 → 1/4 | freq:230.404707 ]", - "[ 1/8 → 1/4 | freq:275 ]", "[ 1/4 → 3/8 | freq:290.2917404 ]", - "[ 1/4 → 3/8 | freq:252.7136381 ]", - "[ 1/4 → 3/8 | freq:241.3014955 ]", - "[ 1/4 → 3/8 | freq:293.3333333 ]", "[ 3/8 → 1/2 | freq:333.4576446 ]", - "[ 3/8 → 1/2 | freq:270.8517709 ]", - "[ 3/8 → 1/2 | freq:252.7136381 ]", - "[ 3/8 → 1/2 | freq:330 ]", "[ 1/2 → 5/8 | freq:383.0422479 ]", - "[ 1/2 → 5/8 | freq:290.2917404 ]", - "[ 1/2 → 5/8 | freq:264.6655079 ]", - "[ 1/2 → 5/8 | freq:352 ]", - "[ 5/8 → 3/4 | freq:440 ]", - "[ 5/8 → 3/4 | freq:311.1269837 ]", - "[ 5/8 → 3/4 | freq:277.182631 ]", "[ 5/8 → 3/4 | freq:440 ]", "[ 3/4 → 7/8 | freq:505.4272762 ]", - "[ 3/4 → 7/8 | freq:333.4576446 ]", - "[ 3/4 → 7/8 | freq:290.2917404 ]", - "[ 3/4 → 7/8 | freq:550 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", - "[ 7/8 → 1/1 | freq:357.3910544 ]", - "[ 7/8 → 1/1 | freq:304.0208336 ]", - "[ 7/8 → 1/1 | freq:586.6666667 ]", "[ 1/1 → 9/8 | freq:220 ]", - "[ 1/1 → 9/8 | freq:220 ]", - "[ 1/1 → 9/8 | freq:220 ]", - "[ 1/1 → 9/8 | freq:220 ]", - "[ 9/8 → 5/4 | freq:252.7136381 ]", "[ 9/8 → 5/4 | freq:235.7901618 ]", - "[ 9/8 → 5/4 | freq:230.404707 ]", - "[ 9/8 → 5/4 | freq:275 ]", - "[ 5/4 → 11/8 | freq:290.2917404 ]", "[ 5/4 → 11/8 | freq:252.7136381 ]", - "[ 5/4 → 11/8 | freq:241.3014955 ]", - "[ 5/4 → 11/8 | freq:293.3333333 ]", - "[ 11/8 → 3/2 | freq:333.4576446 ]", "[ 11/8 → 3/2 | freq:270.8517709 ]", - "[ 11/8 → 3/2 | freq:252.7136381 ]", - "[ 11/8 → 3/2 | freq:330 ]", - "[ 3/2 → 13/8 | freq:383.0422479 ]", "[ 3/2 → 13/8 | freq:290.2917404 ]", - "[ 3/2 → 13/8 | freq:264.6655079 ]", - "[ 3/2 → 13/8 | freq:352 ]", - "[ 13/8 → 7/4 | freq:440 ]", "[ 13/8 → 7/4 | freq:311.1269837 ]", - "[ 13/8 → 7/4 | freq:277.182631 ]", - "[ 13/8 → 7/4 | freq:440 ]", - "[ 7/4 → 15/8 | freq:505.4272762 ]", "[ 7/4 → 15/8 | freq:333.4576446 ]", - "[ 7/4 → 15/8 | freq:290.2917404 ]", - "[ 7/4 → 15/8 | freq:550 ]", - "[ 15/8 → 2/1 | freq:580.5834807 ]", "[ 15/8 → 2/1 | freq:357.3910544 ]", - "[ 15/8 → 2/1 | freq:304.0208336 ]", - "[ 15/8 → 2/1 | freq:586.6666667 ]", "[ 2/1 → 17/8 | freq:220 ]", - "[ 2/1 → 17/8 | freq:220 ]", - "[ 2/1 → 17/8 | freq:220 ]", - "[ 2/1 → 17/8 | freq:220 ]", - "[ 17/8 → 9/4 | freq:252.7136381 ]", - "[ 17/8 → 9/4 | freq:235.7901618 ]", "[ 17/8 → 9/4 | freq:230.404707 ]", - "[ 17/8 → 9/4 | freq:275 ]", - "[ 9/4 → 19/8 | freq:290.2917404 ]", - "[ 9/4 → 19/8 | freq:252.7136381 ]", "[ 9/4 → 19/8 | freq:241.3014955 ]", - "[ 9/4 → 19/8 | freq:293.3333333 ]", - "[ 19/8 → 5/2 | freq:333.4576446 ]", - "[ 19/8 → 5/2 | freq:270.8517709 ]", "[ 19/8 → 5/2 | freq:252.7136381 ]", - "[ 19/8 → 5/2 | freq:330 ]", - "[ 5/2 → 21/8 | freq:383.0422479 ]", - "[ 5/2 → 21/8 | freq:290.2917404 ]", "[ 5/2 → 21/8 | freq:264.6655079 ]", - "[ 5/2 → 21/8 | freq:352 ]", - "[ 21/8 → 11/4 | freq:440 ]", - "[ 21/8 → 11/4 | freq:311.1269837 ]", "[ 21/8 → 11/4 | freq:277.182631 ]", - "[ 21/8 → 11/4 | freq:440 ]", - "[ 11/4 → 23/8 | freq:505.4272762 ]", - "[ 11/4 → 23/8 | freq:333.4576446 ]", "[ 11/4 → 23/8 | freq:290.2917404 ]", - "[ 11/4 → 23/8 | freq:550 ]", - "[ 23/8 → 3/1 | freq:580.5834807 ]", - "[ 23/8 → 3/1 | freq:357.3910544 ]", "[ 23/8 → 3/1 | freq:304.0208336 ]", - "[ 23/8 → 3/1 | freq:586.6666667 ]", "[ 3/1 → 25/8 | freq:220 ]", - "[ 3/1 → 25/8 | freq:220 ]", - "[ 3/1 → 25/8 | freq:220 ]", - "[ 3/1 → 25/8 | freq:220 ]", - "[ 25/8 → 13/4 | freq:252.7136381 ]", - "[ 25/8 → 13/4 | freq:235.7901618 ]", - "[ 25/8 → 13/4 | freq:230.404707 ]", "[ 25/8 → 13/4 | freq:275 ]", - "[ 13/4 → 27/8 | freq:290.2917404 ]", - "[ 13/4 → 27/8 | freq:252.7136381 ]", - "[ 13/4 → 27/8 | freq:241.3014955 ]", "[ 13/4 → 27/8 | freq:293.3333333 ]", - "[ 27/8 → 7/2 | freq:333.4576446 ]", - "[ 27/8 → 7/2 | freq:270.8517709 ]", - "[ 27/8 → 7/2 | freq:252.7136381 ]", "[ 27/8 → 7/2 | freq:330 ]", - "[ 7/2 → 29/8 | freq:383.0422479 ]", - "[ 7/2 → 29/8 | freq:290.2917404 ]", - "[ 7/2 → 29/8 | freq:264.6655079 ]", "[ 7/2 → 29/8 | freq:352 ]", "[ 29/8 → 15/4 | freq:440 ]", - "[ 29/8 → 15/4 | freq:311.1269837 ]", - "[ 29/8 → 15/4 | freq:277.182631 ]", - "[ 29/8 → 15/4 | freq:440 ]", - "[ 15/4 → 31/8 | freq:505.4272762 ]", - "[ 15/4 → 31/8 | freq:333.4576446 ]", - "[ 15/4 → 31/8 | freq:290.2917404 ]", "[ 15/4 → 31/8 | freq:550 ]", - "[ 31/8 → 4/1 | freq:580.5834807 ]", - "[ 31/8 → 4/1 | freq:357.3910544 ]", - "[ 31/8 → 4/1 | freq:304.0208336 ]", "[ 31/8 → 4/1 | freq:586.6666667 ]", ] `; @@ -14662,330 +14566,55 @@ exports[`runs examples > example "wtphaserand" example index 0 1`] = ` exports[`runs examples > example "xen" example index 0 1`] = ` [ "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", ] `; -exports[`runs examples > example "xen" example index 1 1`] = ` -[ - "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", -] -`; +exports[`runs examples > example "xen" example index 1 1`] = `[]`; exports[`runs examples > example "xen" example index 2 1`] = ` [ - "[ 0/1 → 1/8 | freq:220 s:tri ]", - "[ 0/1 → 1/8 | freq:220 s:tri ]", - "[ 0/1 → 1/8 | freq:220 s:tri ]", - "[ 0/1 → 1/8 | freq:220 s:tri ]", - "[ 0/1 → 1/8 | freq:220 s:tri ]", - "[ 0/1 → 1/8 | freq:220 s:tri ]", - "[ 0/1 → 1/8 | freq:220 s:tri ]", "[ 0/1 → 1/8 | freq:220 s:tri ]", "[ 0/1 → 1/2 | freq:330 s:tri ]", - "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", - "[ 0/1 → 1/2 | freq:330 s:tri ]", - "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", - "[ 0/1 → 1/2 | freq:330 s:tri ]", - "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", - "[ 0/1 → 1/2 | freq:330 s:tri ]", - "[ 0/1 → 1/2 | freq:293.3333333 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", - "[ 1/8 → 1/4 | freq:275 s:tri ]", "[ 1/8 → 1/4 | freq:275 s:tri ]", "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 3/8 → 1/2 | freq:366.6666667 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", - "[ 1/2 → 5/8 | freq:220 s:tri ]", "[ 1/2 → 5/8 | freq:220 s:tri ]", "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 5/8 → 3/4 | freq:275 s:tri ]", - "[ 3/4 → 7/8 | freq:330 s:tri ]", - "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", - "[ 3/4 → 7/8 | freq:330 s:tri ]", - "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", - "[ 3/4 → 7/8 | freq:330 s:tri ]", - "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", - "[ 3/4 → 7/8 | freq:330 s:tri ]", "[ 3/4 → 7/8 | freq:293.3333333 s:tri ]", "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 7/8 → 1/1 | freq:366.6666667 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", - "[ 1/1 → 9/8 | freq:220 s:tri ]", "[ 1/1 → 9/8 | freq:220 s:tri ]", "[ 1/1 → 3/2 | freq:330 s:tri ]", - "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", - "[ 1/1 → 3/2 | freq:330 s:tri ]", - "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", - "[ 1/1 → 3/2 | freq:330 s:tri ]", - "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", - "[ 1/1 → 3/2 | freq:330 s:tri ]", - "[ 1/1 → 3/2 | freq:293.3333333 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", - "[ 9/8 → 5/4 | freq:275 s:tri ]", "[ 9/8 → 5/4 | freq:275 s:tri ]", "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 11/8 → 3/2 | freq:366.6666667 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", - "[ 3/2 → 13/8 | freq:220 s:tri ]", "[ 3/2 → 13/8 | freq:220 s:tri ]", "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 13/8 → 7/4 | freq:275 s:tri ]", - "[ 7/4 → 15/8 | freq:330 s:tri ]", - "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", - "[ 7/4 → 15/8 | freq:330 s:tri ]", - "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", - "[ 7/4 → 15/8 | freq:330 s:tri ]", - "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", - "[ 7/4 → 15/8 | freq:330 s:tri ]", "[ 7/4 → 15/8 | freq:293.3333333 s:tri ]", "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 15/8 → 2/1 | freq:366.6666667 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", - "[ 2/1 → 17/8 | freq:220 s:tri ]", "[ 2/1 → 17/8 | freq:220 s:tri ]", "[ 2/1 → 5/2 | freq:330 s:tri ]", - "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", - "[ 2/1 → 5/2 | freq:330 s:tri ]", - "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", - "[ 2/1 → 5/2 | freq:330 s:tri ]", - "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", - "[ 2/1 → 5/2 | freq:330 s:tri ]", - "[ 2/1 → 5/2 | freq:293.3333333 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", - "[ 17/8 → 9/4 | freq:275 s:tri ]", "[ 17/8 → 9/4 | freq:275 s:tri ]", "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 19/8 → 5/2 | freq:366.6666667 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", - "[ 5/2 → 21/8 | freq:220 s:tri ]", "[ 5/2 → 21/8 | freq:220 s:tri ]", "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 21/8 → 11/4 | freq:275 s:tri ]", - "[ 11/4 → 23/8 | freq:330 s:tri ]", - "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", - "[ 11/4 → 23/8 | freq:330 s:tri ]", - "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", - "[ 11/4 → 23/8 | freq:330 s:tri ]", - "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", - "[ 11/4 → 23/8 | freq:330 s:tri ]", "[ 11/4 → 23/8 | freq:293.3333333 s:tri ]", "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 23/8 → 3/1 | freq:366.6666667 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", - "[ 3/1 → 25/8 | freq:220 s:tri ]", "[ 3/1 → 25/8 | freq:220 s:tri ]", "[ 3/1 → 7/2 | freq:330 s:tri ]", - "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", - "[ 3/1 → 7/2 | freq:330 s:tri ]", - "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", - "[ 3/1 → 7/2 | freq:330 s:tri ]", - "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", - "[ 3/1 → 7/2 | freq:330 s:tri ]", - "[ 3/1 → 7/2 | freq:293.3333333 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", - "[ 25/8 → 13/4 | freq:275 s:tri ]", "[ 25/8 → 13/4 | freq:275 s:tri ]", "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 27/8 → 7/2 | freq:366.6666667 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", - "[ 7/2 → 29/8 | freq:220 s:tri ]", "[ 7/2 → 29/8 | freq:220 s:tri ]", "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 29/8 → 15/4 | freq:275 s:tri ]", - "[ 15/4 → 31/8 | freq:330 s:tri ]", "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", - "[ 15/4 → 31/8 | freq:330 s:tri ]", - "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", - "[ 15/4 → 31/8 | freq:330 s:tri ]", - "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", - "[ 15/4 → 31/8 | freq:330 s:tri ]", - "[ 15/4 → 31/8 | freq:293.3333333 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", - "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", "[ 31/8 → 4/1 | freq:366.6666667 s:tri ]", ] `; @@ -14993,233 +14622,65 @@ exports[`runs examples > example "xen" example index 2 1`] = ` exports[`runs examples > example "xen" example index 3 1`] = ` [ "[ 0/1 → 1/6 | freq:220 ]", - "[ 0/1 → 1/6 | freq:220 ]", - "[ 0/1 → 1/6 | freq:220 ]", - "[ 0/1 → 1/6 | freq:220 ]", - "[ 1/6 → 1/3 | freq:275 ]", - "[ 1/6 → 1/3 | freq:275 ]", - "[ 1/6 → 1/3 | freq:275 ]", "[ 1/6 → 1/3 | freq:275 ]", "[ 1/3 → 1/2 | freq:293.3333333 ]", - "[ 1/3 → 1/2 | freq:293.3333333 ]", - "[ 1/3 → 1/2 | freq:293.3333333 ]", - "[ 1/3 → 1/2 | freq:293.3333333 ]", - "[ 1/2 → 2/3 | freq:330 ]", - "[ 1/2 → 2/3 | freq:330 ]", - "[ 1/2 → 2/3 | freq:330 ]", "[ 1/2 → 2/3 | freq:330 ]", "[ 2/3 → 5/6 | freq:352 ]", - "[ 2/3 → 5/6 | freq:352 ]", - "[ 2/3 → 5/6 | freq:352 ]", - "[ 2/3 → 5/6 | freq:352 ]", - "[ 5/6 → 1/1 | freq:440 ]", - "[ 5/6 → 1/1 | freq:440 ]", - "[ 5/6 → 1/1 | freq:440 ]", "[ 5/6 → 1/1 | freq:440 ]", "[ 1/1 → 7/6 | freq:220 ]", - "[ 1/1 → 7/6 | freq:220 ]", - "[ 1/1 → 7/6 | freq:220 ]", - "[ 1/1 → 7/6 | freq:220 ]", - "[ 7/6 → 4/3 | freq:275 ]", - "[ 7/6 → 4/3 | freq:275 ]", - "[ 7/6 → 4/3 | freq:275 ]", "[ 7/6 → 4/3 | freq:275 ]", "[ 4/3 → 3/2 | freq:293.3333333 ]", - "[ 4/3 → 3/2 | freq:293.3333333 ]", - "[ 4/3 → 3/2 | freq:293.3333333 ]", - "[ 4/3 → 3/2 | freq:293.3333333 ]", - "[ 3/2 → 5/3 | freq:330 ]", - "[ 3/2 → 5/3 | freq:330 ]", - "[ 3/2 → 5/3 | freq:330 ]", "[ 3/2 → 5/3 | freq:330 ]", "[ 5/3 → 11/6 | freq:352 ]", - "[ 5/3 → 11/6 | freq:352 ]", - "[ 5/3 → 11/6 | freq:352 ]", - "[ 5/3 → 11/6 | freq:352 ]", - "[ 11/6 → 2/1 | freq:440 ]", - "[ 11/6 → 2/1 | freq:440 ]", - "[ 11/6 → 2/1 | freq:440 ]", "[ 11/6 → 2/1 | freq:440 ]", "[ 2/1 → 13/6 | freq:220 ]", - "[ 2/1 → 13/6 | freq:220 ]", - "[ 2/1 → 13/6 | freq:220 ]", - "[ 2/1 → 13/6 | freq:220 ]", - "[ 13/6 → 7/3 | freq:275 ]", - "[ 13/6 → 7/3 | freq:275 ]", - "[ 13/6 → 7/3 | freq:275 ]", "[ 13/6 → 7/3 | freq:275 ]", "[ 7/3 → 5/2 | freq:293.3333333 ]", - "[ 7/3 → 5/2 | freq:293.3333333 ]", - "[ 7/3 → 5/2 | freq:293.3333333 ]", - "[ 7/3 → 5/2 | freq:293.3333333 ]", - "[ 5/2 → 8/3 | freq:330 ]", - "[ 5/2 → 8/3 | freq:330 ]", - "[ 5/2 → 8/3 | freq:330 ]", "[ 5/2 → 8/3 | freq:330 ]", "[ 8/3 → 17/6 | freq:352 ]", - "[ 8/3 → 17/6 | freq:352 ]", - "[ 8/3 → 17/6 | freq:352 ]", - "[ 8/3 → 17/6 | freq:352 ]", - "[ 17/6 → 3/1 | freq:440 ]", - "[ 17/6 → 3/1 | freq:440 ]", - "[ 17/6 → 3/1 | freq:440 ]", "[ 17/6 → 3/1 | freq:440 ]", "[ 3/1 → 19/6 | freq:220 ]", - "[ 3/1 → 19/6 | freq:220 ]", - "[ 3/1 → 19/6 | freq:220 ]", - "[ 3/1 → 19/6 | freq:220 ]", - "[ 19/6 → 10/3 | freq:275 ]", - "[ 19/6 → 10/3 | freq:275 ]", - "[ 19/6 → 10/3 | freq:275 ]", "[ 19/6 → 10/3 | freq:275 ]", "[ 10/3 → 7/2 | freq:293.3333333 ]", - "[ 10/3 → 7/2 | freq:293.3333333 ]", - "[ 10/3 → 7/2 | freq:293.3333333 ]", - "[ 10/3 → 7/2 | freq:293.3333333 ]", - "[ 7/2 → 11/3 | freq:330 ]", - "[ 7/2 → 11/3 | freq:330 ]", - "[ 7/2 → 11/3 | freq:330 ]", "[ 7/2 → 11/3 | freq:330 ]", "[ 11/3 → 23/6 | freq:352 ]", - "[ 11/3 → 23/6 | freq:352 ]", - "[ 11/3 → 23/6 | freq:352 ]", - "[ 11/3 → 23/6 | freq:352 ]", - "[ 23/6 → 4/1 | freq:440 ]", - "[ 23/6 → 4/1 | freq:440 ]", - "[ 23/6 → 4/1 | freq:440 ]", "[ 23/6 → 4/1 | freq:440 ]", ] `; exports[`runs examples > example "xen" example index 4 1`] = ` [ - "[ 0/1 → 1/8 | freq:220 ]", - "[ 0/1 → 1/8 | freq:220 ]", - "[ 0/1 → 1/8 | freq:220 ]", "[ 0/1 → 1/8 | freq:220 ]", "[ 1/8 → 1/4 | freq:252.7136381 ]", - "[ 1/8 → 1/4 | freq:235.7901618 ]", - "[ 1/8 → 1/4 | freq:230.404707 ]", - "[ 1/8 → 1/4 | freq:275 ]", "[ 1/4 → 3/8 | freq:290.2917404 ]", - "[ 1/4 → 3/8 | freq:252.7136381 ]", - "[ 1/4 → 3/8 | freq:241.3014955 ]", - "[ 1/4 → 3/8 | freq:293.3333333 ]", "[ 3/8 → 1/2 | freq:333.4576446 ]", - "[ 3/8 → 1/2 | freq:270.8517709 ]", - "[ 3/8 → 1/2 | freq:252.7136381 ]", - "[ 3/8 → 1/2 | freq:330 ]", "[ 1/2 → 5/8 | freq:383.0422479 ]", - "[ 1/2 → 5/8 | freq:290.2917404 ]", - "[ 1/2 → 5/8 | freq:264.6655079 ]", - "[ 1/2 → 5/8 | freq:352 ]", - "[ 5/8 → 3/4 | freq:440 ]", - "[ 5/8 → 3/4 | freq:311.1269837 ]", - "[ 5/8 → 3/4 | freq:277.182631 ]", "[ 5/8 → 3/4 | freq:440 ]", "[ 3/4 → 7/8 | freq:505.4272762 ]", - "[ 3/4 → 7/8 | freq:333.4576446 ]", - "[ 3/4 → 7/8 | freq:290.2917404 ]", - "[ 3/4 → 7/8 | freq:550 ]", "[ 7/8 → 1/1 | freq:580.5834807 ]", - "[ 7/8 → 1/1 | freq:357.3910544 ]", - "[ 7/8 → 1/1 | freq:304.0208336 ]", - "[ 7/8 → 1/1 | freq:586.6666667 ]", "[ 1/1 → 9/8 | freq:220 ]", - "[ 1/1 → 9/8 | freq:220 ]", - "[ 1/1 → 9/8 | freq:220 ]", - "[ 1/1 → 9/8 | freq:220 ]", - "[ 9/8 → 5/4 | freq:252.7136381 ]", "[ 9/8 → 5/4 | freq:235.7901618 ]", - "[ 9/8 → 5/4 | freq:230.404707 ]", - "[ 9/8 → 5/4 | freq:275 ]", - "[ 5/4 → 11/8 | freq:290.2917404 ]", "[ 5/4 → 11/8 | freq:252.7136381 ]", - "[ 5/4 → 11/8 | freq:241.3014955 ]", - "[ 5/4 → 11/8 | freq:293.3333333 ]", - "[ 11/8 → 3/2 | freq:333.4576446 ]", "[ 11/8 → 3/2 | freq:270.8517709 ]", - "[ 11/8 → 3/2 | freq:252.7136381 ]", - "[ 11/8 → 3/2 | freq:330 ]", - "[ 3/2 → 13/8 | freq:383.0422479 ]", "[ 3/2 → 13/8 | freq:290.2917404 ]", - "[ 3/2 → 13/8 | freq:264.6655079 ]", - "[ 3/2 → 13/8 | freq:352 ]", - "[ 13/8 → 7/4 | freq:440 ]", "[ 13/8 → 7/4 | freq:311.1269837 ]", - "[ 13/8 → 7/4 | freq:277.182631 ]", - "[ 13/8 → 7/4 | freq:440 ]", - "[ 7/4 → 15/8 | freq:505.4272762 ]", "[ 7/4 → 15/8 | freq:333.4576446 ]", - "[ 7/4 → 15/8 | freq:290.2917404 ]", - "[ 7/4 → 15/8 | freq:550 ]", - "[ 15/8 → 2/1 | freq:580.5834807 ]", "[ 15/8 → 2/1 | freq:357.3910544 ]", - "[ 15/8 → 2/1 | freq:304.0208336 ]", - "[ 15/8 → 2/1 | freq:586.6666667 ]", "[ 2/1 → 17/8 | freq:220 ]", - "[ 2/1 → 17/8 | freq:220 ]", - "[ 2/1 → 17/8 | freq:220 ]", - "[ 2/1 → 17/8 | freq:220 ]", - "[ 17/8 → 9/4 | freq:252.7136381 ]", - "[ 17/8 → 9/4 | freq:235.7901618 ]", "[ 17/8 → 9/4 | freq:230.404707 ]", - "[ 17/8 → 9/4 | freq:275 ]", - "[ 9/4 → 19/8 | freq:290.2917404 ]", - "[ 9/4 → 19/8 | freq:252.7136381 ]", "[ 9/4 → 19/8 | freq:241.3014955 ]", - "[ 9/4 → 19/8 | freq:293.3333333 ]", - "[ 19/8 → 5/2 | freq:333.4576446 ]", - "[ 19/8 → 5/2 | freq:270.8517709 ]", "[ 19/8 → 5/2 | freq:252.7136381 ]", - "[ 19/8 → 5/2 | freq:330 ]", - "[ 5/2 → 21/8 | freq:383.0422479 ]", - "[ 5/2 → 21/8 | freq:290.2917404 ]", "[ 5/2 → 21/8 | freq:264.6655079 ]", - "[ 5/2 → 21/8 | freq:352 ]", - "[ 21/8 → 11/4 | freq:440 ]", - "[ 21/8 → 11/4 | freq:311.1269837 ]", "[ 21/8 → 11/4 | freq:277.182631 ]", - "[ 21/8 → 11/4 | freq:440 ]", - "[ 11/4 → 23/8 | freq:505.4272762 ]", - "[ 11/4 → 23/8 | freq:333.4576446 ]", "[ 11/4 → 23/8 | freq:290.2917404 ]", - "[ 11/4 → 23/8 | freq:550 ]", - "[ 23/8 → 3/1 | freq:580.5834807 ]", - "[ 23/8 → 3/1 | freq:357.3910544 ]", "[ 23/8 → 3/1 | freq:304.0208336 ]", - "[ 23/8 → 3/1 | freq:586.6666667 ]", "[ 3/1 → 25/8 | freq:220 ]", - "[ 3/1 → 25/8 | freq:220 ]", - "[ 3/1 → 25/8 | freq:220 ]", - "[ 3/1 → 25/8 | freq:220 ]", - "[ 25/8 → 13/4 | freq:252.7136381 ]", - "[ 25/8 → 13/4 | freq:235.7901618 ]", - "[ 25/8 → 13/4 | freq:230.404707 ]", "[ 25/8 → 13/4 | freq:275 ]", - "[ 13/4 → 27/8 | freq:290.2917404 ]", - "[ 13/4 → 27/8 | freq:252.7136381 ]", - "[ 13/4 → 27/8 | freq:241.3014955 ]", "[ 13/4 → 27/8 | freq:293.3333333 ]", - "[ 27/8 → 7/2 | freq:333.4576446 ]", - "[ 27/8 → 7/2 | freq:270.8517709 ]", - "[ 27/8 → 7/2 | freq:252.7136381 ]", "[ 27/8 → 7/2 | freq:330 ]", - "[ 7/2 → 29/8 | freq:383.0422479 ]", - "[ 7/2 → 29/8 | freq:290.2917404 ]", - "[ 7/2 → 29/8 | freq:264.6655079 ]", "[ 7/2 → 29/8 | freq:352 ]", "[ 29/8 → 15/4 | freq:440 ]", - "[ 29/8 → 15/4 | freq:311.1269837 ]", - "[ 29/8 → 15/4 | freq:277.182631 ]", - "[ 29/8 → 15/4 | freq:440 ]", - "[ 15/4 → 31/8 | freq:505.4272762 ]", - "[ 15/4 → 31/8 | freq:333.4576446 ]", - "[ 15/4 → 31/8 | freq:290.2917404 ]", "[ 15/4 → 31/8 | freq:550 ]", - "[ 31/8 → 4/1 | freq:580.5834807 ]", - "[ 31/8 → 4/1 | freq:357.3910544 ]", - "[ 31/8 → 4/1 | freq:304.0208336 ]", "[ 31/8 → 4/1 | freq:586.6666667 ]", ] `; From f2acdc580f3bef060e06bcf1cf19cb98315b77dc Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 14:01:57 -0400 Subject: [PATCH 36/66] fix edo bug --- packages/xen/xen.mjs | 8 ++++++-- test/__snapshots__/examples.test.mjs.snap | 24 +++++++++++------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 7dc4501c8..46e93d1e9 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -216,12 +216,17 @@ export const xen = register( } else { // scale is named scale(s) for (let j = 0; j < sc.length; j++) { - let scale = getXenScale(sc[j].value); + let scaleName = sc[j].value; + let scale = getXenScale(scaleName); let freq = xenOffset(scale, parseNumeral(i)); let newHap = hap; freq = trimFreq(freq); newHap.value = { ...otherValues, freq }; newHap = newHap.setContext(newHap.combineContext(sc[j])); + if (isEdo(scaleName)) { + edo = scaleName.match(/^([1-9]+[0-9]*)edo$/)[1]; + newHap = newHap.setContext({ ...newHap.context, edoSize: edo }); + } resHaps.push(newHap); } } @@ -258,7 +263,6 @@ export const xen = register( // // 10 is somewhat arbitrary // freq = trimFreq(freq); // hap.value = { ...otherValues, freq }; - return edo ? hap.setContext({ ...hap.context, edoSize: edo }) : hap; }); // }); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 26bbdfe7a..715ba5b42 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5084,18 +5084,18 @@ exports[`runs examples > example "ftranspose" example index 0 1`] = ` exports[`runs examples > example "ftranspose" example index 1 1`] = ` [ - "[ 0/1 → 1/3 | freq:349.2282314 ]", - "[ 1/3 → 2/3 | freq:417.6327085 ]", - "[ 2/3 → 1/1 | freq:522.2770651 ]", - "[ 1/1 → 4/3 | freq:138.5913155 ]", - "[ 4/3 → 5/3 | freq:165.7376502 ]", - "[ 5/3 → 2/1 | freq:207.2657906 ]", - "[ 2/1 → 7/3 | freq:349.2282314 ]", - "[ 7/3 → 8/3 | freq:417.6327085 ]", - "[ 8/3 → 3/1 | freq:522.2770651 ]", - "[ 3/1 → 10/3 | freq:138.5913155 ]", - "[ 10/3 → 11/3 | freq:165.7376502 ]", - "[ 11/3 → 4/1 | freq:207.2657906 ]", + "[ 0/1 → 1/3 | freq:263.0921203 ]", + "[ 1/3 → 2/3 | freq:314.6248353 ]", + "[ 2/3 → 1/1 | freq:393.4589706 ]", + "[ 1/1 → 4/3 | freq:183.965981 ]", + "[ 4/3 → 5/3 | freq:220 ]", + "[ 5/3 → 2/1 | freq:275.1244143 ]", + "[ 2/1 → 7/3 | freq:263.0921203 ]", + "[ 7/3 → 8/3 | freq:314.6248353 ]", + "[ 8/3 → 3/1 | freq:393.4589706 ]", + "[ 3/1 → 10/3 | freq:183.965981 ]", + "[ 10/3 → 11/3 | freq:220 ]", + "[ 11/3 → 4/1 | freq:275.1244143 ]", ] `; From 4596926ac25829e430ee92211ac084b16db7d15b Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 14:16:26 -0400 Subject: [PATCH 37/66] support old functionality --- packages/xen/xen.mjs | 19 +++++++++++++++---- test/__snapshots__/examples.test.mjs.snap | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 46e93d1e9..7f67d353a 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -186,13 +186,16 @@ export const xen = register( // Format: An array of values // Each value is a pattern, representing an index on the scale let sc = scaleNameOrRatios.query(st); - // Check to see if input is attempting to be a named scale + // Check to see if input is attempting to be a named scale or a raw scale let isStr = typeof sc[0].value == 'string'; - if (!isStr) { + let isRawScale = Array.isArray(sc[0].value) && sc[0].value.every((x) => typeof x == 'number'); + if (!isStr && !isRawScale) { sc = sc[0].value; + } else if (isRawScale) { + sc = getXenScale(sc[0].value); } - if (!isStr) { + if (!isStr && !isRawScale) { let scaleByIndices = []; for (let j = 0; j < sc.length; j++) { scaleByIndices[j] = sc[j].query(st); @@ -213,7 +216,13 @@ export const xen = register( newHap = newHap.setContext(newHap.combineContext(scaleByIndices[i][j])); resHaps.push(newHap); } - } else { + } else if (isRawScale) { + let freq = xenOffset(sc, parseNumeral(i)); + let newHap = hap; + freq = trimFreq(freq); + newHap.value = { ...otherValues, freq }; + resHaps.push(newHap); + } else if (isStr) { // scale is named scale(s) for (let j = 0; j < sc.length; j++) { let scaleName = sc[j].value; @@ -229,6 +238,8 @@ export const xen = register( } resHaps.push(newHap); } + } else { + throw new Error('failed to parse scale'); } /* diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 715ba5b42..d4e7c64b6 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -14580,7 +14580,22 @@ exports[`runs examples > example "xen" example index 0 1`] = ` ] `; -exports[`runs examples > example "xen" example index 1 1`] = `[]`; +exports[`runs examples > example "xen" example index 1 1`] = ` +[ + "[ 0/1 → 1/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 1/3 → 2/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 2/3 → 1/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 1/1 → 4/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 4/3 → 5/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/3 → 2/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/1 → 7/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 7/3 → 8/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 8/3 → 3/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/1 → 10/3 | freq:220 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 10/3 → 11/3 | freq:263.0921203 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 11/3 → 4/1 | freq:329.0139341 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", +] +`; exports[`runs examples > example "xen" example index 2 1`] = ` [ From a58711a311e9384532282a665f84b4c348fea8f7 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 14:40:52 -0400 Subject: [PATCH 38/66] bugfix --- packages/xen/xen.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 7f67d353a..66c318296 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -207,13 +207,14 @@ export const xen = register( */ let sc2 = JIsc2(sc).query(st); - for (let j = 0; j < scaleByIndices[i].length; j++) { + let index = _mod(i, sc.length) + for (let j = 0; j < scaleByIndices[index].length; j++) { let scale = getXenScale(sc2[j].value); let freq = xenOffset(scale, parseNumeral(i)); let newHap = hap; freq = trimFreq(freq); newHap.value = { ...otherValues, freq }; - newHap = newHap.setContext(newHap.combineContext(scaleByIndices[i][j])); + newHap = newHap.setContext(newHap.combineContext(scaleByIndices[index][j])); resHaps.push(newHap); } } else if (isRawScale) { From 581bbc05fc6c7a820b2e307e747cf3a6a9f3a1f7 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 15:24:30 -0400 Subject: [PATCH 39/66] room isn't a 0-1 value --- packages/core/controls.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index ac0b26c7c..19e4d5ae7 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2370,7 +2370,7 @@ export const { mode } = registerControl(['mode', 'anchor']); * * @name room * @tags orbit, superdough - * @param {number | Pattern} level between 0 and 1 + * @param {number | Pattern} level * @example * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") * @example From 8ad767363f979ea2c4534b639a54540affcc2737 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 15:54:42 -0400 Subject: [PATCH 40/66] choosein tag --- packages/core/signal.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 9bd13d102..57bc59038 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -594,6 +594,7 @@ export const choose = (...xs) => chooseWith(rand, xs); * * @name chooseIn * @deprecated + * @tags temporal * @returns {Pattern} */ export const chooseIn = (...xs) => chooseInWith(rand, xs); From 6df34625c321d01602551f301126acccca0218c7 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 17:31:19 -0400 Subject: [PATCH 41/66] chooseInWith examples, other fixes --- packages/core/pattern.mjs | 27 +++-- packages/core/signal.mjs | 20 ++-- test/__snapshots__/examples.test.mjs.snap | 115 ++++++++++++++++------ 3 files changed, 110 insertions(+), 52 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 594aab7a8..1dcb8a7f1 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -504,6 +504,8 @@ export class Pattern { /** * Returns a new pattern with the given function applied to the list of haps returned by every query. + * + * As an end user, you will most likely not need to use this; look at `withContext`, `withHapSpan`, etc. instead. * @tags internals * @param {Function} func * @returns Pattern @@ -511,10 +513,13 @@ export class Pattern { * @example * note("[a b, c d] a b c d").s("gm_piano").withHaps(x=>{ * if (x.length > 1) { - * x.map(hap=>{ - * // select gm_piano:1 sample - * hap.value.n = 1 - * }) + * x = x.map(hap => hap.withValue(val => { + * return { + * n: 1, + * // select gm_piano:1 sample + * ...val + * } + * })); * } * return x * }) @@ -527,6 +532,8 @@ export class Pattern { /** * As with `withHaps`, but applies the function to every hap, rather than every list of haps. + * + * As an end user, you will most likely not need to use this; look at `withContext`, `withHapSpan`, etc. instead. * @tags internals * @param {Function} func * @returns Pattern @@ -1211,7 +1218,7 @@ const COMPOSERS = { pow: [numeralArgs(Math.pow)], /** * - * Preforms a bitwise `and` on each number and the given factor + * Performs a bitwise `and` on each number and the given factor * @name band * @memberof Pattern * @tags math @@ -1219,7 +1226,7 @@ const COMPOSERS = { band: [numeralArgs((a, b) => a & b)], /** * - * Preforms a bitwise `or` on each number and the given factor + * Performs a bitwise `or` on each number and the given factor * @name bor * @memberof Pattern * @tags math @@ -1227,7 +1234,7 @@ const COMPOSERS = { bor: [numeralArgs((a, b) => a | b)], /** * - * Preforms a bitwise `xor` on each number and the given factor + * Performs a bitwise `xor` on each number and the given factor * @name bxor * @memberof Pattern * @tags math @@ -1235,7 +1242,7 @@ const COMPOSERS = { bxor: [numeralArgs((a, b) => a ^ b)], /** * - * Preforms a bitwise `left shift` on each number by the given factor + * Performs a bitwise `left shift` on each number by the given factor * @name blshift * @memberof Pattern * @tags math @@ -1243,7 +1250,7 @@ const COMPOSERS = { blshift: [numeralArgs((a, b) => a << b)], /** * - * Preforms a bitwise `right shift` on each number by the given factor + * Performs a bitwise `right shift` on each number by the given factor * @name brshift * @memberof Pattern * @tags math @@ -1595,7 +1602,7 @@ export function stackRight(...pats) { } /** - * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCentre/stackCenter` + * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackRight` * @name stackCentre * @tags temporal, stepwise, combiners * @synonyms stackCenter diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 57bc59038..ef2306f92 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -562,6 +562,9 @@ export const __chooseWith = (pat, xs) => { * @returns {Pattern} * @example * note("c2 g2!2 d2 f1").s(chooseWith(sine.fast(2), ["sawtooth", "triangle", "bd:6"])) + * @example + * // Compare with chooseInWith + * note(chooseWith("<0 0.5 1>*3", ["c3", "b3", "d3"])) */ export const chooseWith = (pat, xs) => { return __chooseWith(pat, xs).outerJoin(); @@ -573,6 +576,13 @@ export const chooseWith = (pat, xs) => { * @tags temporal * @param {Pattern} pat * @param {*} xs + * @example + * // Because the structure comes from the chosen values (the notes, here) + * // The choosing pattern will only advance once per cycle (not 3 times per cycle) + * note(chooseInWith("<0 0.5 1>*3", ["c3", "b3", "d3"])) + * @example + * // Here, the chosen values have a structure such that the choosing pattern will advance 3 times per cycle + * note(chooseInWith("<0 0.5 1>*3", ["c3*3", "b3*3", "d3*3"])) * @returns {Pattern} */ export const chooseInWith = (pat, xs) => { @@ -589,16 +599,6 @@ export const chooseInWith = (pat, xs) => { */ export const choose = (...xs) => chooseWith(rand, xs); -/** - * Has no difference with `choose` - * - * @name chooseIn - * @deprecated - * @tags temporal - * @returns {Pattern} - */ -export const chooseIn = (...xs) => chooseInWith(rand, xs); - /** * Chooses from the given list of values (or patterns of values), according * to the pattern that the method is called on. The pattern should be in diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index dbf94bf76..89af4be4e 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2133,6 +2133,40 @@ exports[`runs examples > example "chooseCycles" example index 1 1`] = ` ] `; +exports[`runs examples > example "chooseInWith" example index 0 1`] = ` +[ + "[ (0/1 → 1/3) ⇝ 1/1 | note:c3 ]", + "[ 0/1 ⇜ (1/3 → 2/3) ⇝ 1/1 | note:b3 ]", + "[ 0/1 ⇜ (2/3 → 1/1) | note:d3 ]", + "[ (1/1 → 4/3) ⇝ 2/1 | note:c3 ]", + "[ 1/1 ⇜ (4/3 → 5/3) ⇝ 2/1 | note:b3 ]", + "[ 1/1 ⇜ (5/3 → 2/1) | note:d3 ]", + "[ (2/1 → 7/3) ⇝ 3/1 | note:c3 ]", + "[ 2/1 ⇜ (7/3 → 8/3) ⇝ 3/1 | note:b3 ]", + "[ 2/1 ⇜ (8/3 → 3/1) | note:d3 ]", + "[ (3/1 → 10/3) ⇝ 4/1 | note:c3 ]", + "[ 3/1 ⇜ (10/3 → 11/3) ⇝ 4/1 | note:b3 ]", + "[ 3/1 ⇜ (11/3 → 4/1) | note:d3 ]", +] +`; + +exports[`runs examples > example "chooseInWith" example index 1 1`] = ` +[ + "[ 0/1 → 1/3 | note:c3 ]", + "[ 1/3 → 2/3 | note:b3 ]", + "[ 2/3 → 1/1 | note:d3 ]", + "[ 1/1 → 4/3 | note:c3 ]", + "[ 4/3 → 5/3 | note:b3 ]", + "[ 5/3 → 2/1 | note:d3 ]", + "[ 2/1 → 7/3 | note:c3 ]", + "[ 7/3 → 8/3 | note:b3 ]", + "[ 8/3 → 3/1 | note:d3 ]", + "[ 3/1 → 10/3 | note:c3 ]", + "[ 10/3 → 11/3 | note:b3 ]", + "[ 11/3 → 4/1 | note:d3 ]", +] +`; + exports[`runs examples > example "chooseWith" example index 0 1`] = ` [ "[ 0/1 → 1/5 | note:c2 s:triangle ]", @@ -2158,6 +2192,23 @@ exports[`runs examples > example "chooseWith" example index 0 1`] = ` ] `; +exports[`runs examples > example "chooseWith" example index 1 1`] = ` +[ + "[ 0/1 → 1/3 | note:c3 ]", + "[ 1/3 → 2/3 | note:b3 ]", + "[ 2/3 → 1/1 | note:d3 ]", + "[ 1/1 → 4/3 | note:c3 ]", + "[ 4/3 → 5/3 | note:b3 ]", + "[ 5/3 → 2/1 | note:d3 ]", + "[ 2/1 → 7/3 | note:c3 ]", + "[ 7/3 → 8/3 | note:b3 ]", + "[ 8/3 → 3/1 | note:d3 ]", + "[ 3/1 → 10/3 | note:c3 ]", + "[ 10/3 → 11/3 | note:b3 ]", + "[ 11/3 → 4/1 | note:d3 ]", +] +`; + exports[`runs examples > example "chop" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:rhodes begin:0.75 end:1 speed:0.25 unit:c ]", @@ -15366,38 +15417,38 @@ exports[`runs examples > example "withHap" example index 0 1`] = ` exports[`runs examples > example "withHaps" example index 0 1`] = ` [ - "[ 0/1 → 1/10 | note:a s:gm_piano n:1 ]", - "[ 0/1 → 1/10 | note:c s:gm_piano n:1 ]", - "[ 1/10 → 1/5 | note:b s:gm_piano n:1 ]", - "[ 1/10 → 1/5 | note:d s:gm_piano n:1 ]", - "[ 1/5 → 2/5 | note:a s:gm_piano n:1 ]", - "[ 2/5 → 3/5 | note:b s:gm_piano n:1 ]", - "[ 3/5 → 4/5 | note:c s:gm_piano n:1 ]", - "[ 4/5 → 1/1 | note:d s:gm_piano n:1 ]", - "[ 1/1 → 11/10 | note:a s:gm_piano n:1 ]", - "[ 1/1 → 11/10 | note:c s:gm_piano n:1 ]", - "[ 11/10 → 6/5 | note:b s:gm_piano n:1 ]", - "[ 11/10 → 6/5 | note:d s:gm_piano n:1 ]", - "[ 6/5 → 7/5 | note:a s:gm_piano n:1 ]", - "[ 7/5 → 8/5 | note:b s:gm_piano n:1 ]", - "[ 8/5 → 9/5 | note:c s:gm_piano n:1 ]", - "[ 9/5 → 2/1 | note:d s:gm_piano n:1 ]", - "[ 2/1 → 21/10 | note:a s:gm_piano n:1 ]", - "[ 2/1 → 21/10 | note:c s:gm_piano n:1 ]", - "[ 21/10 → 11/5 | note:b s:gm_piano n:1 ]", - "[ 21/10 → 11/5 | note:d s:gm_piano n:1 ]", - "[ 11/5 → 12/5 | note:a s:gm_piano n:1 ]", - "[ 12/5 → 13/5 | note:b s:gm_piano n:1 ]", - "[ 13/5 → 14/5 | note:c s:gm_piano n:1 ]", - "[ 14/5 → 3/1 | note:d s:gm_piano n:1 ]", - "[ 3/1 → 31/10 | note:a s:gm_piano n:1 ]", - "[ 3/1 → 31/10 | note:c s:gm_piano n:1 ]", - "[ 31/10 → 16/5 | note:b s:gm_piano n:1 ]", - "[ 31/10 → 16/5 | note:d s:gm_piano n:1 ]", - "[ 16/5 → 17/5 | note:a s:gm_piano n:1 ]", - "[ 17/5 → 18/5 | note:b s:gm_piano n:1 ]", - "[ 18/5 → 19/5 | note:c s:gm_piano n:1 ]", - "[ 19/5 → 4/1 | note:d s:gm_piano n:1 ]", + "[ 0/1 → 1/10 | n:1 note:a s:gm_piano ]", + "[ 0/1 → 1/10 | n:1 note:c s:gm_piano ]", + "[ 1/10 → 1/5 | n:1 note:b s:gm_piano ]", + "[ 1/10 → 1/5 | n:1 note:d s:gm_piano ]", + "[ 1/5 → 2/5 | n:1 note:a s:gm_piano ]", + "[ 2/5 → 3/5 | n:1 note:b s:gm_piano ]", + "[ 3/5 → 4/5 | n:1 note:c s:gm_piano ]", + "[ 4/5 → 1/1 | n:1 note:d s:gm_piano ]", + "[ 1/1 → 11/10 | n:1 note:a s:gm_piano ]", + "[ 1/1 → 11/10 | n:1 note:c s:gm_piano ]", + "[ 11/10 → 6/5 | n:1 note:b s:gm_piano ]", + "[ 11/10 → 6/5 | n:1 note:d s:gm_piano ]", + "[ 6/5 → 7/5 | n:1 note:a s:gm_piano ]", + "[ 7/5 → 8/5 | n:1 note:b s:gm_piano ]", + "[ 8/5 → 9/5 | n:1 note:c s:gm_piano ]", + "[ 9/5 → 2/1 | n:1 note:d s:gm_piano ]", + "[ 2/1 → 21/10 | n:1 note:a s:gm_piano ]", + "[ 2/1 → 21/10 | n:1 note:c s:gm_piano ]", + "[ 21/10 → 11/5 | n:1 note:b s:gm_piano ]", + "[ 21/10 → 11/5 | n:1 note:d s:gm_piano ]", + "[ 11/5 → 12/5 | n:1 note:a s:gm_piano ]", + "[ 12/5 → 13/5 | n:1 note:b s:gm_piano ]", + "[ 13/5 → 14/5 | n:1 note:c s:gm_piano ]", + "[ 14/5 → 3/1 | n:1 note:d s:gm_piano ]", + "[ 3/1 → 31/10 | n:1 note:a s:gm_piano ]", + "[ 3/1 → 31/10 | n:1 note:c s:gm_piano ]", + "[ 31/10 → 16/5 | n:1 note:b s:gm_piano ]", + "[ 31/10 → 16/5 | n:1 note:d s:gm_piano ]", + "[ 16/5 → 17/5 | n:1 note:a s:gm_piano ]", + "[ 17/5 → 18/5 | n:1 note:b s:gm_piano ]", + "[ 18/5 → 19/5 | n:1 note:c s:gm_piano ]", + "[ 19/5 → 4/1 | n:1 note:d s:gm_piano ]", ] `; From 91681983e27a645b8aec0583aa4634100a4ead34 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 17:32:14 -0400 Subject: [PATCH 42/66] wording --- packages/core/signal.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index ef2306f92..8d2c6cb55 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -744,7 +744,7 @@ export const perlin = signal((t, controls) => _perlin(t, controls.randSeed)); export const berlin = signal((t, controls) => _berlin(t, controls.randSeed)); /** - * `degradeByWith` is like `degradeBy`, but with more granular control. + * `degradeByWith` is like `degradeBy`, but with control over the random or other signal used to filter out the events. * * `degradeBy(x)` is essentially `degradeByWith(rand, x)` * From aeeaa500719696027d986c2b36eb75a30f17914e Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 18:03:50 -0400 Subject: [PATCH 43/66] cleanup --- packages/xen/xen.mjs | 90 +------------------------------------------- 1 file changed, 1 insertion(+), 89 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 66c318296..cf4eb5f7e 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -72,15 +72,6 @@ function xenOffset(xenScale, offset, index = 0) { const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); -// accepts a scale name such as 31edo, and a pattern -// pattern expected to follow format such that a value can be mapped -// to an edostep within the scale. Returns the pattern with -// values mapped to the frequencies associated with the given edosteps -// scaleNameOrRatios: string || number[], steps?: number - -// old, currently unsupported -// TODO support - /** * Assumes a numerical pattern of scale steps, and a scale. Scales accepted are all preset scale names of `tune`, arbitrary edos such as 31edo, or an array of frequency ratios. Assumes scales repeat at octave (2/1). Returns a new pattern with all values mapped to their associated frequency, assuming a base frequency of 220hz. * @@ -131,43 +122,7 @@ export const xen = register( }); }; - // TODO fix the following: - /* - let p1 = i("0 1").xen("hexany15 hexany23") - let p2 = i("[0 1]").xen([ - "[1:1 2:1]", // tonic - "5:4", // Major 3rd - ]).s("tri") - - let p3 = i("[0 1]").xen([ - "[<1:1 2:1>]", // tonic - "5:4", // Major 3rd - ]).s("tri") - */ - const query = (state) => { - // let input = scaleNameOrRatios.query(state); - - // if (input.length == 0) return []; - - // let scPat = input[0].value; - // let scs; - // if (typeof scPat == 'string') { - // scs = input.map((x) => x.value); - // } else if (scPat.every((x) => typeof x == 'number')) { - // scs = [scPat]; - // } else { - // scs = JIsc2(scPat).query(state); - // } - - // outside should be haps - // which then queries the scales using their hap timespans - - // input[0].value.forEach((inp, ith) => { - // let smth = inp.query(state) - // let scs = JIsc2(inp).query(state) - - // let k = scs.map((sc, jth) => { let haps = pat.query(state); let resHaps = []; @@ -207,7 +162,7 @@ export const xen = register( */ let sc2 = JIsc2(sc).query(st); - let index = _mod(i, sc.length) + let index = _mod(i, sc.length); for (let j = 0; j < scaleByIndices[index].length; j++) { let scale = getXenScale(sc2[j].value); let freq = xenOffset(scale, parseNumeral(i)); @@ -242,50 +197,7 @@ export const xen = register( } else { throw new Error('failed to parse scale'); } - - /* - We need to know: - - How many Haps to make, given a scale index (i value) from a Hap from pat (sc can tell us) - - The whole scale, so the frequency can be set (sc2 can tell us) - - if sc2.length > 1, how do we deal? - - The frequency of the ith element of the scale, given a Hap (from pat) with i value of ith - - The whole scale definitions in sc2 can be indexed by the index of Hap within sc: - - */ - - // if (typeof sc == 'string') { - // scale = getXenScale(sc); - // if (isEdo(sc)) { - // edo = sc.match(/^([1-9]+[0-9]*)edo$/)[1]; - // } - // hap = hap.setContext(hap.combineContext(input[ith])); - // } else if (Array.isArray(sc) && sc.every((x) => typeof x == 'number')) { - // scale = getXenScale(sc); - // } else { - // scale = getXenScale(sc.value); - // for (let h of scPat[_mod(i, scale.length)].query(state)) { - // let ts = hap.wholeOrPart().intersection(h.wholeOrPart()); - // if (ts == undefined) return undefined; - // hap = hap.withSpan((x) => ts); - // hap = hap.setContext(hap.combineContext(h)); - // } - // } - // let freq = xenOffset(scale, parseNumeral(i)); - // // 10 is somewhat arbitrary - // freq = trimFreq(freq); - // hap.value = { ...otherValues, freq }; }); - - // }); - // return flatten(k); - // }) - - /* - perhaps instead of - return new Pattern(query) - we could do a join on scaleNameOrRatios which creates multiple patterns and then joins them - */ return resHaps; }; return new Pattern(query); From 08eaa8a57851da1f9243a5adac43957e9a60836a Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 11 Mar 2026 18:04:29 -0400 Subject: [PATCH 44/66] more cleanup --- packages/xen/xen.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index cf4eb5f7e..32ed0111d 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -11,10 +11,7 @@ import { removeUndefineds, Pattern, parray, - reify, State, - flatten, - Hap, } from '@strudel/core'; import Tune from './tunejs.js'; From 948f78da1144d805c04e13be3e95e87504f94096 Mon Sep 17 00:00:00 2001 From: tyow Date: Thu, 12 Mar 2026 12:23:30 -0400 Subject: [PATCH 45/66] wording --- packages/xen/xen.mjs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 32ed0111d..de347d38b 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -4,15 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { - register, - _mod, - parseNumeral, - removeUndefineds, - Pattern, - parray, - State, -} from '@strudel/core'; +import { register, _mod, parseNumeral, removeUndefineds, Pattern, parray, State } from '@strudel/core'; import Tune from './tunejs.js'; // returns a list of frequency ratios for given edo scale @@ -70,7 +62,7 @@ function xenOffset(xenScale, offset, index = 0) { const trimFreq = (freq) => parseFloat(freq.toPrecision(10)); /** - * Assumes a numerical pattern of scale steps, and a scale. Scales accepted are all preset scale names of `tune`, arbitrary edos such as 31edo, or an array of frequency ratios. Assumes scales repeat at octave (2/1). Returns a new pattern with all values mapped to their associated frequency, assuming a base frequency of 220hz. + * Assumes a numerical pattern of scale steps, and a scale. Scales accepted are all preset scale names of `tune`, arbitrary edos such as 31edo, or an array of frequency ratios. Assumes scales repeat at octave (2/1). Returns a new pattern with all values mapped to their associated frequency, assuming a base frequency of 220hz. See first 4 examples for the different ways you can input a scale into `xen` * * @name xen * @returns Pattern From 8f1a0840c03c309634fd622ad8a98b675581c218 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 25 Mar 2026 14:07:10 -0400 Subject: [PATCH 46/66] edit dry tags to add to reference --- packages/core/controls.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 19e4d5ae7..2a13c4df2 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1988,11 +1988,10 @@ export const { spread } = registerControl('spread'); * Set dryness of reverb. See `room` and `size` for more information about reverb. * * @name dry - * @tags superdirt + * @tags superdirt, superdough * @param {number | Pattern} dry 0 = wet, 1 = dry * @example * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() - * @superdirtOnly * */ export const { dry } = registerControl('dry'); From 3dff15eab0e0b5fe2c8cf30cafebf3f5e0a64b20 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 25 Mar 2026 15:36:26 -0400 Subject: [PATCH 47/66] readded chooseIn due to mondough using it --- packages/core/signal.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 8d2c6cb55..a1cf37286 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -599,6 +599,8 @@ export const chooseInWith = (pat, xs) => { */ export const choose = (...xs) => chooseWith(rand, xs); +export const chooseIn = (...xs) => chooseInWith(rand, xs); + /** * Chooses from the given list of values (or patterns of values), according * to the pattern that the method is called on. The pattern should be in From 0d73c02314a857d77aee67e74e6ee1d161f886c8 Mon Sep 17 00:00:00 2001 From: tyow Date: Thu, 26 Mar 2026 01:10:42 -0400 Subject: [PATCH 48/66] format --- packages/core/signal.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index a1cf37286..c43366ebd 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -599,7 +599,7 @@ export const chooseInWith = (pat, xs) => { */ export const choose = (...xs) => chooseWith(rand, xs); -export const chooseIn = (...xs) => chooseInWith(rand, xs); +export const chooseIn = (...xs) => chooseInWith(rand, xs); /** * Chooses from the given list of values (or patterns of values), according From 81257b5f066fe50dbcc96e5dd5f2fc603ead7cfb Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 29 Apr 2026 15:30:32 -0400 Subject: [PATCH 49/66] lt,gt,lte,etc. --- packages/core/pattern.mjs | 130 ++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 1dcb8a7f1..34ad1b4b3 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1258,15 +1258,105 @@ const COMPOSERS = { brshift: [numeralArgs((a, b) => a >> b)], // TODO - force numerical comparison if both look like numbers? + /** + * preforms a < b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ lt: [(a, b) => a < b], + /** + * preforms a > b + * @name gt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ gt: [(a, b) => a > b], + /** + * preforms a <= b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ lte: [(a, b) => a <= b], + /** + * preforms a >= b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ gte: [(a, b) => a >= b], + /** + * preforms a == b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ eq: [(a, b) => a == b], + /** + * preforms a === b (strictly equal) + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ eqt: [(a, b) => a === b], + /** + * preforms a != b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ ne: [(a, b) => a != b], + /** + * preforms a !== b (strictly not equal) + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ net: [(a, b) => a !== b], + /** + * preforms a && b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ and: [(a, b) => a && b], + /** + * preforms a || b + * @name lt + * @memberof Pattern + * @tags math + * @param {any} a + * @param {any} b + * @returns Pattern with boolean value(s) + */ or: [(a, b) => a || b], // bitwise ops @@ -1759,10 +1849,10 @@ export function arrange(...sections) { * @tags combiners * @return {Pattern} * @example -seqPLoop( - [0, 2, "bd(3,8)"], - [1, 3, "cp(3,8)"] -).sound() + * seqPLoop( + * [0, 2, "bd(3,8)"], + * [1, 3, "cp(3,8)"] + * ).sound() */ export function seqPLoop(...parts) { let total = Fraction(0); @@ -2433,11 +2523,11 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a * @param {number} slices to play * @example * note("0 1 2 3 4 5 6 7".scale('c:mixolydian')) - *.bite(4, "3 2 1 0") + * .bite(4, "3 2 1 0") * @example * sound("bd - bd bd*2, - sd:6 - sd:5 sd:1 - [- sd:2] -, hh [- cp:7]") - .bank("RolandTR909").speed(1.2) - .bite(4, "0 0 [1 2] <3 2> 0 0 [2 1] 3") + * .bank("RolandTR909").speed(1.2) + * .bite(4, "0 0 [1 2] <3 2> 0 0 [2 1] 3") */ export const bite = register( 'bite', @@ -3510,8 +3600,8 @@ export const shrinklist = (amount, pat) => pat.shrinklist(amount); * "tha dhi thom nam".shrink("1 -1").sound().bank("mridangam").pace(4) * @example * note("0 1 2 3 4 5 6 7".scale("C:ritusen")).sound("folkharp") - .shrink("1 -1").pace(8) - + * .shrink("1 -1").pace(8) + * */ export const shrink = register( @@ -3550,7 +3640,7 @@ export const shrink = register( * "tha dhi thom nam".grow("1 -1").sound().bank("mridangam").pace(4) * @example * note("0 1 2 3 4 5 6 7".scale("C:ritusen")).sound("folkharp") - .grow("1 -1").pace(8) + * .grow("1 -1").pace(8) */ export const grow = register( @@ -3574,17 +3664,17 @@ export const grow = register( /** * *Experimental* - * - * Inserts a pattern into a list of patterns. On the first repetition it will be inserted at the end of the list, then moved backwards through the list - * on successive repetitions. The patterns are added together stepwise, with all repetitions taking place over a single cycle. Using `pace` to set the + * + * Inserts a pattern into a list of patterns. On the first repetition it will be inserted at the end of the list, then moved backwards through the list + * on successive repetitions. The patterns are added together stepwise, with all repetitions taking place over a single cycle. Using `pace` to set the * number of steps per cycle is therefore usually recommended. * * @tags stepwise * @return {Pattern} * @example * "[c g]".tour("e f", "e f g", "g f e c").note() - .sound("folkharp") - .pace(8) + * .sound("folkharp") + * .pace(8) */ export const tour = function (pat, ...many) { return pat.tour(...many); @@ -3602,16 +3692,16 @@ Pattern.prototype.tour = function (...many) { /** * *Experimental* - * - * 'zips' together the steps of the provided patterns. This can create a long repetition, taking place over a single, dense cycle. + * + * 'zips' together the steps of the provided patterns. This can create a long repetition, taking place over a single, dense cycle. * Using `pace` to set the number of steps per cycle is therefore usually recommended. - * + * * @tags stepwise * @returns {Pattern} * @example * zip("e f", "e f g", "g [f e] a f4 c").note() - .sound("folkharp") - .pace(8) + * .sound("folkharp") + * .pace(8) */ export const zip = function (...pats) { pats = pats.filter((pat) => pat.hasSteps); From d5171e2ab794dd6ca6e772579a72d87b936e0a58 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 29 Apr 2026 15:35:05 -0400 Subject: [PATCH 50/66] named things --- packages/core/pattern.mjs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 34ad1b4b3..ff1ef17ef 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1280,7 +1280,7 @@ const COMPOSERS = { gt: [(a, b) => a > b], /** * preforms a <= b - * @name lt + * @name lte * @memberof Pattern * @tags math * @param {any} a @@ -1290,7 +1290,7 @@ const COMPOSERS = { lte: [(a, b) => a <= b], /** * preforms a >= b - * @name lt + * @name gte * @memberof Pattern * @tags math * @param {any} a @@ -1300,7 +1300,7 @@ const COMPOSERS = { gte: [(a, b) => a >= b], /** * preforms a == b - * @name lt + * @name eq * @memberof Pattern * @tags math * @param {any} a @@ -1310,7 +1310,7 @@ const COMPOSERS = { eq: [(a, b) => a == b], /** * preforms a === b (strictly equal) - * @name lt + * @name eqt * @memberof Pattern * @tags math * @param {any} a @@ -1320,7 +1320,7 @@ const COMPOSERS = { eqt: [(a, b) => a === b], /** * preforms a != b - * @name lt + * @name ne * @memberof Pattern * @tags math * @param {any} a @@ -1330,7 +1330,7 @@ const COMPOSERS = { ne: [(a, b) => a != b], /** * preforms a !== b (strictly not equal) - * @name lt + * @name net * @memberof Pattern * @tags math * @param {any} a @@ -1340,7 +1340,7 @@ const COMPOSERS = { net: [(a, b) => a !== b], /** * preforms a && b - * @name lt + * @name and * @memberof Pattern * @tags math * @param {any} a @@ -1350,7 +1350,7 @@ const COMPOSERS = { and: [(a, b) => a && b], /** * preforms a || b - * @name lt + * @name or * @memberof Pattern * @tags math * @param {any} a From 704018f774a758236784d328c42cd07191079c2f Mon Sep 17 00:00:00 2001 From: tyow Date: Tue, 21 Jul 2026 22:48:28 -0400 Subject: [PATCH 51/66] preform/perform, mod --- packages/core/pattern.mjs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index ff1ef17ef..5a981dd9d 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1202,8 +1202,8 @@ const COMPOSERS = { div: [numeralArgs((a, b) => a / b)], /** * - * Divides each number by the given factor. - * @name div + * Divides each number by the given factor and returns the remainder + * @name mod * @memberof Pattern * @tags math */ @@ -1259,7 +1259,7 @@ const COMPOSERS = { // TODO - force numerical comparison if both look like numbers? /** - * preforms a < b + * performs a < b * @name lt * @memberof Pattern * @tags math @@ -1269,7 +1269,7 @@ const COMPOSERS = { */ lt: [(a, b) => a < b], /** - * preforms a > b + * performs a > b * @name gt * @memberof Pattern * @tags math @@ -1279,7 +1279,7 @@ const COMPOSERS = { */ gt: [(a, b) => a > b], /** - * preforms a <= b + * performs a <= b * @name lte * @memberof Pattern * @tags math @@ -1289,7 +1289,7 @@ const COMPOSERS = { */ lte: [(a, b) => a <= b], /** - * preforms a >= b + * performs a >= b * @name gte * @memberof Pattern * @tags math @@ -1299,7 +1299,7 @@ const COMPOSERS = { */ gte: [(a, b) => a >= b], /** - * preforms a == b + * performs a == b * @name eq * @memberof Pattern * @tags math @@ -1309,7 +1309,7 @@ const COMPOSERS = { */ eq: [(a, b) => a == b], /** - * preforms a === b (strictly equal) + * performs a === b (strictly equal) * @name eqt * @memberof Pattern * @tags math @@ -1319,7 +1319,7 @@ const COMPOSERS = { */ eqt: [(a, b) => a === b], /** - * preforms a != b + * performs a != b * @name ne * @memberof Pattern * @tags math @@ -1329,7 +1329,7 @@ const COMPOSERS = { */ ne: [(a, b) => a != b], /** - * preforms a !== b (strictly not equal) + * performs a !== b (strictly not equal) * @name net * @memberof Pattern * @tags math @@ -1339,7 +1339,7 @@ const COMPOSERS = { */ net: [(a, b) => a !== b], /** - * preforms a && b + * performs a && b * @name and * @memberof Pattern * @tags math @@ -1349,7 +1349,7 @@ const COMPOSERS = { */ and: [(a, b) => a && b], /** - * preforms a || b + * performs a || b * @name or * @memberof Pattern * @tags math From 14ac26fa4593117f98d5e0ac111b9e135a37eeb0 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Thu, 23 Jul 2026 09:17:17 +0200 Subject: [PATCH 52/66] what's a hap? --- website/src/pages/learn/faq.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index fc3bcdb2d..6bcfdc7a1 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -337,3 +337,11 @@ s increase by one semitone, i.e. sharp, works for steps of scales, note names $: at the start of a line, defines a member of the stack. is the only stack name that should occur multiple names _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), see above for a different usage. ``` + +## What is a 'hap'? + +A 'hap' is a name for a thing that happens, e.g. ```note("c3 -")``` playing a note and then stopping after a while. Haps have a start and an end. Every separate sound that is produced by strudel is a hap on its own. + +You could call it event if this particular name wasn't used by JavaScript to describe things that happen on html pages, like clicking on a button or hovering over a line of text. + +Since strudel is written in JavaScript, this confusion of having the same name for different things is avoided by making up a new name (which is explained here). \ No newline at end of file From 692170e1178cb2c01c1e8505b566e96a423093dd Mon Sep 17 00:00:00 2001 From: yaxu Date: Mon, 27 Jul 2026 16:55:41 +0200 Subject: [PATCH 53/66] Update website/src/pages/learn/faq.mdx --- website/src/pages/learn/faq.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 6bcfdc7a1..7764d91e2 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -342,6 +342,6 @@ _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), A 'hap' is a name for a thing that happens, e.g. ```note("c3 -")``` playing a note and then stopping after a while. Haps have a start and an end. Every separate sound that is produced by strudel is a hap on its own. -You could call it event if this particular name wasn't used by JavaScript to describe things that happen on html pages, like clicking on a button or hovering over a line of text. +We would have called it 'event' if this particular name wasn't used by JavaScript to describe things that happen on html pages, like clicking on a button or hovering over a line of text. -Since strudel is written in JavaScript, this confusion of having the same name for different things is avoided by making up a new name (which is explained here). \ No newline at end of file +Since strudel is written in JavaScript, this confusion of having the same name for different things is avoided by calling events 'haps'. \ No newline at end of file From a3a38cfe33fe67d5bf402a46fa181543ca11e5c8 Mon Sep 17 00:00:00 2001 From: Bernhard Wagner Date: Thu, 20 Aug 2026 11:08:23 +0200 Subject: [PATCH 54/66] keep space reserved for the sidebars above 1280px (fixes #2110) At xl the wrapper dropped its left padding and relied on centering to clear the fixed sidebars, which only works from about 1338px up. Reserving the right gutter instead leaves md:pl-[300px] in force, so the column narrows between 1280px and 1338px rather than sliding under the nav. --- website/src/layouts/MainLayout.astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/layouts/MainLayout.astro b/website/src/layouts/MainLayout.astro index c6f6653ce..f56d7f58d 100644 --- a/website/src/layouts/MainLayout.astro +++ b/website/src/layouts/MainLayout.astro @@ -36,7 +36,7 @@ const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
-
+
From d7384dfc3d81dc041c92a95f9e90fd1bd6c85bdc Mon Sep 17 00:00:00 2001 From: Tijmen Zwaan Date: Thu, 13 Aug 2026 04:00:41 +0200 Subject: [PATCH 55/66] Negative steps change euclid into euclidLegato and vice versa Also fix that euclidLegato did not support negative pulses for inversion --- packages/core/euclid.mjs | 35 +++++++++++++++---- packages/core/test/euclid.test.js | 56 +++++++++++++++++++++++++++++++ packages/mini/test/mini.test.mjs | 50 +++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 6 deletions(-) diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index 49f96384b..c477ddfc7 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -59,6 +59,11 @@ export const bjorklund = function (ons, steps) { * really useful for computer/algorithmic music because they can * describe a large number of rhythms with a couple of numbers. * + * A negative number of pulses inverts the pattern + * so pulses happen in the gaps of the normal euclidean rhythm. + * + * A negative number of steps changes it into `euclidLegato`. + * * @memberof Pattern * @name euclid * @tags temporal @@ -138,6 +143,9 @@ const _euclidRot = function (pulses, steps, rotation) { }; export const euclid = register('euclid', function (pulses, steps, pat) { + if (steps < 0) { + return _euclidLegato(pulses, -steps, 0, pat); + } return pat.struct(_euclidRot(pulses, steps, 0)); }); @@ -150,12 +158,17 @@ export const bjork = register('bjork', function (euc, pat) { }); export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) { + if (steps < 0) { + return _euclidLegato(pulses, -steps, rotation, pat); + } return pat.struct(_euclidRot(pulses, steps, rotation)); }); /** * Similar to `euclid`, but each pulse is held until the next pulse, * so there will be no gaps. + * + * A negative number of steps changes it into `euclid`. * @name euclidLegato * @memberof Pattern * @tags temporal @@ -168,26 +181,33 @@ export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], fun */ const _euclidLegato = function (pulses, steps, rotation, pat) { - if (pulses < 1) { - return silence; - } - const bin_pat = _euclidRot(pulses, steps, 0); + var bin_pat = _euclidRot(pulses, steps, 0); + // Because the `gapless` algorithm logic assumes the first entry in bin_pat is a 1, + // we rotate the pattern so the first onset is at the start. + const firstOnset = bin_pat.indexOf(1); + bin_pat = rotate(bin_pat, firstOnset); + steps = Math.abs(steps); const gapless = bin_pat .join('') .split('1') .slice(1) .map((s) => [s.length + 1, true]); - return pat.struct(timeCat(...gapless)).late(Fraction(rotation).div(steps)); + return pat.struct(timeCat(...gapless)).late(Fraction(rotation + firstOnset).div(steps)); }; export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) { + if (steps < 0) { + return pat.struct(_euclidRot(pulses, -steps, 0)); + } return _euclidLegato(pulses, steps, 0, pat); }); /** * Similar to `euclid`, but each pulse is held until the next pulse, * so there will be no gaps, and has an additional parameter for 'rotating' - * the resulting sequence + * the resulting sequence. + * + * A negative number of steps changes it into `euclidRot`. * @name euclidLegatoRot * @memberof Pattern * @tags temporal @@ -198,6 +218,9 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps, * note("c3").euclidLegatoRot(3,5,2) */ export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) { + if (steps < 0) { + return pat.struct(_euclidRot(pulses, -steps, rotation)); + } return _euclidLegato(pulses, steps, rotation, pat); }); diff --git a/packages/core/test/euclid.test.js b/packages/core/test/euclid.test.js index 6169abd71..bf02a6109 100644 --- a/packages/core/test/euclid.test.js +++ b/packages/core/test/euclid.test.js @@ -29,6 +29,34 @@ describe('euclid', () => { .map((a) => a.showWhole(true)), ).toStrictEqual(['0/1 → 1/8: a', '1/4 → 3/8: a', '3/8 → 1/2: a', '5/8 → 3/4: a', '3/4 → 7/8: a']); }); + it('Can create euclidLegato using negative steps', () => { + expect( + fastcat('a') + .euclid(3, -8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual( + fastcat('a') + .euclidLegato(3, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ); + expect( + fastcat('a') + .euclid(5, -8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual( + fastcat('a') + .euclidLegato(5, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ); + }); }); describe('euclidRot', () => { @@ -67,6 +95,34 @@ describe('euclidLegato', () => { .map((a) => a.showWhole(true)), ).toStrictEqual(['0/1 → 1/4: a', '1/4 → 3/8: a', '3/8 → 5/8: a', '5/8 → 3/4: a', '3/4 → 1/1: a']); }); + it('Can create euclid using negative steps', () => { + expect( + fastcat('a') + .euclidLegato(3, -8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual( + fastcat('a') + .euclid(3, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ); + expect( + fastcat('a') + .euclidLegato(5, -8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual( + fastcat('a') + .euclid(5, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ); + }); }); describe('euclidLegatoRot', () => { diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index c38997308..c84819e87 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -84,6 +84,22 @@ describe('mini', () => { it('supports patterning euclidean rhythms', () => { expect(minS('[a(<3 5>, <8 16>)]*2')).toEqual(minS('a(3,8) a(5,16)')); }); + it('supports inverted euclidean rhythms', () => { + expect(minS('a(-3, 8)')).toEqual(['a: 1/8 - 1/4', 'a: 1/4 - 3/8', 'a: 1/2 - 5/8', 'a: 5/8 - 3/4', 'a: 7/8 - 1']); + }); + it('supports euclidean legato rhythms', () => { + expect(minS('a(3, -8)')).toEqual(['a: 0 - 3/8', 'a: 3/8 - 3/4', 'a: 3/4 - 1']); + }); + it('supports inverted euclidean legato rhythms', () => { + expect(minS('a(-3, -8)')).toEqual([ + 'a: -1/8 - 1/8', + 'a: 1/8 - 1/4', + 'a: 1/4 - 1/2', + 'a: 1/2 - 5/8', + 'a: 5/8 - 7/8', + 'a: 7/8 - 9/8', + ]); + }); it("reproduces Toussaint's example euclidean algorithms", () => { const checkEuclid = function (spec, target) { expect(minS(`x(${spec[0]},${spec[1]})`)).toEqual(minS(target)); @@ -118,6 +134,40 @@ describe('mini', () => { checkEuclid([11, 24], 'x ~ ~ x ~ x ~ x ~ x ~ x ~ ~ x ~ x ~ x ~ x ~ x ~'); checkEuclid([13, 24], 'x ~ x x ~ x ~ x ~ x ~ x ~ x x ~ x ~ x ~ x ~ x ~'); }); + it("reproduces Toussaint's example euclidean legato algorithms", () => { + const checkEuclid = function (spec, target) { + expect(minS(`x(${spec[0]},${spec[1]})`)).toEqual(minS(target)); + }; + checkEuclid([1, -2], 'x _'); + checkEuclid([1, -3], 'x _ _'); + checkEuclid([1, -4], 'x _ _ _'); + checkEuclid([4, -12], 'x _ _ x _ _ x _ _ x _ _'); + checkEuclid([2, -5], 'x _ x _ _'); + // checkEuclid([3, 4], "x _ x x"); // Toussaint is wrong.. + checkEuclid([3, -4], 'x x x _'); // correction + checkEuclid([3, -5], 'x _ x _ x'); + checkEuclid([3, -7], 'x _ x _ x _ _'); + checkEuclid([3, -8], 'x _ _ x _ _ x _'); + checkEuclid([4, -7], 'x _ x _ x _ x'); + checkEuclid([4, -9], 'x _ x _ x _ x _ _'); + checkEuclid([4, -11], 'x _ _ x _ _ x _ _ x _'); + // checkEuclid([5, 6], "x _ x x x x"); // Toussaint is wrong.. + checkEuclid([5, -6], 'x x x x x _'); // correction + checkEuclid([5, -7], 'x _ x x _ x x'); + checkEuclid([5, -8], 'x _ x x _ x x _'); + checkEuclid([5, -9], 'x _ x _ x _ x _ x'); + checkEuclid([5, -11], 'x _ x _ x _ x _ x _ _'); + checkEuclid([5, -12], 'x _ _ x _ x _ _ x _ x _'); + // checkEuclid([5, 16], "x _ _ x _ _ x _ _ x _ _ x _ _ _ _"); // Toussaint is wrong.. + checkEuclid([5, -16], 'x _ _ x _ _ x _ _ x _ _ x _ _ _'); // correction + // checkEuclid([7, 8], "x _ x x x x x x"); // Toussaint is wrong.. + checkEuclid([7, -8], 'x x x x x x x _'); // Correction + checkEuclid([7, -12], 'x _ x x _ x _ x x _ x _'); + checkEuclid([7, -16], 'x _ _ x _ x _ x _ _ x _ x _ x _'); + checkEuclid([9, -16], 'x _ x x _ x _ x _ x x _ x _ x _'); + checkEuclid([11, -24], 'x _ _ x _ x _ x _ x _ x _ _ x _ x _ x _ x _ x _'); + checkEuclid([13, -24], 'x _ x x _ x _ x _ x _ x _ x x _ x _ x _ x _ x _'); + }); it('supports the - alias for ~', () => { expect(minS('a - b [- c]')).toEqual(minS('a ~ b [~ c]')); }); From 078aa60779a6c723f4fee6ccd3ab79373e44e2d4 Mon Sep 17 00:00:00 2001 From: Tijmen Zwaan Date: Fri, 21 Aug 2026 23:56:19 +0200 Subject: [PATCH 56/66] Allow euclid pulses > steps to stretch the rhythm over multiple cycles Also fix a bug in euclidLegato where the rotation offset would be applied to the whole source pattern, instead of the euclid structure. --- packages/core/euclid.mjs | 67 ++++++++++++----------- packages/core/test/euclid.test.js | 21 +++++++ test/__snapshots__/examples.test.mjs.snap | 9 ++- test/__snapshots__/tunes.test.mjs.snap | 24 ++++---- 4 files changed, 74 insertions(+), 47 deletions(-) diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index c477ddfc7..eba3f3303 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -142,11 +142,38 @@ const _euclidRot = function (pulses, steps, rotation) { return b; }; -export const euclid = register('euclid', function (pulses, steps, pat) { - if (steps < 0) { - return _euclidLegato(pulses, -steps, 0, pat); +const _euclidLegato = function (pulses, steps, rotation, pat) { + var bin_pat = _euclidRot(pulses, steps, 0); + // Because the `gapless` algorithm logic assumes the first entry in bin_pat is a 1, + // we rotate the pattern so the first onset is at the start. + const firstOnset = bin_pat.indexOf(1); + bin_pat = rotate(bin_pat, firstOnset); + steps = Math.abs(steps); + const gapless = bin_pat + .join('') + .split('1') + .slice(1) + .map((s) => [s.length + 1, true]); + return pat.struct(timeCat(...gapless).late(Fraction(rotation + firstOnset).div(steps))); +}; + +const _euclid = function (pulses, steps, rotation, pat) { + if (steps === 0 || pulses === 0) { + return silence; } - return pat.struct(_euclidRot(pulses, steps, 0)); + const cycles = Math.ceil(Math.abs(pulses) / Math.abs(steps)); + const totalSteps = cycles * steps; + if (totalSteps < 0) { + return _euclidLegato(pulses, -totalSteps, rotation, pat.fast(cycles)).slow(cycles); + } + return pat + .fast(cycles) + .struct(_euclidRot(pulses, totalSteps, rotation)) + .slow(cycles); +}; + +export const euclid = register('euclid', function (pulses, steps, pat) { + return _euclid(pulses, steps, 0, pat); }); export const bjork = register('bjork', function (euc, pat) { @@ -154,14 +181,11 @@ export const bjork = register('bjork', function (euc, pat) { euc = [euc]; } const [pulses, steps = pulses, rot = 0] = euc; - return pat.struct(_euclidRot(pulses, steps, rot)); + return _euclid(pulses, steps, rot, pat); }); export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) { - if (steps < 0) { - return _euclidLegato(pulses, -steps, rotation, pat); - } - return pat.struct(_euclidRot(pulses, steps, rotation)); + return _euclid(pulses, steps, rotation, pat); }); /** @@ -180,26 +204,8 @@ export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], fun * note("c3").euclidLegato(3,8) */ -const _euclidLegato = function (pulses, steps, rotation, pat) { - var bin_pat = _euclidRot(pulses, steps, 0); - // Because the `gapless` algorithm logic assumes the first entry in bin_pat is a 1, - // we rotate the pattern so the first onset is at the start. - const firstOnset = bin_pat.indexOf(1); - bin_pat = rotate(bin_pat, firstOnset); - steps = Math.abs(steps); - const gapless = bin_pat - .join('') - .split('1') - .slice(1) - .map((s) => [s.length + 1, true]); - return pat.struct(timeCat(...gapless)).late(Fraction(rotation + firstOnset).div(steps)); -}; - export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) { - if (steps < 0) { - return pat.struct(_euclidRot(pulses, -steps, 0)); - } - return _euclidLegato(pulses, steps, 0, pat); + return _euclid(pulses, -steps, 0, pat); }); /** @@ -218,10 +224,7 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps, * note("c3").euclidLegatoRot(3,5,2) */ export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) { - if (steps < 0) { - return pat.struct(_euclidRot(pulses, -steps, rotation)); - } - return _euclidLegato(pulses, steps, rotation, pat); + return _euclid(pulses, -steps, rotation, pat); }); /** diff --git a/packages/core/test/euclid.test.js b/packages/core/test/euclid.test.js index bf02a6109..a7623b3e0 100644 --- a/packages/core/test/euclid.test.js +++ b/packages/core/test/euclid.test.js @@ -57,6 +57,16 @@ describe('euclid', () => { .map((a) => a.showWhole(true)), ); }); + it('stretches over multiple cycles if pulses is greater than steps', () => { + expect( + fastcat('a') + .euclid(5, 4) + .fast(2) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/8: a', '1/4 → 3/8: a', '3/8 → 1/2: a', '5/8 → 3/4: a', '3/4 → 7/8: a']); + }); }); describe('euclidRot', () => { @@ -123,6 +133,17 @@ describe('euclidLegato', () => { .map((a) => a.showWhole(true)), ); }); + it('stretches over multiple cycles if pulses is greater than steps', () => { + expect( + fastcat('a') + .euclidLegato(5, 4) + .fast(2) + .firstCycle() + .filter(hap => hap.hasOnset()) + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/4: a', '1/4 → 3/8: a', '3/8 → 5/8: a', '5/8 → 3/4: a', '3/4 → 1/1: a']); + }); }); describe('euclidLegatoRot', () => { diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index ccb91377f..a0a81ada8 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -4264,13 +4264,16 @@ exports[`runs examples > example "euclidLegatoRot" example index 0 1`] = ` "[ -1/5 ⇜ (0/1 → 1/5) | note:c3 ]", "[ 1/5 → 2/5 | note:c3 ]", "[ 2/5 → 4/5 | note:c3 ]", - "[ 4/5 → 6/5 | note:c3 ]", + "[ (4/5 → 1/1) ⇝ 6/5 | note:c3 ]", + "[ 4/5 ⇜ (1/1 → 6/5) | note:c3 ]", "[ 6/5 → 7/5 | note:c3 ]", "[ 7/5 → 9/5 | note:c3 ]", - "[ 9/5 → 11/5 | note:c3 ]", + "[ (9/5 → 2/1) ⇝ 11/5 | note:c3 ]", + "[ 9/5 ⇜ (2/1 → 11/5) | note:c3 ]", "[ 11/5 → 12/5 | note:c3 ]", "[ 12/5 → 14/5 | note:c3 ]", - "[ 14/5 → 16/5 | note:c3 ]", + "[ (14/5 → 3/1) ⇝ 16/5 | note:c3 ]", + "[ 14/5 ⇜ (3/1 → 16/5) | note:c3 ]", "[ 16/5 → 17/5 | note:c3 ]", "[ 17/5 → 19/5 | note:c3 ]", "[ (19/5 → 4/1) ⇝ 21/5 | note:c3 ]", diff --git a/test/__snapshots__/tunes.test.mjs.snap b/test/__snapshots__/tunes.test.mjs.snap index 1fb178801..d33592bb4 100644 --- a/test/__snapshots__/tunes.test.mjs.snap +++ b/test/__snapshots__/tunes.test.mjs.snap @@ -7322,12 +7322,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` [ "[ -9/8 ⇜ (0/1 → 3/8) | gain:0.6 note:A3 velocity:0.598990320228 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ -3/4 ⇜ (0/1 → 3/4) | gain:0.6 note:C5 velocity:0.836992966942 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 0/1 → 3/2 | note:F2 s:bass clip:1 gain:0.8 ]", + "[ 0/1 → 3/2 | note:D2 s:bass clip:1 gain:0.8 ]", "[ 0/1 → 9/4 | gain:0.6 note:D3 velocity:0.5 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 3/8 → 21/8 | gain:0.6 note:F5 velocity:0.921303892508 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 3/4 → 3/1 | gain:0.6 note:C5 velocity:0.842607785016 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 3/2 → 9/4 | note:F2 s:bass clip:1 gain:0.8 ]", - "[ 9/4 → 3/1 | note:F2 s:bass clip:1 gain:0.8 ]", + "[ 3/2 → 9/4 | note:D2 s:bass clip:1 gain:0.8 ]", + "[ 9/4 → 3/1 | note:D2 s:bass clip:1 gain:0.8 ]", "[ 9/4 → 9/2 | gain:0.6 note:D4 velocity:0.700696257874 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 21/8 → 39/8 | gain:0.6 note:C4 velocity:0.650794394314 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 3/1 → 9/2 | note:D2 s:bass clip:1 gain:0.8 ]", @@ -7339,12 +7339,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` "[ (21/4 → 6/1) ⇝ 27/4 | gain:0.6 note:D4 velocity:0.698815540411 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 39/8 ⇜ (6/1 → 51/8) | gain:0.6 note:D5 velocity:0.875811336562 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 21/4 ⇜ (6/1 → 27/4) | gain:0.6 note:D4 velocity:0.698815540411 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 6/1 → 15/2 | note:D2 s:bass clip:1 gain:0.8 ]", + "[ 6/1 → 15/2 | note:A2 s:bass clip:1 gain:0.8 ]", "[ 6/1 → 33/4 | gain:0.6 note:G4 velocity:0.75977108255 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 51/8 → 69/8 | gain:0.6 note:G4 velocity:0.774316444062 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 27/4 → 9/1 | gain:0.6 note:C5 velocity:0.836244757287 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 15/2 → 33/4 | note:D2 s:bass clip:1 gain:0.8 ]", - "[ 33/4 → 9/1 | note:D2 s:bass clip:1 gain:0.8 ]", + "[ 15/2 → 33/4 | note:A2 s:bass clip:1 gain:0.8 ]", + "[ 33/4 → 9/1 | note:A2 s:bass clip:1 gain:0.8 ]", "[ 33/4 → 21/2 | gain:0.6 note:A3 velocity:0.59140187595 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 69/8 → 87/8 | gain:0.6 note:G4 velocity:0.754063542932 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 9/1 → 21/2 | note:A2 s:bass clip:1 gain:0.8 ]", @@ -7356,12 +7356,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` "[ (45/4 → 12/1) ⇝ 51/4 | gain:0.6 note:A4 velocity:0.797278589569 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 87/8 ⇜ (12/1 → 99/8) | gain:0.6 note:F4 velocity:0.734787144698 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 45/4 ⇜ (12/1 → 51/4) | gain:0.6 note:A4 velocity:0.797278589569 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 12/1 → 27/2 | note:A2 s:bass clip:1 gain:0.8 ]", + "[ 12/1 → 27/2 | note:G2 s:bass clip:1 gain:0.8 ]", "[ 12/1 → 57/4 | gain:0.6 note:G5 velocity:0.979763559997 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 99/8 → 117/8 | gain:0.6 note:C4 velocity:0.666239210404 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 51/4 → 15/1 | gain:0.6 note:F5 velocity:0.95169514697 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 27/2 → 57/4 | note:A2 s:bass clip:1 gain:0.8 ]", - "[ 57/4 → 15/1 | note:A2 s:bass clip:1 gain:0.8 ]", + "[ 27/2 → 57/4 | note:G2 s:bass clip:1 gain:0.8 ]", + "[ 57/4 → 15/1 | note:G2 s:bass clip:1 gain:0.8 ]", "[ 57/4 → 33/2 | gain:0.6 note:F5 velocity:0.918253320269 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 117/8 → 135/8 | gain:0.6 note:G3 velocity:0.571157105267 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 15/1 → 33/2 | note:G2 s:bass clip:1 gain:0.8 ]", @@ -7373,12 +7373,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` "[ (69/4 → 18/1) ⇝ 75/4 | gain:0.6 note:F3 velocity:0.508127085865 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 135/8 ⇜ (18/1 → 147/8) | gain:0.6 note:F5 velocity:0.945647025481 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 69/4 ⇜ (18/1 → 75/4) | gain:0.6 note:F3 velocity:0.508127085865 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 18/1 → 39/2 | note:G2 s:bass clip:1 gain:0.8 ]", + "[ 18/1 → 39/2 | note:F2 s:bass clip:1 gain:0.8 ]", "[ 18/1 → 81/4 | gain:0.6 note:A3 velocity:0.608644555323 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 147/8 → 165/8 | gain:0.6 note:F3 velocity:0.506259439513 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 75/4 → 21/1 | gain:0.6 note:D4 velocity:0.671621939167 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", - "[ 39/2 → 81/4 | note:G2 s:bass clip:1 gain:0.8 ]", - "[ 81/4 → 21/1 | note:G2 s:bass clip:1 gain:0.8 ]", + "[ 39/2 → 81/4 | note:F2 s:bass clip:1 gain:0.8 ]", + "[ 81/4 → 21/1 | note:F2 s:bass clip:1 gain:0.8 ]", "[ 81/4 → 45/2 | gain:0.6 note:D4 velocity:0.704345900565 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 165/8 → 183/8 | gain:0.6 note:D5 velocity:0.887838857248 s:bell delay:0.2 delaytime:0.333333333333 delayfeedback:0.8 ]", "[ 21/1 → 45/2 | note:F2 s:bass clip:1 gain:0.8 ]", From ecc7bc078c86e9d71b9d6b9e4c88ff1ff8ef2af5 Mon Sep 17 00:00:00 2001 From: Tijmen Zwaan Date: Sat, 22 Aug 2026 00:12:13 +0200 Subject: [PATCH 57/66] Fix formatting --- packages/core/test/euclid.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/euclid.test.js b/packages/core/test/euclid.test.js index a7623b3e0..a5fc23857 100644 --- a/packages/core/test/euclid.test.js +++ b/packages/core/test/euclid.test.js @@ -139,7 +139,7 @@ describe('euclidLegato', () => { .euclidLegato(5, 4) .fast(2) .firstCycle() - .filter(hap => hap.hasOnset()) + .filter((hap) => hap.hasOnset()) .sort((a, b) => a.part.begin.sub(b.part.begin)) .map((a) => a.showWhole(true)), ).toStrictEqual(['0/1 → 1/4: a', '1/4 → 3/8: a', '3/8 → 5/8: a', '5/8 → 3/4: a', '3/4 → 1/1: a']); From 38956e4d160975c863ee2aa135bd933c1f02610a Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Sun, 30 Aug 2026 11:17:51 +0200 Subject: [PATCH 58/66] now with prettier FAQ --- website/src/pages/learn/faq.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 7764d91e2..231b12650 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -340,8 +340,8 @@ _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), ## What is a 'hap'? -A 'hap' is a name for a thing that happens, e.g. ```note("c3 -")``` playing a note and then stopping after a while. Haps have a start and an end. Every separate sound that is produced by strudel is a hap on its own. +A 'hap' is a name for a thing that happens, e.g. `note("c3 -")` playing a note and then stopping after a while. Haps have a start and an end. Every separate sound that is produced by strudel is a hap on its own. We would have called it 'event' if this particular name wasn't used by JavaScript to describe things that happen on html pages, like clicking on a button or hovering over a line of text. -Since strudel is written in JavaScript, this confusion of having the same name for different things is avoided by calling events 'haps'. \ No newline at end of file +Since strudel is written in JavaScript, this confusion of having the same name for different things is avoided by calling events 'haps'. From 9c05f8ebc62552052e5a6f3e4facb4e9d1255b41 Mon Sep 17 00:00:00 2001 From: CatoPaus Date: Fri, 18 Sep 2026 07:30:23 +0200 Subject: [PATCH 59/66] Update website/src/pages/learn/effects.mdx Just a small typo. --- website/src/pages/learn/effects.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 517eb658f..9f8a3370c 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -33,7 +33,7 @@ The signal chain in Strudel is as follows: - This is where the main (volume) ADSR happens - A lowpass filter (`lpf`) - A highpass filter (`hpf`) - - A bandpass filter (`bandpass`) + - A bandpass filter (`bpf`) - A vowel filter (`vowel`) - Sample rate reduction (`coarse`) - Bit crushing (`crush`) From a1be663b157532759ed243be7aea98d3788ce890 Mon Sep 17 00:00:00 2001 From: Tijmen Zwaan Date: Mon, 28 Sep 2026 23:42:51 +0200 Subject: [PATCH 60/66] Add error handling to stack so non-broken patterns continue playing --- packages/core/pattern.mjs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 0f5083901..55e7ad73c 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1449,7 +1449,19 @@ export function sequenceP(pats) { export function stack(...pats) { // Array test here is to avoid infinite recursions.. pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat))); - const query = (state) => flatten(pats.map((pat) => pat.query(state))); + const query = (state) => + flatten( + pats.map((pat) => { + try { + // An error occurring in one part of the stack should not break + // playback of the other parts of the stack. + return pat.query(state); + } catch (e) { + errorLogger(e); + return []; + } + }), + ); const result = new Pattern(query); if (__steps) { result._steps = lcm(...pats.map((pat) => pat._steps)); From 74de75644f4b70515460272038d4dfa44db14d2a Mon Sep 17 00:00:00 2001 From: CatoPaus Date: Sun, 4 Oct 2026 10:04:11 +0200 Subject: [PATCH 61/66] fix(soundfonts): correct preset filename typos in gm.mjs This PR fixes minor filename typos and invalid entries in `packages/soundfonts/gm.mjs`: 1. **`gm_electric_bass_finger`**: Corrected typo `'0330_FluidR3_GM_sf2_fible'` -> `'0330_FluidR3_GM_sf2_file'`. Index 1 previously returned HTTP 404 when cycling through bass soundfont variations. 2. **`gm_slap_bass_2`**: Removed accidental space `'0370_GeneralUserGS_sf2_fil e'` -> `'0370_GeneralUserGS_sf2_file'`. 3. **`gm_gunshot`**: Removed stray empty string `''` from the variations array. --- packages/soundfonts/gm.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/soundfonts/gm.mjs b/packages/soundfonts/gm.mjs index d8b7687d7..5fc8252d6 100644 --- a/packages/soundfonts/gm.mjs +++ b/packages/soundfonts/gm.mjs @@ -479,7 +479,7 @@ export default { gm_electric_bass_finger: [ // Electric Bass (finger): Bass '0330_JCLive_sf2_file', - '0330_FluidR3_GM_sf2_fible', + '0330_FluidR3_GM_sf2_file', '0330_Aspirin_sf2_file', //0330_Chaos_sf2_file // same as last '0330_GeneralUserGS_sf2_file', @@ -527,7 +527,7 @@ export default { '0370_Aspirin_sf2_file', // 0370_Chaos_sf2_file // same as last '0370_FluidR3_GM_sf2_file', - '0370_GeneralUserGS_sf2_fil e', + '0370_GeneralUserGS_sf2_file', '0370_JCLive_sf2_file', //0370_SBLive_sf2 //0370_SoundBlasterOld_sf2 @@ -1782,6 +1782,5 @@ export default { '1272_GeneralUserGS_sf2_file', '1273_GeneralUserGS_sf2_file', '1274_GeneralUserGS_sf2_file', - '', ], }; From 5148066b01efd9da70ea8eb136dc22bdf0bdb106 Mon Sep 17 00:00:00 2001 From: tyow Date: Wed, 7 Oct 2026 16:44:43 -0400 Subject: [PATCH 62/66] stackCenter/Centre changes, fix from merge, snapshot --- packages/core/pattern.mjs | 22 +++++++++++++--------- packages/core/signal.mjs | 1 + test/__snapshots__/examples.test.mjs.snap | 13 ++----------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 8968b508f..1b4ac6b26 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -886,6 +886,10 @@ export class Pattern { return stackRight(this, ...pats); } + stackCenter(...pats) { + return stackCenter(this, ...pats); + } + stackCentre(...pats) { return stackCentre(this, ...pats); } @@ -1666,7 +1670,7 @@ function _stackWith(func, pats) { } /** - * The input patterns are aligned to the left, with silence padded to the shorter pattern(s) in the final steps if there's a stepcount disparity. See also `stackRight` and `stackCentre/stackCenter` + * The input patterns are aligned to the left, with silence padded to the shorter pattern(s) in the final steps if there's a stepcount disparity. See also `stackRight` and `stackCenter/stackCentre` * * @name stackLeft * @tags temporal, stepwise, combiners @@ -1685,7 +1689,7 @@ export function stackLeft(...pats) { } /** - * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCentre/stackCenter` + * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackCenter/stackCentre` * * @name stackRight * @tags temporal, stepwise, combiners @@ -1705,17 +1709,17 @@ export function stackRight(...pats) { /** * The input patterns are aligned to the right, with silence padded to the shorter pattern(s) in the initial steps if there's a stepcount disparity. See also `stackLeft` and `stackRight` - * @name stackCentre + * @name stackCenter * @tags temporal, stepwise, combiners - * @synonyms stackCenter + * @synonyms stackCentre * @return {Pattern} * @example - * stackCentre("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + * stackCenter("0 1 2 3", "3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() * @example * // chainable - * "0 1 2 3".stackCentre("3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() + * "0 1 2 3".stackCenter("3 4", "[4 5 6] 7 8 9 10").scale("c:minor").note() */ -export function stackCentre(...pats) { +export function stackCenter(...pats) { return _stackWith( (steps, pats) => pats.map((pat) => { @@ -1729,8 +1733,8 @@ export function stackCentre(...pats) { ); } -export function stackCenter(pats) { - return stackCentre(pats); +export function stackCentre(...pats) { + return stackCenter(...pats); } /** diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 83a35d677..0316c86fe 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -6,6 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th import { Hap } from './hap.mjs'; import { Pattern, fastcat, pure, register, reify, silence, stack, sequenceP, isPattern } from './pattern.mjs'; +import { _mod } from './util.mjs'; import Fraction from './fraction.mjs'; import { id, keyAlias, getCurrentKeyboardState } from './util.mjs'; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 1d8d69922..d1e8599f3 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -7908,15 +7908,6 @@ exports[`runs examples > example "midi" example index 0 1`] = ` ] `; -exports[`runs examples > example "midi" example index 1 1`] = ` -[ - "[ 0/1 → 1/1 | note:c4 midichan:1 ]", - "[ 1/1 → 2/1 | note:c4 midichan:1 ]", - "[ 2/1 → 3/1 | note:c4 midichan:1 ]", - "[ 3/1 → 4/1 | note:c4 midichan:1 ]", -] -`; - exports[`runs examples > example "midibend" example index 0 1`] = ` [ "[ 0/1 → 1/1 | note:c4 midibend:0 ]", @@ -13144,7 +13135,7 @@ exports[`runs examples > example "stack" example index 1 1`] = ` ] `; -exports[`runs examples > example "stackCentre" example index 0 1`] = ` +exports[`runs examples > example "stackCenter" example index 0 1`] = ` [ "[ 0/1 → 1/15 | note:G3 ]", "[ 1/15 → 2/15 | note:Ab3 ]", @@ -13201,7 +13192,7 @@ exports[`runs examples > example "stackCentre" example index 0 1`] = ` ] `; -exports[`runs examples > example "stackCentre" example index 1 1`] = ` +exports[`runs examples > example "stackCenter" example index 1 1`] = ` [ "[ 0/1 → 1/15 | note:G3 ]", "[ 1/15 → 2/15 | note:Ab3 ]", From 60ac02905d8a38822e1c957910f24af6598b04e7 Mon Sep 17 00:00:00 2001 From: Tijmen Zwaan Date: Tue, 18 Aug 2026 03:28:07 +0200 Subject: [PATCH 63/66] Add cyclestruct function --- packages/core/pattern.mjs | 45 +++++++++++++++++++++++ packages/core/test/pattern.test.mjs | 8 ++++ test/__snapshots__/examples.test.mjs.snap | 29 +++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 55e7ad73c..eeb76ecb7 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2325,6 +2325,51 @@ export const bite = register( false, ); +/** + * Similar to `struct`, but every hap in the struct pattern plays the next + * cycle of the main pattern. + * @tags temporal + * @name cyclestruct + * @memberof Pattern + * @param {Pattern} structure structure of the pattern + * @example + * "<0 1 2 3 4 5>".cyclestruct("[x - x*2]*2").n().scale("c:major") + * // the same as "0 - [1 2] 3 - [4 5]".n().scale("c:major") + */ +export const { cyclestruct, cycleStruct } = register( + ['cyclestruct', 'cycleStruct'], + (structure, pat) => { + const structPat = new Pattern((state) => { + const haps_cycle = structure + .withQuerySpan((span) => span.begin.wholeCycle()) + .query(state) + .filter((hap) => hap.value != 0) + .sort((a, b) => a.whole.begin - b.whole.begin) + .map((hap, i) => hap.withValue((_) => i)); + + const haps = []; + for (const hap of haps_cycle) { + const new_part = state.span.intersection(hap.part); + if (new_part) { + haps.push(new Hap(hap.whole, new_part, hap.value, hap.context)); + } + } + return haps; + }).splitQueries(); + + const stepsPattern = new Pattern((state) => { + const nrHaps = structure + .withQuerySpan((span) => span.begin.wholeCycle()) + .query(state) + .filter((hap) => hap.value != 0).length; + return [new Hap(state.span.begin.wholeCycle(), state.span, nrHaps, {})]; + }).splitQueries(); + + return pat.fast(stepsPattern).bite(stepsPattern, structPat); + }, + false, +); + /** * Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle. * @tags temporal diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 4eb18c647..3e0e0837f 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1320,6 +1320,14 @@ describe('Pattern', () => { ); }); }); + describe('cyclestruct', () => { + it('can play a melody with a structure', () => { + sameFirst( + slowcat(0, 1, 2, 3, 4, 5).cyclestruct(fastcat(true, silence, fastcat(true, true)).fast(2)), + fastcat(0, silence, fastcat(1, 2), 3, silence, fastcat(4, 5)), + ); + }); + }); describe('unjoin', () => { it('destructures a pattern into subcycles', () => { sameFirst( diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index ccb91377f..12551f264 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2855,6 +2855,35 @@ exports[`runs examples > example "cyclesPer" example index 1 1`] = ` ] `; +exports[`runs examples > example "cyclestruct" example index 0 1`] = ` +[ + "[ 0/1 → 1/6 | note:C3 ]", + "[ 1/3 → 5/12 | note:D3 ]", + "[ 5/12 → 1/2 | note:E3 ]", + "[ 1/2 → 2/3 | note:F3 ]", + "[ 5/6 → 11/12 | note:G3 ]", + "[ 11/12 → 1/1 | note:A3 ]", + "[ 1/1 → 7/6 | note:C3 ]", + "[ 4/3 → 17/12 | note:D3 ]", + "[ 17/12 → 3/2 | note:E3 ]", + "[ 3/2 → 5/3 | note:F3 ]", + "[ 11/6 → 23/12 | note:G3 ]", + "[ 23/12 → 2/1 | note:A3 ]", + "[ 2/1 → 13/6 | note:C3 ]", + "[ 7/3 → 29/12 | note:D3 ]", + "[ 29/12 → 5/2 | note:E3 ]", + "[ 5/2 → 8/3 | note:F3 ]", + "[ 17/6 → 35/12 | note:G3 ]", + "[ 35/12 → 3/1 | note:A3 ]", + "[ 3/1 → 19/6 | note:C3 ]", + "[ 10/3 → 41/12 | note:D3 ]", + "[ 41/12 → 7/2 | note:E3 ]", + "[ 7/2 → 11/3 | note:F3 ]", + "[ 23/6 → 47/12 | note:G3 ]", + "[ 47/12 → 4/1 | note:A3 ]", +] +`; + exports[`runs examples > example "decay" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c3 decay:0.1 sustain:0 ]", From c7d0f1582691ad416a734185e555ecc0535590b5 Mon Sep 17 00:00:00 2001 From: Tijmen Zwaan Date: Fri, 9 Oct 2026 22:16:42 +0200 Subject: [PATCH 64/66] Rename cyclestruct to isobite with synonyms and add some examples --- packages/core/pattern.mjs | 33 ++++++-- packages/core/test/pattern.test.mjs | 17 ++++- test/__snapshots__/examples.test.mjs.snap | 93 ++++++++++++++++------- 3 files changed, 106 insertions(+), 37 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index eeb76ecb7..107347a5c 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2326,18 +2326,41 @@ export const bite = register( ); /** - * Similar to `struct`, but every hap in the struct pattern plays the next + * Similar to `struct`, but every hap in the structure pattern plays the next * cycle of the main pattern. + * With a different number of cycles in the main pattern and haps in the + * structure pattern, this creates an isorhythm. + * + * Note: This function works on the assumption that the structure pattern + * repeats every cycle. If the structure pattern only repeats after multiple + * cycles, it may skip or repeat certain parts of the melody. * @tags temporal - * @name cyclestruct + * @name isobite + * @synonyms cycleStruct, cyclestruct * @memberof Pattern * @param {Pattern} structure structure of the pattern * @example - * "<0 1 2 3 4 5>".cyclestruct("[x - x*2]*2").n().scale("c:major") + * "<0 1 2 3 4 5>".isobite("[x - x*2]*2").n().scale("c:major") * // the same as "0 - [1 2] 3 - [4 5]".n().scale("c:major") + * + * @example + * "<0 1 2 3 4>".isobite("x - x").n().scale("c:major") + * // the same as "<[0 - 1] [2 - 3] [4 - 0] [1 - 2] [3 - 4]>".n().scale("c:major") + * + * @example + * // Here the structure pattern repeats after 2 cycles, + * // so certain haps from the melody are skipped. + * "<0 1 2 3>".isobite("x x*2".slow(2)).n().scale("c:major") + * // the same as "<[0] [2 3] [2] [2 3]>".n().scale("c:major") + * + * @example + * // This can be avoided by making the struct pattern 1 cycle, + * // and slowing the whole pattern down as follows: + * "<0 1 2 3>".isobite("x x*2").slow(2).n().scale("c:major") + * // the same as "<[0] [1 2] [3] [0 1] [2] [3 0] [1] [2 3]>".n().scale("c:major") */ -export const { cyclestruct, cycleStruct } = register( - ['cyclestruct', 'cycleStruct'], +export const { isobite, cyclestruct, cycleStruct } = register( + ['isobite', 'cyclestruct', 'cycleStruct'], (structure, pat) => { const structPat = new Pattern((state) => { const haps_cycle = structure diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 3e0e0837f..7c299bb71 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -75,6 +75,10 @@ const sameFirst = (a, b) => { return expect(a.sortHapsByPart().firstCycle()).toStrictEqual(b.sortHapsByPart().firstCycle()); }; +const sameFirstX = (x, a, b) => { + return expect(a.fast(x).sortHapsByPart().firstCycle()).toStrictEqual(b.fast(x).sortHapsByPart().firstCycle()); +}; + describe('TimeSpan', () => { describe('equals()', () => { it('Should be equal to the same value', () => { @@ -1320,11 +1324,18 @@ describe('Pattern', () => { ); }); }); - describe('cyclestruct', () => { + describe('isobite', () => { it('can play a melody with a structure', () => { sameFirst( - slowcat(0, 1, 2, 3, 4, 5).cyclestruct(fastcat(true, silence, fastcat(true, true)).fast(2)), - fastcat(0, silence, fastcat(1, 2), 3, silence, fastcat(4, 5)), + slowcat(0, 1, 2, 3, 4, 5).isobite(fastcat(true, silence, [true, true]).fast(2)), + fastcat(0, silence, [1, 2], 3, silence, [4, 5]), + ); + }); + it('syncopates over multiple cycles if the number of haps in the patterns differ', () => { + sameFirstX( + 4, + slowcat(0, 1, 2, 3).isobite(fastcat(true, [true, true])), + slowcat(0, [1, 2], 3, [0, 1], 2, [3, 0], 1, [2, 3]).fast(2), ); }); }); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 12551f264..4d8093b63 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2855,35 +2855,6 @@ exports[`runs examples > example "cyclesPer" example index 1 1`] = ` ] `; -exports[`runs examples > example "cyclestruct" example index 0 1`] = ` -[ - "[ 0/1 → 1/6 | note:C3 ]", - "[ 1/3 → 5/12 | note:D3 ]", - "[ 5/12 → 1/2 | note:E3 ]", - "[ 1/2 → 2/3 | note:F3 ]", - "[ 5/6 → 11/12 | note:G3 ]", - "[ 11/12 → 1/1 | note:A3 ]", - "[ 1/1 → 7/6 | note:C3 ]", - "[ 4/3 → 17/12 | note:D3 ]", - "[ 17/12 → 3/2 | note:E3 ]", - "[ 3/2 → 5/3 | note:F3 ]", - "[ 11/6 → 23/12 | note:G3 ]", - "[ 23/12 → 2/1 | note:A3 ]", - "[ 2/1 → 13/6 | note:C3 ]", - "[ 7/3 → 29/12 | note:D3 ]", - "[ 29/12 → 5/2 | note:E3 ]", - "[ 5/2 → 8/3 | note:F3 ]", - "[ 17/6 → 35/12 | note:G3 ]", - "[ 35/12 → 3/1 | note:A3 ]", - "[ 3/1 → 19/6 | note:C3 ]", - "[ 10/3 → 41/12 | note:D3 ]", - "[ 41/12 → 7/2 | note:E3 ]", - "[ 7/2 → 11/3 | note:F3 ]", - "[ 23/6 → 47/12 | note:G3 ]", - "[ 47/12 → 4/1 | note:A3 ]", -] -`; - exports[`runs examples > example "decay" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c3 decay:0.1 sustain:0 ]", @@ -6310,6 +6281,70 @@ exports[`runs examples > example "isaw" example index 1 1`] = ` ] `; +exports[`runs examples > example "isobite" example index 0 1`] = ` +[ + "[ 0/1 → 1/6 | note:C3 ]", + "[ 1/3 → 5/12 | note:D3 ]", + "[ 5/12 → 1/2 | note:E3 ]", + "[ 1/2 → 2/3 | note:F3 ]", + "[ 5/6 → 11/12 | note:G3 ]", + "[ 11/12 → 1/1 | note:A3 ]", + "[ 1/1 → 7/6 | note:C3 ]", + "[ 4/3 → 17/12 | note:D3 ]", + "[ 17/12 → 3/2 | note:E3 ]", + "[ 3/2 → 5/3 | note:F3 ]", + "[ 11/6 → 23/12 | note:G3 ]", + "[ 23/12 → 2/1 | note:A3 ]", + "[ 2/1 → 13/6 | note:C3 ]", + "[ 7/3 → 29/12 | note:D3 ]", + "[ 29/12 → 5/2 | note:E3 ]", + "[ 5/2 → 8/3 | note:F3 ]", + "[ 17/6 → 35/12 | note:G3 ]", + "[ 35/12 → 3/1 | note:A3 ]", + "[ 3/1 → 19/6 | note:C3 ]", + "[ 10/3 → 41/12 | note:D3 ]", + "[ 41/12 → 7/2 | note:E3 ]", + "[ 7/2 → 11/3 | note:F3 ]", + "[ 23/6 → 47/12 | note:G3 ]", + "[ 47/12 → 4/1 | note:A3 ]", +] +`; + +exports[`runs examples > example "isobite" example index 1 1`] = ` +[ + "[ 0/1 → 1/3 | note:C3 ]", + "[ 2/3 → 1/1 | note:D3 ]", + "[ 1/1 → 4/3 | note:E3 ]", + "[ 5/3 → 2/1 | note:F3 ]", + "[ 2/1 → 7/3 | note:G3 ]", + "[ 8/3 → 3/1 | note:C3 ]", + "[ 3/1 → 10/3 | note:D3 ]", + "[ 11/3 → 4/1 | note:E3 ]", +] +`; + +exports[`runs examples > example "isobite" example index 2 1`] = ` +[ + "[ 0/1 → 1/1 | note:C3 ]", + "[ 1/1 → 3/2 | note:E3 ]", + "[ 3/2 → 2/1 | note:F3 ]", + "[ 2/1 → 3/1 | note:E3 ]", + "[ 3/1 → 7/2 | note:E3 ]", + "[ 7/2 → 4/1 | note:F3 ]", +] +`; + +exports[`runs examples > example "isobite" example index 3 1`] = ` +[ + "[ 0/1 → 1/1 | note:C3 ]", + "[ 1/1 → 3/2 | note:D3 ]", + "[ 3/2 → 2/1 | note:E3 ]", + "[ 2/1 → 3/1 | note:F3 ]", + "[ 3/1 → 7/2 | note:C3 ]", + "[ 7/2 → 4/1 | note:D3 ]", +] +`; + exports[`runs examples > example "iter" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:A3 ]", From fd2626d11d075be4287402fcaab0127fc3889ae6 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Oct 2026 11:42:38 +0200 Subject: [PATCH 65/66] hotfix: pin ci container (deploy is failing) --- .forgejo/workflows/deploy.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index e102618e4..2b47e5e17 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -10,6 +10,7 @@ concurrency: jobs: build: runs-on: docker + container: node:22-bookworm env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} steps: @@ -34,4 +35,4 @@ jobs: apt update && apt install -y rsync mkdir ~/.ssh ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts - rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy/cold.strudel.cc \ No newline at end of file + rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy/cold.strudel.cc From 076bb8a152562cb625782eb0edd9c15ecbdd2559 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Oct 2026 11:47:21 +0200 Subject: [PATCH 66/66] hotfix: more missing containers --- .forgejo/workflows/deploy-hot.yml | 5 +++-- .forgejo/workflows/deploy-warm-beta.yml | 3 ++- .forgejo/workflows/test.yml | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/deploy-hot.yml b/.forgejo/workflows/deploy-hot.yml index 6c8205ae7..a19917dfa 100644 --- a/.forgejo/workflows/deploy-hot.yml +++ b/.forgejo/workflows/deploy-hot.yml @@ -13,10 +13,11 @@ jobs: build: if: ${{ github.event.label.name == 'serve-hot' }} runs-on: docker + container: node:22-bookworm steps: - uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} + ref: ${{ github.event.pull_request.head.sha }} - uses: pnpm/action-setup@v4 with: version: 9.12.2 @@ -41,4 +42,4 @@ jobs: ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts rsync -atv --delete --delete-after --progress \ ./website/dist/ \ - strudel@matrix.toplap.org:/home/strudel/deploy/pr-${{ github.event.pull_request.number }}.hot.strudel.cc \ No newline at end of file + strudel@matrix.toplap.org:/home/strudel/deploy/pr-${{ github.event.pull_request.number }}.hot.strudel.cc diff --git a/.forgejo/workflows/deploy-warm-beta.yml b/.forgejo/workflows/deploy-warm-beta.yml index 0265fcebd..264f716d0 100644 --- a/.forgejo/workflows/deploy-warm-beta.yml +++ b/.forgejo/workflows/deploy-warm-beta.yml @@ -13,6 +13,7 @@ concurrency: jobs: build: runs-on: docker + container: node:22-bookworm env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} steps: @@ -37,4 +38,4 @@ jobs: apt update && apt install -y rsync mkdir ~/.ssh ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts - rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy/strudel.cc \ No newline at end of file + rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy/strudel.cc diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 6b3759571..c1db1c5f9 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -5,6 +5,7 @@ on: [push, pull_request] jobs: build: runs-on: docker + container: node:22-bookworm strategy: matrix: node-version: [20]