mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 13:13:10 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c8f6302b7 |
@@ -1,37 +0,0 @@
|
|||||||
name: Build and Deploy to beta (warm.strudel.cc)
|
|
||||||
|
|
||||||
on: [workflow_dispatch]
|
|
||||||
|
|
||||||
# Allow one concurrent deployment
|
|
||||||
concurrency:
|
|
||||||
group: "pages"
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: docker
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: 9.12.2
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
# cache: "pnpm"
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: pnpm build
|
|
||||||
|
|
||||||
- name: Deploy
|
|
||||||
run: |
|
|
||||||
eval $(ssh-agent -s)
|
|
||||||
echo "$SSH_PRIVATE_KEY" | ssh-add -
|
|
||||||
apt update && apt install -y rsync
|
|
||||||
mkdir ~/.ssh
|
|
||||||
ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts
|
|
||||||
rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy/warm.strudel.cc
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Build and Deploy to live (strudel.cc)
|
name: Build and Deploy
|
||||||
|
|
||||||
on: [workflow_dispatch]
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ jobs:
|
|||||||
apt update && apt install -y rsync
|
apt update && apt install -y rsync
|
||||||
mkdir ~/.ssh
|
mkdir ~/.ssh
|
||||||
ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts
|
ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts
|
||||||
rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy/strudel.cc
|
rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy
|
||||||
-14
@@ -45,20 +45,6 @@ tidal-drum-machines
|
|||||||
webaudiofontdata
|
webaudiofontdata
|
||||||
src-tauri/target
|
src-tauri/target
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### BEGIN Visual Studio Code ###
|
|
||||||
# Blanket, recursive exclude for .vscode directory and files
|
|
||||||
.vscode/**/*
|
|
||||||
# Unexclude specific files and directories within .vscode
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
### END Visual Studio Code ###
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# BEGIN JetBrains -> END JetBrains
|
# BEGIN JetBrains -> END JetBrains
|
||||||
|
|
||||||
# for JetBrains IDE users, e.g. WebStorm. Source: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# for JetBrains IDE users, e.g. WebStorm. Source: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import jsdoc from '../../doc.json';
|
import jsdoc from '../../doc.json';
|
||||||
import { autocompletion } from '@codemirror/autocomplete';
|
import { autocompletion } from '@codemirror/autocomplete';
|
||||||
import { h } from './html';
|
import { h } from './html';
|
||||||
import { Scale } from '@tonaljs/tonal';
|
|
||||||
import { soundMap } from 'superdough';
|
|
||||||
import { complex } from '@strudel/tonal';
|
|
||||||
|
|
||||||
const escapeHtml = (str) => {
|
const escapeHtml = (str) => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
@@ -78,51 +75,6 @@ const isValidDoc = (doc) => {
|
|||||||
const hasExcludedTags = (doc) =>
|
const hasExcludedTags = (doc) =>
|
||||||
['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag));
|
['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag));
|
||||||
|
|
||||||
export function bankCompletions() {
|
|
||||||
const soundDict = soundMap.get();
|
|
||||||
const banks = new Set();
|
|
||||||
for (const key of Object.keys(soundDict)) {
|
|
||||||
const [bank, suffix] = key.split('_');
|
|
||||||
if (suffix && bank) banks.add(bank);
|
|
||||||
}
|
|
||||||
return Array.from(banks)
|
|
||||||
.sort()
|
|
||||||
.map((name) => ({ label: name, type: 'bank' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to get all scale names from Tonal
|
|
||||||
let scaleCompletions = [];
|
|
||||||
try {
|
|
||||||
scaleCompletions = (Scale.names ? Scale.names() : []).map((name) => ({ label: name, type: 'scale' }));
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('[autocomplete] Could not load scale names from Tonal:', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid mode values for voicing
|
|
||||||
const modeCompletions = [
|
|
||||||
{ label: 'below', type: 'mode' },
|
|
||||||
{ label: 'above', type: 'mode' },
|
|
||||||
{ label: 'duck', type: 'mode' },
|
|
||||||
{ label: 'root', type: 'mode' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Valid chord symbols from ireal dictionary plus empty string for major triads
|
|
||||||
const chordSymbols = ['', ...Object.keys(complex)].sort();
|
|
||||||
const chordSymbolCompletions = chordSymbols.map((symbol) => {
|
|
||||||
if (symbol === '') {
|
|
||||||
return {
|
|
||||||
label: 'major',
|
|
||||||
apply: '',
|
|
||||||
type: 'chord-symbol',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
label: symbol,
|
|
||||||
apply: symbol,
|
|
||||||
type: 'chord-symbol',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getSynonymDoc = (doc, synonym) => {
|
export const getSynonymDoc = (doc, synonym) => {
|
||||||
const synonyms = doc.synonyms || [];
|
const synonyms = doc.synonyms || [];
|
||||||
const docLabel = getDocLabel(doc);
|
const docLabel = getDocLabel(doc);
|
||||||
@@ -161,299 +113,19 @@ const jsdocCompletions = (() => {
|
|||||||
return completions;
|
return completions;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// --- Handler functions for each context ---
|
|
||||||
const pitchNames = [
|
|
||||||
'C',
|
|
||||||
'C#',
|
|
||||||
'Db',
|
|
||||||
'D',
|
|
||||||
'D#',
|
|
||||||
'Eb',
|
|
||||||
'E',
|
|
||||||
'E#',
|
|
||||||
'Fb',
|
|
||||||
'F',
|
|
||||||
'F#',
|
|
||||||
'Gb',
|
|
||||||
'G',
|
|
||||||
'G#',
|
|
||||||
'Ab',
|
|
||||||
'A',
|
|
||||||
'A#',
|
|
||||||
'Bb',
|
|
||||||
'B',
|
|
||||||
'B#',
|
|
||||||
'Cb',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Cached regex patterns for scaleHandler
|
|
||||||
const SCALE_NO_QUOTES_REGEX = /scale\(\s*$/;
|
|
||||||
const SCALE_AFTER_COLON_REGEX = /scale\(\s*['"][^'"]*:[^'"]*$/;
|
|
||||||
const SCALE_PRE_COLON_REGEX = /scale\(\s*['"][^'"]*$/;
|
|
||||||
const SCALE_PITCH_MATCH_REGEX = /([A-Ga-g][#b]*)?$/;
|
|
||||||
const SCALE_SPACES_TO_COLON_REGEX = /\s+/g;
|
|
||||||
|
|
||||||
function scaleHandler(context) {
|
|
||||||
// First check for scale context without quotes - block with empty completions
|
|
||||||
let scaleNoQuotesContext = context.matchBefore(SCALE_NO_QUOTES_REGEX);
|
|
||||||
if (scaleNoQuotesContext) {
|
|
||||||
return {
|
|
||||||
from: scaleNoQuotesContext.to,
|
|
||||||
options: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for after-colon context first (more specific)
|
|
||||||
let scaleAfterColonContext = context.matchBefore(SCALE_AFTER_COLON_REGEX);
|
|
||||||
if (scaleAfterColonContext) {
|
|
||||||
const text = scaleAfterColonContext.text;
|
|
||||||
const colonIdx = text.lastIndexOf(':');
|
|
||||||
if (colonIdx !== -1) {
|
|
||||||
const fragment = text.slice(colonIdx + 1);
|
|
||||||
const filteredScales = scaleCompletions.filter((s) => s.label.startsWith(fragment));
|
|
||||||
const options = filteredScales.map((s) => ({
|
|
||||||
...s,
|
|
||||||
apply: s.label.replace(SCALE_SPACES_TO_COLON_REGEX, ':'),
|
|
||||||
}));
|
|
||||||
const from = scaleAfterColonContext.from + colonIdx + 1;
|
|
||||||
return {
|
|
||||||
from,
|
|
||||||
options,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for pre-colon context
|
|
||||||
let scalePreColonContext = context.matchBefore(SCALE_PRE_COLON_REGEX);
|
|
||||||
if (scalePreColonContext) {
|
|
||||||
if (!scalePreColonContext.text.includes(':')) {
|
|
||||||
if (context.explicit) {
|
|
||||||
const text = scalePreColonContext.text;
|
|
||||||
const match = text.match(SCALE_PITCH_MATCH_REGEX);
|
|
||||||
const fragment = match ? match[0] : '';
|
|
||||||
const filtered = pitchNames.filter((p) => p.toLowerCase().startsWith(fragment.toLowerCase()));
|
|
||||||
const from = scalePreColonContext.to - fragment.length;
|
|
||||||
const options = filtered.map((p) => ({ label: p, type: 'pitch' }));
|
|
||||||
return { from, options };
|
|
||||||
} else {
|
|
||||||
return { from: scalePreColonContext.to, options: [] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cached regex patterns for soundHandler
|
|
||||||
const SOUND_NO_QUOTES_REGEX = /(s|sound)\(\s*$/;
|
|
||||||
const SOUND_WITH_QUOTES_REGEX = /(s|sound)\(\s*['"][^'"]*$/;
|
|
||||||
const SOUND_FRAGMENT_MATCH_REGEX = /(?:[\s[{(<])([\w]*)$/;
|
|
||||||
|
|
||||||
function soundHandler(context) {
|
|
||||||
// First check for sound context without quotes - block with empty completions
|
|
||||||
let soundNoQuotesContext = context.matchBefore(SOUND_NO_QUOTES_REGEX);
|
|
||||||
if (soundNoQuotesContext) {
|
|
||||||
return {
|
|
||||||
from: soundNoQuotesContext.to,
|
|
||||||
options: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for sound context with quotes - provide completions
|
|
||||||
let soundContext = context.matchBefore(SOUND_WITH_QUOTES_REGEX);
|
|
||||||
if (!soundContext) return null;
|
|
||||||
|
|
||||||
const text = soundContext.text;
|
|
||||||
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
|
|
||||||
if (quoteIdx === -1) return null;
|
|
||||||
const inside = text.slice(quoteIdx + 1);
|
|
||||||
const fragMatch = inside.match(SOUND_FRAGMENT_MATCH_REGEX);
|
|
||||||
const fragment = fragMatch ? fragMatch[1] : inside;
|
|
||||||
const soundNames = Object.keys(soundMap.get()).sort();
|
|
||||||
const filteredSounds = soundNames.filter((name) => name.includes(fragment));
|
|
||||||
let options = filteredSounds.map((name) => ({ label: name, type: 'sound' }));
|
|
||||||
const from = soundContext.to - fragment.length;
|
|
||||||
return {
|
|
||||||
from,
|
|
||||||
options,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cached regex patterns for bankHandler
|
|
||||||
const BANK_NO_QUOTES_REGEX = /bank\(\s*$/;
|
|
||||||
const BANK_WITH_QUOTES_REGEX = /bank\(\s*['"][^'"]*$/;
|
|
||||||
|
|
||||||
function bankHandler(context) {
|
|
||||||
// First check for bank context without quotes - block with empty completions
|
|
||||||
let bankNoQuotesContext = context.matchBefore(BANK_NO_QUOTES_REGEX);
|
|
||||||
if (bankNoQuotesContext) {
|
|
||||||
return {
|
|
||||||
from: bankNoQuotesContext.to,
|
|
||||||
options: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for bank context with quotes - provide completions
|
|
||||||
let bankMatch = context.matchBefore(BANK_WITH_QUOTES_REGEX);
|
|
||||||
if (!bankMatch) return null;
|
|
||||||
|
|
||||||
const text = bankMatch.text;
|
|
||||||
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
|
|
||||||
if (quoteIdx === -1) return null;
|
|
||||||
const inside = text.slice(quoteIdx + 1);
|
|
||||||
const fragment = inside;
|
|
||||||
let banks = bankCompletions();
|
|
||||||
const filteredBanks = banks.filter((b) => b.label.startsWith(fragment));
|
|
||||||
const from = bankMatch.to - fragment.length;
|
|
||||||
return {
|
|
||||||
from,
|
|
||||||
options: filteredBanks,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cached regex patterns for modeHandler
|
|
||||||
const MODE_NO_QUOTES_REGEX = /mode\(\s*$/;
|
|
||||||
const MODE_AFTER_COLON_REGEX = /mode\(\s*['"][^'"]*:[^'"]*$/;
|
|
||||||
const MODE_PRE_COLON_REGEX = /mode\(\s*['"][^'"]*$/;
|
|
||||||
const MODE_FRAGMENT_MATCH_REGEX = /(?:[\s[{(<])([\w:]*)$/;
|
|
||||||
|
|
||||||
function modeHandler(context) {
|
|
||||||
// First check for mode context without quotes - block with empty completions
|
|
||||||
let modeNoQuotesContext = context.matchBefore(MODE_NO_QUOTES_REGEX);
|
|
||||||
if (modeNoQuotesContext) {
|
|
||||||
return {
|
|
||||||
from: modeNoQuotesContext.to,
|
|
||||||
options: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for after-colon context first (more specific)
|
|
||||||
let modeAfterColonContext = context.matchBefore(MODE_AFTER_COLON_REGEX);
|
|
||||||
if (modeAfterColonContext) {
|
|
||||||
const text = modeAfterColonContext.text;
|
|
||||||
const colonIdx = text.lastIndexOf(':');
|
|
||||||
if (colonIdx !== -1) {
|
|
||||||
const fragment = text.slice(colonIdx + 1);
|
|
||||||
// For anchor after colon, we can suggest pitch names
|
|
||||||
const filtered = pitchNames.filter((p) => p.toLowerCase().startsWith(fragment.toLowerCase()));
|
|
||||||
const options = filtered.map((p) => ({ label: p, type: 'pitch' }));
|
|
||||||
const from = modeAfterColonContext.from + colonIdx + 1;
|
|
||||||
return {
|
|
||||||
from,
|
|
||||||
options,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for pre-colon context
|
|
||||||
let modeContext = context.matchBefore(MODE_PRE_COLON_REGEX);
|
|
||||||
if (!modeContext) return null;
|
|
||||||
|
|
||||||
const text = modeContext.text;
|
|
||||||
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
|
|
||||||
if (quoteIdx === -1) return null;
|
|
||||||
const inside = text.slice(quoteIdx + 1);
|
|
||||||
const fragMatch = inside.match(MODE_FRAGMENT_MATCH_REGEX);
|
|
||||||
const fragment = fragMatch ? fragMatch[1] : inside;
|
|
||||||
const filteredModes = modeCompletions.filter((m) => m.label.startsWith(fragment));
|
|
||||||
const from = modeContext.to - fragment.length;
|
|
||||||
return {
|
|
||||||
from,
|
|
||||||
options: filteredModes,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cached regex patterns for chordHandler
|
|
||||||
const CHORD_NO_QUOTES_REGEX = /chord\(\s*$/;
|
|
||||||
const CHORD_WITH_QUOTES_REGEX = /chord\(\s*['"][^'"]*$/;
|
|
||||||
const CHORD_FRAGMENT_MATCH_REGEX = /(?:[\s[{(<])([\w#b+^:-]*)$/;
|
|
||||||
|
|
||||||
function chordHandler(context) {
|
|
||||||
// First check for chord context without quotes - block with empty completions
|
|
||||||
let chordNoQuotesContext = context.matchBefore(CHORD_NO_QUOTES_REGEX);
|
|
||||||
if (chordNoQuotesContext) {
|
|
||||||
return {
|
|
||||||
from: chordNoQuotesContext.to,
|
|
||||||
options: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for chord context with quotes - provide completions
|
|
||||||
let chordContext = context.matchBefore(CHORD_WITH_QUOTES_REGEX);
|
|
||||||
if (!chordContext) return null;
|
|
||||||
|
|
||||||
const text = chordContext.text;
|
|
||||||
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
|
|
||||||
if (quoteIdx === -1) return null;
|
|
||||||
const inside = text.slice(quoteIdx + 1);
|
|
||||||
|
|
||||||
// Use same fragment matching as sound/mode for expressions like "<G Am>"
|
|
||||||
const fragMatch = inside.match(CHORD_FRAGMENT_MATCH_REGEX);
|
|
||||||
const fragment = fragMatch ? fragMatch[1] : inside;
|
|
||||||
|
|
||||||
// Check if fragment contains any pitch name at start (for root + symbol)
|
|
||||||
let rootMatch = null;
|
|
||||||
let symbolFragment = fragment;
|
|
||||||
for (const pitch of pitchNames) {
|
|
||||||
if (fragment.toLowerCase().startsWith(pitch.toLowerCase())) {
|
|
||||||
rootMatch = pitch;
|
|
||||||
symbolFragment = fragment.slice(pitch.length);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rootMatch) {
|
|
||||||
// We have a root, now complete chord symbols
|
|
||||||
const filteredSymbols = chordSymbolCompletions.filter((s) =>
|
|
||||||
s.label.toLowerCase().startsWith(symbolFragment.toLowerCase()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create completions that replace the entire chord, not just the symbol part
|
|
||||||
const options = filteredSymbols;
|
|
||||||
|
|
||||||
const from = chordContext.to - symbolFragment.length;
|
|
||||||
return { from, options };
|
|
||||||
} else {
|
|
||||||
// No root yet, complete with pitch names
|
|
||||||
const filteredPitches = pitchNames.filter((p) => p.toLowerCase().startsWith(fragment.toLowerCase()));
|
|
||||||
const options = filteredPitches.map((p) => ({ label: p, type: 'pitch' }));
|
|
||||||
const from = chordContext.to - fragment.length;
|
|
||||||
return { from, options };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cached regex patterns for fallbackHandler
|
|
||||||
const FALLBACK_WORD_REGEX = /\w*/;
|
|
||||||
|
|
||||||
function fallbackHandler(context) {
|
|
||||||
const word = context.matchBefore(FALLBACK_WORD_REGEX);
|
|
||||||
if (word && word.from === word.to && !context.explicit) return null;
|
|
||||||
if (word) {
|
|
||||||
return {
|
|
||||||
from: word.from,
|
|
||||||
options: jsdocCompletions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlers = [
|
|
||||||
soundHandler,
|
|
||||||
bankHandler,
|
|
||||||
chordHandler,
|
|
||||||
scaleHandler,
|
|
||||||
modeHandler,
|
|
||||||
// this handler *must* be last
|
|
||||||
fallbackHandler,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const strudelAutocomplete = (context) => {
|
export const strudelAutocomplete = (context) => {
|
||||||
for (const handler of handlers) {
|
const word = context.matchBefore(/\w*/);
|
||||||
const result = handler(context);
|
if (word.from === word.to && !context.explicit) return null;
|
||||||
if (result) {
|
|
||||||
return result;
|
return {
|
||||||
}
|
from: word.from,
|
||||||
}
|
options: jsdocCompletions,
|
||||||
return null;
|
/* options: [
|
||||||
|
{ label: 'match', type: 'keyword' },
|
||||||
|
{ label: 'hello', type: 'variable', info: '(World)' },
|
||||||
|
{ label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' },
|
||||||
|
], */
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isAutoCompletionEnabled = (enabled) =>
|
export const isAutoCompletionEnabled = (enabled) =>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/codemirror",
|
"name": "@strudel/codemirror",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "Codemirror Extensions for Strudel",
|
"description": "Codemirror Extensions for Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -46,11 +46,8 @@
|
|||||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||||
"@strudel/core": "workspace:*",
|
"@strudel/core": "workspace:*",
|
||||||
"@strudel/draw": "workspace:*",
|
"@strudel/draw": "workspace:*",
|
||||||
"@strudel/tonal": "workspace:*",
|
|
||||||
"@strudel/transpiler": "workspace:*",
|
"@strudel/transpiler": "workspace:*",
|
||||||
"@tonaljs/tonal": "^4.10.0",
|
"nanostores": "^0.11.3"
|
||||||
"nanostores": "^0.11.3",
|
|
||||||
"superdough": "workspace:*"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11"
|
||||||
|
|||||||
@@ -1887,52 +1887,19 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size',
|
|||||||
export const { shape } = registerControl(['shape', 'shapevol']);
|
export const { shape } = registerControl(['shape', 'shapevol']);
|
||||||
/**
|
/**
|
||||||
* Wave shaping distortion. CAUTION: it can get loud.
|
* Wave shaping distortion. CAUTION: it can get loud.
|
||||||
* Second option in optional array syntax (ex: ".9:.5") applies a postgain to the output. Third option sets the waveshaping type.
|
* Second option in optional array syntax (ex: ".9:.5") applies a postgain to the output.
|
||||||
* Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;)
|
* Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;)
|
||||||
*
|
*
|
||||||
* @name distort
|
* @name distort
|
||||||
* @synonyms dist
|
* @synonyms dist
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
* @param {number | Pattern} distortion
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
* @param {number | string | Pattern} type type of distortion to apply
|
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd [~ bd] sd,hh*8").distort("<0 2 3 10:.5>")
|
* s("bd sd [~ bd] sd,hh*8").distort("<0 2 3 10:.5>")
|
||||||
* @example
|
* @example
|
||||||
* note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).distort("8:.4")
|
* note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).distort("8:.4")
|
||||||
* @example
|
|
||||||
* s("bd:4*4").bank("tr808").distort("3:0.5:diode")
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { distort, dist } = registerControl(['distort', 'distortvol', 'distorttype'], 'dist');
|
export const { distort, dist } = registerControl(['distort', 'distortvol'], 'dist');
|
||||||
|
|
||||||
/**
|
|
||||||
* Postgain for waveshaping distortion.
|
|
||||||
*
|
|
||||||
* @name distortvol
|
|
||||||
* @synonyms distvol
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
* @example
|
|
||||||
* s("bd*4").bank("tr909").distort(2).distortvol(0.8)
|
|
||||||
*/
|
|
||||||
export const { distortvol } = registerControl('distortvol', 'distvol');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of waveshaping distortion to apply.
|
|
||||||
*
|
|
||||||
* @name distorttype
|
|
||||||
* @synonyms disttype
|
|
||||||
* @param {number | string | Pattern} type type of distortion to apply
|
|
||||||
* @example
|
|
||||||
* s("bd*4").bank("tr909").distort(2).distorttype("<0 1 2>")
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* s("sine").note("F1*2").release(1)
|
|
||||||
* .penv(24).pdecay(0.05)
|
|
||||||
* .distort(rand.range(1, 8))
|
|
||||||
* .distorttype("<fold chebyshev scurve diode asym sinefold>")
|
|
||||||
*/
|
|
||||||
export const { distorttype } = registerControl('distorttype', 'disttype');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamics Compressor. The params are `compressor("threshold:ratio:knee:attack:release")`
|
* Dynamics Compressor. The params are `compressor("threshold:ratio:knee:attack:release")`
|
||||||
* More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties)
|
* More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties)
|
||||||
@@ -2320,24 +2287,6 @@ export const { miditouch } = registerControl('miditouch');
|
|||||||
// TODO: what is this?
|
// TODO: what is this?
|
||||||
export const { polyTouch } = registerControl('polyTouch');
|
export const { polyTouch } = registerControl('polyTouch');
|
||||||
|
|
||||||
/**
|
|
||||||
* The host to send open sound control messages to. Requires running the OSC bridge.
|
|
||||||
* @name oschost
|
|
||||||
* @param {string | Pattern} oschost e.g. 'localhost'
|
|
||||||
* @example
|
|
||||||
* note("c4").oschost('127.0.0.1').oscport(57120).osc();
|
|
||||||
*/
|
|
||||||
export const { oschost } = registerControl('oschost');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port to send open sound control messages to. Requires running the OSC bridge.
|
|
||||||
* @name oscport
|
|
||||||
* @param {number | Pattern} oscport e.g. 57120
|
|
||||||
* @example
|
|
||||||
* note("c4").oschost('127.0.0.1').oscport(57120).osc();
|
|
||||||
*/
|
|
||||||
export const { oscport } = registerControl('oscport');
|
|
||||||
|
|
||||||
export const getControlName = (alias) => {
|
export const getControlName = (alias) => {
|
||||||
if (controlAlias.has(alias)) {
|
if (controlAlias.has(alias)) {
|
||||||
return controlAlias.get(alias);
|
return controlAlias.get(alias);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/core",
|
"name": "@strudel/core",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Port of Tidal Cycles to JavaScript",
|
"description": "Port of Tidal Cycles to JavaScript",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+17
-107
@@ -963,26 +963,13 @@ export const arp = register(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
* Takes a time duration followed by one or more patterns, and shifts the given patterns in time, so they are
|
|
||||||
* distributed equally over the given time duration. They are then combined with the pattern 'weave' is called on, after it has been stretched out (i.e. slowed down by) the time duration.
|
|
||||||
* @name weave
|
|
||||||
* @memberof Pattern
|
|
||||||
* @example pan(saw).weave(4, s("bd(3,8)"), s("~ sd"))
|
|
||||||
* @example n("0 1 2 3 4 5 6 7").weave(8, s("bd(3,8)"), s("~ sd"))
|
|
||||||
|
|
||||||
addToPrototype('weave', function (t, ...pats) {
|
|
||||||
return this.weaveWith(t, ...pats.map((x) => set.out(x)));
|
|
||||||
});
|
|
||||||
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
* Like 'weave', but accepts functions rather than patterns, which are applied to the pattern.
|
* Like 'weave', but accepts functions rather than patterns, which are applied to the pattern.
|
||||||
* @name weaveWith
|
* @name weaveWith
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
|
*/
|
||||||
|
|
||||||
addToPrototype('weaveWith', function (t, ...funcs) {
|
export const weaveWith = register('weaveWith', function (t, funcs, pat) {
|
||||||
const pat = this;
|
|
||||||
const l = funcs.length;
|
const l = funcs.length;
|
||||||
t = Fraction(t);
|
t = Fraction(t);
|
||||||
if (l == 0) {
|
if (l == 0) {
|
||||||
@@ -990,7 +977,21 @@ addToPrototype('weaveWith', function (t, ...funcs) {
|
|||||||
}
|
}
|
||||||
return stack(...funcs.map((func, i) => pat.inside(t, func).early(Fraction(i).div(l))))._slow(t);
|
return stack(...funcs.map((func, i) => pat.inside(t, func).early(Fraction(i).div(l))))._slow(t);
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
/*
|
||||||
|
* Takes a time duration followed by one or more patterns, and shifts the given patterns in time, so they are
|
||||||
|
* distributed equally over the given time duration. They are then combined with the pattern 'weave' is called on, after it has been stretched out (i.e. slowed down by) the time duration.
|
||||||
|
* @name weave
|
||||||
|
* @memberof Pattern
|
||||||
|
* @example pan(saw).weave(4, [s("bd(3,8)"), s("~ sd")])
|
||||||
|
* @example n("0 1 2 3 4 5 6 7").weave(8, [s("bd(3,8)"), s("~ sd")])
|
||||||
|
*/
|
||||||
|
export const weave = register('weave', function (t, pats, pat) {
|
||||||
|
return pat.weaveWith(
|
||||||
|
t,
|
||||||
|
pats.map((x) => (y) => y.set.out(x)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// compose matrix functions
|
// compose matrix functions
|
||||||
@@ -2998,24 +2999,6 @@ export const extend = stepRegister('extend', function (factor, pat) {
|
|||||||
return pat.fast(factor).expand(factor);
|
return pat.fast(factor).expand(factor);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* *Experimental*
|
|
||||||
*
|
|
||||||
* `replicate` is similar to `fast` in that it increases its density, but it also increases the step count
|
|
||||||
* accordingly. So `stepcat("a b".replicate(2), "c d")` would be the same as `"a b a b c d"`, whereas
|
|
||||||
* `stepcat("a b".fast(2), "c d")` would be the same as `"[a b] [a b] c d"`.
|
|
||||||
*
|
|
||||||
* TODO: find out how this function differs from extend
|
|
||||||
* @example
|
|
||||||
* stepcat(
|
|
||||||
* sound("bd bd - cp").replicate(2),
|
|
||||||
* sound("bd - sd -")
|
|
||||||
* ).pace(8)
|
|
||||||
*/
|
|
||||||
export const replicate = stepRegister('replicate', function (factor, pat) {
|
|
||||||
return pat.repeatCycles(factor).fast(factor).expand(factor);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* *Experimental*
|
* *Experimental*
|
||||||
*
|
*
|
||||||
@@ -3551,76 +3534,3 @@ export const morph = (frompat, topat, bypat) => {
|
|||||||
bypat = reify(bypat);
|
bypat = reify(bypat);
|
||||||
return frompat.innerBind((from) => topat.innerBind((to) => bypat.innerBind((by) => _morph(from, to, by))));
|
return frompat.innerBind((from) => topat.innerBind((to) => bypat.innerBind((by) => _morph(from, to, by))));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Soft-clipping distortion
|
|
||||||
*
|
|
||||||
* @name soft
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Hard-clipping distortion
|
|
||||||
*
|
|
||||||
* @name hard
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Cubic polynomial distortion
|
|
||||||
*
|
|
||||||
* @name cubic
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Diode-emulating distortion
|
|
||||||
*
|
|
||||||
* @name diode
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Asymmetrical diode distortion
|
|
||||||
*
|
|
||||||
* @name asym
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Wavefolding distortion
|
|
||||||
*
|
|
||||||
* @name fold
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Wavefolding distortion composed with sinusoid
|
|
||||||
*
|
|
||||||
* @name sinefold
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Distortion via Chebyshev polynomials
|
|
||||||
*
|
|
||||||
* @name chebyshev
|
|
||||||
* @param {number | Pattern} distortion amount of distortion to apply
|
|
||||||
* @param {number | Pattern} volume linear postgain of the distortion
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const distAlgoNames = ['scurve', 'soft', 'hard', 'cubic', 'diode', 'asym', 'fold', 'sinefold', 'chebyshev'];
|
|
||||||
for (const name of distAlgoNames) {
|
|
||||||
// Add aliases for distortion algorithms
|
|
||||||
Pattern.prototype[name] = function (args) {
|
|
||||||
const argsPat = reify(args).fmap((v) => (Array.isArray(v) ? [...v, name] : [v, 1, name]));
|
|
||||||
return this.distort(argsPat);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/csound",
|
"name": "@strudel/csound",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "csound bindings for strudel",
|
"description": "csound bindings for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -84,18 +84,9 @@ Pattern.prototype.onPaint = function (painter) {
|
|||||||
state.controls.painters = [];
|
state.controls.painters = [];
|
||||||
}
|
}
|
||||||
state.controls.painters.push(painter);
|
state.controls.painters.push(painter);
|
||||||
return state;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO - Why isn't this pure deep copy not working?
|
|
||||||
// Pattern.prototype.onPaint = function (painter) {
|
|
||||||
// return this.withState((state) => {
|
|
||||||
// const painters = state.controls.painters ? [...state.controls.painters, painter] : [painter];
|
|
||||||
// return new State(state.span, { ...state.controls, painters });
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
Pattern.prototype.getPainters = function () {
|
Pattern.prototype.getPainters = function () {
|
||||||
let painters = [];
|
let painters = [];
|
||||||
this.queryArc(0, 0, { painters });
|
this.queryArc(0, 0, { painters });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/draw",
|
"name": "@strudel/draw",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Helpers for drawing with Strudel",
|
"description": "Helpers for drawing with Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/gamepad",
|
"name": "@strudel/gamepad",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Gamepad Inputs for strudel",
|
"description": "Gamepad Inputs for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/hydra",
|
"name": "@strudel/hydra",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Hydra integration for strudel",
|
"description": "Hydra integration for strudel",
|
||||||
"main": "hydra.mjs",
|
"main": "hydra.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/midi",
|
"name": "@strudel/midi",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "Midi API for strudel",
|
"description": "Midi API for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/mini",
|
"name": "@strudel/mini",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Mini notation for strudel",
|
"description": "Mini notation for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
slow,
|
slow,
|
||||||
seq,
|
seq,
|
||||||
stepcat,
|
stepcat,
|
||||||
replicate,
|
extend,
|
||||||
expand,
|
expand,
|
||||||
pace,
|
pace,
|
||||||
chooseIn,
|
chooseIn,
|
||||||
@@ -36,7 +36,7 @@ lib.square = (...args) => stepcat(...args).setSteps(1);
|
|||||||
lib.angle = (...args) => stepcat(...args).pace(1);
|
lib.angle = (...args) => stepcat(...args).pace(1);
|
||||||
lib['*'] = fast;
|
lib['*'] = fast;
|
||||||
lib['/'] = slow;
|
lib['/'] = slow;
|
||||||
lib['!'] = replicate;
|
lib['!'] = extend;
|
||||||
lib['@'] = expand;
|
lib['@'] = expand;
|
||||||
lib['%'] = pace;
|
lib['%'] = pace;
|
||||||
lib['?'] = degradeBy; // todo: default 0.5 not working..
|
lib['?'] = degradeBy; // todo: default 0.5 not working..
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/mondo",
|
"name": "@strudel/mondo",
|
||||||
"version": "1.1.5",
|
"version": "1.1.4",
|
||||||
"description": "mondo notation for strudel",
|
"description": "mondo notation for strudel",
|
||||||
"main": "mondough.mjs",
|
"main": "mondough.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/motion",
|
"name": "@strudel/motion",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "DeviceMotion API for strudel",
|
"description": "DeviceMotion API for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/mqtt",
|
"name": "@strudel/mqtt",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "MQTT API for strudel",
|
"description": "MQTT API for strudel",
|
||||||
"main": "mqtt.mjs",
|
"main": "mqtt.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+16
-17
@@ -4,6 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/
|
|||||||
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/>.
|
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 OSC from 'osc-js';
|
||||||
|
|
||||||
import { logger, parseNumeral, register, isNote, noteToMidi, ClockCollator } from '@strudel/core';
|
import { logger, parseNumeral, register, isNote, noteToMidi, ClockCollator } from '@strudel/core';
|
||||||
|
|
||||||
let connection; // Promise<OSC>
|
let connection; // Promise<OSC>
|
||||||
@@ -11,18 +13,19 @@ function connect() {
|
|||||||
if (!connection) {
|
if (!connection) {
|
||||||
// make sure this runs only once
|
// make sure this runs only once
|
||||||
connection = new Promise((resolve, reject) => {
|
connection = new Promise((resolve, reject) => {
|
||||||
const ws = new WebSocket('ws://localhost:8080');
|
const osc = new OSC();
|
||||||
ws.addEventListener('open', (event) => {
|
osc.open();
|
||||||
logger(`[osc] websocket connected`);
|
osc.on('open', () => {
|
||||||
resolve(ws);
|
const url = osc.options?.plugin?.socket?.url;
|
||||||
|
logger(`[osc] connected${url ? ` to ${url}` : ''}`);
|
||||||
|
resolve(osc);
|
||||||
});
|
});
|
||||||
ws.addEventListener('close', (event) => {
|
osc.on('close', () => {
|
||||||
logger(`[osc] websocket closed`);
|
|
||||||
connection = undefined; // allows new connection afterwards
|
connection = undefined; // allows new connection afterwards
|
||||||
console.log('[osc] disconnected');
|
console.log('[osc] disconnected');
|
||||||
reject('OSC connection closed');
|
reject('OSC connection closed');
|
||||||
});
|
});
|
||||||
ws.addEventListener('error', (err) => reject(err));
|
osc.on('error', (err) => reject(err));
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
connection = undefined;
|
connection = undefined;
|
||||||
throw new Error('Could not connect to OSC server. Is it running?');
|
throw new Error('Could not connect to OSC server. Is it running?');
|
||||||
@@ -58,19 +61,15 @@ export function parseControlsFromHap(hap, cps) {
|
|||||||
const collator = new ClockCollator({});
|
const collator = new ClockCollator({});
|
||||||
|
|
||||||
export async function oscTrigger(hap, currentTime, cps = 1, targetTime) {
|
export async function oscTrigger(hap, currentTime, cps = 1, targetTime) {
|
||||||
const ws = await connect();
|
const osc = await connect();
|
||||||
const controls = parseControlsFromHap(hap, cps);
|
const controls = parseControlsFromHap(hap, cps);
|
||||||
const keyvals = Object.entries(controls).flat();
|
const keyvals = Object.entries(controls).flat();
|
||||||
const ts = collator.calculateTimestamp(currentTime, targetTime) * 1000;
|
|
||||||
const msg = { address: '/dirt/play', args: keyvals, timestamp: ts };
|
|
||||||
|
|
||||||
if ('oschost' in hap.value) {
|
const ts = Math.round(collator.calculateTimestamp(currentTime, targetTime) * 1000);
|
||||||
msg['host'] = hap.value['oschost'];
|
const message = new OSC.Message('/dirt/play', ...keyvals);
|
||||||
}
|
const bundle = new OSC.Bundle([message], ts);
|
||||||
if ('oscport' in hap.value) {
|
bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60
|
||||||
msg['port'] = hap.value['oscport'];
|
osc.send(bundle);
|
||||||
}
|
|
||||||
ws.send(JSON.stringify(msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/osc",
|
"name": "@strudel/osc",
|
||||||
"version": "1.3.0",
|
"version": "1.2.10",
|
||||||
"description": "OSC messaging for strudel",
|
"description": "OSC messaging for strudel",
|
||||||
"main": "osc.mjs",
|
"main": "osc.mjs",
|
||||||
"bin": "./server.js",
|
"bin": "./server.js",
|
||||||
@@ -38,8 +38,7 @@
|
|||||||
"homepage": "https://codeberg.org/uzu/strudel#readme",
|
"homepage": "https://codeberg.org/uzu/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/core": "workspace:*",
|
"@strudel/core": "workspace:*",
|
||||||
"osc": "^2.4.5",
|
"osc-js": "^2.4.1"
|
||||||
"ws": "^8.18.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
|
|||||||
Executable → Regular
+58
-47
@@ -6,59 +6,70 @@ Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/
|
|||||||
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/>.
|
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 OSC from 'osc-js';
|
import OSC from 'osc-js';
|
||||||
|
|
||||||
import { WebSocketServer } from 'ws';
|
const args = process.argv.slice(2);
|
||||||
import osc from 'osc';
|
function getArgValue(flag) {
|
||||||
|
const i = args.indexOf(flag);
|
||||||
|
if (i !== -1) {
|
||||||
|
const nextIsFlag = args[i + 1]?.startsWith('--') ?? true;
|
||||||
|
if (nextIsFlag) return true;
|
||||||
|
return args[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const WS_PORT = 8080; // WebSocket server port
|
let udpClientPort = Number(getArgValue('--port')) || 57120;
|
||||||
const OSC_REMOTE_IP = '127.0.0.1';
|
let debug = Number(getArgValue('--debug')) || 0;
|
||||||
const OSC_REMOTE_PORT = 57120;
|
|
||||||
|
|
||||||
const udpPort = new osc.UDPPort({
|
const config = {
|
||||||
localAddress: '0.0.0.0',
|
receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client
|
||||||
localPort: 0,
|
udpServer: {
|
||||||
remoteAddress: OSC_REMOTE_IP,
|
host: 'localhost', // @param {string} Hostname of udp server to bind to
|
||||||
remotePort: OSC_REMOTE_PORT,
|
port: 57121, // @param {number} Port of udp client for messaging
|
||||||
});
|
// enabling the following line will receive tidal messages:
|
||||||
|
// port: 57120, // @param {number} Port of udp client for messaging
|
||||||
|
exclusive: false, // @param {boolean} Exclusive flag
|
||||||
|
},
|
||||||
|
udpClient: {
|
||||||
|
host: 'localhost', // @param {string} Hostname of udp client for messaging
|
||||||
|
port: udpClientPort, // @param {number} Port of udp client for messaging
|
||||||
|
},
|
||||||
|
wsServer: {
|
||||||
|
host: 'localhost', // @param {string} Hostname of WebSocket server
|
||||||
|
port: 8080, // @param {number} Port of WebSocket server
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
udpPort.open();
|
const osc = new OSC({ plugin: new OSC.BridgePlugin(config) });
|
||||||
console.log(`[Sending OSC] ${OSC_REMOTE_IP}:${OSC_REMOTE_PORT}`);
|
|
||||||
|
|
||||||
udpPort.on('error', (e) => {
|
if (debug) {
|
||||||
console.log('Error: ', e);
|
osc.on('*', (message) => {
|
||||||
});
|
const { address, args } = message;
|
||||||
|
let str = '';
|
||||||
const wss = new WebSocketServer({ port: WS_PORT });
|
for (let i = 0; i < args.length; i += 2) {
|
||||||
console.log(`[Listening WS] ws://localhost:${WS_PORT}`);
|
str += `${args[i]}: ${args[i + 1]} `;
|
||||||
|
|
||||||
wss.on('connection', (ws) => {
|
|
||||||
console.log('New WebSocket connection');
|
|
||||||
|
|
||||||
ws.on('message', (message) => {
|
|
||||||
let osc_host = '127.0.0.1';
|
|
||||||
let osc_port = 57120;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(message);
|
|
||||||
if ('host' in data) {
|
|
||||||
osc_host = data['host'];
|
|
||||||
}
|
|
||||||
if ('port' in data) {
|
|
||||||
osc_port = data['port'];
|
|
||||||
}
|
|
||||||
let msg = { address: data['address'], args: data['args'] };
|
|
||||||
if ('timestamp' in data) {
|
|
||||||
msg = { timeTag: osc.timeTag(0, data['timestamp']), packets: [msg] };
|
|
||||||
}
|
|
||||||
|
|
||||||
udpPort.send(msg, osc_host, osc_port);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error parsing message:', err);
|
|
||||||
}
|
}
|
||||||
|
console.log(`${address} ${str}`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ws.on('close', () => {
|
osc.on('error', (message) => {
|
||||||
console.log('WebSocket connection closed');
|
if (message.toString().includes('EADDRINUSE')) {
|
||||||
});
|
console.log(`------ ERROR -------
|
||||||
|
a server is already running on port 57121! to stop it:
|
||||||
|
1. run "lsof -ti :57121 | xargs kill -9" (macos / linux)
|
||||||
|
2. re-run the osc server
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
osc.open();
|
||||||
|
|
||||||
|
console.log('osc client running on port', config.udpClient.port);
|
||||||
|
console.log('osc server running on port', config.udpServer.port);
|
||||||
|
console.log('websocket server running on port', config.wsServer.port);
|
||||||
|
if (debug) {
|
||||||
|
console.log('debug logs enabled. incoming messages will appear below');
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/repl",
|
"name": "@strudel/repl",
|
||||||
"version": "1.2.7",
|
"version": "1.2.6",
|
||||||
"description": "Strudel REPL as a Web Component",
|
"description": "Strudel REPL as a Web Component",
|
||||||
"module": "index.mjs",
|
"module": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -20,13 +20,10 @@ export async function prebake() {
|
|||||||
// import('@strudel/osc'),
|
// import('@strudel/osc'),
|
||||||
);
|
);
|
||||||
// load samples
|
// load samples
|
||||||
const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main';
|
const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main/';
|
||||||
|
|
||||||
// TODO: move this onto the strudel repo
|
// TODO: move this onto the strudel repo
|
||||||
const ts = 'https://raw.githubusercontent.com/todepond/samples/main';
|
const ts = 'https://raw.githubusercontent.com/todepond/samples/main/';
|
||||||
|
|
||||||
const tc = 'https://raw.githubusercontent.com/tidalcycles/uzu-drumkit/main';
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
modulesLoading,
|
modulesLoading,
|
||||||
registerSynthSounds(),
|
registerSynthSounds(),
|
||||||
@@ -39,9 +36,9 @@ export async function prebake() {
|
|||||||
samples(`${ds}/tidal-drum-machines.json`),
|
samples(`${ds}/tidal-drum-machines.json`),
|
||||||
samples(`${ds}/piano.json`),
|
samples(`${ds}/piano.json`),
|
||||||
samples(`${ds}/Dirt-Samples.json`),
|
samples(`${ds}/Dirt-Samples.json`),
|
||||||
|
samples(`${ds}/uzu-drumkit.json`),
|
||||||
samples(`${ds}/vcsl.json`),
|
samples(`${ds}/vcsl.json`),
|
||||||
samples(`${ds}/mridangam.json`),
|
samples(`${ds}/mridangam.json`),
|
||||||
samples(`${tc}/strudel.json`),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
aliasBank(`${ts}/tidal-drum-machines-alias.json`);
|
aliasBank(`${ts}/tidal-drum-machines-alias.json`);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/serial",
|
"name": "@strudel/serial",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Webserial API for strudel",
|
"description": "Webserial API for strudel",
|
||||||
"main": "serial.mjs",
|
"main": "serial.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/soundfonts",
|
"name": "@strudel/soundfonts",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "Soundsfont support for strudel",
|
"description": "Soundsfont support for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ samples('github:tidalcycles/dirt-samples')
|
|||||||
|
|
||||||
The format is `github:<user>/<repo>/<branch>`.
|
The format is `github:<user>/<repo>/<branch>`.
|
||||||
|
|
||||||
If `<repo>` and `<branch>` are not specified, they will default to `samples` and `main` respectively.
|
|
||||||
It expects a `strudel.json` file to be present at the root of the given repository, which declares the sample paths in the repo.
|
It expects a `strudel.json` file to be present at the root of the given repository, which declares the sample paths in the repo.
|
||||||
|
|
||||||
The format is also expected to be the same as explained above.
|
The format is also expected to be the same as explained above.
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
let audioContext;
|
|
||||||
|
|
||||||
export const setDefaultAudioContext = () => {
|
|
||||||
audioContext = new AudioContext();
|
|
||||||
return audioContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAudioContext = () => {
|
|
||||||
if (!audioContext) {
|
|
||||||
return setDefaultAudioContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
return audioContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getAudioContextCurrentTime() {
|
|
||||||
return getAudioContext().currentTime;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAudioContext } from './audioContext.mjs';
|
import { getAudioContext } from './superdough.mjs';
|
||||||
|
|
||||||
let worklet;
|
let worklet;
|
||||||
export async function dspWorklet(ac, code) {
|
export async function dspWorklet(ac, code) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { getAudioContext } from './audioContext.mjs';
|
import { getAudioContext } from './superdough.mjs';
|
||||||
import { clamp, nanFallback, midiToFreq, noteToMidi } from './util.mjs';
|
import { clamp, nanFallback, midiToFreq, noteToMidi } from './util.mjs';
|
||||||
import { getNoiseBuffer } from './noise.mjs';
|
import { getNoiseBuffer } from './noise.mjs';
|
||||||
import { logger } from './logger.mjs';
|
|
||||||
|
|
||||||
export const noises = ['pink', 'white', 'brown', 'crackle'];
|
export const noises = ['pink', 'white', 'brown', 'crackle'];
|
||||||
|
|
||||||
@@ -382,102 +381,6 @@ export function applyFM(param, value, begin) {
|
|||||||
return { stop };
|
return { stop };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saturation curves
|
|
||||||
|
|
||||||
const __squash = (x) => x / (1 + x); // [0, inf) to [0, 1)
|
|
||||||
const _mod = (n, m) => ((n % m) + m) % m;
|
|
||||||
|
|
||||||
const _scurve = (x, k) => ((1 + k) * x) / (1 + k * Math.abs(x));
|
|
||||||
const _soft = (x, k) => Math.tanh(x * (1 + k));
|
|
||||||
const _hard = (x, k) => clamp((1 + k) * x, -1, 1);
|
|
||||||
|
|
||||||
const _fold = (x, k) => {
|
|
||||||
// Closed form folding for audio rate
|
|
||||||
let y = (1 + 0.5 * k) * x;
|
|
||||||
const window = _mod(y + 1, 4);
|
|
||||||
return 1 - Math.abs(window - 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
const _sineFold = (x, k) => Math.sin((Math.PI / 2) * _fold(x, k));
|
|
||||||
|
|
||||||
const _cubic = (x, k) => {
|
|
||||||
const t = __squash(Math.log1p(k));
|
|
||||||
const cubic = (x - (t / 3) * x * x * x) / (1 - t / 3); // normalized to go from (-1, 1)
|
|
||||||
return _soft(cubic, k);
|
|
||||||
};
|
|
||||||
|
|
||||||
const _diode = (x, k, asym = false) => {
|
|
||||||
const g = 1 + 2 * k; // gain
|
|
||||||
const t = __squash(Math.log1p(k));
|
|
||||||
const bias = 0.07 * t;
|
|
||||||
const pos = _soft(x + bias, 2 * k);
|
|
||||||
const neg = _soft(asym ? bias : -x + bias, 2 * k);
|
|
||||||
const y = pos - neg;
|
|
||||||
// We divide by the derivative at 0 so that the distortion is roughly
|
|
||||||
// the identity map near 0 => small values are preserved and undistorted
|
|
||||||
const sech = 1 / Math.cosh(g * bias);
|
|
||||||
const sech2 = sech * sech; // derivative of soft (i.e. tanh) is sech^2
|
|
||||||
const denom = Math.max(1e-8, (asym ? 1 : 2) * g * sech2); // g from chain rule; 2 if both pos/neg have x
|
|
||||||
return _soft(y / denom, k);
|
|
||||||
};
|
|
||||||
|
|
||||||
const _asym = (x, k) => _diode(x, k, true);
|
|
||||||
|
|
||||||
const _chebyshev = (x, k) => {
|
|
||||||
const kl = 10 * Math.log1p(k);
|
|
||||||
let tnm1 = 1;
|
|
||||||
let tnm2 = x;
|
|
||||||
let tn;
|
|
||||||
let y = 0;
|
|
||||||
for (let i = 1; i < 64; i++) {
|
|
||||||
if (i < 2) {
|
|
||||||
// Already set inital conditions
|
|
||||||
y += i == 0 ? tnm1 : tnm2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tn = 2 * x * tnm1 - tnm2; // https://en.wikipedia.org/wiki/Chebyshev_polynomials#Recurrence_definition
|
|
||||||
tnm2 = tnm1;
|
|
||||||
tnm1 = tn;
|
|
||||||
if (i % 2 === 0) {
|
|
||||||
y += Math.min((1.3 * kl) / i, 2) * tn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Soft clip
|
|
||||||
return _soft(y, kl / 20);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const distortionAlgorithms = {
|
|
||||||
scurve: _scurve,
|
|
||||||
soft: _soft,
|
|
||||||
hard: _hard,
|
|
||||||
cubic: _cubic,
|
|
||||||
diode: _diode,
|
|
||||||
asym: _asym,
|
|
||||||
fold: _fold,
|
|
||||||
sinefold: _sineFold,
|
|
||||||
chebyshev: _chebyshev,
|
|
||||||
};
|
|
||||||
const _algoNames = Object.freeze(Object.keys(distortionAlgorithms));
|
|
||||||
|
|
||||||
export const getDistortionAlgorithm = (algo) => {
|
|
||||||
let index = algo;
|
|
||||||
if (typeof algo === 'string') {
|
|
||||||
index = _algoNames.indexOf(algo);
|
|
||||||
if (index === -1) {
|
|
||||||
logger(`[superdough] Could not find waveshaping algorithm ${algo}.
|
|
||||||
Available options are ${_algoNames.join(', ')}.
|
|
||||||
Defaulting to ${_algoNames[0]}.`);
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const name = _algoNames[index % _algoNames.length]; // allow for wrapping if algo was a number
|
|
||||||
return distortionAlgorithms[name];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDistortion = (distort, postgain, algorithm) => {
|
|
||||||
return getWorklet(getAudioContext(), 'distort-processor', { distort, postgain }, { processorOptions: { algorithm } });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getFrequencyFromValue = (value, defaultNote = 36) => {
|
export const getFrequencyFromValue = (value, defaultNote = 36) => {
|
||||||
let { note, freq } = value;
|
let { note, freq } = value;
|
||||||
note = note || defaultNote;
|
note = note || defaultNote;
|
||||||
|
|||||||
@@ -11,5 +11,4 @@ export * from './synth.mjs';
|
|||||||
export * from './zzfx.mjs';
|
export * from './zzfx.mjs';
|
||||||
export * from './logger.mjs';
|
export * from './logger.mjs';
|
||||||
export * from './dspworklet.mjs';
|
export * from './dspworklet.mjs';
|
||||||
export * from './audioContext.mjs';
|
|
||||||
export * from './wavetable.mjs';
|
export * from './wavetable.mjs';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { drywet } from './helpers.mjs';
|
import { drywet } from './helpers.mjs';
|
||||||
import { getAudioContext } from './audioContext.mjs';
|
import { getAudioContext } from './superdough.mjs';
|
||||||
|
|
||||||
let noiseCache = {};
|
let noiseCache = {};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "superdough",
|
"name": "superdough",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
|
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { getCommonSampleInfo } from './util.mjs';
|
import { noteToMidi, valueToMidi, getSoundIndex, getCommonSampleInfo } from './util.mjs';
|
||||||
import { registerSound, registerWaveTable } from './index.mjs';
|
import { getAudioContext, registerSound, registerWaveTable } from './index.mjs';
|
||||||
import { getAudioContext } from './audioContext.mjs';
|
|
||||||
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
|
|
||||||
@@ -121,18 +120,13 @@ function githubPath(base, subpath = '') {
|
|||||||
if (!base.startsWith('github:')) {
|
if (!base.startsWith('github:')) {
|
||||||
throw new Error('expected "github:" at the start of pseudoUrl');
|
throw new Error('expected "github:" at the start of pseudoUrl');
|
||||||
}
|
}
|
||||||
let path = base.slice('github:'.length);
|
let [_, path] = base.split('github:');
|
||||||
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||||
|
if (path.split('/').length === 2) {
|
||||||
let components = path.split('/');
|
// assume main as default branch if none set
|
||||||
let user = components[0];
|
path += '/main';
|
||||||
let repo = components.length >= 2 ? components[1] : 'samples';
|
}
|
||||||
let branch = components.length >= 3 ? components[2] : 'main';
|
return `https://raw.githubusercontent.com/${path}/${subpath}`;
|
||||||
let other = components.slice(3);
|
|
||||||
other.push(subpath ? subpath : '');
|
|
||||||
other = other.join('/');
|
|
||||||
|
|
||||||
return `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${other}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
|
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ import './reverb.mjs';
|
|||||||
import './vowel.mjs';
|
import './vowel.mjs';
|
||||||
import { nanFallback, _mod, cycleToSeconds } from './util.mjs';
|
import { nanFallback, _mod, cycleToSeconds } from './util.mjs';
|
||||||
import workletsUrl from './worklets.mjs?audioworklet';
|
import workletsUrl from './worklets.mjs?audioworklet';
|
||||||
import { createFilter, gainNode, getCompressor, getDistortion, getLfo, getWorklet, effectSend } from './helpers.mjs';
|
import { createFilter, gainNode, getCompressor, getLfo, getWorklet, effectSend } from './helpers.mjs';
|
||||||
import { map } from 'nanostores';
|
import { map } from 'nanostores';
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
import { loadBuffer } from './sampler.mjs';
|
import { loadBuffer } from './sampler.mjs';
|
||||||
import { getAudioContext } from './audioContext.mjs';
|
|
||||||
import { SuperdoughAudioController } from './superdoughoutput.mjs';
|
import { SuperdoughAudioController } from './superdoughoutput.mjs';
|
||||||
|
|
||||||
export const DEFAULT_MAX_POLYPHONY = 128;
|
export const DEFAULT_MAX_POLYPHONY = 128;
|
||||||
@@ -155,7 +154,6 @@ let defaultDefaultValues = {
|
|||||||
phaserdepth: 0.75,
|
phaserdepth: 0.75,
|
||||||
shapevol: 1,
|
shapevol: 1,
|
||||||
distortvol: 1,
|
distortvol: 1,
|
||||||
distorttype: 0,
|
|
||||||
delay: 0,
|
delay: 0,
|
||||||
byteBeatExpression: '0',
|
byteBeatExpression: '0',
|
||||||
delayfeedback: 0.5,
|
delayfeedback: 0.5,
|
||||||
@@ -202,6 +200,25 @@ export function setVersionDefaults(version) {
|
|||||||
|
|
||||||
export const resetLoadedSounds = () => soundMap.set({});
|
export const resetLoadedSounds = () => soundMap.set({});
|
||||||
|
|
||||||
|
let audioContext;
|
||||||
|
|
||||||
|
export const setDefaultAudioContext = () => {
|
||||||
|
audioContext = new AudioContext({ latencyHint: 'playback' });
|
||||||
|
return audioContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAudioContext = () => {
|
||||||
|
if (!audioContext) {
|
||||||
|
return setDefaultAudioContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getAudioContextCurrentTime() {
|
||||||
|
return getAudioContext().currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
let externalWorklets = [];
|
let externalWorklets = [];
|
||||||
export function registerWorklet(url) {
|
export function registerWorklet(url) {
|
||||||
externalWorklets.push(url);
|
externalWorklets.push(url);
|
||||||
@@ -456,7 +473,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||||||
shapevol = getDefaultValue('shapevol'),
|
shapevol = getDefaultValue('shapevol'),
|
||||||
distort,
|
distort,
|
||||||
distortvol = getDefaultValue('distortvol'),
|
distortvol = getDefaultValue('distortvol'),
|
||||||
distorttype = getDefaultValue('distorttype'),
|
|
||||||
pan,
|
pan,
|
||||||
vowel,
|
vowel,
|
||||||
delay = getDefaultValue('delay'),
|
delay = getDefaultValue('delay'),
|
||||||
@@ -631,7 +647,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||||||
coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse }));
|
coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse }));
|
||||||
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
||||||
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol }));
|
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol }));
|
||||||
distort !== undefined && chain.push(getDistortion(distort, distortvol, distorttype));
|
distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol }));
|
||||||
|
|
||||||
if (tremolosync != null) {
|
if (tremolosync != null) {
|
||||||
tremolo = cps * tremolosync;
|
tremolo = cps * tremolosync;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { clamp } from './util.mjs';
|
import { clamp } from './util.mjs';
|
||||||
import { registerSound, soundMap } from './superdough.mjs';
|
import { registerSound, getAudioContext, soundMap } from './superdough.mjs';
|
||||||
import { getAudioContext } from './audioContext.mjs';
|
|
||||||
import {
|
import {
|
||||||
applyFM,
|
applyFM,
|
||||||
destroyAudioWorkletNode,
|
destroyAudioWorkletNode,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { getAudioContext, registerSound } from './index.mjs';
|
import { getAudioContext, registerSound } from './index.mjs';
|
||||||
import { getCommonSampleInfo } from './util.mjs';
|
import { getCommonSampleInfo } from './util.mjs';
|
||||||
import {
|
import {
|
||||||
applyFM,
|
|
||||||
applyParameterModulators,
|
applyParameterModulators,
|
||||||
destroyAudioWorkletNode,
|
destroyAudioWorkletNode,
|
||||||
getADSRValues,
|
getADSRValues,
|
||||||
@@ -231,12 +230,12 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
|
|||||||
begin: t,
|
begin: t,
|
||||||
end: envEnd,
|
end: envEnd,
|
||||||
frequency,
|
frequency,
|
||||||
freqspread: value.detune,
|
detune: value.detune,
|
||||||
position: value.wt,
|
position: value.wt,
|
||||||
warp: value.warp,
|
warp: value.warp,
|
||||||
warpMode: warpmode,
|
warpMode: warpmode,
|
||||||
voices: Math.max(value.unison ?? 1, 1),
|
voices: Math.max(value.unison ?? 1, 1),
|
||||||
panspread: value.spread,
|
spread: value.spread,
|
||||||
phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0,
|
phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0,
|
||||||
},
|
},
|
||||||
{ outputChannelCount: [2] },
|
{ outputChannelCount: [2] },
|
||||||
@@ -309,7 +308,6 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const vibratoOscillator = getVibratoOscillator(source.parameters.get('detune'), value, t);
|
const vibratoOscillator = getVibratoOscillator(source.parameters.get('detune'), value, t);
|
||||||
const fm = applyFM(source.parameters.get('frequency'), value, t);
|
|
||||||
const envGain = ac.createGain();
|
const envGain = ac.createGain();
|
||||||
const node = source.connect(envGain);
|
const node = source.connect(envGain);
|
||||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, t, holdEnd, 'linear');
|
getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, t, holdEnd, 'linear');
|
||||||
@@ -320,7 +318,6 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
|
|||||||
() => {
|
() => {
|
||||||
destroyAudioWorkletNode(source);
|
destroyAudioWorkletNode(source);
|
||||||
vibratoOscillator?.stop();
|
vibratoOscillator?.stop();
|
||||||
fm?.stop();
|
|
||||||
node.disconnect();
|
node.disconnect();
|
||||||
wtPosModulators?.disconnect();
|
wtPosModulators?.disconnect();
|
||||||
wtWarpModulators?.disconnect();
|
wtWarpModulators?.disconnect();
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import OLAProcessor from './ola-processor';
|
import OLAProcessor from './ola-processor';
|
||||||
import FFT from './fft.js';
|
import FFT from './fft.js';
|
||||||
import { getDistortionAlgorithm } from './helpers.mjs';
|
|
||||||
|
|
||||||
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||||
const mod = (n, m) => ((n % m) + m) % m;
|
const mod = (n, m) => ((n % m) + m) % m;
|
||||||
@@ -431,10 +430,9 @@ class DistortProcessor extends AudioWorkletProcessor {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor({ processorOptions }) {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.algorithm = getDistortionAlgorithm(processorOptions.algorithm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process(inputs, outputs, parameters) {
|
process(inputs, outputs, parameters) {
|
||||||
@@ -446,12 +444,13 @@ class DistortProcessor extends AudioWorkletProcessor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.started = hasInput;
|
this.started = hasInput;
|
||||||
|
|
||||||
|
const shape = Math.expm1(parameters.distort[0]);
|
||||||
|
const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0]));
|
||||||
|
|
||||||
for (let n = 0; n < blockSize; n++) {
|
for (let n = 0; n < blockSize; n++) {
|
||||||
const postgain = clamp(pv(parameters.postgain, n), 0.001, 1);
|
for (let i = 0; i < input.length; i++) {
|
||||||
const shape = Math.expm1(pv(parameters.distort, n));
|
output[i][n] = (((1 + shape) * input[i][n]) / (1 + shape * Math.abs(input[i][n]))) * postgain;
|
||||||
for (let ch = 0; ch < input.length; ch++) {
|
|
||||||
const x = input[ch][n];
|
|
||||||
output[ch][n] = postgain * this.algorithm(x, shape);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -1050,15 +1049,14 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
return [
|
return [
|
||||||
{ name: 'begin', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
{ name: 'begin', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
||||||
{ name: 'end', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
{ name: 'end', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
||||||
{ name: 'frequency', defaultValue: 440, min: Number.EPSILON },
|
{ name: 'frequency', defaultValue: 220, minValue: 0.01, maxValue: 20000 },
|
||||||
{ name: 'detune', defaultValue: 0 },
|
{ name: 'detune', defaultValue: 0.18 },
|
||||||
{ name: 'freqspread', defaultValue: 0.18, min: 0 },
|
{ name: 'position', defaultValue: 0, minValue: 0, maxValue: 1 },
|
||||||
{ name: 'position', defaultValue: 0, min: 0, max: 1 },
|
{ name: 'warp', defaultValue: 0, minValue: 0, maxValue: 1 },
|
||||||
{ name: 'warp', defaultValue: 0, min: 0, max: 1 },
|
|
||||||
{ name: 'warpMode', defaultValue: 0 },
|
{ name: 'warpMode', defaultValue: 0 },
|
||||||
{ name: 'voices', defaultValue: 1, min: 1 },
|
{ name: 'voices', defaultValue: 1, minValue: 1, maxValue: 32 },
|
||||||
{ name: 'panspread', defaultValue: 0.7, min: 0, max: 1 },
|
{ name: 'spread', defaultValue: 0.7, minValue: 0, maxValue: 1 },
|
||||||
{ name: 'phaserand', defaultValue: 0, min: 0, max: 1 },
|
{ name: 'phaserand', defaultValue: 0, minValue: 0, maxValue: 1 },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1236,12 +1234,10 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
_sampleFrame(frame, phase) {
|
_sampleFrame(frame, phase) {
|
||||||
const len = frame.length;
|
const len = frame.length;
|
||||||
const pos = phase * len;
|
const pos = phase * len;
|
||||||
let i = pos | 0;
|
const i = pos | 0;
|
||||||
if (i >= len) i = 0; // fast wrap
|
|
||||||
const frac = pos - i;
|
const frac = pos - i;
|
||||||
const a = frame[i];
|
const a = frame[i];
|
||||||
let i1 = i + 1;
|
const i1 = i + 1 < len ? i + 1 : 0; // fast wrap
|
||||||
if (i1 >= len) i1 = 0;
|
|
||||||
const b = frame[i1];
|
const b = frame[i1];
|
||||||
return a + (b - a) * frac;
|
return a + (b - a) * frac;
|
||||||
}
|
}
|
||||||
@@ -1271,7 +1267,6 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
}
|
}
|
||||||
for (let i = 0; i < outL.length; i++) {
|
for (let i = 0; i < outL.length; i++) {
|
||||||
const detune = pv(parameters.detune, i);
|
const detune = pv(parameters.detune, i);
|
||||||
const freqspread = pv(parameters.freqspread, i);
|
|
||||||
const tablePos = clamp(pv(parameters.position, i), 0, 1);
|
const tablePos = clamp(pv(parameters.position, i), 0, 1);
|
||||||
const idx = tablePos * (this.numFrames - 1);
|
const idx = tablePos * (this.numFrames - 1);
|
||||||
const fIdx = idx | 0;
|
const fIdx = idx | 0;
|
||||||
@@ -1280,9 +1275,9 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
const warpMode = pv(parameters.warpMode, i);
|
const warpMode = pv(parameters.warpMode, i);
|
||||||
const voices = pv(parameters.voices, i);
|
const voices = pv(parameters.voices, i);
|
||||||
const phaseRand = clamp(pv(parameters.phaserand, i), 0, 1);
|
const phaseRand = clamp(pv(parameters.phaserand, i), 0, 1);
|
||||||
const panspread = voices > 1 ? clamp(pv(parameters.panspread, i), 0, 1) : 0;
|
const spread = voices > 1 ? clamp(pv(parameters.spread, i), 0, 1) : 0;
|
||||||
const gain1 = Math.sqrt(0.5 - 0.5 * panspread);
|
const gain1 = Math.sqrt(0.5 - 0.5 * spread);
|
||||||
const gain2 = Math.sqrt(0.5 + 0.5 * panspread);
|
const gain2 = Math.sqrt(0.5 + 0.5 * spread);
|
||||||
let f = pv(parameters.frequency, i);
|
let f = pv(parameters.frequency, i);
|
||||||
f = applySemitoneDetuneToFrequency(f, detune / 100); // overall detune
|
f = applySemitoneDetuneToFrequency(f, detune / 100); // overall detune
|
||||||
const normalizer = 1 / Math.sqrt(voices);
|
const normalizer = 1 / Math.sqrt(voices);
|
||||||
@@ -1295,7 +1290,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
gainL = gain2;
|
gainL = gain2;
|
||||||
gainR = gain1;
|
gainR = gain1;
|
||||||
}
|
}
|
||||||
const fVoice = applySemitoneDetuneToFrequency(f, getUnisonDetune(voices, freqspread, n)); // voice detune
|
const fVoice = applySemitoneDetuneToFrequency(f, getUnisonDetune(voices, detune, n)); // voice detune
|
||||||
const dPhase = fVoice * this.invSR;
|
const dPhase = fVoice * this.invSR;
|
||||||
const level = this._chooseMip(dPhase);
|
const level = this._chooseMip(dPhase);
|
||||||
const table = this.tables[level];
|
const table = this.tables[level];
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
//import { ZZFX } from 'zzfx';
|
//import { ZZFX } from 'zzfx';
|
||||||
import { midiToFreq, noteToMidi } from './util.mjs';
|
import { midiToFreq, noteToMidi } from './util.mjs';
|
||||||
import { registerSound } from './superdough.mjs';
|
import { registerSound, getAudioContext } from './superdough.mjs';
|
||||||
import { getAudioContext } from './audioContext.mjs';
|
|
||||||
import { buildSamples } from './zzfx_fork.mjs';
|
import { buildSamples } from './zzfx_fork.mjs';
|
||||||
|
|
||||||
export const getZZFX = (value, t) => {
|
export const getZZFX = (value, t) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAudioContext } from './audioContext.mjs';
|
import { getAudioContext } from './superdough.mjs';
|
||||||
|
|
||||||
// https://github.com/KilledByAPixel/ZzFX/blob/master/ZzFX.js#L85C5-L180C6
|
// https://github.com/KilledByAPixel/ZzFX/blob/master/ZzFX.js#L85C5-L180C6
|
||||||
// changes: replaced this.volume with 1 + using sampleRate from getAudioContext()
|
// changes: replaced this.volume with 1 + using sampleRate from getAudioContext()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "supradough",
|
"name": "supradough",
|
||||||
"version": "1.2.4",
|
"version": "1.2.3",
|
||||||
"description": "platform agnostic synth and sampler intended for live coding. a reimplementation of superdough.",
|
"description": "platform agnostic synth and sampler intended for live coding. a reimplementation of superdough.",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -32,5 +32,6 @@
|
|||||||
"vite": "^6.0.11",
|
"vite": "^6.0.11",
|
||||||
"vite-plugin-bundle-audioworklet": "workspace:*",
|
"vite-plugin-bundle-audioworklet": "workspace:*",
|
||||||
"wav-encoder": "^1.3.0"
|
"wav-encoder": "^1.3.0"
|
||||||
}
|
},
|
||||||
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import './tonal.mjs';
|
import './tonal.mjs';
|
||||||
import './voicings.mjs';
|
import './voicings.mjs';
|
||||||
import './ireal.mjs';
|
|
||||||
|
|
||||||
export * from './tonal.mjs';
|
export * from './tonal.mjs';
|
||||||
export * from './voicings.mjs';
|
export * from './voicings.mjs';
|
||||||
export * from './ireal.mjs';
|
|
||||||
|
import './ireal.mjs';
|
||||||
|
|
||||||
export const packageName = '@strudel/tonal';
|
export const packageName = '@strudel/tonal';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/tonal",
|
"name": "@strudel/tonal",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Tonal functions for strudel",
|
"description": "Tonal functions for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/transpiler",
|
"name": "@strudel/transpiler",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
|
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/web",
|
"name": "@strudel/web",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
|
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
|
||||||
"module": "web.mjs",
|
"module": "web.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/webaudio",
|
"name": "@strudel/webaudio",
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"description": "Web Audio helpers for Strudel",
|
"description": "Web Audio helpers for Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/xen",
|
"name": "@strudel/xen",
|
||||||
"version": "1.2.5",
|
"version": "1.2.4",
|
||||||
"description": "Xenharmonic API for strudel",
|
"description": "Xenharmonic API for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
Generated
+8
-246
@@ -41,7 +41,7 @@ importers:
|
|||||||
version: 2.2.7
|
version: 2.2.7
|
||||||
'@vitest/coverage-v8':
|
'@vitest/coverage-v8':
|
||||||
specifier: 3.0.4
|
specifier: 3.0.4
|
||||||
version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))
|
version: 3.0.4(vitest@3.0.4)
|
||||||
'@vitest/ui':
|
'@vitest/ui':
|
||||||
specifier: ^3.0.4
|
specifier: ^3.0.4
|
||||||
version: 3.0.4(vitest@3.0.4)
|
version: 3.0.4(vitest@3.0.4)
|
||||||
@@ -212,21 +212,12 @@ importers:
|
|||||||
'@strudel/draw':
|
'@strudel/draw':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../draw
|
version: link:../draw
|
||||||
'@strudel/tonal':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../tonal
|
|
||||||
'@strudel/transpiler':
|
'@strudel/transpiler':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../transpiler
|
version: link:../transpiler
|
||||||
'@tonaljs/tonal':
|
|
||||||
specifier: ^4.10.0
|
|
||||||
version: 4.10.0
|
|
||||||
nanostores:
|
nanostores:
|
||||||
specifier: ^0.11.3
|
specifier: ^0.11.3
|
||||||
version: 0.11.3
|
version: 0.11.3
|
||||||
superdough:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../superdough
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
vite:
|
vite:
|
||||||
specifier: ^6.0.11
|
specifier: ^6.0.11
|
||||||
@@ -415,12 +406,9 @@ importers:
|
|||||||
'@strudel/core':
|
'@strudel/core':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../core
|
version: link:../core
|
||||||
osc:
|
osc-js:
|
||||||
specifier: ^2.4.5
|
specifier: ^2.4.1
|
||||||
version: 2.4.5
|
version: 2.4.1
|
||||||
ws:
|
|
||||||
specifier: ^8.18.3
|
|
||||||
version: 8.18.3
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
pkg:
|
pkg:
|
||||||
specifier: ^5.8.1
|
specifier: ^5.8.1
|
||||||
@@ -2432,70 +2420,6 @@ packages:
|
|||||||
'@rtsao/scc@1.1.0':
|
'@rtsao/scc@1.1.0':
|
||||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||||
|
|
||||||
'@serialport/binding-mock@10.2.2':
|
|
||||||
resolution: {integrity: sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/bindings-cpp@12.0.1':
|
|
||||||
resolution: {integrity: sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==}
|
|
||||||
engines: {node: '>=16.0.0'}
|
|
||||||
|
|
||||||
'@serialport/bindings-interface@1.2.2':
|
|
||||||
resolution: {integrity: sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==}
|
|
||||||
engines: {node: ^12.22 || ^14.13 || >=16}
|
|
||||||
|
|
||||||
'@serialport/parser-byte-length@12.0.0':
|
|
||||||
resolution: {integrity: sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-cctalk@12.0.0':
|
|
||||||
resolution: {integrity: sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-delimiter@11.0.0':
|
|
||||||
resolution: {integrity: sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-delimiter@12.0.0':
|
|
||||||
resolution: {integrity: sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-inter-byte-timeout@12.0.0':
|
|
||||||
resolution: {integrity: sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-packet-length@12.0.0':
|
|
||||||
resolution: {integrity: sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==}
|
|
||||||
engines: {node: '>=8.6.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-readline@11.0.0':
|
|
||||||
resolution: {integrity: sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-readline@12.0.0':
|
|
||||||
resolution: {integrity: sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-ready@12.0.0':
|
|
||||||
resolution: {integrity: sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-regex@12.0.0':
|
|
||||||
resolution: {integrity: sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-slip-encoder@12.0.0':
|
|
||||||
resolution: {integrity: sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/parser-spacepacket@12.0.0':
|
|
||||||
resolution: {integrity: sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@serialport/stream@12.0.0':
|
|
||||||
resolution: {integrity: sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
|
|
||||||
'@shikijs/core@1.29.1':
|
'@shikijs/core@1.29.1':
|
||||||
resolution: {integrity: sha512-Mo1gGGkuOYjDu5H8YwzmOuly9vNr8KDVkqj9xiKhhhFS8jisAtDSEWB9hzqRHLVQgFdA310e8XRJcW4tYhRB2A==}
|
resolution: {integrity: sha512-Mo1gGGkuOYjDu5H8YwzmOuly9vNr8KDVkqj9xiKhhhFS8jisAtDSEWB9hzqRHLVQgFdA310e8XRJcW4tYhRB2A==}
|
||||||
|
|
||||||
@@ -3748,15 +3672,6 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
debug@4.3.4:
|
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
|
||||||
engines: {node: '>=6.0'}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
debug@4.4.0:
|
debug@4.4.0:
|
||||||
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
|
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@@ -5366,9 +5281,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
long@4.0.0:
|
|
||||||
resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
|
|
||||||
|
|
||||||
longest-streak@3.1.0:
|
longest-streak@3.1.0:
|
||||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||||
|
|
||||||
@@ -5752,9 +5664,6 @@ packages:
|
|||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||||
|
|
||||||
ms@2.1.2:
|
|
||||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
@@ -5813,9 +5722,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==}
|
resolution: {integrity: sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
node-addon-api@7.0.0:
|
|
||||||
resolution: {integrity: sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==}
|
|
||||||
|
|
||||||
node-addon-api@8.3.0:
|
node-addon-api@8.3.0:
|
||||||
resolution: {integrity: sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==}
|
resolution: {integrity: sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==}
|
||||||
engines: {node: ^18 || ^20 || >= 21}
|
engines: {node: ^18 || ^20 || >= 21}
|
||||||
@@ -5854,10 +5760,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-yqkmYrMbK1wPrfz7mgeYvA4tBperLg9FQ4S3Sau3nSAkpOA0x0zC8nQ1siBwozy1f4SE8vq2n1WKv99r+PCa1Q==}
|
resolution: {integrity: sha512-yqkmYrMbK1wPrfz7mgeYvA4tBperLg9FQ4S3Sau3nSAkpOA0x0zC8nQ1siBwozy1f4SE8vq2n1WKv99r+PCa1Q==}
|
||||||
engines: {node: '>= 0.6.0'}
|
engines: {node: '>= 0.6.0'}
|
||||||
|
|
||||||
node-gyp-build@4.6.0:
|
|
||||||
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
node-gyp-build@4.8.4:
|
node-gyp-build@4.8.4:
|
||||||
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
|
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -6016,8 +5918,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
|
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
osc@2.4.5:
|
osc-js@2.4.1:
|
||||||
resolution: {integrity: sha512-Nc4/qcl+vA/CMxiKS1xrYgzjfnyB3W94gZnrkn3eTzihlndbEml6+wj1YQNKBI4r+qrw3obCcEoU7SVH/OxpxA==}
|
resolution: {integrity: sha512-QlSeRKJclL47FNvO1MUCAAp9frmCF9zcYbnf6R9HpcklAst8ZyX3ISsk1v/Vghr/5GmXn0bhVjFXF9h+hfnl4Q==}
|
||||||
|
|
||||||
own-keys@1.0.1:
|
own-keys@1.0.1:
|
||||||
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
||||||
@@ -6778,10 +6680,6 @@ packages:
|
|||||||
serialize-javascript@4.0.0:
|
serialize-javascript@4.0.0:
|
||||||
resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==}
|
resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==}
|
||||||
|
|
||||||
serialport@12.0.0:
|
|
||||||
resolution: {integrity: sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==}
|
|
||||||
engines: {node: '>=16.0.0'}
|
|
||||||
|
|
||||||
set-blocking@2.0.0:
|
set-blocking@2.0.0:
|
||||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||||
|
|
||||||
@@ -6869,9 +6767,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
slip@1.0.2:
|
|
||||||
resolution: {integrity: sha512-XrcHe3NAcyD3wO+O4I13RcS4/3AF+S9RvGNj9JhJeS02HyImwD2E3QWLrmn9hBfL+fB6yapagwxRkeyYzhk98g==}
|
|
||||||
|
|
||||||
smart-buffer@4.2.0:
|
smart-buffer@4.2.0:
|
||||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||||
@@ -7747,9 +7642,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
|
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
wolfy87-eventemitter@5.2.9:
|
|
||||||
resolution: {integrity: sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==}
|
|
||||||
|
|
||||||
word-wrap@1.2.5:
|
word-wrap@1.2.5:
|
||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -7872,18 +7764,6 @@ packages:
|
|||||||
utf-8-validate:
|
utf-8-validate:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
ws@8.18.3:
|
|
||||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: '>=5.0.2'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
xmlcreate@2.0.4:
|
xmlcreate@2.0.4:
|
||||||
resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==}
|
resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==}
|
||||||
|
|
||||||
@@ -9880,76 +9760,6 @@ snapshots:
|
|||||||
|
|
||||||
'@rtsao/scc@1.1.0': {}
|
'@rtsao/scc@1.1.0': {}
|
||||||
|
|
||||||
'@serialport/binding-mock@10.2.2':
|
|
||||||
dependencies:
|
|
||||||
'@serialport/bindings-interface': 1.2.2
|
|
||||||
debug: 4.4.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/bindings-cpp@12.0.1':
|
|
||||||
dependencies:
|
|
||||||
'@serialport/bindings-interface': 1.2.2
|
|
||||||
'@serialport/parser-readline': 11.0.0
|
|
||||||
debug: 4.3.4
|
|
||||||
node-addon-api: 7.0.0
|
|
||||||
node-gyp-build: 4.6.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/bindings-interface@1.2.2':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-byte-length@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-cctalk@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-delimiter@11.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-delimiter@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-inter-byte-timeout@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-packet-length@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-readline@11.0.0':
|
|
||||||
dependencies:
|
|
||||||
'@serialport/parser-delimiter': 11.0.0
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-readline@12.0.0':
|
|
||||||
dependencies:
|
|
||||||
'@serialport/parser-delimiter': 12.0.0
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-ready@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-regex@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-slip-encoder@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/parser-spacepacket@12.0.0':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@serialport/stream@12.0.0':
|
|
||||||
dependencies:
|
|
||||||
'@serialport/bindings-interface': 1.2.2
|
|
||||||
debug: 4.3.4
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@shikijs/core@1.29.1':
|
'@shikijs/core@1.29.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/engine-javascript': 1.29.1
|
'@shikijs/engine-javascript': 1.29.1
|
||||||
@@ -10554,7 +10364,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@vitest/coverage-v8@3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))':
|
'@vitest/coverage-v8@3.0.4(vitest@3.0.4)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
'@bcoe/v8-coverage': 1.0.2
|
'@bcoe/v8-coverage': 1.0.2
|
||||||
@@ -11462,11 +11272,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
debug@4.3.4:
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.2
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
debug@4.4.0:
|
debug@4.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
@@ -13336,8 +13141,6 @@ snapshots:
|
|||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
is-unicode-supported: 0.1.0
|
is-unicode-supported: 0.1.0
|
||||||
|
|
||||||
long@4.0.0: {}
|
|
||||||
|
|
||||||
longest-streak@3.1.0: {}
|
longest-streak@3.1.0: {}
|
||||||
|
|
||||||
loupe@3.1.2: {}
|
loupe@3.1.2: {}
|
||||||
@@ -14015,9 +13818,6 @@ snapshots:
|
|||||||
|
|
||||||
ms@2.0.0: {}
|
ms@2.0.0: {}
|
||||||
|
|
||||||
ms@2.1.2:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
multimatch@5.0.0:
|
multimatch@5.0.0:
|
||||||
@@ -14067,9 +13867,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.6.3
|
semver: 7.6.3
|
||||||
|
|
||||||
node-addon-api@7.0.0:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
node-addon-api@8.3.0: {}
|
node-addon-api@8.3.0: {}
|
||||||
|
|
||||||
node-domexception@1.0.0: {}
|
node-domexception@1.0.0: {}
|
||||||
@@ -14096,9 +13893,6 @@ snapshots:
|
|||||||
|
|
||||||
node-getopt@0.3.2: {}
|
node-getopt@0.3.2: {}
|
||||||
|
|
||||||
node-gyp-build@4.6.0:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
node-gyp-build@4.8.4: {}
|
node-gyp-build@4.8.4: {}
|
||||||
|
|
||||||
node-gyp@10.3.1:
|
node-gyp@10.3.1:
|
||||||
@@ -14350,17 +14144,11 @@ snapshots:
|
|||||||
|
|
||||||
os-tmpdir@1.0.2: {}
|
os-tmpdir@1.0.2: {}
|
||||||
|
|
||||||
osc@2.4.5:
|
osc-js@2.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
long: 4.0.0
|
|
||||||
slip: 1.0.2
|
|
||||||
wolfy87-eventemitter: 5.2.9
|
|
||||||
ws: 8.18.0
|
ws: 8.18.0
|
||||||
optionalDependencies:
|
|
||||||
serialport: 12.0.0
|
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- supports-color
|
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
own-keys@1.0.1:
|
own-keys@1.0.1:
|
||||||
@@ -15255,26 +15043,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
randombytes: 2.1.0
|
randombytes: 2.1.0
|
||||||
|
|
||||||
serialport@12.0.0:
|
|
||||||
dependencies:
|
|
||||||
'@serialport/binding-mock': 10.2.2
|
|
||||||
'@serialport/bindings-cpp': 12.0.1
|
|
||||||
'@serialport/parser-byte-length': 12.0.0
|
|
||||||
'@serialport/parser-cctalk': 12.0.0
|
|
||||||
'@serialport/parser-delimiter': 12.0.0
|
|
||||||
'@serialport/parser-inter-byte-timeout': 12.0.0
|
|
||||||
'@serialport/parser-packet-length': 12.0.0
|
|
||||||
'@serialport/parser-readline': 12.0.0
|
|
||||||
'@serialport/parser-ready': 12.0.0
|
|
||||||
'@serialport/parser-regex': 12.0.0
|
|
||||||
'@serialport/parser-slip-encoder': 12.0.0
|
|
||||||
'@serialport/parser-spacepacket': 12.0.0
|
|
||||||
'@serialport/stream': 12.0.0
|
|
||||||
debug: 4.3.4
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
set-blocking@2.0.0: {}
|
set-blocking@2.0.0: {}
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
@@ -15417,8 +15185,6 @@ snapshots:
|
|||||||
|
|
||||||
slash@3.0.0: {}
|
slash@3.0.0: {}
|
||||||
|
|
||||||
slip@1.0.2: {}
|
|
||||||
|
|
||||||
smart-buffer@4.2.0: {}
|
smart-buffer@4.2.0: {}
|
||||||
|
|
||||||
socks-proxy-agent@8.0.5:
|
socks-proxy-agent@8.0.5:
|
||||||
@@ -16328,8 +16094,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
string-width: 7.2.0
|
string-width: 7.2.0
|
||||||
|
|
||||||
wolfy87-eventemitter@5.2.9: {}
|
|
||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
wordwrap@0.0.3: {}
|
wordwrap@0.0.3: {}
|
||||||
@@ -16529,8 +16293,6 @@ snapshots:
|
|||||||
|
|
||||||
ws@8.18.0: {}
|
ws@8.18.0: {}
|
||||||
|
|
||||||
ws@8.18.3: {}
|
|
||||||
|
|
||||||
xmlcreate@2.0.4: {}
|
xmlcreate@2.0.4: {}
|
||||||
|
|
||||||
xtend@4.0.2: {}
|
xtend@4.0.2: {}
|
||||||
|
|||||||
@@ -2829,82 +2829,6 @@ exports[`runs examples > example "distort" example index 1 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "distort" example index 2 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 1/4 → 1/2 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 3/4 → 1/1 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 7/4 → 2/1 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 11/4 → 3/1 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
"[ 15/4 → 4/1 | s:bd n:4 bank:tr808 distort:3 distortvol:0.5 distorttype:diode ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "distorttype" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd bank:tr909 distort:2 distorttype:1 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd bank:tr909 distort:2 distorttype:1 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd bank:tr909 distort:2 distorttype:1 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:bd bank:tr909 distort:2 distorttype:1 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd bank:tr909 distort:2 distorttype:2 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd bank:tr909 distort:2 distorttype:2 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd bank:tr909 distort:2 distorttype:2 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:bd bank:tr909 distort:2 distorttype:2 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:bd bank:tr909 distort:2 distorttype:0 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "distorttype" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ (0/1 → 1/2) ⇝ 1/1 | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:1 distorttype:fold ]",
|
|
||||||
"[ 0/1 ⇜ (1/2 → 1/1) | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:1 distorttype:fold ]",
|
|
||||||
"[ (1/1 → 3/2) ⇝ 2/1 | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:4.6367951557040215 distorttype:chebyshev ]",
|
|
||||||
"[ 1/1 ⇜ (3/2 → 2/1) | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:4.6367951557040215 distorttype:chebyshev ]",
|
|
||||||
"[ (2/1 → 5/2) ⇝ 3/1 | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:7.716689839959145 distorttype:scurve ]",
|
|
||||||
"[ 2/1 ⇜ (5/2 → 3/1) | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:7.716689839959145 distorttype:scurve ]",
|
|
||||||
"[ (3/1 → 7/2) ⇝ 4/1 | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:2.5210237745195627 distorttype:diode ]",
|
|
||||||
"[ 3/1 ⇜ (7/2 → 4/1) | s:sine note:F1 release:1 penv:24 pdecay:0.05 distort:2.5210237745195627 distorttype:diode ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "distortvol" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:bd bank:tr909 distort:2 distortvol:0.8 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "djf" example index 0 1`] = `
|
exports[`runs examples > example "djf" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:D3 s:supersaw djf:0.5 ]",
|
"[ 0/1 → 1/8 | note:D3 s:supersaw djf:0.5 ]",
|
||||||
@@ -6958,24 +6882,6 @@ exports[`runs examples > example "orbit" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "oschost" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "oscport" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:c4 oschost:127.0.0.1 oscport:57120 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "outside" example index 0 1`] = `
|
exports[`runs examples > example "outside" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/1 | note:A3 ]",
|
"[ 0/1 → 1/1 | note:A3 ]",
|
||||||
@@ -8325,33 +8231,6 @@ exports[`runs examples > example "repeatCycles" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "replicate" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | s:bd ]",
|
|
||||||
"[ 1/8 → 1/4 | s:bd ]",
|
|
||||||
"[ 3/8 → 1/2 | s:cp ]",
|
|
||||||
"[ 1/2 → 5/8 | s:bd ]",
|
|
||||||
"[ 5/8 → 3/4 | s:bd ]",
|
|
||||||
"[ 7/8 → 1/1 | s:cp ]",
|
|
||||||
"[ 1/1 → 9/8 | s:bd ]",
|
|
||||||
"[ 5/4 → 11/8 | s:sd ]",
|
|
||||||
"[ 3/2 → 13/8 | s:bd ]",
|
|
||||||
"[ 13/8 → 7/4 | s:bd ]",
|
|
||||||
"[ 15/8 → 2/1 | s:cp ]",
|
|
||||||
"[ 2/1 → 17/8 | s:bd ]",
|
|
||||||
"[ 17/8 → 9/4 | s:bd ]",
|
|
||||||
"[ 19/8 → 5/2 | s:cp ]",
|
|
||||||
"[ 5/2 → 21/8 | s:bd ]",
|
|
||||||
"[ 11/4 → 23/8 | s:sd ]",
|
|
||||||
"[ 3/1 → 25/8 | s:bd ]",
|
|
||||||
"[ 25/8 → 13/4 | s:bd ]",
|
|
||||||
"[ 27/8 → 7/2 | s:cp ]",
|
|
||||||
"[ 7/2 → 29/8 | s:bd ]",
|
|
||||||
"[ 29/8 → 15/4 | s:bd ]",
|
|
||||||
"[ 31/8 → 4/1 | s:cp ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "reset" example index 0 1`] = `
|
exports[`runs examples > example "reset" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | s:hh ]",
|
"[ 0/1 → 1/8 | s:hh ]",
|
||||||
|
|||||||
@@ -60,23 +60,6 @@ Strudel makes heavy use of chained functions. Here is a more sophisticated examp
|
|||||||
.room(0.5)`}
|
.room(0.5)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## Write your own chained function
|
|
||||||
|
|
||||||
You can write your own chained function using `register`. Here's the above chain but registered as a reusable, chained function.
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`const effectChain = register('effectChain', (pat) => pat
|
|
||||||
.s("sawtooth")
|
|
||||||
.cutoff(500)
|
|
||||||
//.delay(0.5)
|
|
||||||
.room(0.5)
|
|
||||||
)
|
|
||||||
note("a3 c#4 e4 a4").effectChain()`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
Try adding `.rev()` after `effectChain()` to hear further effects added.
|
|
||||||
|
|
||||||
# Comments
|
# Comments
|
||||||
|
|
||||||
The `//` in the example above is a line comment, resulting in the `delay` function being ignored.
|
The `//` in the example above is a line comment, resulting in the `delay` function being ignored.
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ Sampler effects are functions that can be used to change the behaviour of sample
|
|||||||
|
|
||||||
### scrub
|
### scrub
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.scrub" h={0} />
|
<JsDoc client:idle name="Pattern.scrub" h={0} />{' '}
|
||||||
|
|
||||||
### speed
|
### speed
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ It has 20 parameters in total, here is a snippet that uses all:
|
|||||||
.pitchJump(0) // +/- pitch change after pitchJumpTime
|
.pitchJump(0) // +/- pitch change after pitchJumpTime
|
||||||
.pitchJumpTime(0) // >0 time after pitchJump is applied
|
.pitchJumpTime(0) // >0 time after pitchJump is applied
|
||||||
.lfo(0) // >0 resets slide + pitchJump + sets tremolo speed
|
.lfo(0) // >0 resets slide + pitchJump + sets tremolo speed
|
||||||
.tremolo(0.5) // 0-1 lfo volume modulation amount
|
.tremolo(0) // 0-1 lfo volume modulation amount
|
||||||
//.duration(.2) // overwrite strudel event duration
|
//.duration(.2) // overwrite strudel event duration
|
||||||
//.gain(1) // change volume
|
//.gain(1) // change volume
|
||||||
._scope() // vizualise waveform (not zzfx related)
|
._scope() // vizualise waveform (not zzfx related)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import './files.mjs';
|
|||||||
|
|
||||||
const { BASE_URL } = import.meta.env;
|
const { BASE_URL } = import.meta.env;
|
||||||
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
|
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
|
||||||
const baseCDN = 'https://strudel.b-cdn.net';
|
|
||||||
|
|
||||||
export async function prebake() {
|
export async function prebake() {
|
||||||
// https://archive.org/details/SalamanderGrandPianoV3
|
// https://archive.org/details/SalamanderGrandPianoV3
|
||||||
@@ -20,23 +19,23 @@ export async function prebake() {
|
|||||||
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
|
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
|
||||||
// seems to be a problem with soundfont2
|
// seems to be a problem with soundfont2
|
||||||
import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
|
import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
|
||||||
samples(`${baseCDN}/piano.json`, `${baseCDN}/piano/`, { prebake: true }),
|
samples(`${baseNoTrailing}/piano.json`, undefined, { prebake: true }),
|
||||||
// https://github.com/sgossner/VCSL/
|
// https://github.com/sgossner/VCSL/
|
||||||
// https://api.github.com/repositories/126427031/contents/
|
// https://api.github.com/repositories/126427031/contents/
|
||||||
// LICENSE: CC0 general-purpose
|
// LICENSE: CC0 general-purpose
|
||||||
samples(`${baseCDN}/vcsl.json`, `${baseCDN}/VCSL/`, { prebake: true }),
|
samples(`${baseNoTrailing}/vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }),
|
||||||
samples(`${baseCDN}/tidal-drum-machines.json`, `${baseCDN}/tidal-drum-machines/machines/`, {
|
samples(`${baseNoTrailing}/tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', {
|
||||||
prebake: true,
|
prebake: true,
|
||||||
tag: 'drum-machines',
|
tag: 'drum-machines',
|
||||||
}),
|
}),
|
||||||
samples(`${baseCDN}/uzu-drumkit.json`, `${baseCDN}/uzu-drumkit/`, {
|
samples(`${baseNoTrailing}/uzu-drumkit.json`, undefined, {
|
||||||
prebake: true,
|
prebake: true,
|
||||||
tag: 'drum-machines',
|
tag: 'drum-machines',
|
||||||
}),
|
}),
|
||||||
samples(`${baseCDN}/uzu-wavetables.json`, `${baseCDN}/uzu-wavetables/`, {
|
samples(`${baseNoTrailing}/uzu-wavetables.json`, undefined, {
|
||||||
prebake: true,
|
prebake: true,
|
||||||
}),
|
}),
|
||||||
samples(`${baseCDN}/mridangam.json`, `${baseCDN}/mrid/`, { prebake: true, tag: 'drum-machines' }),
|
samples(`${baseNoTrailing}/mridangam.json`, undefined, { prebake: true, tag: 'drum-machines' }),
|
||||||
samples(
|
samples(
|
||||||
{
|
{
|
||||||
casio: ['casio/high.wav', 'casio/low.wav', 'casio/noise.wav'],
|
casio: ['casio/high.wav', 'casio/low.wav', 'casio/noise.wav'],
|
||||||
@@ -146,14 +145,14 @@ export async function prebake() {
|
|||||||
'num/20.wav',
|
'num/20.wav',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
`${baseCDN}/Dirt-Samples/`,
|
'github:tidalcycles/dirt-samples',
|
||||||
{
|
{
|
||||||
prebake: true,
|
prebake: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
aliasBank(`${baseCDN}/tidal-drum-machines-alias.json`);
|
aliasBank(`${baseNoTrailing}/tidal-drum-machines-alias.json`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxPan = noteToMidi('C8');
|
const maxPan = noteToMidi('C8');
|
||||||
|
|||||||
Reference in New Issue
Block a user