mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-21 18:48:36 +00:00
add superdough package
This commit is contained in:
parent
2483bf812f
commit
30fe2dd502
35
packages/superdough/README.md
Normal file
35
packages/superdough/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# superdough
|
||||||
|
|
||||||
|
superdough is a simple web audio sampler and synth, intended for live coding.
|
||||||
|
It is the default output of [strudel](https://strudel.tidalcycles.org/).
|
||||||
|
This package has no ties to strudel and can be used to quickly bake your own music system on the web.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
via npm:
|
||||||
|
|
||||||
|
```js
|
||||||
|
npm i superdough --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { superdough, samples } from 'superdough';
|
||||||
|
// load samples from github
|
||||||
|
const loadSamples = samples('github:tidalcycles/Dirt-Samples/master');
|
||||||
|
|
||||||
|
// play some sounds when a button is clicked
|
||||||
|
document.getElementById('play').addEventListener('click', () => {
|
||||||
|
superdough({ s: "bd", delay: .5 }, 0);
|
||||||
|
superdough({ s: "sawtooth", cutoff: 600, resonance: 8 }, 0);
|
||||||
|
superdough({ s: "hh" }, 0.25);
|
||||||
|
superdough({ s: "sd", room: .5 }, 0.5);
|
||||||
|
superdough({ s: "hh" }, 0.75);
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [SuperDirt](https://github.com/musikinformatik/SuperDirt)
|
||||||
|
- [WebDirt](https://github.com/dktr0/WebDirt)
|
||||||
24
packages/superdough/example/.gitignore
vendored
Normal file
24
packages/superdough/example/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
12
packages/superdough/example/index.html
Normal file
12
packages/superdough/example/index.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Superdough Example</title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button id="play">PLAAAAAAAY</button>
|
||||||
|
<script src="/main.js" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
25
packages/superdough/example/main.js
Normal file
25
packages/superdough/example/main.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { superdough, samples, initAudioOnFirstClick, registerSynthSounds } from 'superdough';
|
||||||
|
|
||||||
|
initAudioOnFirstClick();
|
||||||
|
|
||||||
|
const load = Promise.all([samples('github:tidalcycles/Dirt-Samples/master'), registerSynthSounds()]);
|
||||||
|
|
||||||
|
let button = document.getElementById('play');
|
||||||
|
|
||||||
|
const loop = (t = 0) => {
|
||||||
|
superdough({ s: 'bd', delay: 0.5 }, t);
|
||||||
|
superdough({ note: 'g1', s: 'sawtooth', cutoff: 600, resonance: 8 }, t, 0.125);
|
||||||
|
superdough({ note: 'g2', s: 'sawtooth', cutoff: 600, resonance: 8 }, t + 0.25, 0.125);
|
||||||
|
superdough({ s: 'hh' }, t + 0.25);
|
||||||
|
superdough({ s: 'sd', room: 0.5 }, t + 0.5);
|
||||||
|
superdough({ s: 'hh' }, t + 0.75);
|
||||||
|
};
|
||||||
|
|
||||||
|
button.addEventListener('click', async () => {
|
||||||
|
console.log('play');
|
||||||
|
await load;
|
||||||
|
let t = 0.1;
|
||||||
|
while (t < 16) {
|
||||||
|
loop(t++);
|
||||||
|
}
|
||||||
|
});
|
||||||
17
packages/superdough/example/package.json
Normal file
17
packages/superdough/example/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "superdough-example",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"superdough": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^4.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
packages/superdough/feedbackdelay.mjs
Normal file
31
packages/superdough/feedbackdelay.mjs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
if (typeof DelayNode !== 'undefined') {
|
||||||
|
class FeedbackDelayNode extends DelayNode {
|
||||||
|
constructor(ac, wet, time, feedback) {
|
||||||
|
super(ac);
|
||||||
|
wet = Math.abs(wet);
|
||||||
|
this.delayTime.value = time;
|
||||||
|
|
||||||
|
const feedbackGain = ac.createGain();
|
||||||
|
feedbackGain.gain.value = Math.min(Math.abs(feedback), 0.995);
|
||||||
|
this.feedback = feedbackGain.gain;
|
||||||
|
|
||||||
|
const delayGain = ac.createGain();
|
||||||
|
delayGain.gain.value = wet;
|
||||||
|
this.delayGain = delayGain;
|
||||||
|
|
||||||
|
this.connect(feedbackGain);
|
||||||
|
this.connect(delayGain);
|
||||||
|
feedbackGain.connect(this);
|
||||||
|
|
||||||
|
this.connect = (target) => delayGain.connect(target);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
start(t) {
|
||||||
|
this.delayGain.gain.setValueAtTime(this.delayGain.gain.value, t + this.delayTime.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioContext.prototype.createFeedbackDelay = function (wet, time, feedback) {
|
||||||
|
return new FeedbackDelayNode(this, wet, time, feedback);
|
||||||
|
};
|
||||||
|
}
|
||||||
70
packages/superdough/helpers.mjs
Normal file
70
packages/superdough/helpers.mjs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { getAudioContext } from './superdough.mjs';
|
||||||
|
|
||||||
|
export function gainNode(value) {
|
||||||
|
const node = getAudioContext().createGain();
|
||||||
|
node.gain.value = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getOscillator = ({ s, freq, t }) => {
|
||||||
|
// make oscillator
|
||||||
|
const o = getAudioContext().createOscillator();
|
||||||
|
o.type = s || 'triangle';
|
||||||
|
o.frequency.value = Number(freq);
|
||||||
|
o.start(t);
|
||||||
|
//o.stop(t + duration + release);
|
||||||
|
const stop = (time) => o.stop(time);
|
||||||
|
return { node: o, stop };
|
||||||
|
};
|
||||||
|
|
||||||
|
// alternative to getADSR returning the gain node and a stop handle to trigger the release anytime in the future
|
||||||
|
export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => {
|
||||||
|
const gainNode = getAudioContext().createGain();
|
||||||
|
gainNode.gain.setValueAtTime(0, begin);
|
||||||
|
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
|
||||||
|
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
||||||
|
// sustain end
|
||||||
|
return {
|
||||||
|
node: gainNode,
|
||||||
|
stop: (t) => {
|
||||||
|
//if (typeof gainNode.gain.cancelAndHoldAtTime === 'function') {
|
||||||
|
// gainNode.gain.cancelAndHoldAtTime(t); // this seems to release instantly....
|
||||||
|
// see https://discord.com/channels/779427371270275082/937365093082079272/1086053607360712735
|
||||||
|
//} else {
|
||||||
|
// firefox: this will glitch when the sustain has not been reached yet at the time of release
|
||||||
|
gainNode.gain.setValueAtTime(sustain * velocity, t);
|
||||||
|
//}
|
||||||
|
gainNode.gain.linearRampToValueAtTime(0, t + release);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getADSR = (attack, decay, sustain, release, velocity, begin, end) => {
|
||||||
|
const gainNode = getAudioContext().createGain();
|
||||||
|
gainNode.gain.setValueAtTime(0, begin);
|
||||||
|
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
|
||||||
|
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
||||||
|
gainNode.gain.setValueAtTime(sustain * velocity, end); // sustain end
|
||||||
|
gainNode.gain.linearRampToValueAtTime(0, end + release); // release
|
||||||
|
// for some reason, using exponential ramping creates little cracklings
|
||||||
|
/* let t = begin;
|
||||||
|
gainNode.gain.setValueAtTime(0, t);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(velocity, (t += attack));
|
||||||
|
const sustainGain = Math.max(sustain * velocity, 0.001);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(sustainGain, (t += decay));
|
||||||
|
if (end - begin < attack + decay) {
|
||||||
|
gainNode.gain.cancelAndHoldAtTime(end);
|
||||||
|
} else {
|
||||||
|
gainNode.gain.setValueAtTime(sustainGain, end);
|
||||||
|
}
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.001, end + release); // release */
|
||||||
|
return gainNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFilter = (type, frequency, Q) => {
|
||||||
|
const filter = getAudioContext().createBiquadFilter();
|
||||||
|
filter.type = type;
|
||||||
|
filter.frequency.value = frequency;
|
||||||
|
filter.Q.value = Q;
|
||||||
|
return filter;
|
||||||
|
};
|
||||||
10
packages/superdough/index.mjs
Normal file
10
packages/superdough/index.mjs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
index.mjs - <short description TODO>
|
||||||
|
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/superdough/index.mjs>
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './superdough.mjs';
|
||||||
|
export * from './sampler.mjs';
|
||||||
|
export * from './helpers.mjs';
|
||||||
|
export * from './synth.mjs';
|
||||||
41
packages/superdough/package.json
Normal file
41
packages/superdough/package.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "superdough",
|
||||||
|
"version": "0.9.0",
|
||||||
|
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
|
||||||
|
"main": "index.mjs",
|
||||||
|
"type": "module",
|
||||||
|
"publishConfig": {
|
||||||
|
"main": "dist/index.cjs",
|
||||||
|
"module": "dist/index.mjs"
|
||||||
|
},
|
||||||
|
"directories": {
|
||||||
|
"example": "examples"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"prepublishOnly": "npm run build"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"tidalcycles",
|
||||||
|
"strudel",
|
||||||
|
"pattern",
|
||||||
|
"livecoding",
|
||||||
|
"algorave"
|
||||||
|
],
|
||||||
|
"author": "Felix Roos <flix91@gmail.com>",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^4.3.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nanostores": "^0.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages/superdough/reverb.mjs
Normal file
23
packages/superdough/reverb.mjs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
if (typeof AudioContext !== 'undefined') {
|
||||||
|
AudioContext.prototype.impulseResponse = function (duration, channels = 1) {
|
||||||
|
const length = this.sampleRate * duration;
|
||||||
|
const impulse = this.createBuffer(channels, length, this.sampleRate);
|
||||||
|
const IR = impulse.getChannelData(0);
|
||||||
|
for (let i = 0; i < length; i++) IR[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, duration);
|
||||||
|
return impulse;
|
||||||
|
};
|
||||||
|
|
||||||
|
AudioContext.prototype.createReverb = function (duration) {
|
||||||
|
const convolver = this.createConvolver();
|
||||||
|
convolver.setDuration = (d) => {
|
||||||
|
convolver.buffer = this.impulseResponse(d);
|
||||||
|
convolver.duration = duration;
|
||||||
|
return convolver;
|
||||||
|
};
|
||||||
|
convolver.setDuration(duration);
|
||||||
|
return convolver;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make the reverb more exciting
|
||||||
|
// check out https://blog.gskinner.com/archives/2019/02/reverb-web-audio-api.html
|
||||||
288
packages/superdough/sampler.mjs
Normal file
288
packages/superdough/sampler.mjs
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
import { logger, noteToMidi, valueToMidi } from '@strudel.cycles/core';
|
||||||
|
import { getAudioContext, registerSound } from './index.mjs';
|
||||||
|
import { getEnvelope } from './helpers.mjs';
|
||||||
|
|
||||||
|
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
||||||
|
const loadCache = {}; // string: Promise<ArrayBuffer>
|
||||||
|
|
||||||
|
export const getCachedBuffer = (url) => bufferCache[url];
|
||||||
|
|
||||||
|
function humanFileSize(bytes, si) {
|
||||||
|
var thresh = si ? 1000 : 1024;
|
||||||
|
if (bytes < thresh) return bytes + ' B';
|
||||||
|
var units = si
|
||||||
|
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||||
|
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||||
|
var u = -1;
|
||||||
|
do {
|
||||||
|
bytes /= thresh;
|
||||||
|
++u;
|
||||||
|
} while (bytes >= thresh);
|
||||||
|
return bytes.toFixed(1) + ' ' + units[u];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resolveUrl) => {
|
||||||
|
let transpose = 0;
|
||||||
|
if (freq !== undefined && note !== undefined) {
|
||||||
|
logger('[sampler] hap has note and freq. ignoring note', 'warning');
|
||||||
|
}
|
||||||
|
let midi = valueToMidi({ freq, note }, 36);
|
||||||
|
transpose = midi - 36; // C3 is middle C
|
||||||
|
|
||||||
|
const ac = getAudioContext();
|
||||||
|
let sampleUrl;
|
||||||
|
if (Array.isArray(bank)) {
|
||||||
|
sampleUrl = bank[n % bank.length];
|
||||||
|
} else {
|
||||||
|
const midiDiff = (noteA) => noteToMidi(noteA) - midi;
|
||||||
|
// object format will expect keys as notes
|
||||||
|
const closest = Object.keys(bank)
|
||||||
|
.filter((k) => !k.startsWith('_'))
|
||||||
|
.reduce(
|
||||||
|
(closest, key, j) => (!closest || Math.abs(midiDiff(key)) < Math.abs(midiDiff(closest)) ? key : closest),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
transpose = -midiDiff(closest); // semitones to repitch
|
||||||
|
sampleUrl = bank[closest][n % bank[closest].length];
|
||||||
|
}
|
||||||
|
if (resolveUrl) {
|
||||||
|
sampleUrl = await resolveUrl(sampleUrl);
|
||||||
|
}
|
||||||
|
let buffer = await loadBuffer(sampleUrl, ac, s, n);
|
||||||
|
if (speed < 0) {
|
||||||
|
// should this be cached?
|
||||||
|
buffer = reverseBuffer(buffer);
|
||||||
|
}
|
||||||
|
const bufferSource = ac.createBufferSource();
|
||||||
|
bufferSource.buffer = buffer;
|
||||||
|
const playbackRate = 1.0 * Math.pow(2, transpose / 12);
|
||||||
|
// bufferSource.playbackRate.value = Math.pow(2, transpose / 12);
|
||||||
|
bufferSource.playbackRate.value = playbackRate;
|
||||||
|
return bufferSource;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadBuffer = (url, ac, s, n = 0) => {
|
||||||
|
const label = s ? `sound "${s}:${n}"` : 'sample';
|
||||||
|
if (!loadCache[url]) {
|
||||||
|
logger(`[sampler] load ${label}..`, 'load-sample', { url });
|
||||||
|
const timestamp = Date.now();
|
||||||
|
loadCache[url] = fetch(url)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then(async (res) => {
|
||||||
|
const took = Date.now() - timestamp;
|
||||||
|
const size = humanFileSize(res.byteLength);
|
||||||
|
// const downSpeed = humanFileSize(res.byteLength / took);
|
||||||
|
logger(`[sampler] load ${label}... done! loaded ${size} in ${took}ms`, 'loaded-sample', { url });
|
||||||
|
const decoded = await ac.decodeAudioData(res);
|
||||||
|
bufferCache[url] = decoded;
|
||||||
|
return decoded;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return loadCache[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function reverseBuffer(buffer) {
|
||||||
|
const ac = getAudioContext();
|
||||||
|
const reversed = ac.createBuffer(buffer.numberOfChannels, buffer.length, ac.sampleRate);
|
||||||
|
for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
||||||
|
reversed.copyToChannel(buffer.getChannelData(channel).slice().reverse(), channel, channel);
|
||||||
|
}
|
||||||
|
return reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLoadedBuffer = (url) => {
|
||||||
|
return bufferCache[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
|
||||||
|
return Object.entries(sampleMap).forEach(([key, value]) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
value = [value];
|
||||||
|
}
|
||||||
|
if (typeof value !== 'object') {
|
||||||
|
throw new Error('wrong sample map format for ' + key);
|
||||||
|
}
|
||||||
|
baseUrl = value._base || baseUrl;
|
||||||
|
const replaceUrl = (v) => (baseUrl + v).replace('github:', 'https://raw.githubusercontent.com/');
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
//return [key, value.map(replaceUrl)];
|
||||||
|
value = value.map(replaceUrl);
|
||||||
|
} else {
|
||||||
|
// must be object
|
||||||
|
value = Object.fromEntries(
|
||||||
|
Object.entries(value).map(([note, samples]) => {
|
||||||
|
return [note, (typeof samples === 'string' ? [samples] : samples).map(replaceUrl)];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fn(key, value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// allows adding a custom url prefix handler
|
||||||
|
// for example, it is used by the desktop app to load samples starting with '~/music'
|
||||||
|
let resourcePrefixHandlers = {};
|
||||||
|
export function registerSamplesPrefix(prefix, resolve) {
|
||||||
|
resourcePrefixHandlers[prefix] = resolve;
|
||||||
|
}
|
||||||
|
// finds a prefix handler for the given url (if any)
|
||||||
|
function getSamplesPrefixHandler(url) {
|
||||||
|
const handler = Object.entries(resourcePrefixHandlers).find(([key]) => url.startsWith(key));
|
||||||
|
if (handler) {
|
||||||
|
return handler[1];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a collection of samples to use with `s`
|
||||||
|
* @example
|
||||||
|
* samples('github:tidalcycles/Dirt-Samples/master');
|
||||||
|
* s("[bd ~]*2, [~ hh]*2, ~ sd")
|
||||||
|
* @example
|
||||||
|
* samples({
|
||||||
|
* bd: '808bd/BD0000.WAV',
|
||||||
|
* sd: '808sd/SD0010.WAV'
|
||||||
|
* }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
|
||||||
|
* s("[bd ~]*2, [~ hh]*2, ~ sd")
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const samples = async (sampleMap, baseUrl = sampleMap._base || '', options = {}) => {
|
||||||
|
if (typeof sampleMap === 'string') {
|
||||||
|
// check if custom prefix handler
|
||||||
|
const handler = getSamplesPrefixHandler(sampleMap);
|
||||||
|
if (handler) {
|
||||||
|
return handler(sampleMap);
|
||||||
|
}
|
||||||
|
if (sampleMap.startsWith('github:')) {
|
||||||
|
let [_, path] = sampleMap.split('github:');
|
||||||
|
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||||
|
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
|
||||||
|
}
|
||||||
|
if (typeof fetch !== 'function') {
|
||||||
|
// not a browser
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const base = sampleMap.split('/').slice(0, -1).join('/');
|
||||||
|
if (typeof fetch === 'undefined') {
|
||||||
|
// skip fetch when in node / testing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return fetch(sampleMap)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) => samples(json, baseUrl || json._base || base, options))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
throw new Error(`error loading "${sampleMap}"`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { prebake, tag } = options;
|
||||||
|
processSampleMap(
|
||||||
|
sampleMap,
|
||||||
|
(key, value) =>
|
||||||
|
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, value), {
|
||||||
|
type: 'sample',
|
||||||
|
samples: value,
|
||||||
|
baseUrl,
|
||||||
|
prebake,
|
||||||
|
tag,
|
||||||
|
}),
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cutGroups = [];
|
||||||
|
|
||||||
|
export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||||
|
const {
|
||||||
|
s,
|
||||||
|
freq,
|
||||||
|
unit,
|
||||||
|
nudge = 0, // TODO: is this in seconds?
|
||||||
|
cut,
|
||||||
|
loop,
|
||||||
|
clip = undefined, // if 1, samples will be cut off when the hap ends
|
||||||
|
n = 0,
|
||||||
|
note,
|
||||||
|
speed = 1, // sample playback speed
|
||||||
|
begin = 0,
|
||||||
|
end = 1,
|
||||||
|
} = value;
|
||||||
|
// load sample
|
||||||
|
if (speed === 0) {
|
||||||
|
// no playback
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ac = getAudioContext();
|
||||||
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = value;
|
||||||
|
//const soundfont = getSoundfontKey(s);
|
||||||
|
const time = t + nudge;
|
||||||
|
|
||||||
|
const bufferSource = await getSampleBufferSource(s, n, note, speed, freq, bank, resolveUrl);
|
||||||
|
|
||||||
|
// asny stuff above took too long?
|
||||||
|
if (ac.currentTime > t) {
|
||||||
|
logger(`[sampler] still loading sound "${s}:${n}"`, 'highlight');
|
||||||
|
// console.warn('sample still loading:', s, n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bufferSource) {
|
||||||
|
logger(`[sampler] could not load "${s}:${n}"`, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
|
||||||
|
if (unit === 'c') {
|
||||||
|
// are there other units?
|
||||||
|
bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration * 1; //cps;
|
||||||
|
}
|
||||||
|
// "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
|
||||||
|
// rather than the current playback rate, so even if the sound is playing at twice its normal speed,
|
||||||
|
// the midway point through a 10-second audio buffer is still 5."
|
||||||
|
const offset = begin * bufferSource.buffer.duration;
|
||||||
|
bufferSource.start(time, offset);
|
||||||
|
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||||
|
/*if (loop) {
|
||||||
|
// TODO: idea for loopBegin / loopEnd
|
||||||
|
// if one of [loopBegin,loopEnd] is <= 1, interpret it as normlized
|
||||||
|
// if [loopBegin,loopEnd] is bigger >= 1, interpret it as sample number
|
||||||
|
// this will simplify perfectly looping things, while still keeping the normalized option
|
||||||
|
// the only drawback is that looping between samples 0 and 1 is not possible (which is not real use case)
|
||||||
|
bufferSource.loop = true;
|
||||||
|
bufferSource.loopStart = offset;
|
||||||
|
bufferSource.loopEnd = offset + duration;
|
||||||
|
duration = loop * duration;
|
||||||
|
}*/
|
||||||
|
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
|
||||||
|
bufferSource.connect(envelope);
|
||||||
|
const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox...
|
||||||
|
envelope.connect(out);
|
||||||
|
bufferSource.onended = function () {
|
||||||
|
bufferSource.disconnect();
|
||||||
|
envelope.disconnect();
|
||||||
|
out.disconnect();
|
||||||
|
onended();
|
||||||
|
};
|
||||||
|
const stop = (endTime, playWholeBuffer = clip === undefined) => {
|
||||||
|
let releaseTime = endTime;
|
||||||
|
if (playWholeBuffer) {
|
||||||
|
releaseTime = t + (end - begin) * bufferDuration;
|
||||||
|
}
|
||||||
|
bufferSource.stop(releaseTime + release);
|
||||||
|
releaseEnvelope(releaseTime);
|
||||||
|
};
|
||||||
|
const handle = { node: out, bufferSource, stop };
|
||||||
|
|
||||||
|
// cut groups
|
||||||
|
if (cut !== undefined) {
|
||||||
|
const prev = cutGroups[cut];
|
||||||
|
if (prev) {
|
||||||
|
prev.node.gain.setValueAtTime(1, time);
|
||||||
|
prev.node.gain.linearRampToValueAtTime(0, time + 0.01);
|
||||||
|
}
|
||||||
|
cutGroups[cut] = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
249
packages/superdough/superdough.mjs
Normal file
249
packages/superdough/superdough.mjs
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
superdough.mjs - <short description TODO>
|
||||||
|
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/superdough/superdough.mjs>
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './feedbackdelay.mjs';
|
||||||
|
import './reverb.mjs';
|
||||||
|
import './vowel.mjs';
|
||||||
|
import { clamp } from './util.mjs';
|
||||||
|
import workletsUrl from './worklets.mjs?url';
|
||||||
|
import { getFilter, gainNode } from './helpers.mjs';
|
||||||
|
import { map } from 'nanostores';
|
||||||
|
|
||||||
|
export const soundMap = map();
|
||||||
|
export function registerSound(key, onTrigger, data = {}) {
|
||||||
|
soundMap.setKey(key, { onTrigger, data });
|
||||||
|
}
|
||||||
|
export function getSound(s) {
|
||||||
|
return soundMap.get()[s];
|
||||||
|
}
|
||||||
|
export const resetLoadedSounds = () => soundMap.set({});
|
||||||
|
|
||||||
|
let audioContext;
|
||||||
|
export const getAudioContext = () => {
|
||||||
|
if (!audioContext) {
|
||||||
|
audioContext = new AudioContext();
|
||||||
|
}
|
||||||
|
return audioContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
let destination;
|
||||||
|
const getDestination = () => {
|
||||||
|
const ctx = getAudioContext();
|
||||||
|
if (!destination) {
|
||||||
|
destination = ctx.createGain();
|
||||||
|
destination.connect(ctx.destination);
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const panic = () => {
|
||||||
|
getDestination().gain.linearRampToValueAtTime(0, getAudioContext().currentTime + 0.01);
|
||||||
|
destination = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
let workletsLoading;
|
||||||
|
function loadWorklets() {
|
||||||
|
if (workletsLoading) {
|
||||||
|
return workletsLoading;
|
||||||
|
}
|
||||||
|
workletsLoading = getAudioContext().audioWorklet.addModule(workletsUrl);
|
||||||
|
return workletsLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWorklet(ac, processor, params) {
|
||||||
|
const node = new AudioWorkletNode(ac, processor);
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
node.parameters.get(key).value = value;
|
||||||
|
});
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function should be called on first user interaction (to avoid console warning)
|
||||||
|
export async function initAudio() {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
try {
|
||||||
|
await getAudioContext().resume();
|
||||||
|
await loadWorklets();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('could not load AudioWorklet effects coarse, crush and shape', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initAudioOnFirstClick() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
document.addEventListener('click', async function listener() {
|
||||||
|
await initAudio();
|
||||||
|
resolve();
|
||||||
|
document.removeEventListener('click', listener);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let delays = {};
|
||||||
|
const maxfeedback = 0.98;
|
||||||
|
function getDelay(orbit, delaytime, delayfeedback, t) {
|
||||||
|
if (delayfeedback > maxfeedback) {
|
||||||
|
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
||||||
|
}
|
||||||
|
delayfeedback = clamp(delayfeedback, 0, 0.98);
|
||||||
|
if (!delays[orbit]) {
|
||||||
|
const ac = getAudioContext();
|
||||||
|
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||||
|
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||||
|
dly.connect(getDestination());
|
||||||
|
delays[orbit] = dly;
|
||||||
|
}
|
||||||
|
delays[orbit].delayTime.value !== delaytime && delays[orbit].delayTime.setValueAtTime(delaytime, t);
|
||||||
|
delays[orbit].feedback.value !== delayfeedback && delays[orbit].feedback.setValueAtTime(delayfeedback, t);
|
||||||
|
return delays[orbit];
|
||||||
|
}
|
||||||
|
|
||||||
|
let reverbs = {};
|
||||||
|
function getReverb(orbit, duration = 2) {
|
||||||
|
if (!reverbs[orbit]) {
|
||||||
|
const ac = getAudioContext();
|
||||||
|
const reverb = ac.createReverb(duration);
|
||||||
|
reverb.connect(getDestination());
|
||||||
|
reverbs[orbit] = reverb;
|
||||||
|
}
|
||||||
|
if (reverbs[orbit].duration !== duration) {
|
||||||
|
reverbs[orbit] = reverbs[orbit].setDuration(duration);
|
||||||
|
reverbs[orbit].duration = duration;
|
||||||
|
}
|
||||||
|
return reverbs[orbit];
|
||||||
|
}
|
||||||
|
|
||||||
|
function effectSend(input, effect, wet) {
|
||||||
|
const send = gainNode(wet);
|
||||||
|
input.connect(send);
|
||||||
|
send.connect(effect);
|
||||||
|
return send;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const superdough = async (value, deadline, hapDuration, cps) => {
|
||||||
|
const ac = getAudioContext();
|
||||||
|
if (typeof value !== 'object') {
|
||||||
|
throw new Error(
|
||||||
|
`expected hap.value to be an object, but got "${value}". Hint: append .note() or .s() to the end`,
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate absolute time
|
||||||
|
let t = ac.currentTime + deadline;
|
||||||
|
// destructure
|
||||||
|
let {
|
||||||
|
s = 'triangle',
|
||||||
|
bank,
|
||||||
|
source,
|
||||||
|
gain = 0.8,
|
||||||
|
// low pass
|
||||||
|
cutoff,
|
||||||
|
resonance = 1,
|
||||||
|
// high pass
|
||||||
|
hcutoff,
|
||||||
|
hresonance = 1,
|
||||||
|
// band pass
|
||||||
|
bandf,
|
||||||
|
bandq = 1,
|
||||||
|
//
|
||||||
|
coarse,
|
||||||
|
crush,
|
||||||
|
shape,
|
||||||
|
pan,
|
||||||
|
vowel,
|
||||||
|
delay = 0,
|
||||||
|
delayfeedback = 0.5,
|
||||||
|
delaytime = 0.25,
|
||||||
|
orbit = 1,
|
||||||
|
room,
|
||||||
|
size = 2,
|
||||||
|
velocity = 1,
|
||||||
|
} = value;
|
||||||
|
// const { velocity = 1 } = hap.context;
|
||||||
|
gain *= velocity; // legacy fix for velocity
|
||||||
|
let toDisconnect = []; // audio nodes that will be disconnected when the source has ended
|
||||||
|
const onended = () => {
|
||||||
|
toDisconnect.forEach((n) => n?.disconnect());
|
||||||
|
};
|
||||||
|
if (bank && s) {
|
||||||
|
s = `${bank}_${s}`;
|
||||||
|
}
|
||||||
|
// get source AudioNode
|
||||||
|
let sourceNode;
|
||||||
|
if (source) {
|
||||||
|
sourceNode = source(t, value, hapDuration);
|
||||||
|
} else if (getSound(s)) {
|
||||||
|
const { onTrigger } = getSound(s);
|
||||||
|
const soundHandle = await onTrigger(t, value, onended);
|
||||||
|
if (soundHandle) {
|
||||||
|
sourceNode = soundHandle.node;
|
||||||
|
soundHandle.stop(t + hapDuration);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`sound ${s} not found! Is it loaded?`);
|
||||||
|
}
|
||||||
|
if (!sourceNode) {
|
||||||
|
// if onTrigger does not return anything, we will just silently skip
|
||||||
|
// this can be used for things like speed(0) in the sampler
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ac.currentTime > t) {
|
||||||
|
// logger('[webaudio] skip hap: still loading', ac.currentTime - t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const chain = []; // audio nodes that will be connected to each other sequentially
|
||||||
|
chain.push(sourceNode);
|
||||||
|
|
||||||
|
// gain stage
|
||||||
|
chain.push(gainNode(gain));
|
||||||
|
|
||||||
|
// filters
|
||||||
|
cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance));
|
||||||
|
hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance));
|
||||||
|
bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq));
|
||||||
|
vowel !== undefined && chain.push(ac.createVowelFilter(vowel));
|
||||||
|
|
||||||
|
// effects
|
||||||
|
coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse }));
|
||||||
|
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
||||||
|
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape }));
|
||||||
|
|
||||||
|
// panning
|
||||||
|
if (pan !== undefined) {
|
||||||
|
const panner = ac.createStereoPanner();
|
||||||
|
panner.pan.value = 2 * pan - 1;
|
||||||
|
chain.push(panner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last gain
|
||||||
|
const post = gainNode(1);
|
||||||
|
chain.push(post);
|
||||||
|
post.connect(getDestination());
|
||||||
|
|
||||||
|
// delay
|
||||||
|
let delaySend;
|
||||||
|
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||||
|
const delyNode = getDelay(orbit, delaytime, delayfeedback, t);
|
||||||
|
delaySend = effectSend(post, delyNode, delay);
|
||||||
|
}
|
||||||
|
// reverb
|
||||||
|
let reverbSend;
|
||||||
|
if (room > 0 && size > 0) {
|
||||||
|
const reverbNode = getReverb(orbit, size);
|
||||||
|
reverbSend = effectSend(post, reverbNode, room);
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect chain elements together
|
||||||
|
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
||||||
|
|
||||||
|
// toDisconnect = all the node that should be disconnected in onended callback
|
||||||
|
// this is crucial for performance
|
||||||
|
toDisconnect = chain.concat([delaySend, reverbSend]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const superdoughTrigger = (t, hap, ct, cps) => superdough(hap, t - ct, hap.duration / cps, cps);
|
||||||
44
packages/superdough/synth.mjs
Normal file
44
packages/superdough/synth.mjs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { midiToFreq, noteToMidi } from './util.mjs';
|
||||||
|
import { registerSound } from './superdough.mjs';
|
||||||
|
import { getOscillator, gainNode, getEnvelope } from './helpers.mjs';
|
||||||
|
|
||||||
|
export function registerSynthSounds() {
|
||||||
|
['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => {
|
||||||
|
registerSound(
|
||||||
|
wave,
|
||||||
|
(t, value, onended) => {
|
||||||
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
const { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = value;
|
||||||
|
let { n, note, freq } = value;
|
||||||
|
// with synths, n and note are the same thing
|
||||||
|
n = note || n || 36;
|
||||||
|
if (typeof n === 'string') {
|
||||||
|
n = noteToMidi(n); // e.g. c3 => 48
|
||||||
|
}
|
||||||
|
// get frequency
|
||||||
|
if (!freq && typeof n === 'number') {
|
||||||
|
freq = midiToFreq(n); // + 48);
|
||||||
|
}
|
||||||
|
// maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default)
|
||||||
|
// make oscillator
|
||||||
|
const { node: o, stop } = getOscillator({ t, s: wave, freq });
|
||||||
|
const g = gainNode(0.3);
|
||||||
|
// envelope
|
||||||
|
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
|
||||||
|
o.onended = () => {
|
||||||
|
o.disconnect();
|
||||||
|
g.disconnect();
|
||||||
|
onended();
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
node: o.connect(g).connect(envelope),
|
||||||
|
stop: (releaseTime) => {
|
||||||
|
releaseEnvelope(releaseTime);
|
||||||
|
stop(releaseTime + release);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ type: 'synth', prebake: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
29
packages/superdough/util.mjs
Normal file
29
packages/superdough/util.mjs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// currently duplicate with core util.mjs to skip dependency
|
||||||
|
// TODO: add separate util module?
|
||||||
|
|
||||||
|
export const tokenizeNote = (note) => {
|
||||||
|
if (typeof note !== 'string') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)([0-9]*)$/)?.slice(1) || [];
|
||||||
|
if (!pc) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return [pc, acc, oct ? Number(oct) : undefined];
|
||||||
|
};
|
||||||
|
const chromas = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 };
|
||||||
|
const accs = { '#': 1, b: -1, s: 1, f: -1 };
|
||||||
|
|
||||||
|
export const noteToMidi = (note, defaultOctave = 3) => {
|
||||||
|
const [pc, acc, oct = defaultOctave] = tokenizeNote(note);
|
||||||
|
if (!pc) {
|
||||||
|
throw new Error('not a note: "' + note + '"');
|
||||||
|
}
|
||||||
|
const chroma = chromas[pc.toLowerCase()];
|
||||||
|
const offset = acc?.split('').reduce((o, char) => o + accs[char], 0) || 0;
|
||||||
|
return (Number(oct) + 1) * 12 + chroma + offset;
|
||||||
|
};
|
||||||
|
export const midiToFreq = (n) => {
|
||||||
|
return Math.pow(2, (n - 69) / 12) * 440;
|
||||||
|
};
|
||||||
|
export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||||
19
packages/superdough/vite.config.js
Normal file
19
packages/superdough/vite.config.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import { dependencies } from './package.json';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [],
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: resolve(__dirname, 'superdough.mjs'),
|
||||||
|
formats: ['es', 'cjs'],
|
||||||
|
fileName: (ext) => ({ es: 'superdough.mjs', cjs: 'superdough.cjs' }[ext]),
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: [...Object.keys(dependencies)],
|
||||||
|
},
|
||||||
|
target: 'esnext',
|
||||||
|
},
|
||||||
|
});
|
||||||
38
packages/superdough/vowel.mjs
Normal file
38
packages/superdough/vowel.mjs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// credits to webdirt: https://github.com/dktr0/WebDirt/blob/41342e81d6ad694a2310d491fef7b7e8b0929efe/js-src/Graph.js#L597
|
||||||
|
export var vowelFormant = {
|
||||||
|
a: { freqs: [660, 1120, 2750, 3000, 3350], gains: [1, 0.5012, 0.0708, 0.0631, 0.0126], qs: [80, 90, 120, 130, 140] },
|
||||||
|
e: { freqs: [440, 1800, 2700, 3000, 3300], gains: [1, 0.1995, 0.1259, 0.1, 0.1], qs: [70, 80, 100, 120, 120] },
|
||||||
|
i: { freqs: [270, 1850, 2900, 3350, 3590], gains: [1, 0.0631, 0.0631, 0.0158, 0.0158], qs: [40, 90, 100, 120, 120] },
|
||||||
|
o: { freqs: [430, 820, 2700, 3000, 3300], gains: [1, 0.3162, 0.0501, 0.0794, 0.01995], qs: [40, 80, 100, 120, 120] },
|
||||||
|
u: { freqs: [370, 630, 2750, 3000, 3400], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
|
||||||
|
};
|
||||||
|
if (typeof GainNode !== 'undefined') {
|
||||||
|
class VowelNode extends GainNode {
|
||||||
|
constructor(ac, letter) {
|
||||||
|
super(ac);
|
||||||
|
if (!vowelFormant[letter]) {
|
||||||
|
throw new Error('vowel: unknown vowel ' + letter);
|
||||||
|
}
|
||||||
|
const { gains, qs, freqs } = vowelFormant[letter];
|
||||||
|
const makeupGain = ac.createGain();
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
const gain = ac.createGain();
|
||||||
|
gain.gain.value = gains[i];
|
||||||
|
const filter = ac.createBiquadFilter();
|
||||||
|
filter.type = 'bandpass';
|
||||||
|
filter.Q.value = qs[i];
|
||||||
|
filter.frequency.value = freqs[i];
|
||||||
|
this.connect(filter);
|
||||||
|
filter.connect(gain);
|
||||||
|
gain.connect(makeupGain);
|
||||||
|
}
|
||||||
|
makeupGain.gain.value = 8; // how much makeup gain to add?
|
||||||
|
this.connect = (target) => makeupGain.connect(target);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioContext.prototype.createVowelFilter = function (letter) {
|
||||||
|
return new VowelNode(this, letter);
|
||||||
|
};
|
||||||
|
}
|
||||||
108
packages/superdough/worklets.mjs
Normal file
108
packages/superdough/worklets.mjs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE
|
||||||
|
// all the credit goes to dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js
|
||||||
|
// <3
|
||||||
|
|
||||||
|
class CoarseProcessor extends AudioWorkletProcessor {
|
||||||
|
static get parameterDescriptors() {
|
||||||
|
return [{ name: 'coarse', defaultValue: 1 }];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.notStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
process(inputs, outputs, parameters) {
|
||||||
|
const input = inputs[0];
|
||||||
|
const output = outputs[0];
|
||||||
|
const coarse = parameters.coarse;
|
||||||
|
const blockSize = 128;
|
||||||
|
const hasInput = !(input[0] === undefined);
|
||||||
|
if (hasInput) {
|
||||||
|
this.notStarted = false;
|
||||||
|
output[0][0] = input[0][0];
|
||||||
|
for (let n = 1; n < blockSize; n++) {
|
||||||
|
for (let o = 0; o < output.length; o++) {
|
||||||
|
output[o][n] = n % coarse == 0 ? input[0][n] : output[o][n - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.notStarted || hasInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerProcessor('coarse-processor', CoarseProcessor);
|
||||||
|
|
||||||
|
class CrushProcessor extends AudioWorkletProcessor {
|
||||||
|
static get parameterDescriptors() {
|
||||||
|
return [{ name: 'crush', defaultValue: 0 }];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.notStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
process(inputs, outputs, parameters) {
|
||||||
|
const input = inputs[0];
|
||||||
|
const output = outputs[0];
|
||||||
|
const crush = parameters.crush;
|
||||||
|
const blockSize = 128;
|
||||||
|
const hasInput = !(input[0] === undefined);
|
||||||
|
if (hasInput) {
|
||||||
|
this.notStarted = false;
|
||||||
|
if (crush.length === 1) {
|
||||||
|
const x = Math.pow(2, crush[0] - 1);
|
||||||
|
for (let n = 0; n < blockSize; n++) {
|
||||||
|
const value = Math.round(input[0][n] * x) / x;
|
||||||
|
for (let o = 0; o < output.length; o++) {
|
||||||
|
output[o][n] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let n = 0; n < blockSize; n++) {
|
||||||
|
let x = Math.pow(2, crush[n] - 1);
|
||||||
|
const value = Math.round(input[0][n] * x) / x;
|
||||||
|
for (let o = 0; o < output.length; o++) {
|
||||||
|
output[o][n] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.notStarted || hasInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerProcessor('crush-processor', CrushProcessor);
|
||||||
|
|
||||||
|
class ShapeProcessor extends AudioWorkletProcessor {
|
||||||
|
static get parameterDescriptors() {
|
||||||
|
return [{ name: 'shape', defaultValue: 0 }];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.notStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
process(inputs, outputs, parameters) {
|
||||||
|
const input = inputs[0];
|
||||||
|
const output = outputs[0];
|
||||||
|
const shape0 = parameters.shape[0];
|
||||||
|
const shape1 = shape0 < 1 ? shape0 : 1.0 - 4e-10;
|
||||||
|
const shape = (2.0 * shape1) / (1.0 - shape1);
|
||||||
|
const blockSize = 128;
|
||||||
|
const hasInput = !(input[0] === undefined);
|
||||||
|
if (hasInput) {
|
||||||
|
this.notStarted = false;
|
||||||
|
for (let n = 0; n < blockSize; n++) {
|
||||||
|
const value = ((1 + shape) * input[0][n]) / (1 + shape * Math.abs(input[0][n]));
|
||||||
|
for (let o = 0; o < output.length; o++) {
|
||||||
|
output[o][n] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.notStarted || hasInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerProcessor('shape-processor', ShapeProcessor);
|
||||||
337
pnpm-lock.yaml
generated
337
pnpm-lock.yaml
generated
@ -386,6 +386,26 @@ importers:
|
|||||||
specifier: ^4.3.3
|
specifier: ^4.3.3
|
||||||
version: 4.3.3(@types/node@18.16.3)
|
version: 4.3.3(@types/node@18.16.3)
|
||||||
|
|
||||||
|
packages/superdough:
|
||||||
|
dependencies:
|
||||||
|
nanostores:
|
||||||
|
specifier: ^0.8.1
|
||||||
|
version: 0.8.1
|
||||||
|
devDependencies:
|
||||||
|
vite:
|
||||||
|
specifier: ^4.3.3
|
||||||
|
version: 4.3.3(@types/node@18.16.3)
|
||||||
|
|
||||||
|
packages/superdough/example:
|
||||||
|
dependencies:
|
||||||
|
superdough:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:..
|
||||||
|
devDependencies:
|
||||||
|
vite:
|
||||||
|
specifier: ^4.4.5
|
||||||
|
version: 4.4.5(@types/node@18.16.3)
|
||||||
|
|
||||||
packages/tonal:
|
packages/tonal:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@strudel.cycles/core':
|
'@strudel.cycles/core':
|
||||||
@ -498,7 +518,7 @@ importers:
|
|||||||
version: 4.17.0
|
version: 4.17.0
|
||||||
'@astrojs/mdx':
|
'@astrojs/mdx':
|
||||||
specifier: ^0.19.0
|
specifier: ^0.19.0
|
||||||
version: 0.19.0(astro@2.3.2)(rollup@3.21.0)
|
version: 0.19.0(astro@2.3.2)(rollup@3.28.0)
|
||||||
'@astrojs/react':
|
'@astrojs/react':
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1(@types/react-dom@18.2.1)(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
|
version: 2.1.1(@types/react-dom@18.2.1)(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -628,7 +648,7 @@ importers:
|
|||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
vite-plugin-pwa:
|
vite-plugin-pwa:
|
||||||
specifier: ^0.14.7
|
specifier: ^0.14.7
|
||||||
version: 0.14.7(vite@4.3.3)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
version: 0.14.7(vite@4.4.5)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
||||||
workbox-window:
|
workbox-window:
|
||||||
specifier: ^6.5.4
|
specifier: ^6.5.4
|
||||||
version: 6.5.4
|
version: 6.5.4
|
||||||
@ -849,14 +869,14 @@ packages:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
/@astrojs/mdx@0.19.0(astro@2.3.2)(rollup@3.21.0):
|
/@astrojs/mdx@0.19.0(astro@2.3.2)(rollup@3.28.0):
|
||||||
resolution: {integrity: sha512-McFpMV+npinIEKnY5t9hsdzLd76g78GgIRUPxem2OeXPNB8xr2pNS28GeU0+6Pn5STnB+sgcyyeqXLgzauOlMQ==}
|
resolution: {integrity: sha512-McFpMV+npinIEKnY5t9hsdzLd76g78GgIRUPxem2OeXPNB8xr2pNS28GeU0+6Pn5STnB+sgcyyeqXLgzauOlMQ==}
|
||||||
engines: {node: '>=16.12.0'}
|
engines: {node: '>=16.12.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/markdown-remark': 2.1.4(astro@2.3.2)
|
'@astrojs/markdown-remark': 2.1.4(astro@2.3.2)
|
||||||
'@astrojs/prism': 2.1.1
|
'@astrojs/prism': 2.1.1
|
||||||
'@mdx-js/mdx': 2.3.0
|
'@mdx-js/mdx': 2.3.0
|
||||||
'@mdx-js/rollup': 2.3.0(rollup@3.21.0)
|
'@mdx-js/rollup': 2.3.0(rollup@3.28.0)
|
||||||
acorn: 8.8.2
|
acorn: 8.8.2
|
||||||
es-module-lexer: 1.2.1
|
es-module-lexer: 1.2.1
|
||||||
estree-util-visit: 1.2.1
|
estree-util-visit: 1.2.1
|
||||||
@ -2438,6 +2458,15 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/android-arm64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/android-arm@0.17.18:
|
/@esbuild/android-arm@0.17.18:
|
||||||
@ -2446,6 +2475,15 @@ packages:
|
|||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/android-arm@0.18.20:
|
||||||
|
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/android-x64@0.17.18:
|
/@esbuild/android-x64@0.17.18:
|
||||||
@ -2454,6 +2492,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/android-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/darwin-arm64@0.17.18:
|
/@esbuild/darwin-arm64@0.17.18:
|
||||||
@ -2462,6 +2509,15 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/darwin-arm64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/darwin-x64@0.17.18:
|
/@esbuild/darwin-x64@0.17.18:
|
||||||
@ -2470,6 +2526,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/darwin-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/freebsd-arm64@0.17.18:
|
/@esbuild/freebsd-arm64@0.17.18:
|
||||||
@ -2478,6 +2543,15 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/freebsd-arm64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/freebsd-x64@0.17.18:
|
/@esbuild/freebsd-x64@0.17.18:
|
||||||
@ -2486,6 +2560,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/freebsd-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-arm64@0.17.18:
|
/@esbuild/linux-arm64@0.17.18:
|
||||||
@ -2494,6 +2577,15 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-arm64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-arm@0.17.18:
|
/@esbuild/linux-arm@0.17.18:
|
||||||
@ -2502,6 +2594,15 @@ packages:
|
|||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-arm@0.18.20:
|
||||||
|
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-ia32@0.17.18:
|
/@esbuild/linux-ia32@0.17.18:
|
||||||
@ -2510,6 +2611,15 @@ packages:
|
|||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-ia32@0.18.20:
|
||||||
|
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-loong64@0.17.18:
|
/@esbuild/linux-loong64@0.17.18:
|
||||||
@ -2518,6 +2628,15 @@ packages:
|
|||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-loong64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-mips64el@0.17.18:
|
/@esbuild/linux-mips64el@0.17.18:
|
||||||
@ -2526,6 +2645,15 @@ packages:
|
|||||||
cpu: [mips64el]
|
cpu: [mips64el]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-mips64el@0.18.20:
|
||||||
|
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-ppc64@0.17.18:
|
/@esbuild/linux-ppc64@0.17.18:
|
||||||
@ -2534,6 +2662,15 @@ packages:
|
|||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-ppc64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-riscv64@0.17.18:
|
/@esbuild/linux-riscv64@0.17.18:
|
||||||
@ -2542,6 +2679,15 @@ packages:
|
|||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-riscv64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-s390x@0.17.18:
|
/@esbuild/linux-s390x@0.17.18:
|
||||||
@ -2550,6 +2696,15 @@ packages:
|
|||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-s390x@0.18.20:
|
||||||
|
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-x64@0.17.18:
|
/@esbuild/linux-x64@0.17.18:
|
||||||
@ -2558,6 +2713,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/netbsd-x64@0.17.18:
|
/@esbuild/netbsd-x64@0.17.18:
|
||||||
@ -2566,6 +2730,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [netbsd]
|
os: [netbsd]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/netbsd-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/openbsd-x64@0.17.18:
|
/@esbuild/openbsd-x64@0.17.18:
|
||||||
@ -2574,6 +2747,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [openbsd]
|
os: [openbsd]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/openbsd-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/sunos-x64@0.17.18:
|
/@esbuild/sunos-x64@0.17.18:
|
||||||
@ -2582,6 +2764,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [sunos]
|
os: [sunos]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/sunos-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/win32-arm64@0.17.18:
|
/@esbuild/win32-arm64@0.17.18:
|
||||||
@ -2590,6 +2781,15 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/win32-arm64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/win32-ia32@0.17.18:
|
/@esbuild/win32-ia32@0.17.18:
|
||||||
@ -2598,6 +2798,15 @@ packages:
|
|||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/win32-ia32@0.18.20:
|
||||||
|
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@esbuild/win32-x64@0.17.18:
|
/@esbuild/win32-x64@0.17.18:
|
||||||
@ -2606,6 +2815,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/win32-x64@0.18.20:
|
||||||
|
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.39.0):
|
/@eslint-community/eslint-utils@4.4.0(eslint@8.39.0):
|
||||||
@ -2927,14 +3145,14 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@mdx-js/rollup@2.3.0(rollup@3.21.0):
|
/@mdx-js/rollup@2.3.0(rollup@3.28.0):
|
||||||
resolution: {integrity: sha512-wLvRfJS/M4UmdqTd+WoaySEE7q4BIejYf1xAHXYvtT1du/1Tl/z2450Gg2+Hu7fh05KwRRiehiTP9Yc/Dtn0fA==}
|
resolution: {integrity: sha512-wLvRfJS/M4UmdqTd+WoaySEE7q4BIejYf1xAHXYvtT1du/1Tl/z2450Gg2+Hu7fh05KwRRiehiTP9Yc/Dtn0fA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
rollup: '>=2'
|
rollup: '>=2'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@mdx-js/mdx': 2.3.0
|
'@mdx-js/mdx': 2.3.0
|
||||||
'@rollup/pluginutils': 5.0.2(rollup@3.21.0)
|
'@rollup/pluginutils': 5.0.2(rollup@3.28.0)
|
||||||
rollup: 3.21.0
|
rollup: 3.28.0
|
||||||
source-map: 0.7.4
|
source-map: 0.7.4
|
||||||
vfile: 5.3.6
|
vfile: 5.3.6
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -3612,7 +3830,7 @@ packages:
|
|||||||
rollup: 3.12.0
|
rollup: 3.12.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@rollup/pluginutils@5.0.2(rollup@3.21.0):
|
/@rollup/pluginutils@5.0.2(rollup@3.28.0):
|
||||||
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
|
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3624,7 +3842,7 @@ packages:
|
|||||||
'@types/estree': 1.0.0
|
'@types/estree': 1.0.0
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
rollup: 3.21.0
|
rollup: 3.28.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sigstore/protobuf-specs@0.1.0:
|
/@sigstore/protobuf-specs@0.1.0:
|
||||||
@ -4554,7 +4772,7 @@ packages:
|
|||||||
vite-plugin-pwa: ^0.14.0
|
vite-plugin-pwa: ^0.14.0
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: 2.3.2(@types/node@18.16.3)
|
astro: 2.3.2(@types/node@18.16.3)
|
||||||
vite-plugin-pwa: 0.14.7(vite@4.3.3)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
vite-plugin-pwa: 0.14.7(vite@4.4.5)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vitejs/plugin-react@4.0.0(vite@4.3.3):
|
/@vitejs/plugin-react@4.0.0(vite@4.3.3):
|
||||||
@ -5036,13 +5254,14 @@ packages:
|
|||||||
typescript: 4.9.4
|
typescript: 4.9.4
|
||||||
unist-util-visit: 4.1.2
|
unist-util-visit: 4.1.2
|
||||||
vfile: 5.3.6
|
vfile: 5.3.6
|
||||||
vite: 4.3.3(@types/node@18.16.3)
|
vite: 4.4.5(@types/node@18.16.3)
|
||||||
vitefu: 0.2.4(vite@4.3.3)
|
vitefu: 0.2.4(vite@4.4.5)
|
||||||
yargs-parser: 21.1.1
|
yargs-parser: 21.1.1
|
||||||
zod: 3.21.4
|
zod: 3.21.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
|
- lightningcss
|
||||||
- sass
|
- sass
|
||||||
- stylus
|
- stylus
|
||||||
- sugarss
|
- sugarss
|
||||||
@ -6478,6 +6697,36 @@ packages:
|
|||||||
'@esbuild/win32-arm64': 0.17.18
|
'@esbuild/win32-arm64': 0.17.18
|
||||||
'@esbuild/win32-ia32': 0.17.18
|
'@esbuild/win32-ia32': 0.17.18
|
||||||
'@esbuild/win32-x64': 0.17.18
|
'@esbuild/win32-x64': 0.17.18
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/esbuild@0.18.20:
|
||||||
|
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/android-arm': 0.18.20
|
||||||
|
'@esbuild/android-arm64': 0.18.20
|
||||||
|
'@esbuild/android-x64': 0.18.20
|
||||||
|
'@esbuild/darwin-arm64': 0.18.20
|
||||||
|
'@esbuild/darwin-x64': 0.18.20
|
||||||
|
'@esbuild/freebsd-arm64': 0.18.20
|
||||||
|
'@esbuild/freebsd-x64': 0.18.20
|
||||||
|
'@esbuild/linux-arm': 0.18.20
|
||||||
|
'@esbuild/linux-arm64': 0.18.20
|
||||||
|
'@esbuild/linux-ia32': 0.18.20
|
||||||
|
'@esbuild/linux-loong64': 0.18.20
|
||||||
|
'@esbuild/linux-mips64el': 0.18.20
|
||||||
|
'@esbuild/linux-ppc64': 0.18.20
|
||||||
|
'@esbuild/linux-riscv64': 0.18.20
|
||||||
|
'@esbuild/linux-s390x': 0.18.20
|
||||||
|
'@esbuild/linux-x64': 0.18.20
|
||||||
|
'@esbuild/netbsd-x64': 0.18.20
|
||||||
|
'@esbuild/openbsd-x64': 0.18.20
|
||||||
|
'@esbuild/sunos-x64': 0.18.20
|
||||||
|
'@esbuild/win32-arm64': 0.18.20
|
||||||
|
'@esbuild/win32-ia32': 0.18.20
|
||||||
|
'@esbuild/win32-x64': 0.18.20
|
||||||
|
|
||||||
/escalade@3.1.1:
|
/escalade@3.1.1:
|
||||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||||
@ -10854,6 +11103,14 @@ packages:
|
|||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
|
|
||||||
|
/postcss@8.4.27:
|
||||||
|
resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==}
|
||||||
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
dependencies:
|
||||||
|
nanoid: 3.3.6
|
||||||
|
picocolors: 1.0.0
|
||||||
|
source-map-js: 1.0.2
|
||||||
|
|
||||||
/prebuild-install@7.1.1:
|
/prebuild-install@7.1.1:
|
||||||
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
|
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -11658,6 +11915,14 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/rollup@3.28.0:
|
||||||
|
resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==}
|
||||||
|
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
/run-async@2.4.1:
|
/run-async@2.4.1:
|
||||||
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
|
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
|
||||||
@ -13063,10 +13328,11 @@ packages:
|
|||||||
mlly: 1.4.0
|
mlly: 1.4.0
|
||||||
pathe: 1.1.1
|
pathe: 1.1.1
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 4.3.3(@types/node@18.16.3)
|
vite: 4.4.5(@types/node@18.16.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
|
- lightningcss
|
||||||
- sass
|
- sass
|
||||||
- stylus
|
- stylus
|
||||||
- sugarss
|
- sugarss
|
||||||
@ -13074,7 +13340,7 @@ packages:
|
|||||||
- terser
|
- terser
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-pwa@0.14.7(vite@4.3.3)(workbox-build@6.5.4)(workbox-window@6.5.4):
|
/vite-plugin-pwa@0.14.7(vite@4.4.5)(workbox-build@6.5.4)(workbox-window@6.5.4):
|
||||||
resolution: {integrity: sha512-dNJaf0fYOWncmjxv9HiSa2xrSjipjff7IkYE5oIUJ2x5HKu3cXgA8LRgzOwTc5MhwyFYRSU0xyN0Phbx3NsQYw==}
|
resolution: {integrity: sha512-dNJaf0fYOWncmjxv9HiSa2xrSjipjff7IkYE5oIUJ2x5HKu3cXgA8LRgzOwTc5MhwyFYRSU0xyN0Phbx3NsQYw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^3.1.0 || ^4.0.0
|
vite: ^3.1.0 || ^4.0.0
|
||||||
@ -13086,7 +13352,7 @@ packages:
|
|||||||
fast-glob: 3.2.12
|
fast-glob: 3.2.12
|
||||||
pretty-bytes: 6.1.0
|
pretty-bytes: 6.1.0
|
||||||
rollup: 3.12.0
|
rollup: 3.12.0
|
||||||
vite: 4.3.3(@types/node@18.16.3)
|
vite: 4.4.5(@types/node@18.16.3)
|
||||||
workbox-build: 6.5.4
|
workbox-build: 6.5.4
|
||||||
workbox-window: 6.5.4
|
workbox-window: 6.5.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -13124,8 +13390,44 @@ packages:
|
|||||||
rollup: 3.21.0
|
rollup: 3.21.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vitefu@0.2.4(vite@4.3.3):
|
/vite@4.4.5(@types/node@18.16.3):
|
||||||
|
resolution: {integrity: sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==}
|
||||||
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>= 14'
|
||||||
|
less: '*'
|
||||||
|
lightningcss: ^1.21.0
|
||||||
|
sass: '*'
|
||||||
|
stylus: '*'
|
||||||
|
sugarss: '*'
|
||||||
|
terser: ^5.4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
lightningcss:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
sugarss:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 18.16.3
|
||||||
|
esbuild: 0.18.20
|
||||||
|
postcss: 8.4.27
|
||||||
|
rollup: 3.28.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
|
/vitefu@0.2.4(vite@4.4.5):
|
||||||
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
|
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^3.0.0 || ^4.0.0
|
vite: ^3.0.0 || ^4.0.0
|
||||||
@ -13133,7 +13435,7 @@ packages:
|
|||||||
vite:
|
vite:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 4.3.3(@types/node@18.16.3)
|
vite: 4.4.5(@types/node@18.16.3)
|
||||||
|
|
||||||
/vitest@0.33.0(@vitest/ui@0.28.0):
|
/vitest@0.33.0(@vitest/ui@0.28.0):
|
||||||
resolution: {integrity: sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ==}
|
resolution: {integrity: sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ==}
|
||||||
@ -13193,6 +13495,7 @@ packages:
|
|||||||
why-is-node-running: 2.2.2
|
why-is-node-running: 2.2.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- less
|
- less
|
||||||
|
- lightningcss
|
||||||
- sass
|
- sass
|
||||||
- stylus
|
- stylus
|
||||||
- sugarss
|
- sugarss
|
||||||
|
|||||||
@ -6,3 +6,4 @@ packages:
|
|||||||
- "packages/core/examples/vite-vanilla-repl-cm6"
|
- "packages/core/examples/vite-vanilla-repl-cm6"
|
||||||
- "packages/react/examples/nano-repl"
|
- "packages/react/examples/nano-repl"
|
||||||
- "packages/web/examples/repl-example"
|
- "packages/web/examples/repl-example"
|
||||||
|
- "packages/superdough/example"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user