diff --git a/.eslintignore b/.eslintignore index 92c95c85..7d807b65 100644 --- a/.eslintignore +++ b/.eslintignore @@ -20,4 +20,5 @@ vite.config.js **/dist /src-tauri/target/**/* reverbGen.mjs -hydra.mjs \ No newline at end of file +hydra.mjs +jsdoc-synonyms.js \ No newline at end of file diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js new file mode 100644 index 00000000..09190846 --- /dev/null +++ b/jsdoc/jsdoc-synonyms.js @@ -0,0 +1,17 @@ +/* +jsdoc-synonyms.js - Add support for @synonym tag +Copyright (C) 2023 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +function defineTags(dictionary) { + dictionary.defineTag('synonyms', { + mustHaveValue: true, + onTagged: function (doclet, tag) { + doclet.synonyms_text = tag.value; + doclet.synonyms = doclet.synonyms_text.split(/[ ,]+/); + }, + }); +} + +module.exports = { defineTags: defineTags }; diff --git a/jsdoc.config.json b/jsdoc/jsdoc.config.json similarity index 78% rename from jsdoc.config.json rename to jsdoc/jsdoc.config.json index ca9c3d81..073b16c5 100644 --- a/jsdoc.config.json +++ b/jsdoc/jsdoc.config.json @@ -3,7 +3,7 @@ "includePattern": ".+\\.(js(doc|x)?|mjs)$", "excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser|dist" }, - "plugins": ["plugins/markdown"], + "plugins": ["plugins/markdown", "jsdoc/jsdoc-synonyms"], "opts": { "destination": "./out/", "recurse": true diff --git a/undocumented.mjs b/jsdoc/undocumented.mjs similarity index 91% rename from undocumented.mjs rename to jsdoc/undocumented.mjs index 281d27d8..9376a8b6 100644 --- a/undocumented.mjs +++ b/jsdoc/undocumented.mjs @@ -65,7 +65,7 @@ async function getUndocumented(path, docs) { } // read doc.json file -const { docs } = JSON.parse(await readFile(resolve(__dirname, 'doc.json'), 'utf8')); +const { docs } = JSON.parse(await readFile(resolve(__dirname, '..', 'doc.json'), 'utf8')); const paths = dependencyTree.toList({ filename: 'index.mjs', @@ -76,7 +76,9 @@ const paths = dependencyTree.toList({ // const paths = ['../packages/core/pattern.mjs', '../packages/core/hap.mjs'].map((rel) => resolve(__dirname, rel)); const undocumented = Object.fromEntries( - await Promise.all(paths.map(async (path) => [path, await getUndocumented(path, docs)])), + await Promise.all( + paths.map(async (path) => [path.replace(resolve(__dirname, '..'), ''), await getUndocumented(path, docs)]), + ), ); console.log(JSON.stringify(undocumented, null, 2)); diff --git a/package.json b/package.json index 36229611..172832ee 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,12 @@ "build": "npm run prebuild && cd website && npm run build", "preview": "cd website && npm run preview", "osc": "cd packages/osc && npm run server", - "jsdoc": "jsdoc packages/ -c jsdoc.config.json", - "jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json", + "jsdoc": "jsdoc packages/ -c jsdoc/jsdoc.config.json", + "jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc/jsdoc.config.json", "lint": "eslint . --ext mjs,js --quiet", "codeformat": "prettier --write .", "format-check": "prettier --check .", - "report-undocumented": "npm run jsdoc-json && node undocumented.mjs > undocumented.json", + "report-undocumented": "npm run jsdoc-json && node jsdoc/undocumented.mjs > undocumented.json", "check": "npm run format-check && npm run lint && npm run test", "iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf" }, diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index a03658a8..6449969e 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1214,6 +1214,16 @@ const generic_params = [ * @name waveloss */ ['waveloss'], + /* + * Noise crackle density + * + * @name density + * @param {number | Pattern} density between 0 and x + * @example + * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) + * + */ + ['density'], // TODO: midi effects? ['dur'], // ['modwheel'], diff --git a/packages/hydra/hydra.mjs b/packages/hydra/hydra.mjs index a75614ec..b63fc46c 100644 --- a/packages/hydra/hydra.mjs +++ b/packages/hydra/hydra.mjs @@ -1,33 +1,33 @@ import { getDrawContext } from '@strudel.cycles/core'; -let audio = false; let hydra; function appendCanvas(c) { const { canvas: testCanvas } = getDrawContext(); c.canvas.id = 'hydra-canvas'; - c.canvas.style.position = 'absolute'; + c.canvas.style.position = 'fixed'; c.canvas.style.top = '0px'; testCanvas.after(c.canvas); + return testCanvas; } -export async function initHydra(config) { - audio = config?.audio || false; +export async function initHydra(options = {}) { //load and init hydra if (!document.getElementById('hydra-canvas')) { - await import('https://unpkg.com/hydra-synth'); - hydra = new Hydra({ detectAudio: audio }); + const { src = 'https://unpkg.com/hydra-synth', ...opts } = options; + await import(src); + + hydra = new Hydra(opts); appendCanvas(hydra); + // s0.init({ src: hydraCanvas }); // whats that? } - // if config.audio is true + // if options.detectAudio is true // and current canvas des not detect audio - if (config?.audio && !hydra.detectAudio) { + if (options?.detectAudio && !hydra?.detectAudio) { //remove previous canvas without audio detection document.getElementById('hydra-canvas').remove(); - // create and append a new audio responsive canvas - hydra = new Hydra({ detectAudio: audio }); - appendCanvas(hydra); + return initHydra(options); } } diff --git a/packages/react/src/components/Autocomplete.jsx b/packages/react/src/components/Autocomplete.jsx index 677baa6e..9a0b9de4 100644 --- a/packages/react/src/components/Autocomplete.jsx +++ b/packages/react/src/components/Autocomplete.jsx @@ -2,16 +2,23 @@ import { createRoot } from 'react-dom/client'; import jsdoc from '../../../../doc.json'; const getDocLabel = (doc) => doc.name || doc.longname; +const getDocSynonyms = (doc) => [getDocLabel(doc), ...(doc.synonyms || [])]; const getInnerText = (html) => { var div = document.createElement('div'); div.innerHTML = html; return div.textContent || div.innerText || ''; }; -export function Autocomplete({ doc }) { +export function Autocomplete({ doc, label = getDocLabel(doc) }) { + const synonyms = getDocSynonyms(doc).filter((a) => a !== label); return (
-

{getDocLabel(doc)}

+

{label}

{' '} + {!!synonyms.length && ( + + Synonyms: {synonyms.join(', ')} + + )}
    {doc.params?.map(({ name, type, description }, i) => ( @@ -48,18 +55,24 @@ const jsdocCompletions = jsdoc.docs !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)), ) // https://codemirror.net/docs/ref/#autocomplete.Completion - .map((doc) /*: Completion */ => ({ - label: getDocLabel(doc), - // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. - info: () => { - const node = document.createElement('div'); - // if Autocomplete is non-interactive, it could also be rendered at build time.. - // .. using renderToStaticMarkup - createRoot(node).render(); - return node; - }, - type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type - })); + .reduce( + (acc, doc) /*: Completion */ => + acc.concat( + [getDocLabel(doc), ...(doc.synonyms || [])].map((label) => ({ + label, + // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. + info: () => { + const node = document.createElement('div'); + // if Autocomplete is non-interactive, it could also be rendered at build time.. + // .. using renderToStaticMarkup + createRoot(node).render(); + return node; + }, + type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type + })), + ), + [], + ); export const strudelAutocomplete = (context /* : CompletionContext */) => { let word = context.matchBefore(/\w*/); diff --git a/packages/react/src/components/Tooltip.jsx b/packages/react/src/components/Tooltip.jsx index a443c123..43a53476 100644 --- a/packages/react/src/components/Tooltip.jsx +++ b/packages/react/src/components/Tooltip.jsx @@ -31,6 +31,9 @@ window.addEventListener( export const strudelTooltip = hoverTooltip( (view, pos, side) => { // Word selection from CodeMirror Hover Tooltip example https://codemirror.net/examples/tooltip/#hover-tooltips + if (!ctrlDown) { + return null; + } let { from, to, text } = view.state.doc.lineAt(pos); let start = pos, end = pos; @@ -47,11 +50,13 @@ export const strudelTooltip = hoverTooltip( // Get entry from Strudel documentation let entry = jsdoc.docs.filter((doc) => getDocLabel(doc) === word)[0]; if (!entry) { - return null; - } - if (!ctrlDown) { - return null; + // Try for synonyms + entry = jsdoc.docs.filter((doc) => doc.synonyms && doc.synonyms.includes(word))[0]; + if (!entry) { + return null; + } } + return { pos: start, end, @@ -60,7 +65,7 @@ export const strudelTooltip = hoverTooltip( create(view) { let dom = document.createElement('div'); dom.className = 'strudel-tooltip'; - createRoot(dom).render(); + createRoot(dom).render(); return { dom }; }, }; diff --git a/packages/superdough/noise.mjs b/packages/superdough/noise.mjs index 2c8c1d4a..24779470 100644 --- a/packages/superdough/noise.mjs +++ b/packages/superdough/noise.mjs @@ -4,7 +4,7 @@ import { getAudioContext } from './superdough.mjs'; let noiseCache = {}; // lazy generates noise buffers and keeps them forever -function getNoiseBuffer(type) { +function getNoiseBuffer(type, density) { const ac = getAudioContext(); if (noiseCache[type]) { return noiseCache[type]; @@ -34,17 +34,26 @@ function getNoiseBuffer(type) { output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; output[i] *= 0.11; b6 = white * 0.115926; + } else if (type === 'crackle') { + const probability = density * 0.01; + if (Math.random() < probability) { + output[i] = Math.random() * 2 - 1; + } else { + output[i] = 0; + } } } - noiseCache[type] = noiseBuffer; + + // Prevent caching to randomize crackles + if (type !== 'crackle') noiseCache[type] = noiseBuffer; return noiseBuffer; } // expects one of noises as type -export function getNoiseOscillator(type = 'white', t) { +export function getNoiseOscillator(type = 'white', t, density = 0.02) { const ac = getAudioContext(); const o = ac.createBufferSource(); - o.buffer = getNoiseBuffer(type); + o.buffer = getNoiseBuffer(type, density); o.loop = true; o.start(t); return { diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 00e2f42c..ed133366 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -241,6 +241,7 @@ export const superdough = async (value, deadline, hapDuration) => { source, gain = 0.8, postgain = 1, + density = 0.03, // filters ftype = '12db', fanchor = 0.5, diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index dafc2e7c..edc3c01e 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -22,7 +22,7 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { }; const waveforms = ['sine', 'square', 'triangle', 'sawtooth']; -const noises = ['pink', 'white', 'brown']; +const noises = ['pink', 'white', 'brown', 'crackle']; export function registerSynthSounds() { [...waveforms, ...noises].forEach((s) => { @@ -36,7 +36,8 @@ export function registerSynthSounds() { if (waveforms.includes(s)) { sound = getOscillator(s, t, value); } else { - sound = getNoiseOscillator(s, t); + let { density } = value; + sound = getNoiseOscillator(s, t, density); } let { node: o, stop, triggerRelease } = sound; diff --git a/undocumented.json b/undocumented.json index 0d28835d..0a807c90 100644 --- a/undocumented.json +++ b/undocumented.json @@ -1,17 +1,17 @@ { - "/home/felix/projects/strudel/packages/core/fraction.mjs": [ + "/packages/core/fraction.mjs": [ "gcd" ], - "/home/felix/projects/strudel/packages/core/timespan.mjs": [ + "/packages/core/timespan.mjs": [ "TimeSpan" ], - "/home/felix/projects/strudel/packages/core/hap.mjs": [ + "/packages/core/hap.mjs": [ "Hap" ], - "/home/felix/projects/strudel/packages/core/state.mjs": [ + "/packages/core/state.mjs": [ "State" ], - "/home/felix/projects/strudel/packages/core/util.mjs": [ + "/packages/core/util.mjs": [ "isNoteWithOctave", "isNote", "tokenizeNote", @@ -34,23 +34,31 @@ "mapArgs", "numeralArgs", "parseFractional", - "fractionalArgs" + "fractionalArgs", + "splitAt", + "zipWith", + "clamp", + "sol2note" ], - "/home/felix/projects/strudel/packages/core/value.mjs": [ + "/packages/core/value.mjs": [ "unionWithObj", "valued", "Value", "map" ], - "/home/felix/projects/strudel/packages/core/drawLine.mjs": [], - "/home/felix/projects/strudel/packages/core/logger.mjs": [ + "/packages/core/drawLine.mjs": [], + "/packages/core/logger.mjs": [ "logKey", "logger" ], - "/home/felix/projects/strudel/packages/core/pattern.mjs": [ + "/packages/core/pattern.mjs": [ "setStringParser", + "polyrhythm", + "pr", + "pm", "isPattern", "reify", + "fastcat", "set", "keep", "keepif", @@ -74,22 +82,33 @@ "func", "compressSpan", "compressspan", + "fastgap", "focusSpan", "focusspan", + "density", + "sparsity", "zoomArc", "zoomarc", + "inv", + "juxby", + "echowith", + "stutWith", + "stutwith", + "iterback", + "chunkback", "bypass", "duration", - "color", "colour", - "striate" + "loopat", + "loopatcps" ], - "/home/felix/projects/strudel/packages/core/controls.mjs": [], - "/home/felix/projects/strudel/packages/core/euclid.mjs": [ + "/packages/core/controls.mjs": [], + "/packages/core/euclid.mjs": [ + "bjork", "euclidrot", "euclidLegatoRot" ], - "/home/felix/projects/strudel/packages/core/signal.mjs": [ + "/packages/core/signal.mjs": [ "steady", "signal", "isaw", @@ -112,34 +131,37 @@ "degradeByWith", "undegrade" ], - "/home/felix/projects/strudel/packages/core/speak.mjs": [ + "/packages/core/speak.mjs": [ "speak" ], - "/home/felix/projects/strudel/packages/core/evaluate.mjs": [ + "/packages/core/evaluate.mjs": [ "evalScope", "evaluate" ], - "/home/felix/projects/strudel/packages/core/zyklus.mjs": [], - "/home/felix/projects/strudel/packages/core/cyclist.mjs": [ + "/packages/core/zyklus.mjs": [], + "/packages/core/cyclist.mjs": [ "Cyclist" ], - "/home/felix/projects/strudel/packages/core/time.mjs": [ + "/packages/core/time.mjs": [ "getTime", "setTime" ], - "/home/felix/projects/strudel/packages/core/repl.mjs": [ - "repl" + "/packages/core/repl.mjs": [ + "repl", + "getTrigger" ], - "/home/felix/projects/strudel/packages/core/draw.mjs": [ + "/packages/core/draw.mjs": [ "getDrawContext", - "cleanupDraw" + "cleanupDraw", + "Framer", + "Drawer" ], - "/home/felix/projects/strudel/packages/core/animate.mjs": [ + "/packages/core/animate.mjs": [ "x", "y", "w", "h", - "a", + "angle", "r", "fill", "smear", @@ -147,85 +169,121 @@ "moveXY", "zoomIn" ], - "/home/felix/projects/strudel/packages/core/pianoroll.mjs": [ - "pianoroll" + "/packages/core/pianoroll.mjs": [ + "getDrawOptions", + "drawPianoroll" ], - "/home/felix/projects/strudel/packages/core/ui.mjs": [ + "/packages/core/spiral.mjs": [], + "/packages/core/ui.mjs": [ "backgroundImage", "cleanupUi" ], - "/home/felix/projects/strudel/packages/core/gist.js": [], - "/home/felix/projects/strudel/packages/core/index.mjs": [], - "/home/felix/projects/strudel/packages/midi/midi.mjs": [ + "/packages/core/gist.js": [], + "/packages/core/index.mjs": [], + "/packages/csound/index.mjs": [ + "loadCSound", + "loadcsound", + "loadCsound", + "csound", + "loadOrc" + ], + "/packages/desktopbridge/utils.mjs": [ + "Invoke", + "isTauri" + ], + "/packages/desktopbridge/midibridge.mjs": [], + "/packages/desktopbridge/oscbridge.mjs": [], + "/packages/desktopbridge/index.mjs": [], + "/packages/midi/midi.mjs": [ "WebMidi", - "enableWebMidi" + "enableWebMidi", + "midin" ], - "/home/felix/projects/strudel/packages/midi/index.mjs": [], - "/home/felix/projects/strudel/packages/mini/krill-parser.js": [], - "/home/felix/projects/strudel/packages/mini/mini.mjs": [ + "/packages/midi/index.mjs": [], + "/packages/mini/krill-parser.js": [], + "/packages/mini/mini.mjs": [ "patternifyAST", + "getLeafLocation", + "mini2ast", + "getLeaves", + "getLeafLocations", "mini", + "m", "h", - "minify" + "minify", + "miniAllStrings" ], - "/home/felix/projects/strudel/packages/mini/index.mjs": [], - "/home/felix/projects/strudel/packages/soundfonts/fontloader.mjs": [ + "/packages/mini/index.mjs": [], + "/packages/soundfonts/gm.mjs": [], + "/packages/soundfonts/fontloader.mjs": [ "getFontBufferSource", - "getFontPitch" + "getFontPitch", + "registerSoundfonts" ], - "/home/felix/projects/strudel/packages/soundfonts/list.mjs": [ + "/packages/soundfonts/list.mjs": [ "instruments", "drums", "instrumentNames" ], - "/home/felix/projects/strudel/packages/soundfonts/sfumato.mjs": [ + "/packages/soundfonts/sfumato.mjs": [ "loadSoundfont" ], - "/home/felix/projects/strudel/packages/soundfonts/index.mjs": [], - "/home/felix/projects/strudel/packages/tonal/tonal.mjs": [], - "/home/felix/projects/strudel/packages/tonal/voicings.mjs": [ - "voicingRegistry", - "setVoicingRange" + "/packages/soundfonts/index.mjs": [], + "/packages/tonal/tonal.mjs": [], + "/packages/tonal/tonleiter.mjs": [ + "pc2chroma", + "rotateChroma", + "chroma2pc", + "tokenizeChord", + "note2pc", + "note2oct", + "note2chroma", + "midi2chroma", + "pitch2chroma", + "step2semitones", + "x2midi", + "scaleStep", + "renderVoicing", + "accidentalOffset", + "Step", + "Note" ], - "/home/felix/projects/strudel/packages/tonal/index.mjs": [], - "/home/felix/projects/strudel/packages/transpiler/transpiler.mjs": [ + "/packages/tonal/ireal.mjs": [ + "simple", + "complex" + ], + "/packages/tonal/voicings.mjs": [ + "voicingRegistry", + "setVoicingRange", + "registerVoicings", + "voicingAlias" + ], + "/packages/tonal/index.mjs": [], + "/packages/transpiler/transpiler.mjs": [ "transpiler" ], - "/home/felix/projects/strudel/packages/transpiler/index.mjs": [ + "/packages/transpiler/index.mjs": [ "evaluate" ], - "/home/felix/projects/strudel/packages/webaudio/feedbackdelay.mjs": [], - "/home/felix/projects/strudel/packages/webaudio/reverb.mjs": [], - "/home/felix/projects/strudel/packages/webaudio/sampler.mjs": [ - "getCachedBuffer", - "getSampleBufferSource", - "loadBuffer", - "reverseBuffer", - "getLoadedBuffer", - "resetLoadedSamples", - "getLoadedSamples" - ], - "/home/felix/projects/strudel/packages/webaudio/vowel.mjs": [ - "vowelFormant" - ], - "/home/felix/projects/strudel/packages/webaudio/webaudio.mjs": [ - "getAudioContext", - "panic", - "initAudio", - "initAudioOnFirstClick", + "/packages/webaudio/webaudio.mjs": [ + "webaudioOutputTrigger", "webaudioOutput", - "webaudioOutputTrigger" + "webaudioScheduler" ], - "/home/felix/projects/strudel/packages/webaudio/index.mjs": [], - "/home/felix/projects/strudel/packages/xen/xen.mjs": [ + "/packages/webaudio/scope.mjs": [ + "drawTimeScope", + "drawFrequencyScope" + ], + "/packages/webaudio/index.mjs": [], + "/packages/xen/xen.mjs": [ "edo", "xen", "tuning" ], - "/home/felix/projects/strudel/packages/xen/tunejs.js": [], - "/home/felix/projects/strudel/packages/xen/tune.mjs": [ + "/packages/xen/tunejs.js": [], + "/packages/xen/tune.mjs": [ "tune" ], - "/home/felix/projects/strudel/packages/xen/index.mjs": [], - "/home/felix/projects/strudel/index.mjs": [] + "/packages/xen/index.mjs": [], + "/index.mjs": [] } diff --git a/website/src/docs/JsDoc.jsx b/website/src/docs/JsDoc.jsx index 1ba80e0a..1a7a034f 100644 --- a/website/src/docs/JsDoc.jsx +++ b/website/src/docs/JsDoc.jsx @@ -2,19 +2,15 @@ import jsdoc from '../../../doc.json'; // doc.json is built with `npm run jsdoc- const docs = jsdoc.docs.reduce((acc, obj) => Object.assign(acc, { [obj.longname]: obj }), {}); import { MiniRepl } from './MiniRepl'; -const getTag = (title, item) => item.tags?.find((t) => t.title === title)?.text; - export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight }) { const item = docs[name]; if (!item) { console.warn('Not found: ' + name); return
    ; } - const synonyms = getTag('synonyms', item)?.split(', ') || []; const CustomHeading = `h${h}`; const description = item.description?.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => { - // console.log(_, 'a', a, 'b', b); return `${a}${b ? `#${b}` : ''}`; }) || ''; return ( @@ -22,9 +18,9 @@ export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight }) {!!h && {item.longname}} {!hideDescription && ( <> - {!!synonyms.length && ( + {!!item.synonyms_text && ( - Synonyms: {synonyms.join(', ')} + Synonyms: {item.synonyms_text} )}
    diff --git a/website/src/pages/learn/hydra.mdx b/website/src/pages/learn/hydra.mdx index dd892ff7..d5219e6f 100644 --- a/website/src/pages/learn/hydra.mdx +++ b/website/src/pages/learn/hydra.mdx @@ -52,11 +52,11 @@ n(pattern).scale("A:minor").piano().room(1) `} /> -To use hydra audio capture, call `initHydra` with `{audio:true}` configuration param: +To use hydra audio capture, call `initHydra` with `{detectAudio:true}` configuration param: ").scope()`} /> +You can also use the `crackle` type to play some subtle noise crackles. You can control noise amount by using the `density` parameter: + +".slow(4)).scope()`} /> + ### Additive Synthesis To tame the harsh sound of the basic waveforms, we can set the `n` control to limit the overtones of the waveform: diff --git a/website/src/repl/Reference.jsx b/website/src/repl/Reference.jsx index cf6fd5b1..b43f365e 100644 --- a/website/src/repl/Reference.jsx +++ b/website/src/repl/Reference.jsx @@ -37,6 +37,11 @@ export function Reference() { {visibleFunctions.map((entry, i) => (

    {entry.name}

    + {!!entry.synonyms_text && ( +

    + Synonyms: {entry.synonyms_text} +

    + )} {/* {entry.meta.filename} */}