mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 13:13:10 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3568f0e437 |
@@ -0,0 +1,62 @@
|
|||||||
|
name: Tauri Builder
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
rust_target: x86_64-unknown-linux-gnu
|
||||||
|
- os: macos-latest
|
||||||
|
rust_target: x86_64-apple-darwin
|
||||||
|
- os: macos-latest
|
||||||
|
rust_target: aarch64-apple-darwin
|
||||||
|
- os: windows-latest
|
||||||
|
rust_target: x86_64-pc-windows-msvc
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8.6.2
|
||||||
|
|
||||||
|
- name: Node.js setup
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: latest
|
||||||
|
# node-version-file: '.nvmrc'
|
||||||
|
|
||||||
|
- name: Install Rust (Stable)
|
||||||
|
run:
|
||||||
|
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
|
|
||||||
|
- name: Install dependencies (ubuntu only)
|
||||||
|
if: matrix.platform == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf libasound2-dev
|
||||||
|
|
||||||
|
- name: Install app dependencies from lockfile and build web
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build the app
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
# tauri-action replaces \_\_VERSION\_\_ with the app version
|
||||||
|
tagName: ${{ github.ref_name }}
|
||||||
|
releaseName: "Strudel v__VERSION__"
|
||||||
|
releaseBody: |
|
||||||
|
See the assets to download this version and install.
|
||||||
|
releaseDraft: true
|
||||||
|
prerelease: false
|
||||||
+1
-2
@@ -71,7 +71,6 @@
|
|||||||
"jsdoc-json": "^2.0.2",
|
"jsdoc-json": "^2.0.2",
|
||||||
"lerna": "^8.1.9",
|
"lerna": "^8.1.9",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"vitest": "^3.0.4",
|
"vitest": "^3.0.4"
|
||||||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ export const flashField = StateField.define({
|
|||||||
for (let e of tr.effects) {
|
for (let e of tr.effects) {
|
||||||
if (e.is(setFlash)) {
|
if (e.is(setFlash)) {
|
||||||
if (e.value && tr.newDoc.length > 0) {
|
if (e.value && tr.newDoc.length > 0) {
|
||||||
const mark = Decoration.mark({
|
const mark = Decoration.mark({ attributes: { style: `background-color: #FFCA2880` } });
|
||||||
attributes: { style: `background-color: rgba(255,255,255, .4); filter: invert(10%)` },
|
|
||||||
});
|
|
||||||
flash = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
flash = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
||||||
} else {
|
} else {
|
||||||
flash = Decoration.set([]);
|
flash = Decoration.set([]);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/codemirror",
|
"name": "@strudel/codemirror",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "Codemirror Extensions for Strudel",
|
"description": "Codemirror Extensions for Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
Vendored
+32
-24
@@ -5,15 +5,16 @@ import whitescreen, { settings as whitescreenSettings } from './themes/whitescre
|
|||||||
import teletext, { settings as teletextSettings } from './themes/teletext.mjs';
|
import teletext, { settings as teletextSettings } from './themes/teletext.mjs';
|
||||||
import algoboy, { settings as algoboySettings } from './themes/algoboy.mjs';
|
import algoboy, { settings as algoboySettings } from './themes/algoboy.mjs';
|
||||||
import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs';
|
import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs';
|
||||||
import sonicPink, { settings as sonicPinkSettings } from './themes/sonic-pink.mjs';
|
import terminal, { settings as terminalSettings } from './themes/terminal.mjs';
|
||||||
import redText, { settings as redTextSettings } from './themes/red-text.mjs';
|
import abcdef, { settings as abcdefSettings } from './themes/abcdef.mjs';
|
||||||
import greenText, { settings as greenTextSettings } from './themes/green-text.mjs';
|
|
||||||
import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs';
|
import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs';
|
||||||
import atomone, { settings as atomOneSettings } from './themes/atomone.mjs';
|
import atomone, { settings as atomOneSettings } from './themes/atomone.mjs';
|
||||||
import aura, { settings as auraSettings } from './themes/aura.mjs';
|
import aura, { settings as auraSettings } from './themes/aura.mjs';
|
||||||
|
import bespin, { settings as bespinSettings } from './themes/bespin.mjs';
|
||||||
import darcula, { settings as darculaSettings } from './themes/darcula.mjs';
|
import darcula, { settings as darculaSettings } from './themes/darcula.mjs';
|
||||||
import dracula, { settings as draculaSettings } from './themes/dracula.mjs';
|
import dracula, { settings as draculaSettings } from './themes/dracula.mjs';
|
||||||
import duotoneDark, { settings as duotoneDarkSettings } from './themes/duotoneDark.mjs';
|
import duotoneDark, { settings as duotoneDarkSettings } from './themes/duotoneDark.mjs';
|
||||||
|
import duotoneLight, { settings as duotoneLightSettings } from './themes/duotoneLight.mjs';
|
||||||
import eclipse, { settings as eclipseSettings } from './themes/eclipse.mjs';
|
import eclipse, { settings as eclipseSettings } from './themes/eclipse.mjs';
|
||||||
import githubDark, { settings as githubDarkSettings } from './themes/githubDark.mjs';
|
import githubDark, { settings as githubDarkSettings } from './themes/githubDark.mjs';
|
||||||
import githubLight, { settings as githubLightSettings } from './themes/githubLight.mjs';
|
import githubLight, { settings as githubLightSettings } from './themes/githubLight.mjs';
|
||||||
@@ -31,47 +32,52 @@ import tokyoNightStorm, { settings as tokyoNightStormSettings } from './themes/t
|
|||||||
import tokyoNightDay, { settings as tokyoNightDaySettings } from './themes/tokyoNightDay.mjs';
|
import tokyoNightDay, { settings as tokyoNightDaySettings } from './themes/tokyoNightDay.mjs';
|
||||||
import vscodeDark, { settings as vscodeDarkSettings } from './themes/vscodeDark.mjs';
|
import vscodeDark, { settings as vscodeDarkSettings } from './themes/vscodeDark.mjs';
|
||||||
import vscodeLight, { settings as vscodeLightSettings } from './themes/vscodeLight.mjs';
|
import vscodeLight, { settings as vscodeLightSettings } from './themes/vscodeLight.mjs';
|
||||||
|
// import xcodeDark, { settings as xcodeDarkSettings } from './themes/xcodeDark.mjs';
|
||||||
import xcodeLight, { settings as xcodeLightSettings } from './themes/xcodeLight.mjs';
|
import xcodeLight, { settings as xcodeLightSettings } from './themes/xcodeLight.mjs';
|
||||||
import bbedit, { settings as bbeditSettings } from './themes/bbedit.mjs';
|
import bbedit, { settings as bbeditSettings } from './themes/bbedit.mjs';
|
||||||
import noctisLilac, { settings as noctisLilacSettings } from './themes/noctisLilac.mjs';
|
import noctisLilac, { settings as noctisLilacSettings } from './themes/noctisLilac.mjs';
|
||||||
|
|
||||||
import { setTheme } from '@strudel/draw';
|
import { setTheme } from '@strudel/draw';
|
||||||
|
|
||||||
export const themes = {
|
export const themes = {
|
||||||
strudelTheme,
|
strudelTheme,
|
||||||
|
bluescreen,
|
||||||
|
blackscreen,
|
||||||
|
whitescreen,
|
||||||
|
teletext,
|
||||||
algoboy,
|
algoboy,
|
||||||
androidstudio,
|
|
||||||
atomone,
|
atomone,
|
||||||
aura,
|
aura,
|
||||||
bbedit,
|
|
||||||
blackscreen,
|
|
||||||
bluescreen,
|
|
||||||
CutiePi,
|
|
||||||
darcula,
|
darcula,
|
||||||
dracula,
|
dracula,
|
||||||
|
// todo: optimize
|
||||||
|
// bespin,
|
||||||
|
//abcdef,
|
||||||
|
androidstudio,
|
||||||
duotoneDark,
|
duotoneDark,
|
||||||
eclipse,
|
|
||||||
githubDark,
|
githubDark,
|
||||||
githubLight,
|
CutiePi,
|
||||||
greenText,
|
|
||||||
gruvboxDark,
|
gruvboxDark,
|
||||||
gruvboxLight,
|
|
||||||
sonicPink,
|
|
||||||
materialDark,
|
materialDark,
|
||||||
materialLight,
|
|
||||||
monokai,
|
|
||||||
noctisLilac,
|
|
||||||
nord,
|
nord,
|
||||||
redText,
|
monokai,
|
||||||
solarizedDark,
|
solarizedDark,
|
||||||
solarizedLight,
|
|
||||||
sublime,
|
sublime,
|
||||||
teletext,
|
|
||||||
tokyoNight,
|
tokyoNight,
|
||||||
tokyoNightDay,
|
|
||||||
tokyoNightStorm,
|
tokyoNightStorm,
|
||||||
vscodeDark,
|
vscodeDark,
|
||||||
|
//xcodeDark,
|
||||||
|
// LIGHT
|
||||||
|
bbedit,
|
||||||
|
//duotoneLight,
|
||||||
|
eclipse,
|
||||||
|
githubLight,
|
||||||
|
gruvboxLight,
|
||||||
|
materialLight,
|
||||||
vscodeLight,
|
vscodeLight,
|
||||||
whitescreen,
|
noctisLilac,
|
||||||
|
solarizedLight,
|
||||||
|
tokyoNightDay,
|
||||||
xcodeLight,
|
xcodeLight,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,19 +88,21 @@ export const settings = {
|
|||||||
whitescreen: whitescreenSettings,
|
whitescreen: whitescreenSettings,
|
||||||
teletext: teletextSettings,
|
teletext: teletextSettings,
|
||||||
algoboy: algoboySettings,
|
algoboy: algoboySettings,
|
||||||
|
terminal: terminalSettings,
|
||||||
|
abcdef: abcdefSettings,
|
||||||
androidstudio: androidstudioSettings,
|
androidstudio: androidstudioSettings,
|
||||||
atomone: atomOneSettings,
|
atomone: atomOneSettings,
|
||||||
aura: auraSettings,
|
aura: auraSettings,
|
||||||
bbedit: bbeditSettings,
|
bbedit: bbeditSettings,
|
||||||
|
bespin: bespinSettings,
|
||||||
darcula: darculaSettings,
|
darcula: darculaSettings,
|
||||||
dracula: draculaSettings,
|
dracula: draculaSettings,
|
||||||
|
duotoneLight: duotoneLightSettings,
|
||||||
duotoneDark: duotoneDarkSettings,
|
duotoneDark: duotoneDarkSettings,
|
||||||
eclipse: eclipseSettings,
|
eclipse: eclipseSettings,
|
||||||
CutiePi: CutiePiSettings,
|
CutiePi: CutiePiSettings,
|
||||||
sonicPink: sonicPinkSettings,
|
|
||||||
githubLight: githubLightSettings,
|
githubLight: githubLightSettings,
|
||||||
githubDark: githubDarkSettings,
|
githubDark: githubDarkSettings,
|
||||||
greenText: greenTextSettings,
|
|
||||||
gruvboxDark: gruvboxDarkSettings,
|
gruvboxDark: gruvboxDarkSettings,
|
||||||
gruvboxLight: gruvboxLightSettings,
|
gruvboxLight: gruvboxLightSettings,
|
||||||
materialDark: materialDarkSettings,
|
materialDark: materialDarkSettings,
|
||||||
@@ -102,7 +110,6 @@ export const settings = {
|
|||||||
noctisLilac: noctisLilacSettings,
|
noctisLilac: noctisLilacSettings,
|
||||||
nord: nordSettings,
|
nord: nordSettings,
|
||||||
monokai: monokaiSettings,
|
monokai: monokaiSettings,
|
||||||
redText: redTextSettings,
|
|
||||||
solarizedLight: solarizedLightSettings,
|
solarizedLight: solarizedLightSettings,
|
||||||
solarizedDark: solarizedDarkSettings,
|
solarizedDark: solarizedDarkSettings,
|
||||||
sublime: sublimeSettings,
|
sublime: sublimeSettings,
|
||||||
@@ -111,6 +118,7 @@ export const settings = {
|
|||||||
vscodeDark: vscodeDarkSettings,
|
vscodeDark: vscodeDarkSettings,
|
||||||
vscodeLight: vscodeLightSettings,
|
vscodeLight: vscodeLightSettings,
|
||||||
xcodeLight: xcodeLightSettings,
|
xcodeLight: xcodeLightSettings,
|
||||||
|
//xcodeDark: xcodeDarkSettings,
|
||||||
tokyoNightDay: tokyoNightDaySettings,
|
tokyoNightDay: tokyoNightDaySettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Cutie Pi
|
* @name Cutie Pi
|
||||||
* by Switch Angel
|
* by Switch Angel
|
||||||
*/
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @name abcdef
|
||||||
|
* @author codemirror.net
|
||||||
|
* https://codemirror.net/5/theme/abcdef.css
|
||||||
|
*/
|
||||||
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
import { createTheme } from './theme-helper.mjs';
|
||||||
|
|
||||||
|
export const settings = {
|
||||||
|
background: '#0f0f0f',
|
||||||
|
lineBackground: '#0f0f0f99',
|
||||||
|
foreground: '#defdef',
|
||||||
|
caret: '#00FF00',
|
||||||
|
selection: '#515151',
|
||||||
|
selectionMatch: '#515151',
|
||||||
|
gutterBackground: '#555',
|
||||||
|
gutterForeground: '#FFFFFF',
|
||||||
|
lineHighlight: '#314151',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createTheme({
|
||||||
|
theme: 'dark',
|
||||||
|
settings: {
|
||||||
|
background: '#0f0f0f',
|
||||||
|
foreground: '#defdef',
|
||||||
|
caret: '#00FF00',
|
||||||
|
selection: '#515151',
|
||||||
|
selectionMatch: '#515151',
|
||||||
|
// gutterBackground: '#555',
|
||||||
|
gutterBackground: 'transparent',
|
||||||
|
/* gutterForeground: '#FFFFFF', */
|
||||||
|
gutterForeground: '#7a7b7c',
|
||||||
|
lineHighlight: '#0a6bcb3d',
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{ tag: t.labelName, color: 'inherit' },
|
||||||
|
{ tag: t.keyword, color: 'darkgoldenrod', fontWeight: 'bold' },
|
||||||
|
{ tag: t.atom, color: '#77F' },
|
||||||
|
{ tag: t.comment, color: '#7a7b7c', fontStyle: 'italic' },
|
||||||
|
{ tag: t.number, color: 'violet' },
|
||||||
|
{ tag: t.definition(t.variableName), color: '#fffabc' },
|
||||||
|
{ tag: t.variableName, color: '#abcdef' },
|
||||||
|
{ tag: t.function(t.variableName), color: '#fffabc' },
|
||||||
|
{ tag: t.typeName, color: '#FFDD44' },
|
||||||
|
{ tag: t.tagName, color: '#def' },
|
||||||
|
{ tag: t.string, color: '#2b4' },
|
||||||
|
{ tag: t.meta, color: '#C9F' },
|
||||||
|
// { tag: t.qualifier, color: '#FFF700' },
|
||||||
|
// { tag: t.builtin, color: '#30aabc' },
|
||||||
|
{ tag: t.bracket, color: '#8a8a8a' },
|
||||||
|
{ tag: t.attributeName, color: '#DDFF00' },
|
||||||
|
{ tag: t.heading, color: 'aquamarine', fontWeight: 'bold' },
|
||||||
|
{ tag: t.link, color: 'blueviolet', fontWeight: 'bold' },
|
||||||
|
],
|
||||||
|
});
|
||||||
-1
@@ -54,7 +54,6 @@ export default createTheme({
|
|||||||
tag: [t.keyword, t.tagName, t.arithmeticOperator],
|
tag: [t.keyword, t.tagName, t.arithmeticOperator],
|
||||||
color: palette[1],
|
color: palette[1],
|
||||||
},
|
},
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: palette[0] },
|
|
||||||
{ tag: [t.function(t.variableName), t.propertyName], color: palette[0] },
|
{ tag: [t.function(t.variableName), t.propertyName], color: palette[0] },
|
||||||
{ tag: t.atom, color: palette[1] },
|
{ tag: t.atom, color: palette[1] },
|
||||||
],
|
],
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* androidstudio
|
* @name androidstudio
|
||||||
*/
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from './theme-helper.mjs';
|
||||||
|
|||||||
+2
-4
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Atom One
|
* @name Atom One
|
||||||
* Atom One dark syntax theme
|
* Atom One dark syntax theme
|
||||||
*
|
*
|
||||||
* https://github.com/atom/one-dark-syntax
|
* https://github.com/atom/one-dark-syntax
|
||||||
@@ -38,14 +38,12 @@ export default createTheme({
|
|||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
||||||
color: 'hsl(207, 82%, 66%)',
|
color: 'hsl(207, 82%, 66%)',
|
||||||
},
|
},
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: 'hsl( 29, 54%, 61%)' },
|
|
||||||
{ tag: [t.tagName, t.heading], color: '#e06c75' },
|
{ tag: [t.tagName, t.heading], color: '#e06c75' },
|
||||||
{ tag: t.comment, color: '#54636D' },
|
{ tag: t.comment, color: '#54636D' },
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: 'hsl(220, 14%, 71%)' },
|
{ tag: [t.variableName, t.propertyName, t.labelName], color: 'hsl(220, 14%, 71%)' },
|
||||||
{ tag: [t.attributeName, t.number], color: 'hsl( 29, 54%, 61%)' },
|
{ tag: [t.attributeName, t.number], color: 'hsl( 29, 54%, 61%)' },
|
||||||
{ tag: t.className, color: 'hsl( 39, 67%, 69%)' },
|
{ tag: t.className, color: 'hsl( 39, 67%, 69%)' },
|
||||||
{ tag: t.keyword, color: 'hsl(286, 60%, 67%)' },
|
{ tag: t.keyword, color: 'hsl(286, 60%, 67%)' },
|
||||||
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: '#98c379' },
|
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: '#98c379' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
// this is different from https://thememirror.net/bespin
|
||||||
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
import { createTheme } from './theme-helper.mjs';
|
||||||
|
|
||||||
|
export const settings = {
|
||||||
|
background: '#28211c',
|
||||||
|
lineBackground: '#28211c99',
|
||||||
|
foreground: '#9d9b97',
|
||||||
|
caret: '#797977',
|
||||||
|
selection: '#36312e',
|
||||||
|
selectionMatch: '#4f382b',
|
||||||
|
gutterBackground: '#28211c',
|
||||||
|
gutterForeground: '#666666',
|
||||||
|
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
};
|
||||||
|
export default createTheme({
|
||||||
|
theme: 'dark',
|
||||||
|
settings: {
|
||||||
|
background: '#28211c',
|
||||||
|
foreground: '#9d9b97',
|
||||||
|
caret: '#797977',
|
||||||
|
selection: '#4f382b',
|
||||||
|
selectionMatch: '#4f382b',
|
||||||
|
gutterBackground: '#28211c',
|
||||||
|
gutterForeground: '#666666',
|
||||||
|
lineHighlight: '#ffffff1a',
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{ tag: [t.atom, t.number, t.link, t.bool], color: '#9b859d' },
|
||||||
|
{ tag: t.comment, color: '#937121' },
|
||||||
|
{ tag: [t.keyword, t.tagName], color: '#cf6a4c' },
|
||||||
|
{ tag: t.string, color: '#f9ee98' },
|
||||||
|
{ tag: t.bracket, color: '#9d9b97' },
|
||||||
|
{ tag: [t.variableName], color: '#5ea6ea' },
|
||||||
|
{ tag: t.definition(t.variableName), color: '#cf7d34' },
|
||||||
|
{ tag: [t.function(t.variableName), t.className], color: '#cf7d34' },
|
||||||
|
{ tag: [t.propertyName, t.attributeName], color: '#54be0d' },
|
||||||
|
],
|
||||||
|
});
|
||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/**
|
||||||
* darcula
|
* @name darcula
|
||||||
|
* @author darcula
|
||||||
* Name: IntelliJ IDEA darcula theme
|
* Name: IntelliJ IDEA darcula theme
|
||||||
* From IntelliJ IDEA by JetBrains
|
* From IntelliJ IDEA by JetBrains
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/**
|
||||||
* @name dracula
|
* @name dracula
|
||||||
|
* @author dracula
|
||||||
* Michael Kaminsky (http://github.com/mkaminsky11)
|
* Michael Kaminsky (http://github.com/mkaminsky11)
|
||||||
* Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)
|
* Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/**
|
||||||
* duotone
|
* @name duotone
|
||||||
* author Bram de Haan
|
* @author Bram de Haan
|
||||||
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
|
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
|
||||||
*/
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/**
|
||||||
* duotone
|
* @name duotone
|
||||||
* author Bram de Haan
|
* @author Bram de Haan
|
||||||
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
|
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
|
||||||
*/
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @name github
|
||||||
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from './theme-helper.mjs';
|
||||||
|
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @name github
|
||||||
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from './theme-helper.mjs';
|
||||||
|
|
||||||
|
|||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
const hex = ['#000000', '#8ed675', '#56bd2a', '#54636D', '#171717'];
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: hex[0],
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: hex[2],
|
|
||||||
selection: hex[4],
|
|
||||||
selectionMatch: hex[0],
|
|
||||||
gutterBackground: hex[0],
|
|
||||||
gutterForeground: hex[3],
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: hex[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: hex[2],
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
|
|
||||||
{ tag: t.comment, color: hex[3] },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
|
|
||||||
{ tag: [t.attributeName, t.number], color: hex[1] },
|
|
||||||
{ tag: t.keyword, color: hex[2] },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+4
-3
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/**
|
||||||
* gruvbox-dark
|
* @name gruvbox-dark
|
||||||
* author morhetz
|
* @author morhetz
|
||||||
|
* Name: Gruvbox
|
||||||
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-dark.css
|
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-dark.css
|
||||||
*/
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
|||||||
+4
-3
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/**
|
||||||
* gruvbox-light
|
* @name gruvbox-light
|
||||||
* author morhetz
|
* @author morhetz
|
||||||
|
* Name: Gruvbox
|
||||||
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-light.css
|
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-light.css
|
||||||
*/
|
*/
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
|||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
const hex = ['#000000', '#ff5356', '#bd312a', '#54636D', '#171717'];
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: hex[0],
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: hex[2],
|
|
||||||
selection: hex[4],
|
|
||||||
selectionMatch: hex[0],
|
|
||||||
gutterBackground: hex[0],
|
|
||||||
gutterForeground: hex[3],
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: hex[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: hex[2],
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
|
|
||||||
{ tag: t.comment, color: hex[3] },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
|
|
||||||
{ tag: [t.attributeName, t.number], color: hex[1] },
|
|
||||||
{ tag: t.keyword, color: hex[2] },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
const hex = ['#1e1e1e', '#fbde2d', '#ff1493', '#4c83ff', '#ededed', '#cccccc', '#ffffff30', '#dc2f8c'];
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#000000',
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: hex[4],
|
|
||||||
selection: hex[6],
|
|
||||||
gutterBackground: hex[0],
|
|
||||||
gutterForeground: hex[5],
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: hex[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: hex[4],
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[3] },
|
|
||||||
|
|
||||||
{ tag: t.comment, color: '#54636D' },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[4] },
|
|
||||||
{ tag: [t.attributeName, t.number], color: hex[3] },
|
|
||||||
{ tag: t.keyword, color: hex[1] },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+3
-1
@@ -5,11 +5,14 @@ export const settings = {
|
|||||||
background: '#222',
|
background: '#222',
|
||||||
lineBackground: '#22222299',
|
lineBackground: '#22222299',
|
||||||
foreground: '#fff',
|
foreground: '#fff',
|
||||||
|
// foreground: '#75baff',
|
||||||
caret: '#ffcc00',
|
caret: '#ffcc00',
|
||||||
selection: 'rgba(128, 203, 196, 0.5)',
|
selection: 'rgba(128, 203, 196, 0.5)',
|
||||||
selectionMatch: '#036dd626',
|
selectionMatch: '#036dd626',
|
||||||
|
// lineHighlight: '#8a91991a', // original
|
||||||
lineHighlight: '#00000050',
|
lineHighlight: '#00000050',
|
||||||
gutterBackground: 'transparent',
|
gutterBackground: 'transparent',
|
||||||
|
// gutterForeground: '#8a919966',
|
||||||
gutterForeground: '#8a919966',
|
gutterForeground: '#8a919966',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -17,7 +20,6 @@ export default createTheme({
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
settings,
|
settings,
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#89ddff' },
|
|
||||||
{ tag: t.labelName, color: '#89ddff' },
|
{ tag: t.labelName, color: '#89ddff' },
|
||||||
{ tag: t.keyword, color: '#c792ea' },
|
{ tag: t.keyword, color: '#c792ea' },
|
||||||
{ tag: t.operator, color: '#89ddff' },
|
{ tag: t.operator, color: '#89ddff' },
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
import { createTheme } from './theme-helper.mjs';
|
||||||
|
|
||||||
|
export const settings = {
|
||||||
|
background: '#292A30',
|
||||||
|
lineBackground: '#292A3099',
|
||||||
|
foreground: '#CECFD0',
|
||||||
|
caret: '#fff',
|
||||||
|
selection: '#727377',
|
||||||
|
selectionMatch: '#727377',
|
||||||
|
lineHighlight: '#2F3239',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createTheme({
|
||||||
|
theme: 'dark',
|
||||||
|
settings: {
|
||||||
|
background: '#292A30',
|
||||||
|
foreground: '#CECFD0',
|
||||||
|
caret: '#fff',
|
||||||
|
selection: '#727377',
|
||||||
|
selectionMatch: '#727377',
|
||||||
|
lineHighlight: '#ffffff0f',
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{ tag: [t.comment, t.quote], color: '#7F8C98' },
|
||||||
|
{ tag: [t.keyword], color: '#FF7AB2', fontWeight: 'bold' },
|
||||||
|
{ tag: [t.string, t.meta], color: '#FF8170' },
|
||||||
|
{ tag: [t.typeName], color: '#DABAFF' },
|
||||||
|
{ tag: [t.definition(t.variableName)], color: '#6BDFFF' },
|
||||||
|
{ tag: [t.name], color: '#6BAA9F' },
|
||||||
|
{ tag: [t.variableName], color: '#ACF2E4' },
|
||||||
|
{ tag: [t.regexp, t.link], color: '#FF8170' },
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -458,41 +458,6 @@ export const { drive } = registerControl('drive');
|
|||||||
*/
|
*/
|
||||||
export const { channels, ch } = registerControl('channels', 'ch');
|
export const { channels, ch } = registerControl('channels', 'ch');
|
||||||
|
|
||||||
/**
|
|
||||||
* controls the pulsewidth of the pulse oscillator
|
|
||||||
*
|
|
||||||
* @name pw
|
|
||||||
* @param {number | Pattern} pulsewidth
|
|
||||||
* @example
|
|
||||||
* note("{f a c e}%16").s("pulse").pw(".8:1:.2")
|
|
||||||
* @example
|
|
||||||
* n(run(8)).scale("D:pentatonic").s("pulse").pw("0 .75 .5 1")
|
|
||||||
*/
|
|
||||||
export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* controls the lfo rate for the pulsewidth of the pulse oscillator
|
|
||||||
*
|
|
||||||
* @name pwrate
|
|
||||||
* @param {number | Pattern} rate
|
|
||||||
* @example
|
|
||||||
* n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>")
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const { pwrate } = registerControl('pwrate');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* controls the lfo sweep for the pulsewidth of the pulse oscillator
|
|
||||||
*
|
|
||||||
* @name pwsweep
|
|
||||||
* @param {number | Pattern} sweep
|
|
||||||
* @example
|
|
||||||
* n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>")
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const { pwsweep } = registerControl('pwsweep');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phaser audio effect that approximates popular guitar pedals.
|
* Phaser audio effect that approximates popular guitar pedals.
|
||||||
*
|
*
|
||||||
@@ -1496,7 +1461,7 @@ export const { vowel } = registerControl('vowel');
|
|||||||
* @name waveloss
|
* @name waveloss
|
||||||
*/
|
*/
|
||||||
export const { waveloss } = registerControl('waveloss');
|
export const { waveloss } = registerControl('waveloss');
|
||||||
/**
|
/*
|
||||||
* Noise crackle density
|
* Noise crackle density
|
||||||
*
|
*
|
||||||
* @name density
|
* @name density
|
||||||
@@ -1815,32 +1780,3 @@ export const as = register('as', (mapping, pat) => {
|
|||||||
return v;
|
return v;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file
|
|
||||||
* in the optional array syntax ex: "0.5:2", the second value controls the speed of playback
|
|
||||||
* @name scrub
|
|
||||||
* @memberof Pattern
|
|
||||||
* @returns Pattern
|
|
||||||
* @example
|
|
||||||
* samples('github:switchangel/pad')
|
|
||||||
* s("swpad:0").scrub("{0.1!2 .25@3 0.7!2 <0.8:1.5>}%8")
|
|
||||||
* @example
|
|
||||||
* samples('github:yaxu/clean-breaks/main');
|
|
||||||
* s("amen/4").fit().scrub("{0@3 0@2 4@3}%8".div(16))
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const scrub = register(
|
|
||||||
'scrub',
|
|
||||||
(beginPat, pat) => {
|
|
||||||
return beginPat.outerBind((v) => {
|
|
||||||
if (!Array.isArray(v)) {
|
|
||||||
v = [v];
|
|
||||||
}
|
|
||||||
const [beginVal, speedMultiplier = 1] = v;
|
|
||||||
|
|
||||||
return pat.begin(beginVal).mul(speed(speedMultiplier)).clip(1);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/core",
|
"name": "@strudel/core",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "Port of Tidal Cycles to JavaScript",
|
"description": "Port of Tidal Cycles to JavaScript",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
numeralArgs,
|
numeralArgs,
|
||||||
parseNumeral,
|
parseNumeral,
|
||||||
pairs,
|
pairs,
|
||||||
|
listifyOp,
|
||||||
} from './util.mjs';
|
} from './util.mjs';
|
||||||
import drawLine from './drawLine.mjs';
|
import drawLine from './drawLine.mjs';
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
@@ -1049,7 +1050,8 @@ function _composeOp(a, b, func) {
|
|||||||
const hows = ['In', 'Out', 'Mix', 'Squeeze', 'SqueezeOut', 'Reset', 'Restart', 'Poly'];
|
const hows = ['In', 'Out', 'Mix', 'Squeeze', 'SqueezeOut', 'Reset', 'Restart', 'Poly'];
|
||||||
|
|
||||||
// generate methods to do what and how
|
// generate methods to do what and how
|
||||||
for (const [what, [op, preprocess]] of Object.entries(composers)) {
|
for (const [what, [_op, preprocess]] of Object.entries(composers)) {
|
||||||
|
const op = listifyOp(_op);
|
||||||
// make plain version, e.g. pat._add(value) adds that plain value
|
// make plain version, e.g. pat._add(value) adds that plain value
|
||||||
// to all the values in pat
|
// to all the values in pat
|
||||||
Pattern.prototype['_' + what] = function (value) {
|
Pattern.prototype['_' + what] = function (value) {
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ export const pickmodReset = register('pickmodReset', function (lookup, pat) {
|
|||||||
return _pick(lookup, pat, true).resetJoin();
|
return _pick(lookup, pat, true).resetJoin();
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
/**
|
||||||
|
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
||||||
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
|
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
|
||||||
* @name inhabit
|
* @name inhabit
|
||||||
* @synonyms pickSqueeze
|
* @synonyms pickSqueeze
|
||||||
|
|||||||
@@ -279,9 +279,9 @@ const _rearrangeWith = (ipat, n, pat) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @name shuffle
|
||||||
* Slices a pattern into the given number of parts, then plays those parts in random order.
|
* Slices a pattern into the given number of parts, then plays those parts in random order.
|
||||||
* Each part will be played exactly once per cycle.
|
* Each part will be played exactly once per cycle.
|
||||||
* @name shuffle
|
|
||||||
* @example
|
* @example
|
||||||
* note("c d e f").sound("piano").shuffle(4)
|
* note("c d e f").sound("piano").shuffle(4)
|
||||||
* @example
|
* @example
|
||||||
@@ -292,9 +292,9 @@ export const shuffle = register('shuffle', (n, pat) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @name scramble
|
||||||
* Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`,
|
* Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`,
|
||||||
* but parts might be played more than once, or not at all, per cycle.
|
* but parts might be played more than once, or not at all, per cycle.
|
||||||
* @name scramble
|
|
||||||
* @example
|
* @example
|
||||||
* note("c d e f").sound("piano").scramble(4)
|
* note("c d e f").sound("piano").scramble(4)
|
||||||
* @example
|
* @example
|
||||||
|
|||||||
@@ -466,6 +466,16 @@ export function getCurrentKeyboardState() {
|
|||||||
return { ...keyState }; // Return a shallow copy of the key state object
|
return { ...keyState }; // Return a shallow copy of the key state object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listifyOp(op) {
|
||||||
|
return function (a, b) {
|
||||||
|
if (!Array.isArray(a)) {
|
||||||
|
return op(a, Array.isArray(b) ? b[0] : b);
|
||||||
|
}
|
||||||
|
b = Array.isArray(b) ? b : [b];
|
||||||
|
return a.map((x, i) => (i < b.length ? op(x, b[i]) : x));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Floating point versions, see Fraction for rational versions
|
// Floating point versions, see Fraction for rational versions
|
||||||
// // greatest common divisor
|
// // greatest common divisor
|
||||||
// export const gcd = function (x, y, ...z) {
|
// export const gcd = function (x, y, ...z) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/csound",
|
"name": "@strudel/csound",
|
||||||
"version": "1.2.2",
|
"version": "1.1.0",
|
||||||
"description": "csound bindings for strudel",
|
"description": "csound bindings for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/draw",
|
"name": "@strudel/draw",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "Helpers for drawing with Strudel",
|
"description": "Helpers for drawing with Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,37 +1,38 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/gamepad",
|
"name": "@strudel/gamepad",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "Gamepad Inputs for strudel",
|
"description": "Gamepad Inputs for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"main": "dist/index.mjs"
|
"main": "dist/index.mjs"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"titdalcycles",
|
"titdalcycles",
|
||||||
"strudel",
|
"strudel",
|
||||||
"pattern",
|
"pattern",
|
||||||
"livecoding",
|
"livecoding",
|
||||||
"algorave"
|
"algorave"
|
||||||
],
|
],
|
||||||
"author": "Yuta Nakayama <nkymut@gmail.com>",
|
"author": "Yuta Nakayama <nkymut@gmail.com>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/core": "workspace:*"
|
"@strudel/core": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hs2js",
|
"name": "hs2js",
|
||||||
"version": "0.2.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
|
||||||
"description": "Experimental Haskell in JavaScript interpreter",
|
"description": "Experimental Haskell in JavaScript interpreter",
|
||||||
"main": "src/index.mjs",
|
"main": "src/index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/hydra",
|
"name": "@strudel/hydra",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"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.2",
|
"version": "1.1.0",
|
||||||
"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.1",
|
"version": "1.1.0",
|
||||||
"description": "Mini notation for strudel",
|
"description": "Mini notation for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,37 +1,38 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/motion",
|
"name": "@strudel/motion",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "DeviceMotion API for strudel",
|
"description": "DeviceMotion API for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"main": "dist/index.mjs"
|
"main": "dist/index.mjs"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"titdalcycles",
|
"titdalcycles",
|
||||||
"strudel",
|
"strudel",
|
||||||
"pattern",
|
"pattern",
|
||||||
"livecoding",
|
"livecoding",
|
||||||
"algorave"
|
"algorave"
|
||||||
],
|
],
|
||||||
"author": "Yuta Nakayama <nkymut@gmail.com>",
|
"author": "Yuta Nakayama <nkymut@gmail.com>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/core": "workspace:*"
|
"@strudel/core": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/mqtt",
|
"name": "@strudel/mqtt",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "MQTT API for strudel",
|
"description": "MQTT API for strudel",
|
||||||
"main": "mqtt.mjs",
|
"main": "mqtt.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ function connect() {
|
|||||||
export function parseControlsFromHap(hap, cps) {
|
export function parseControlsFromHap(hap, cps) {
|
||||||
hap.ensureObjectValue();
|
hap.ensureObjectValue();
|
||||||
const cycle = hap.wholeOrPart().begin.valueOf();
|
const cycle = hap.wholeOrPart().begin.valueOf();
|
||||||
const delta = hap.duration.valueOf() / cps;
|
const delta = hap.duration.valueOf();
|
||||||
const controls = Object.assign({}, { cps, cycle, delta }, hap.value);
|
const controls = Object.assign({}, { cps, cycle, delta }, hap.value);
|
||||||
// make sure n and note are numbers
|
// make sure n and note are numbers
|
||||||
controls.n && (controls.n = parseNumeral(controls.n));
|
controls.n && (controls.n = parseNumeral(controls.n));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/osc",
|
"name": "@strudel/osc",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"description": "OSC messaging for strudel",
|
"description": "OSC messaging for strudel",
|
||||||
"main": "osc.mjs",
|
"main": "osc.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/reference",
|
"name": "@strudel/reference",
|
||||||
"version": "1.2.0",
|
"version": "1.1.0",
|
||||||
"description": "Headless reference of all strudel functions",
|
"description": "Headless reference of all strudel functions",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
|
"dependencies": {
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
import { dependencies } from './package.json';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
@@ -10,6 +11,9 @@ export default defineConfig({
|
|||||||
formats: ['es'],
|
formats: ['es'],
|
||||||
fileName: (ext) => ({ es: 'index.mjs' })[ext],
|
fileName: (ext) => ({ es: 'index.mjs' })[ext],
|
||||||
},
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: [...Object.keys(dependencies)],
|
||||||
|
},
|
||||||
target: 'esnext',
|
target: 'esnext',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/repl",
|
"name": "@strudel/repl",
|
||||||
"version": "1.2.2",
|
"version": "1.1.0",
|
||||||
"description": "Strudel REPL as a Web Component",
|
"description": "Strudel REPL as a Web Component",
|
||||||
"module": "index.mjs",
|
"module": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-replace": "^6.0.2",
|
"@rollup/plugin-replace": "^6.0.2",
|
||||||
"vite": "^6.0.11",
|
"vite": "^6.0.11"
|
||||||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
import bundleAudioWorkletPlugin from 'vite-plugin-bundle-audioworklet';
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: './',
|
base: './',
|
||||||
plugins: [bundleAudioWorkletPlugin()],
|
plugins: [],
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'index.mjs'),
|
entry: resolve(__dirname, 'index.mjs'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/sampler",
|
"name": "@strudel/sampler",
|
||||||
"version": "0.2.0",
|
"version": "0.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"tidalcycles",
|
"tidalcycles",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/serial",
|
"name": "@strudel/serial",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"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.2",
|
"version": "1.1.0",
|
||||||
"description": "Soundsfont support for strudel",
|
"description": "Soundsfont support for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -212,7 +212,6 @@ export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) {
|
|||||||
constantNode.onended = () => {
|
constantNode.onended = () => {
|
||||||
onComplete();
|
onComplete();
|
||||||
};
|
};
|
||||||
return constantNode;
|
|
||||||
}
|
}
|
||||||
const mod = (freq, range = 1, type = 'sine') => {
|
const mod = (freq, range = 1, type = 'sine') => {
|
||||||
const ctx = getAudioContext();
|
const ctx = getAudioContext();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "superdough",
|
"name": "superdough",
|
||||||
"version": "1.2.2",
|
"version": "1.1.0",
|
||||||
"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",
|
||||||
@@ -32,8 +32,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11",
|
"vite": "^6.0.11"
|
||||||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanostores": "^0.11.3"
|
"nanostores": "^0.11.3"
|
||||||
|
|||||||
@@ -344,9 +344,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
|||||||
};
|
};
|
||||||
let envEnd = holdEnd + release + 0.01;
|
let envEnd = holdEnd + release + 0.01;
|
||||||
bufferSource.stop(envEnd);
|
bufferSource.stop(envEnd);
|
||||||
const stop = (endTime) => {
|
const stop = (endTime, playWholeBuffer) => {};
|
||||||
bufferSource.stop(endTime);
|
|
||||||
};
|
|
||||||
const handle = { node: out, bufferSource, stop };
|
const handle = { node: out, bufferSource, stop };
|
||||||
|
|
||||||
// cut groups
|
// cut groups
|
||||||
|
|||||||
@@ -8,26 +8,12 @@ import './feedbackdelay.mjs';
|
|||||||
import './reverb.mjs';
|
import './reverb.mjs';
|
||||||
import './vowel.mjs';
|
import './vowel.mjs';
|
||||||
import { clamp, nanFallback, _mod } from './util.mjs';
|
import { clamp, nanFallback, _mod } from './util.mjs';
|
||||||
import workletsUrl from './worklets.mjs?audioworklet';
|
import workletsUrl from './worklets.mjs?worker&url';
|
||||||
import { createFilter, gainNode, getCompressor, getWorklet } from './helpers.mjs';
|
import { createFilter, gainNode, getCompressor, getWorklet } 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';
|
||||||
|
|
||||||
export const DEFAULT_MAX_POLYPHONY = 128;
|
|
||||||
const DEFAULT_AUDIO_DEVICE_NAME = 'System Standard';
|
|
||||||
|
|
||||||
let maxPolyphony = DEFAULT_MAX_POLYPHONY;
|
|
||||||
|
|
||||||
export function setMaxPolyphony(polyphony) {
|
|
||||||
maxPolyphony = parseInt(polyphony) ?? DEFAULT_MAX_POLYPHONY;
|
|
||||||
}
|
|
||||||
|
|
||||||
let multiChannelOrbits = false;
|
|
||||||
export function setMultiChannelOrbits(bool) {
|
|
||||||
multiChannelOrbits = bool == true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const soundMap = map();
|
export const soundMap = map();
|
||||||
|
|
||||||
export function registerSound(key, onTrigger, data = {}) {
|
export function registerSound(key, onTrigger, data = {}) {
|
||||||
@@ -35,16 +21,6 @@ export function registerSound(key, onTrigger, data = {}) {
|
|||||||
soundMap.setKey(key, { onTrigger, data });
|
soundMap.setKey(key, { onTrigger, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
let gainCurveFunc = (val) => val;
|
|
||||||
|
|
||||||
export function applyGainCurve(val) {
|
|
||||||
return gainCurveFunc(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setGainCurve(newGainCurveFunc) {
|
|
||||||
gainCurveFunc = newGainCurveFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function aliasBankMap(aliasMap) {
|
function aliasBankMap(aliasMap) {
|
||||||
// Make all bank keys lower case for case insensitivity
|
// Make all bank keys lower case for case insensitivity
|
||||||
for (const key in aliasMap) {
|
for (const key in aliasMap) {
|
||||||
@@ -110,18 +86,6 @@ export function getSound(s) {
|
|||||||
return soundMap.get()[s.toLowerCase()];
|
return soundMap.get()[s.toLowerCase()];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAudioDevices = async () => {
|
|
||||||
await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
||||||
let mediaDevices = await navigator.mediaDevices.enumerateDevices();
|
|
||||||
mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default');
|
|
||||||
const devicesMap = new Map();
|
|
||||||
devicesMap.set(DEFAULT_AUDIO_DEVICE_NAME, '');
|
|
||||||
mediaDevices.forEach((device) => {
|
|
||||||
devicesMap.set(device.label, device.deviceId);
|
|
||||||
});
|
|
||||||
return devicesMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultDefaultValues = {
|
const defaultDefaultValues = {
|
||||||
s: 'triangle',
|
s: 'triangle',
|
||||||
gain: 0.8,
|
gain: 0.8,
|
||||||
@@ -192,47 +156,18 @@ export function getAudioContextCurrentTime() {
|
|||||||
let workletsLoading;
|
let workletsLoading;
|
||||||
function loadWorklets() {
|
function loadWorklets() {
|
||||||
if (!workletsLoading) {
|
if (!workletsLoading) {
|
||||||
const audioCtx = getAudioContext();
|
workletsLoading = getAudioContext().audioWorklet.addModule(workletsUrl);
|
||||||
workletsLoading = audioCtx.audioWorklet.addModule(workletsUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return workletsLoading;
|
return workletsLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function should be called on first user interaction (to avoid console warning)
|
// this function should be called on first user interaction (to avoid console warning)
|
||||||
export async function initAudio(options = {}) {
|
export async function initAudio(options = {}) {
|
||||||
const {
|
const { disableWorklets = false } = options;
|
||||||
disableWorklets = false,
|
|
||||||
maxPolyphony,
|
|
||||||
audioDeviceName = DEFAULT_AUDIO_DEVICE_NAME,
|
|
||||||
multiChannelOrbits = false,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
setMaxPolyphony(maxPolyphony);
|
|
||||||
setMultiChannelOrbits(multiChannelOrbits);
|
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await getAudioContext().resume();
|
||||||
const audioCtx = getAudioContext();
|
|
||||||
|
|
||||||
if (audioDeviceName != null && audioDeviceName != DEFAULT_AUDIO_DEVICE_NAME) {
|
|
||||||
try {
|
|
||||||
const devices = await getAudioDevices();
|
|
||||||
const id = devices.get(audioDeviceName);
|
|
||||||
const isValidID = (id ?? '').length > 0;
|
|
||||||
if (audioCtx.sinkId !== id && isValidID) {
|
|
||||||
await audioCtx.setSinkId(id);
|
|
||||||
}
|
|
||||||
logger(
|
|
||||||
`[superdough] Audio Device set to ${audioDeviceName}, it might take a few seconds before audio plays on all output channels`,
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
logger('[superdough] failed to set audio interface', 'warning');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await audioCtx.resume();
|
|
||||||
if (disableWorklets) {
|
if (disableWorklets) {
|
||||||
logger('[superdough]: AudioWorklets disabled with disableWorklets');
|
logger('[superdough]: AudioWorklets disabled with disableWorklets');
|
||||||
return;
|
return;
|
||||||
@@ -290,7 +225,7 @@ export const connectToDestination = (input, channels = [0, 1]) => {
|
|||||||
});
|
});
|
||||||
stereoMix.connect(splitter);
|
stereoMix.connect(splitter);
|
||||||
channels.forEach((ch, i) => {
|
channels.forEach((ch, i) => {
|
||||||
splitter.connect(channelMerger, i % stereoMix.channelCount, ch % ctx.destination.channelCount);
|
splitter.connect(channelMerger, i % stereoMix.channelCount, clamp(ch, 0, ctx.destination.channelCount - 1));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -303,7 +238,7 @@ export const panic = () => {
|
|||||||
channelMerger == null;
|
channelMerger == null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getDelay(orbit, delaytime, delayfeedback, t, channels) {
|
function getDelay(orbit, delaytime, delayfeedback, t) {
|
||||||
if (delayfeedback > maxfeedback) {
|
if (delayfeedback > maxfeedback) {
|
||||||
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
||||||
}
|
}
|
||||||
@@ -312,7 +247,7 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) {
|
|||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||||
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||||
connectToDestination(dly, channels);
|
connectToDestination(dly, [0, 1]);
|
||||||
delays[orbit] = dly;
|
delays[orbit] = dly;
|
||||||
}
|
}
|
||||||
delays[orbit].delayTime.value !== delaytime && delays[orbit].delayTime.setValueAtTime(delaytime, t);
|
delays[orbit].delayTime.value !== delaytime && delays[orbit].delayTime.setValueAtTime(delaytime, t);
|
||||||
@@ -320,9 +255,16 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) {
|
|||||||
return delays[orbit];
|
return delays[orbit];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLfo(audioContext, time, end, properties = {}) {
|
function getPhaser(time, end, frequency = 1, depth = 0.5, centerFrequency = 1000, sweep = 2000) {
|
||||||
return getWorklet(audioContext, 'lfo-processor', {
|
//gain
|
||||||
frequency: 1,
|
const ac = getAudioContext();
|
||||||
|
const lfoGain = ac.createGain();
|
||||||
|
lfoGain.gain.value = sweep * 2;
|
||||||
|
// centerFrequency = centerFrequency * 2;
|
||||||
|
// sweep = sweep * 1.5;
|
||||||
|
|
||||||
|
const lfo = getWorklet(ac, 'lfo-processor', {
|
||||||
|
frequency,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
skew: 0,
|
skew: 0,
|
||||||
phaseoffset: 0,
|
phaseoffset: 0,
|
||||||
@@ -330,13 +272,8 @@ export function getLfo(audioContext, time, end, properties = {}) {
|
|||||||
end,
|
end,
|
||||||
shape: 1,
|
shape: 1,
|
||||||
dcoffset: -0.5,
|
dcoffset: -0.5,
|
||||||
...properties,
|
|
||||||
});
|
});
|
||||||
}
|
lfo.connect(lfoGain);
|
||||||
|
|
||||||
function getPhaser(time, end, frequency = 1, depth = 0.5, centerFrequency = 1000, sweep = 2000) {
|
|
||||||
const ac = getAudioContext();
|
|
||||||
const lfoGain = getLfo(ac, time, end, { frequency, depth: sweep * 2 });
|
|
||||||
|
|
||||||
//filters
|
//filters
|
||||||
const numStages = 2; //num of filters in series
|
const numStages = 2; //num of filters in series
|
||||||
@@ -367,12 +304,12 @@ function getFilterType(ftype) {
|
|||||||
|
|
||||||
let reverbs = {};
|
let reverbs = {};
|
||||||
let hasChanged = (now, before) => now !== undefined && now !== before;
|
let hasChanged = (now, before) => now !== undefined && now !== before;
|
||||||
function getReverb(orbit, duration, fade, lp, dim, ir, channels) {
|
function getReverb(orbit, duration, fade, lp, dim, ir) {
|
||||||
// If no reverb has been created for a given orbit, create one
|
// If no reverb has been created for a given orbit, create one
|
||||||
if (!reverbs[orbit]) {
|
if (!reverbs[orbit]) {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
const reverb = ac.createReverb(duration, fade, lp, dim, ir);
|
const reverb = ac.createReverb(duration, fade, lp, dim, ir);
|
||||||
connectToDestination(reverb, channels);
|
connectToDestination(reverb, [0, 1]);
|
||||||
reverbs[orbit] = reverb;
|
reverbs[orbit] = reverb;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -438,13 +375,6 @@ export function resetGlobalEffects() {
|
|||||||
analysersData = {};
|
analysersData = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
let activeSoundSources = new Map();
|
|
||||||
//music programs/audio gear usually increments inputs/outputs from 1, we need to subtract 1 from the input because the webaudio API channels start at 0
|
|
||||||
|
|
||||||
function mapChannelNumbers(channels) {
|
|
||||||
return (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const superdough = async (value, t, hapDuration) => {
|
export const superdough = async (value, t, hapDuration) => {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t;
|
t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t;
|
||||||
@@ -506,7 +436,7 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
bpsustain,
|
bpsustain,
|
||||||
bprelease,
|
bprelease,
|
||||||
bandq = getDefaultValue('bandq'),
|
bandq = getDefaultValue('bandq'),
|
||||||
|
channels = getDefaultValue('channels'),
|
||||||
//phaser
|
//phaser
|
||||||
phaserrate: phaser,
|
phaserrate: phaser,
|
||||||
phaserdepth = getDefaultValue('phaserdepth'),
|
phaserdepth = getDefaultValue('phaserdepth'),
|
||||||
@@ -542,34 +472,16 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
compressorRelease,
|
compressorRelease,
|
||||||
} = value;
|
} = value;
|
||||||
|
|
||||||
const orbitChannels = mapChannelNumbers(
|
gain = nanFallback(gain, 1);
|
||||||
multiChannelOrbits && orbit > 0 ? [orbit * 2 - 1, orbit * 2] : getDefaultValue('channels'),
|
|
||||||
);
|
//music programs/audio gear usually increments inputs/outputs from 1, so imitate that behavior
|
||||||
const channels = value.channels != null ? mapChannelNumbers(value.channels) : orbitChannels;
|
channels = (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1);
|
||||||
|
|
||||||
gain = applyGainCurve(nanFallback(gain, 1));
|
|
||||||
postgain = applyGainCurve(postgain);
|
|
||||||
shapevol = applyGainCurve(shapevol);
|
|
||||||
distortvol = applyGainCurve(distortvol);
|
|
||||||
delay = applyGainCurve(delay);
|
|
||||||
velocity = applyGainCurve(velocity);
|
|
||||||
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
||||||
|
let toDisconnect = []; // audio nodes that will be disconnected when the source has ended
|
||||||
const chainID = Math.round(Math.random() * 1000000);
|
const onended = () => {
|
||||||
|
toDisconnect.forEach((n) => n?.disconnect());
|
||||||
// oldest audio nodes will be destroyed if maximum polyphony is exceeded
|
};
|
||||||
for (let i = 0; i <= activeSoundSources.size - maxPolyphony; i++) {
|
|
||||||
const ch = activeSoundSources.entries().next();
|
|
||||||
const source = ch.value[1];
|
|
||||||
const chainID = ch.value[0];
|
|
||||||
const endTime = t + 0.25;
|
|
||||||
source?.node?.gain?.linearRampToValueAtTime(0, endTime);
|
|
||||||
source?.stop?.(endTime);
|
|
||||||
activeSoundSources.delete(chainID);
|
|
||||||
}
|
|
||||||
|
|
||||||
let audioNodes = [];
|
|
||||||
|
|
||||||
if (bank && s) {
|
if (bank && s) {
|
||||||
s = `${bank}_${s}`;
|
s = `${bank}_${s}`;
|
||||||
value.s = s;
|
value.s = s;
|
||||||
@@ -581,15 +493,10 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
sourceNode = source(t, value, hapDuration);
|
sourceNode = source(t, value, hapDuration);
|
||||||
} else if (getSound(s)) {
|
} else if (getSound(s)) {
|
||||||
const { onTrigger } = getSound(s);
|
const { onTrigger } = getSound(s);
|
||||||
const onEnded = () => {
|
const soundHandle = await onTrigger(t, value, onended);
|
||||||
audioNodes.forEach((n) => n?.disconnect());
|
|
||||||
activeSoundSources.delete(chainID);
|
|
||||||
};
|
|
||||||
const soundHandle = await onTrigger(t, value, onEnded);
|
|
||||||
|
|
||||||
if (soundHandle) {
|
if (soundHandle) {
|
||||||
sourceNode = soundHandle.node;
|
sourceNode = soundHandle.node;
|
||||||
activeSoundSources.set(chainID, soundHandle);
|
soundHandle.stop(t + hapDuration);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`sound ${s} not found! Is it loaded?`);
|
throw new Error(`sound ${s} not found! Is it loaded?`);
|
||||||
@@ -717,9 +624,8 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
// delay
|
// delay
|
||||||
let delaySend;
|
let delaySend;
|
||||||
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||||
const delyNode = getDelay(orbit, delaytime, delayfeedback, t, orbitChannels);
|
const delyNode = getDelay(orbit, delaytime, delayfeedback, t);
|
||||||
delaySend = effectSend(post, delyNode, delay);
|
delaySend = effectSend(post, delyNode, delay);
|
||||||
audioNodes.push(delaySend);
|
|
||||||
}
|
}
|
||||||
// reverb
|
// reverb
|
||||||
let reverbSend;
|
let reverbSend;
|
||||||
@@ -735,9 +641,8 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
}
|
}
|
||||||
roomIR = await loadBuffer(url, ac, ir, 0);
|
roomIR = await loadBuffer(url, ac, ir, 0);
|
||||||
}
|
}
|
||||||
const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, roomIR, orbitChannels);
|
const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, roomIR);
|
||||||
reverbSend = effectSend(post, reverbNode, room);
|
reverbSend = effectSend(post, reverbNode, room);
|
||||||
audioNodes.push(reverbSend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyser
|
// analyser
|
||||||
@@ -745,12 +650,14 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
if (analyze) {
|
if (analyze) {
|
||||||
const analyserNode = getAnalyserById(analyze, 2 ** (fft + 5));
|
const analyserNode = getAnalyserById(analyze, 2 ** (fft + 5));
|
||||||
analyserSend = effectSend(post, analyserNode, 1);
|
analyserSend = effectSend(post, analyserNode, 1);
|
||||||
audioNodes.push(analyserSend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect chain elements together
|
// connect chain elements together
|
||||||
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
||||||
audioNodes = audioNodes.concat(chain);
|
|
||||||
|
// toDisconnect = all the node that should be disconnected in onended callback
|
||||||
|
// this is crucial for performance
|
||||||
|
toDisconnect = chain.concat([delaySend, reverbSend, analyserSend]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const superdoughTrigger = (t, hap, ct, cps) => {
|
export const superdoughTrigger = (t, hap, ct, cps) => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { clamp, midiToFreq, noteToMidi } from './util.mjs';
|
import { clamp, midiToFreq, noteToMidi } from './util.mjs';
|
||||||
import { registerSound, getAudioContext, getLfo } from './superdough.mjs';
|
import { registerSound, getAudioContext } from './superdough.mjs';
|
||||||
import {
|
import {
|
||||||
applyFM,
|
applyFM,
|
||||||
gainNode,
|
gainNode,
|
||||||
@@ -25,13 +25,6 @@ const getFrequencyFromValue = (value) => {
|
|||||||
|
|
||||||
return Number(freq);
|
return Number(freq);
|
||||||
};
|
};
|
||||||
function destroyAudioWorkletNode(node) {
|
|
||||||
if (node == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
node.disconnect();
|
|
||||||
node.parameters.get('end')?.setValueAtTime(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
||||||
const noises = ['pink', 'white', 'brown', 'crackle'];
|
const noises = ['pink', 'white', 'brown', 'crackle'];
|
||||||
@@ -70,9 +63,7 @@ export function registerSynthSounds() {
|
|||||||
stop(envEnd);
|
stop(envEnd);
|
||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
stop: (endTime) => {
|
stop: (releaseTime) => {},
|
||||||
stop(endTime);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ type: 'synth', prebake: true },
|
{ type: 'synth', prebake: true },
|
||||||
@@ -119,12 +110,10 @@ export function registerSynthSounds() {
|
|||||||
let envGain = gainNode(1);
|
let envGain = gainNode(1);
|
||||||
envGain = o.connect(envGain);
|
envGain = o.connect(envGain);
|
||||||
|
|
||||||
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear');
|
webAudioTimeout(
|
||||||
|
|
||||||
let timeoutNode = webAudioTimeout(
|
|
||||||
ac,
|
ac,
|
||||||
() => {
|
() => {
|
||||||
destroyAudioWorkletNode(o);
|
o.disconnect();
|
||||||
envGain.disconnect();
|
envGain.disconnect();
|
||||||
onended();
|
onended();
|
||||||
fm?.stop();
|
fm?.stop();
|
||||||
@@ -134,11 +123,11 @@ export function registerSynthSounds() {
|
|||||||
end,
|
end,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
node: envGain,
|
node: envGain,
|
||||||
stop: (time) => {
|
stop: (time) => {},
|
||||||
timeoutNode.stop(time);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ prebake: true, type: 'synth' },
|
{ prebake: true, type: 'synth' },
|
||||||
@@ -148,20 +137,7 @@ export function registerSynthSounds() {
|
|||||||
'pulse',
|
'pulse',
|
||||||
(begin, value, onended) => {
|
(begin, value, onended) => {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
let { pwrate, pwsweep } = value;
|
let { duration, n: pulsewidth = 0.5 } = value;
|
||||||
if (pwsweep == null) {
|
|
||||||
if (pwrate != null) {
|
|
||||||
pwsweep = 0.3;
|
|
||||||
} else {
|
|
||||||
pwsweep = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pwrate == null && pwsweep != null) {
|
|
||||||
pwrate = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { duration, pw: pulsewidth = 0.5 } = value;
|
|
||||||
const frequency = getFrequencyFromValue(value);
|
const frequency = getFrequencyFromValue(value);
|
||||||
|
|
||||||
const [attack, decay, sustain, release] = getADSRValues(
|
const [attack, decay, sustain, release] = getADSRValues(
|
||||||
@@ -169,8 +145,10 @@ export function registerSynthSounds() {
|
|||||||
'linear',
|
'linear',
|
||||||
[0.001, 0.05, 0.6, 0.01],
|
[0.001, 0.05, 0.6, 0.01],
|
||||||
);
|
);
|
||||||
|
|
||||||
const holdend = begin + duration;
|
const holdend = begin + duration;
|
||||||
const end = holdend + release + 0.01;
|
const end = holdend + release + 0.01;
|
||||||
|
|
||||||
let o = getWorklet(
|
let o = getWorklet(
|
||||||
ac,
|
ac,
|
||||||
'pulse-oscillator',
|
'pulse-oscillator',
|
||||||
@@ -191,17 +169,10 @@ export function registerSynthSounds() {
|
|||||||
let envGain = gainNode(1);
|
let envGain = gainNode(1);
|
||||||
envGain = o.connect(envGain);
|
envGain = o.connect(envGain);
|
||||||
|
|
||||||
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear');
|
webAudioTimeout(
|
||||||
let lfo;
|
|
||||||
if (pwsweep != 0) {
|
|
||||||
lfo = getLfo(ac, begin, end, { frequency: pwrate, depth: pwsweep });
|
|
||||||
lfo.connect(o.parameters.get('pulsewidth'));
|
|
||||||
}
|
|
||||||
let timeoutNode = webAudioTimeout(
|
|
||||||
ac,
|
ac,
|
||||||
() => {
|
() => {
|
||||||
destroyAudioWorkletNode(o);
|
o.disconnect();
|
||||||
destroyAudioWorkletNode(lfo);
|
|
||||||
envGain.disconnect();
|
envGain.disconnect();
|
||||||
onended();
|
onended();
|
||||||
fm?.stop();
|
fm?.stop();
|
||||||
@@ -211,11 +182,11 @@ export function registerSynthSounds() {
|
|||||||
end,
|
end,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
node: envGain,
|
node: envGain,
|
||||||
stop: (time) => {
|
stop: (time) => {},
|
||||||
timeoutNode.stop(time);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ prebake: true, type: 'synth' },
|
{ prebake: true, type: 'synth' },
|
||||||
@@ -258,9 +229,7 @@ export function registerSynthSounds() {
|
|||||||
stop(envEnd);
|
stop(envEnd);
|
||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
stop: (endTime) => {
|
stop: (releaseTime) => {},
|
||||||
stop(endTime);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ type: 'synth', prebake: true },
|
{ type: 'synth', prebake: true },
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import { dependencies } from './package.json';
|
import { dependencies } from './package.json';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import bundleAudioWorkletPlugin from 'vite-plugin-bundle-audioworklet';
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [bundleAudioWorkletPlugin()],
|
plugins: [],
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'index.mjs'),
|
entry: resolve(__dirname, 'index.mjs'),
|
||||||
|
|||||||
@@ -710,9 +710,6 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process(inputs, outputs, params) {
|
process(inputs, outputs, params) {
|
||||||
if (this.disconnected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (currentTime <= params.begin[0]) {
|
if (currentTime <= params.begin[0]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/tidal",
|
"name": "@strudel/tidal",
|
||||||
"version": "0.2.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
|
||||||
"description": "Experimental Tidal Code interpreter",
|
"description": "Experimental Tidal Code interpreter",
|
||||||
"module": "tidal.mjs",
|
"module": "tidal.mjs",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/tonal",
|
"name": "@strudel/tonal",
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"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.1",
|
"version": "1.1.0",
|
||||||
"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,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "vite-plugin-bundle-audioworklet",
|
|
||||||
"main": "./vite-plugin-bundle-audioworklet.js",
|
|
||||||
"version": "0.1.1",
|
|
||||||
"description": "",
|
|
||||||
"keywords": [
|
|
||||||
"vite",
|
|
||||||
"audioworklet"
|
|
||||||
],
|
|
||||||
"author": "Felix Roos <flix91@gmail.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "module",
|
|
||||||
"devDependencies": {
|
|
||||||
"vite": "^6.0.11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import { createLogger, build } from 'vite';
|
|
||||||
|
|
||||||
const end = '?audioworklet';
|
|
||||||
|
|
||||||
function bundleAudioWorkletPlugin() /* : PluginOption */ {
|
|
||||||
let viteConfig /* : UserConfig */;
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'vite-plugin-bundle-audioworklet',
|
|
||||||
/* apply: 'build', */
|
|
||||||
enforce: 'post',
|
|
||||||
|
|
||||||
config(config) {
|
|
||||||
viteConfig = config;
|
|
||||||
},
|
|
||||||
|
|
||||||
async transform(_code, id) {
|
|
||||||
if (!id.endsWith(end)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const entry = id.replace(end, '');
|
|
||||||
const quietLogger = createLogger();
|
|
||||||
quietLogger.info = () => undefined;
|
|
||||||
|
|
||||||
const output = await build({
|
|
||||||
configFile: false,
|
|
||||||
clearScreen: false,
|
|
||||||
customLogger: quietLogger,
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry,
|
|
||||||
name: '_',
|
|
||||||
formats: ['iife'],
|
|
||||||
},
|
|
||||||
write: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!(output instanceof Array)) {
|
|
||||||
throw new Error('Expected output to be Array');
|
|
||||||
}
|
|
||||||
const iife = output[0].output[0].code;
|
|
||||||
const encoded = Buffer.from(iife, 'utf8').toString('base64');
|
|
||||||
return `export default "data:text/javascript;base64,${encoded}";`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default bundleAudioWorkletPlugin;
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/web",
|
"name": "@strudel/web",
|
||||||
"version": "1.2.2",
|
"version": "1.1.0",
|
||||||
"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": {
|
||||||
@@ -41,7 +41,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-replace": "^6.0.2",
|
"@rollup/plugin-replace": "^6.0.2",
|
||||||
"vite": "^6.0.11",
|
"vite": "^6.0.11"
|
||||||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import { defineConfig } from 'vite';
|
|||||||
import { dependencies } from './package.json';
|
import { dependencies } from './package.json';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
import bundleAudioWorkletPlugin from 'vite-plugin-bundle-audioworklet';
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: './',
|
base: './',
|
||||||
plugins: [bundleAudioWorkletPlugin()],
|
plugins: [],
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'web.mjs'),
|
entry: resolve(__dirname, 'web.mjs'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/webaudio",
|
"name": "@strudel/webaudio",
|
||||||
"version": "1.2.2",
|
"version": "1.1.0",
|
||||||
"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.1",
|
"version": "1.1.0",
|
||||||
"description": "Xenharmonic API for strudel",
|
"description": "Xenharmonic API for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
Generated
+3
-23
@@ -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)
|
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))
|
||||||
'@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)
|
||||||
@@ -75,9 +75,6 @@ importers:
|
|||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.4.2
|
specifier: ^3.4.2
|
||||||
version: 3.4.2
|
version: 3.4.2
|
||||||
vite-plugin-bundle-audioworklet:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:packages/vite-plugin-bundle-audioworklet
|
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^3.0.4
|
specifier: ^3.0.4
|
||||||
version: 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(@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)
|
||||||
@@ -431,9 +428,6 @@ importers:
|
|||||||
vite:
|
vite:
|
||||||
specifier: ^6.0.11
|
specifier: ^6.0.11
|
||||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
||||||
vite-plugin-bundle-audioworklet:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../vite-plugin-bundle-audioworklet
|
|
||||||
|
|
||||||
packages/sampler:
|
packages/sampler:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -482,9 +476,6 @@ importers:
|
|||||||
vite:
|
vite:
|
||||||
specifier: ^6.0.11
|
specifier: ^6.0.11
|
||||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
||||||
vite-plugin-bundle-audioworklet:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../vite-plugin-bundle-audioworklet
|
|
||||||
|
|
||||||
packages/tidal:
|
packages/tidal:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -549,12 +540,6 @@ importers:
|
|||||||
specifier: ^3.0.4
|
specifier: ^3.0.4
|
||||||
version: 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(@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)
|
||||||
|
|
||||||
packages/vite-plugin-bundle-audioworklet:
|
|
||||||
devDependencies:
|
|
||||||
vite:
|
|
||||||
specifier: ^6.0.11
|
|
||||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
|
||||||
|
|
||||||
packages/web:
|
packages/web:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@strudel/core':
|
'@strudel/core':
|
||||||
@@ -579,9 +564,6 @@ importers:
|
|||||||
vite:
|
vite:
|
||||||
specifier: ^6.0.11
|
specifier: ^6.0.11
|
||||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
||||||
vite-plugin-bundle-audioworklet:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../vite-plugin-bundle-audioworklet
|
|
||||||
|
|
||||||
packages/webaudio:
|
packages/webaudio:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -798,9 +780,6 @@ importers:
|
|||||||
sharp:
|
sharp:
|
||||||
specifier: ^0.33.5
|
specifier: ^0.33.5
|
||||||
version: 0.33.5
|
version: 0.33.5
|
||||||
vite-plugin-bundle-audioworklet:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../packages/vite-plugin-bundle-audioworklet
|
|
||||||
workbox-window:
|
workbox-window:
|
||||||
specifier: ^7.3.0
|
specifier: ^7.3.0
|
||||||
version: 7.3.0
|
version: 7.3.0
|
||||||
@@ -7582,6 +7561,7 @@ packages:
|
|||||||
|
|
||||||
workbox-google-analytics@7.0.0:
|
workbox-google-analytics@7.0.0:
|
||||||
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
|
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
|
||||||
|
deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
|
||||||
|
|
||||||
workbox-navigation-preload@7.0.0:
|
workbox-navigation-preload@7.0.0:
|
||||||
resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==}
|
resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==}
|
||||||
@@ -10268,7 +10248,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@vitest/coverage-v8@3.0.4(vitest@3.0.4)':
|
'@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))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
'@bcoe/v8-coverage': 1.0.2
|
'@bcoe/v8-coverage': 1.0.2
|
||||||
|
|||||||
@@ -2403,27 +2403,6 @@ exports[`runs examples > example "delaytime" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "density" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:crackle density:0.01 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:crackle density:0.01 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:crackle density:0.01 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:crackle density:0.01 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:crackle density:0.01 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:crackle density:0.01 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:crackle density:0.01 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:crackle density:0.01 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:crackle density:0.01 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "detune" example index 0 1`] = `
|
exports[`runs examples > example "detune" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/12 | note:d s:supersaw detune:0.1 ]",
|
"[ 0/1 → 1/12 | note:d s:supersaw detune:0.1 ]",
|
||||||
@@ -6742,186 +6721,6 @@ exports[`runs examples > example "pure" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "pw" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 1/16 → 1/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 1/8 → 3/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 3/16 → 1/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 1/4 → 5/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 5/16 → 3/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 3/8 → 7/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 7/16 → 1/2 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 1/2 → 9/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 9/16 → 5/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 5/8 → 11/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 11/16 → 3/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 3/4 → 13/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 13/16 → 7/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 7/8 → 15/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 15/16 → 1/1 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 1/1 → 17/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 17/16 → 9/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 9/8 → 19/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 19/16 → 5/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 5/4 → 21/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 21/16 → 11/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 11/8 → 23/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 23/16 → 3/2 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 3/2 → 25/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 25/16 → 13/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 13/8 → 27/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 27/16 → 7/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 7/4 → 29/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 29/16 → 15/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 15/8 → 31/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 31/16 → 2/1 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 2/1 → 33/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 33/16 → 17/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 17/8 → 35/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 35/16 → 9/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 9/4 → 37/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 37/16 → 19/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 19/8 → 39/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 39/16 → 5/2 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 5/2 → 41/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 41/16 → 21/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 21/8 → 43/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 43/16 → 11/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 11/4 → 45/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 45/16 → 23/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 23/8 → 47/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 47/16 → 3/1 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 3/1 → 49/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 49/16 → 25/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 25/8 → 51/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 51/16 → 13/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 13/4 → 53/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 53/16 → 27/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 27/8 → 55/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 55/16 → 7/2 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 7/2 → 57/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 57/16 → 29/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 29/8 → 59/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 59/16 → 15/4 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 15/4 → 61/16 | note:f s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 61/16 → 31/8 | note:a s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 31/8 → 63/16 | note:c s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
"[ 63/16 → 4/1 | note:e s:pulse pw:0.8 pwrate:1 pwsweep:0.2 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "pw" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | note:D3 s:pulse pw:0 ]",
|
|
||||||
"[ 1/8 → 1/4 | note:E3 s:pulse pw:0 ]",
|
|
||||||
"[ 1/4 → 3/8 | note:F#3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 3/8 → 1/2 | note:A3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 1/2 → 5/8 | note:B3 s:pulse pw:0.5 ]",
|
|
||||||
"[ 5/8 → 3/4 | note:D4 s:pulse pw:0.5 ]",
|
|
||||||
"[ 3/4 → 7/8 | note:E4 s:pulse pw:1 ]",
|
|
||||||
"[ 7/8 → 1/1 | note:F#4 s:pulse pw:1 ]",
|
|
||||||
"[ 1/1 → 9/8 | note:D3 s:pulse pw:0 ]",
|
|
||||||
"[ 9/8 → 5/4 | note:E3 s:pulse pw:0 ]",
|
|
||||||
"[ 5/4 → 11/8 | note:F#3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 11/8 → 3/2 | note:A3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 3/2 → 13/8 | note:B3 s:pulse pw:0.5 ]",
|
|
||||||
"[ 13/8 → 7/4 | note:D4 s:pulse pw:0.5 ]",
|
|
||||||
"[ 7/4 → 15/8 | note:E4 s:pulse pw:1 ]",
|
|
||||||
"[ 15/8 → 2/1 | note:F#4 s:pulse pw:1 ]",
|
|
||||||
"[ 2/1 → 17/8 | note:D3 s:pulse pw:0 ]",
|
|
||||||
"[ 17/8 → 9/4 | note:E3 s:pulse pw:0 ]",
|
|
||||||
"[ 9/4 → 19/8 | note:F#3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 19/8 → 5/2 | note:A3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 5/2 → 21/8 | note:B3 s:pulse pw:0.5 ]",
|
|
||||||
"[ 21/8 → 11/4 | note:D4 s:pulse pw:0.5 ]",
|
|
||||||
"[ 11/4 → 23/8 | note:E4 s:pulse pw:1 ]",
|
|
||||||
"[ 23/8 → 3/1 | note:F#4 s:pulse pw:1 ]",
|
|
||||||
"[ 3/1 → 25/8 | note:D3 s:pulse pw:0 ]",
|
|
||||||
"[ 25/8 → 13/4 | note:E3 s:pulse pw:0 ]",
|
|
||||||
"[ 13/4 → 27/8 | note:F#3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 27/8 → 7/2 | note:A3 s:pulse pw:0.75 ]",
|
|
||||||
"[ 7/2 → 29/8 | note:B3 s:pulse pw:0.5 ]",
|
|
||||||
"[ 29/8 → 15/4 | note:D4 s:pulse pw:0.5 ]",
|
|
||||||
"[ 15/4 → 31/8 | note:E4 s:pulse pw:1 ]",
|
|
||||||
"[ 31/8 → 4/1 | note:F#4 s:pulse pw:1 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "pwrate" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | note:D3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/8 → 1/4 | note:E3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/4 → 3/8 | note:F#3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 3/8 → 1/2 | note:A3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/2 → 5/8 | note:B3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 5/8 → 3/4 | note:D4 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 3/4 → 7/8 | note:E4 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 7/8 → 1/1 | note:F#4 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/1 → 9/8 | note:D3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 9/8 → 5/4 | note:E3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 5/4 → 11/8 | note:F#3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 11/8 → 3/2 | note:A3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 3/2 → 13/8 | note:B3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 13/8 → 7/4 | note:D4 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 7/4 → 15/8 | note:E4 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 15/8 → 2/1 | note:F#4 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 2/1 → 17/8 | note:D3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 17/8 → 9/4 | note:E3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 9/4 → 19/8 | note:F#3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 19/8 → 5/2 | note:A3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 5/2 → 21/8 | note:B3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 21/8 → 11/4 | note:D4 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 11/4 → 23/8 | note:E4 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 23/8 → 3/1 | note:F#4 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 3/1 → 25/8 | note:D3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 25/8 → 13/4 | note:E3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 13/4 → 27/8 | note:F#3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 27/8 → 7/2 | note:A3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 7/2 → 29/8 | note:B3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 29/8 → 15/4 | note:D4 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 15/4 → 31/8 | note:E4 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 31/8 → 4/1 | note:F#4 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "pwsweep" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | note:D3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/8 → 1/4 | note:E3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/4 → 3/8 | note:F#3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 3/8 → 1/2 | note:A3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/2 → 5/8 | note:B3 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 5/8 → 3/4 | note:D4 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 3/4 → 7/8 | note:E4 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 7/8 → 1/1 | note:F#4 s:pulse pw:0.5 pwrate:5 pwsweep:0.3 ]",
|
|
||||||
"[ 1/1 → 9/8 | note:D3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 9/8 → 5/4 | note:E3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 5/4 → 11/8 | note:F#3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 11/8 → 3/2 | note:A3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 3/2 → 13/8 | note:B3 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 13/8 → 7/4 | note:D4 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 7/4 → 15/8 | note:E4 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 15/8 → 2/1 | note:F#4 s:pulse pw:0.5 pwrate:0.1 pwsweep:0.8 ]",
|
|
||||||
"[ 2/1 → 17/8 | note:D3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 17/8 → 9/4 | note:E3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 9/4 → 19/8 | note:F#3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 19/8 → 5/2 | note:A3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 5/2 → 21/8 | note:B3 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 21/8 → 11/4 | note:D4 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 11/4 → 23/8 | note:E4 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 23/8 → 3/1 | note:F#4 s:pulse pw:0.5 pwrate:25 pwsweep:0.3 ]",
|
|
||||||
"[ 3/1 → 25/8 | note:D3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 25/8 → 13/4 | note:E3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 13/4 → 27/8 | note:F#3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 27/8 → 7/2 | note:A3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 7/2 → 29/8 | note:B3 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 29/8 → 15/4 | note:D4 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 15/4 → 31/8 | note:E4 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
"[ 31/8 → 4/1 | note:F#4 s:pulse pw:0.5 pwrate:5 pwsweep:0.8 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "queryArc" example index 0 1`] = `[]`;
|
exports[`runs examples > example "queryArc" example index 0 1`] = `[]`;
|
||||||
|
|
||||||
exports[`runs examples > example "rand" example index 0 1`] = `
|
exports[`runs examples > example "rand" example index 0 1`] = `
|
||||||
@@ -8072,98 +7871,6 @@ but parts might be played more than once, or not at all, per cycle." example ind
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "scramble" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | note:c s:piano ]",
|
|
||||||
"[ 1/4 → 1/2 | note:d s:piano ]",
|
|
||||||
"[ 1/2 → 3/4 | note:d s:piano ]",
|
|
||||||
"[ 3/4 → 1/1 | note:c s:piano ]",
|
|
||||||
"[ 1/1 → 5/4 | note:e s:piano ]",
|
|
||||||
"[ 5/4 → 3/2 | note:e s:piano ]",
|
|
||||||
"[ 3/2 → 7/4 | note:e s:piano ]",
|
|
||||||
"[ 7/4 → 2/1 | note:c s:piano ]",
|
|
||||||
"[ 2/1 → 9/4 | note:f s:piano ]",
|
|
||||||
"[ 9/4 → 5/2 | note:d s:piano ]",
|
|
||||||
"[ 5/2 → 11/4 | note:d s:piano ]",
|
|
||||||
"[ 11/4 → 3/1 | note:e s:piano ]",
|
|
||||||
"[ 3/1 → 13/4 | note:c s:piano ]",
|
|
||||||
"[ 13/4 → 7/2 | note:f s:piano ]",
|
|
||||||
"[ 7/2 → 15/4 | note:d s:piano ]",
|
|
||||||
"[ 15/4 → 4/1 | note:f s:piano ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "scramble" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | note:c s:piano ]",
|
|
||||||
"[ 1/8 → 1/4 | note:d s:piano ]",
|
|
||||||
"[ 1/4 → 3/8 | note:d s:piano ]",
|
|
||||||
"[ 3/8 → 1/2 | note:c s:piano ]",
|
|
||||||
"[ 1/2 → 1/1 | note:g s:piano ]",
|
|
||||||
"[ 1/1 → 9/8 | note:e s:piano ]",
|
|
||||||
"[ 9/8 → 5/4 | note:e s:piano ]",
|
|
||||||
"[ 5/4 → 11/8 | note:e s:piano ]",
|
|
||||||
"[ 11/8 → 3/2 | note:c s:piano ]",
|
|
||||||
"[ 3/2 → 2/1 | note:g s:piano ]",
|
|
||||||
"[ 2/1 → 17/8 | note:f s:piano ]",
|
|
||||||
"[ 17/8 → 9/4 | note:d s:piano ]",
|
|
||||||
"[ 9/4 → 19/8 | note:d s:piano ]",
|
|
||||||
"[ 19/8 → 5/2 | note:e s:piano ]",
|
|
||||||
"[ 5/2 → 3/1 | note:g s:piano ]",
|
|
||||||
"[ 3/1 → 25/8 | note:c s:piano ]",
|
|
||||||
"[ 25/8 → 13/4 | note:f s:piano ]",
|
|
||||||
"[ 13/4 → 27/8 | note:d s:piano ]",
|
|
||||||
"[ 27/8 → 7/2 | note:f s:piano ]",
|
|
||||||
"[ 7/2 → 4/1 | note:g s:piano ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "scrub" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 1/8 → 1/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 1/4 → 5/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]",
|
|
||||||
"[ 5/8 → 3/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 3/4 → 7/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 7/8 → 1/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]",
|
|
||||||
"[ 1/1 → 9/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 9/8 → 5/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 5/4 → 13/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]",
|
|
||||||
"[ 13/8 → 7/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 7/4 → 15/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 15/8 → 2/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]",
|
|
||||||
"[ 2/1 → 17/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 17/8 → 9/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 9/4 → 21/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]",
|
|
||||||
"[ 21/8 → 11/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 11/4 → 23/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 23/8 → 3/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]",
|
|
||||||
"[ 3/1 → 25/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 25/8 → 13/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]",
|
|
||||||
"[ 13/4 → 29/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]",
|
|
||||||
"[ 29/8 → 15/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 15/4 → 31/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]",
|
|
||||||
"[ 31/8 → 4/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "scrub" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 3/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 3/8 → 5/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 5/8 → 1/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]",
|
|
||||||
"[ 1/1 → 11/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 11/8 → 13/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 13/8 → 2/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]",
|
|
||||||
"[ 2/1 → 19/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 19/8 → 21/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 21/8 → 3/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]",
|
|
||||||
"[ 3/1 → 27/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 27/8 → 29/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]",
|
|
||||||
"[ 29/8 → 4/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "segment" example index 0 1`] = `
|
exports[`runs examples > example "segment" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/24 | note:40 ]",
|
"[ 0/1 → 1/24 | note:40 ]",
|
||||||
@@ -8548,7 +8255,9 @@ exports[`runs examples > example "shrink" example index 3 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "shuffle" example index 0 1`] = `
|
exports[`runs examples > example "shuffle
|
||||||
|
Slices a pattern into the given number of parts, then plays those parts in random order.
|
||||||
|
Each part will be played exactly once per cycle." example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | note:c s:piano ]",
|
"[ 0/1 → 1/4 | note:c s:piano ]",
|
||||||
"[ 1/4 → 1/2 | note:d s:piano ]",
|
"[ 1/4 → 1/2 | note:d s:piano ]",
|
||||||
@@ -8569,7 +8278,9 @@ exports[`runs examples > example "shuffle" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "shuffle" example index 1 1`] = `
|
exports[`runs examples > example "shuffle
|
||||||
|
Slices a pattern into the given number of parts, then plays those parts in random order.
|
||||||
|
Each part will be played exactly once per cycle." example index 1 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:c s:piano ]",
|
"[ 0/1 → 1/8 | note:c s:piano ]",
|
||||||
"[ 1/8 → 1/4 | note:d s:piano ]",
|
"[ 1/8 → 1/4 | note:d s:piano ]",
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { defineConfig } from 'vitest/config';
|
import { configDefaults, defineConfig } from 'vitest/config';
|
||||||
import bundleAudioWorkletPlugin from 'vite-plugin-bundle-audioworklet';
|
|
||||||
|
|
||||||
/// <reference types="vitest" />
|
/// <reference types="vitest" />
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [bundleAudioWorkletPlugin()],
|
|
||||||
test: {
|
test: {
|
||||||
reporters: 'verbose',
|
reporters: 'verbose',
|
||||||
isolate: false,
|
isolate: false,
|
||||||
@@ -5,7 +5,6 @@ import remarkToc from 'remark-toc';
|
|||||||
import rehypeSlug from 'rehype-slug';
|
import rehypeSlug from 'rehype-slug';
|
||||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||||
import rehypeUrls from 'rehype-urls';
|
import rehypeUrls from 'rehype-urls';
|
||||||
import bundleAudioWorkletPlugin from 'vite-plugin-bundle-audioworklet';
|
|
||||||
|
|
||||||
import tailwind from '@astrojs/tailwind';
|
import tailwind from '@astrojs/tailwind';
|
||||||
import AstroPWA from '@vite-pwa/astro';
|
import AstroPWA from '@vite-pwa/astro';
|
||||||
@@ -135,7 +134,6 @@ export default defineConfig({
|
|||||||
site,
|
site,
|
||||||
base,
|
base,
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [bundleAudioWorkletPlugin()],
|
|
||||||
ssr: {
|
ssr: {
|
||||||
// Example: Force a broken package to skip SSR processing, if needed
|
// Example: Force a broken package to skip SSR processing, if needed
|
||||||
// external: ['fraction.js'], // https://github.com/infusion/Fraction.js/issues/51
|
// external: ['fraction.js'], // https://github.com/infusion/Fraction.js/issues/51
|
||||||
|
|||||||
@@ -73,7 +73,6 @@
|
|||||||
"@vite-pwa/astro": "^0.5.0",
|
"@vite-pwa/astro": "^0.5.0",
|
||||||
"html-escaper": "^3.0.3",
|
"html-escaper": "^3.0.3",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"workbox-window": "^7.3.0",
|
"workbox-window": "^7.3.0"
|
||||||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ is equivalent to this Pattern without Mini Notation:
|
|||||||
Similarly, there is an equivalent function for every aspect of the mini notation.
|
Similarly, there is an equivalent function for every aspect of the mini notation.
|
||||||
|
|
||||||
Which representation to use is a matter of context. As a rule of thumb, functions
|
Which representation to use is a matter of context. As a rule of thumb, functions
|
||||||
are better suited in a larger context, while mini notation is more practical for individual rhythms.
|
are better suited in a larger context, while mini notation is more practical for individiual rhythms.
|
||||||
|
|
||||||
## Limits of Mini Notation
|
## Limits of Mini Notation
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ While it saves resources, it can also lead to sounds not being audible the first
|
|||||||
|
|
||||||
# Sound Banks
|
# Sound Banks
|
||||||
|
|
||||||
If we open the `sounds` tab and then `drum-machines`, we can see that the drum samples are all prefixed with drum machine names: `RolandTR808_bd`, `RolandTR808_sd`, `RolandTR808_hh` etc..
|
If we open the `sounds` tab and then `drum machines`, we can see that the drum samples are all prefixed with drum machine names: `RolandTR808_bd`, `RolandTR808_sd`, `RolandTR808_hh` etc..
|
||||||
|
|
||||||
We _could_ use them like this:
|
We _could_ use them like this:
|
||||||
|
|
||||||
@@ -180,12 +180,11 @@ It assumes a `strudel.json` file to be present at the root of the repository:
|
|||||||
https://raw.githubusercontent.com/<user>/<repo>/<branch>/strudel.json
|
https://raw.githubusercontent.com/<user>/<repo>/<branch>/strudel.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## From Disk via "Import Sounds Folder"
|
## From Disk via "Import Sounds"
|
||||||
|
|
||||||
If you don't want to upload your samples to the internet, you can also load them from your local disk.
|
If you don't want to upload your samples to the internet, you can also load them from your local disk.
|
||||||
Go to the `sounds` tab in the REPL and open the `import-sounds` tab below the search bar.
|
Go to the `sounds` tab in the REPL and press "import sounds".
|
||||||
Press the "import sounds folder" button and select a folder that contains audio files.
|
Then you can select a folder that contains audio files. The folder you select can also contain subfolders with audio files.
|
||||||
The folder you select can also contain subfolders with audio files.
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -201,13 +200,12 @@ Example:
|
|||||||
```
|
```
|
||||||
|
|
||||||
In the above example the folder `samples` contains 2 subfolders `swoop` and `smash`, which contain audio files.
|
In the above example the folder `samples` contains 2 subfolders `swoop` and `smash`, which contain audio files.
|
||||||
If you select that `samples` folder, the `user` tab (next to the `import-sounds` tab) will then contain 2 new sounds: `swoop(3) smash(3)`
|
If you select that `samples` folder, the `user` tab (next to the import sounds button) will then contain 2 new sounds: `swoop(3) smash(3)`
|
||||||
The individual samples can the be played normally like `s("swoop:0 swoop:1 smash:2")`.
|
The individual samples can the be played normally like `s("swoop:0 swoop:1 smash:2")`.
|
||||||
The samples within each sound use zero-based indexing in alphabetical order.
|
|
||||||
|
|
||||||
## From Disk via @strudel/sampler
|
## From Disk via @strudel/sampler
|
||||||
|
|
||||||
Instead of loading your samples into your browser with the "import sounds folder" button, you can also serve the samples from a local file server.
|
Instead of loading your samples into your browser with the "import sounds" button, you can also serve the samples from a local file server.
|
||||||
The easiest way to do this is using [@strudel/sampler](https://www.npmjs.com/package/@strudel/sampler):
|
The easiest way to do this is using [@strudel/sampler](https://www.npmjs.com/package/@strudel/sampler):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
---
|
---
|
||||||
import HeadCommon from '@components/HeadCommon.astro';
|
|
||||||
import { Udels } from '../../components/Udels/Udels.jsx';
|
import { Udels } from '../../components/Udels/Udels.jsx';
|
||||||
|
|
||||||
|
|
||||||
|
const { BASE_URL } = import.meta.env;
|
||||||
|
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang="en" class="m-0">
|
|
||||||
<head>
|
<body class="m-0">
|
||||||
<HeadCommon />
|
<Udels client:only="react" />
|
||||||
<title>Strudel UDELS</title>
|
</body>
|
||||||
</head>
|
|
||||||
<body class="m-0">
|
|
||||||
<Udels client:only="react" />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { getAudioDevices, setAudioDevice } from '../../util.mjs';
|
||||||
import { SelectInput } from './SelectInput';
|
import { SelectInput } from './SelectInput';
|
||||||
import { getAudioDevices } from '@strudel/webaudio';
|
|
||||||
|
|
||||||
const initdevices = new Map();
|
const initdevices = new Map();
|
||||||
|
|
||||||
@@ -22,7 +21,9 @@ export function AudioDeviceSelector({ audioDeviceName, onChange, isDisabled }) {
|
|||||||
if (!devicesInitialized) {
|
if (!devicesInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const deviceID = devices.get(deviceName);
|
||||||
onChange(deviceName);
|
onChange(deviceName);
|
||||||
|
setAudioDevice(deviceID);
|
||||||
};
|
};
|
||||||
const options = new Map();
|
const options = new Map();
|
||||||
Array.from(devices.keys()).forEach((deviceName) => {
|
Array.from(devices.keys()).forEach((deviceName) => {
|
||||||
|
|||||||
@@ -20,42 +20,25 @@ export default function ImportSoundsButton({ onComplete }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<label
|
||||||
<label
|
style={{ alignItems: 'center' }}
|
||||||
style={{ alignItems: 'center', borderColor: 'red', border: 1 }}
|
className="flex bg-background ml-2 pl-2 pr-2 max-w-[300px] rounded-md hover:opacity-50 whitespace-nowrap cursor-pointer"
|
||||||
className="flex bg-background p-4 w-fit rounded-xl hover:opacity-50 whitespace-nowrap cursor-pointer"
|
>
|
||||||
>
|
<input
|
||||||
<svg
|
disabled={isUploading}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
ref={fileUploadRef}
|
||||||
fill="none"
|
id="audio_file"
|
||||||
viewBox="0 0 24 24"
|
style={{ display: 'none' }}
|
||||||
strokeWidth={1.5}
|
type="file"
|
||||||
stroke="currentColor"
|
directory=""
|
||||||
className="size-6 mr-2"
|
webkitdirectory=""
|
||||||
>
|
multiple
|
||||||
<path
|
accept="audio/*, .wav, .mp3, .m4a, .flac, .aac, .ogg"
|
||||||
strokeLinecap="round"
|
onChange={() => {
|
||||||
strokeLinejoin="round"
|
onChange();
|
||||||
d="M7.5 7.5h-.75A2.25 2.25 0 0 0 4.5 9.75v7.5a2.25 2.25 0 0 0 2.25 2.25h7.5a2.25 2.25 0 0 0 2.25-2.25v-7.5a2.25 2.25 0 0 0-2.25-2.25h-.75m0-3-3-3m0 0-3 3m3-3v11.25m6-2.25h.75a2.25 2.25 0 0 1 2.25 2.25v7.5a2.25 2.25 0 0 1-2.25 2.25h-7.5a2.25 2.25 0 0 1-2.25-2.25v-.75"
|
}}
|
||||||
/>
|
/>
|
||||||
</svg>
|
{isUploading ? 'importing...' : 'import sounds'}
|
||||||
|
</label>
|
||||||
<input
|
|
||||||
disabled={isUploading}
|
|
||||||
ref={fileUploadRef}
|
|
||||||
id="audio_file"
|
|
||||||
style={{ display: 'none' }}
|
|
||||||
type="file"
|
|
||||||
directory=""
|
|
||||||
webkitdirectory=""
|
|
||||||
multiple
|
|
||||||
accept="audio/*, .wav, .mp3, .m4a, .flac, .aac, .ogg"
|
|
||||||
onChange={() => {
|
|
||||||
onChange();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{isUploading ? 'importing...' : 'import sounds folder'}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function SelectInput({ value, options, onChange, onClick, isDisabled }) {
|
|||||||
<select
|
<select
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="p-2 bg-background rounded-md text-foreground border-foreground"
|
className="p-2 bg-background rounded-md text-foreground"
|
||||||
value={value ?? ''}
|
value={value ?? ''}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs';
|
import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs';
|
||||||
import { themes } from '@strudel/codemirror';
|
import { themes } from '@strudel/codemirror';
|
||||||
import { Textbox } from '../textbox/Textbox.jsx';
|
|
||||||
import { isUdels } from '../../util.mjs';
|
import { isUdels } from '../../util.mjs';
|
||||||
import { ButtonGroup } from './Forms.jsx';
|
import { ButtonGroup } from './Forms.jsx';
|
||||||
import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
||||||
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
||||||
import { confirmDialog } from '../../util.mjs';
|
import { confirmDialog } from '../../util.mjs';
|
||||||
import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony, setMultiChannelOrbits } from '@strudel/webaudio';
|
|
||||||
|
|
||||||
function Checkbox({ label, value, onChange, disabled = false }) {
|
function Checkbox({ label, value, onChange, disabled = false }) {
|
||||||
return (
|
return (
|
||||||
@@ -20,7 +18,7 @@ function Checkbox({ label, value, onChange, disabled = false }) {
|
|||||||
function SelectInput({ value, options, onChange }) {
|
function SelectInput({ value, options, onChange }) {
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
className="p-2 bg-background rounded-md text-foreground border-foreground"
|
className="p-2 bg-background rounded-md text-foreground"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
>
|
>
|
||||||
@@ -55,7 +53,7 @@ function NumberSlider({ value, onChange, step = 1, ...rest }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormItem({ label, children, sublabel }) {
|
function FormItem({ label, children }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<label>{label}</label>
|
<label>{label}</label>
|
||||||
@@ -107,8 +105,6 @@ export function SettingsTab({ started }) {
|
|||||||
audioDeviceName,
|
audioDeviceName,
|
||||||
audioEngineTarget,
|
audioEngineTarget,
|
||||||
togglePanelTrigger,
|
togglePanelTrigger,
|
||||||
maxPolyphony,
|
|
||||||
multiChannelOrbits,
|
|
||||||
} = useSettings();
|
} = useSettings();
|
||||||
const shouldAlwaysSync = isUdels();
|
const shouldAlwaysSync = isUdels();
|
||||||
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
||||||
@@ -143,42 +139,6 @@ export function SettingsTab({ started }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem label="Maximum Polyphony">
|
|
||||||
<Textbox
|
|
||||||
min={1}
|
|
||||||
max={Infinity}
|
|
||||||
onBlur={(e) => {
|
|
||||||
let v = parseInt(e.target.value);
|
|
||||||
v = isNaN(v) ? DEFAULT_MAX_POLYPHONY : v;
|
|
||||||
setMaxPolyphony(v);
|
|
||||||
settingsMap.setKey('maxPolyphony', v);
|
|
||||||
}}
|
|
||||||
onChange={(v) => {
|
|
||||||
v = Math.max(1, parseInt(v));
|
|
||||||
settingsMap.setKey('maxPolyphony', isNaN(v) ? undefined : v);
|
|
||||||
}}
|
|
||||||
type="number"
|
|
||||||
placeholder=""
|
|
||||||
value={maxPolyphony ?? ''}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem>
|
|
||||||
<Checkbox
|
|
||||||
label="Multi Channel Orbits"
|
|
||||||
onChange={(cbEvent) => {
|
|
||||||
const val = cbEvent.target.checked;
|
|
||||||
confirmDialog(RELOAD_MSG).then((r) => {
|
|
||||||
if (r == true) {
|
|
||||||
settingsMap.setKey('multiChannelOrbits', val);
|
|
||||||
setMultiChannelOrbits(val);
|
|
||||||
return window.location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
value={multiChannelOrbits}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Theme">
|
<FormItem label="Theme">
|
||||||
<SelectInput options={themeOptions} value={theme} onChange={(theme) => settingsMap.setKey('theme', theme)} />
|
<SelectInput options={themeOptions} value={theme} onChange={(theme) => settingsMap.setKey('theme', theme)} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ export function SoundsTab() {
|
|||||||
const sounds = useStore(soundMap);
|
const sounds = useStore(soundMap);
|
||||||
const { soundsFilter } = useSettings();
|
const { soundsFilter } = useSettings();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const { BASE_URL } = import.meta.env;
|
|
||||||
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
|
|
||||||
|
|
||||||
const soundEntries = useMemo(() => {
|
const soundEntries = useMemo(() => {
|
||||||
if (!sounds) {
|
if (!sounds) {
|
||||||
@@ -39,9 +37,6 @@ export function SoundsTab() {
|
|||||||
if (soundsFilter === 'synths') {
|
if (soundsFilter === 'synths') {
|
||||||
return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type));
|
return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type));
|
||||||
}
|
}
|
||||||
if (soundsFilter === 'importSounds') {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}, [sounds, soundsFilter, search]);
|
}, [sounds, soundsFilter, search]);
|
||||||
|
|
||||||
@@ -56,6 +51,7 @@ export function SoundsTab() {
|
|||||||
ref?.stop(getAudioContext().currentTime + 0.01);
|
ref?.stop(getAudioContext().currentTime + 0.01);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full text-foreground">
|
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full text-foreground">
|
||||||
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} />
|
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} />
|
||||||
@@ -69,9 +65,9 @@ export function SoundsTab() {
|
|||||||
drums: 'drum-machines',
|
drums: 'drum-machines',
|
||||||
synths: 'Synths',
|
synths: 'Synths',
|
||||||
user: 'User',
|
user: 'User',
|
||||||
importSounds: 'import-sounds',
|
|
||||||
}}
|
}}
|
||||||
></ButtonGroup>
|
></ButtonGroup>
|
||||||
|
<ImportSoundsButton onComplete={() => settingsMap.setKey('soundsFilter', 'user')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal pb-2">
|
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal pb-2">
|
||||||
@@ -105,55 +101,7 @@ export function SoundsTab() {
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{!soundEntries.length && soundsFilter === 'importSounds' ? (
|
{!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''}
|
||||||
<div className="prose dark:prose-invert min-w-full pt-2 pb-8 px-4">
|
|
||||||
<ImportSoundsButton onComplete={() => settingsMap.setKey('soundsFilter', 'user')} />
|
|
||||||
<p>
|
|
||||||
To import sounds into strudel, they must be contained{' '}
|
|
||||||
<a href={`${baseNoTrailing}/learn/samples/#from-disk-via-import-sounds-folder`} target="_blank">
|
|
||||||
within a folder or subfolder
|
|
||||||
</a>
|
|
||||||
. The best way to do this is to upload a “samples” folder containing subfolders of individual sounds or
|
|
||||||
soundbanks (see diagram below).{' '}
|
|
||||||
</p>
|
|
||||||
<pre className="bg-background" key={'sample-diagram'}>
|
|
||||||
{`└─ samples <-- import this folder
|
|
||||||
├─ swoop
|
|
||||||
│ ├─ swoopshort.wav
|
|
||||||
│ ├─ swooplong.wav
|
|
||||||
│ └─ swooptight.wav
|
|
||||||
└─ smash
|
|
||||||
├─ smashhigh.wav
|
|
||||||
├─ smashlow.wav
|
|
||||||
└─ smashmiddle.wav`}
|
|
||||||
</pre>
|
|
||||||
<p>
|
|
||||||
The name of a subfolder corresponds to the sound name under the “user” tab. Multiple samples within a
|
|
||||||
subfolder are all labelled with the same name, but can be accessed using “.n( )” - remember sounds are
|
|
||||||
zero-indexed and in alphabetical order!
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
For more information, and other ways to use your own sounds in strudel,{' '}
|
|
||||||
<a href={`${baseNoTrailing}/learn/samples/#from-disk-via-import-sounds-folder`} target="_blank">
|
|
||||||
check out the docs
|
|
||||||
</a>
|
|
||||||
!
|
|
||||||
</p>
|
|
||||||
<h3>Preview Sounds</h3>
|
|
||||||
<pre className="bg-background" key={'sample-preview'}>
|
|
||||||
n("0 1 2 3 4 5").s("sample-name")
|
|
||||||
</pre>
|
|
||||||
<p>
|
|
||||||
Paste the line above into the main editor to hear the uploaded folder. Remember to use the name of your
|
|
||||||
sample as it appears under the "user" tab.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
{!soundEntries.length && soundsFilter !== 'importSounds'
|
|
||||||
? 'No custom sounds loaded in this pattern (yet).'
|
|
||||||
: ''}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import cx from '@src/cx.mjs';
|
|||||||
export function Textbox({ onChange, className, ...inputProps }) {
|
export function Textbox({ onChange, className, ...inputProps }) {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
className={cx(
|
className={cx('p-1 bg-background rounded-md my-2 border-foreground', className)}
|
||||||
'p-2 bg-background rounded-md border-foreground text-foreground placeholder-foreground',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -54,23 +54,25 @@ export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete =
|
|||||||
splitRelativePath[splitRelativePath.length - 2] ?? soundFile.id.split(/\W+/)[0] ?? 'user';
|
splitRelativePath[splitRelativePath.length - 2] ?? soundFile.id.split(/\W+/)[0] ?? 'user';
|
||||||
const blob = soundFile.blob;
|
const blob = soundFile.blob;
|
||||||
|
|
||||||
|
// Files used to be uploaded as base64 strings, After Jan 1 2025 this check can be safely deleted
|
||||||
|
if (typeof blob === 'string') {
|
||||||
|
const soundPaths = sounds.get(parentDirectory) ?? new Set();
|
||||||
|
soundPaths.add(blob);
|
||||||
|
sounds.set(parentDirectory, soundPaths);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return blobToDataUrl(blob).then((soundPath) => {
|
return blobToDataUrl(blob).then((soundPath) => {
|
||||||
const titlePathMap = sounds.get(parentDirectory) ?? new Map();
|
const soundPaths = sounds.get(parentDirectory) ?? new Set();
|
||||||
|
soundPaths.add(soundPath);
|
||||||
titlePathMap.set(title, soundPath);
|
sounds.set(parentDirectory, soundPaths);
|
||||||
|
|
||||||
sounds.set(parentDirectory, titlePathMap);
|
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
sounds.forEach((titlePathMap, key) => {
|
sounds.forEach((soundPaths, key) => {
|
||||||
const value = Array.from(titlePathMap.keys())
|
const value = Array.from(soundPaths);
|
||||||
.sort((a, b) => {
|
|
||||||
return a.localeCompare(b);
|
|
||||||
})
|
|
||||||
.map((title) => titlePathMap.get(title));
|
|
||||||
|
|
||||||
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, value), {
|
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, value), {
|
||||||
type: 'sample',
|
type: 'sample',
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ import {
|
|||||||
resetLoadedSounds,
|
resetLoadedSounds,
|
||||||
initAudioOnFirstClick,
|
initAudioOnFirstClick,
|
||||||
} from '@strudel/webaudio';
|
} from '@strudel/webaudio';
|
||||||
import { setVersionDefaultsFrom } from './util.mjs';
|
import { getAudioDevices, setAudioDevice, setVersionDefaultsFrom } from './util.mjs';
|
||||||
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
|
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
|
||||||
import { clearHydra } from '@strudel/hydra';
|
import { clearHydra } from '@strudel/hydra';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { parseBoolean, settingsMap, useSettings } from '../settings.mjs';
|
import { settingsMap, useSettings } from '../settings.mjs';
|
||||||
import {
|
import {
|
||||||
setActivePattern,
|
setActivePattern,
|
||||||
setLatestCode,
|
setLatestCode,
|
||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
setViewingPatternData,
|
setViewingPatternData,
|
||||||
} from '../user_pattern_utils.mjs';
|
} from '../user_pattern_utils.mjs';
|
||||||
import { superdirtOutput } from '@strudel/osc/superdirtoutput';
|
import { superdirtOutput } from '@strudel/osc/superdirtoutput';
|
||||||
import { audioEngineTargets } from '../settings.mjs';
|
import { audioEngineTargets, defaultAudioDeviceName } from '../settings.mjs';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { prebake } from './prebake.mjs';
|
import { prebake } from './prebake.mjs';
|
||||||
import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs';
|
import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs';
|
||||||
@@ -36,15 +36,11 @@ import './Repl.css';
|
|||||||
import { setInterval, clearInterval } from 'worker-timers';
|
import { setInterval, clearInterval } from 'worker-timers';
|
||||||
import { getMetadata } from '../metadata_parser';
|
import { getMetadata } from '../metadata_parser';
|
||||||
|
|
||||||
const { latestCode, maxPolyphony, audioDeviceName, multiChannelOrbits } = settingsMap.get();
|
const { latestCode } = settingsMap.get();
|
||||||
let modulesLoading, presets, drawContext, clearCanvas, audioReady;
|
let modulesLoading, presets, drawContext, clearCanvas, audioReady;
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
audioReady = initAudioOnFirstClick({
|
audioReady = initAudioOnFirstClick();
|
||||||
maxPolyphony,
|
|
||||||
audioDeviceName,
|
|
||||||
multiChannelOrbits: parseBoolean(multiChannelOrbits),
|
|
||||||
});
|
|
||||||
modulesLoading = loadModules();
|
modulesLoading = loadModules();
|
||||||
presets = prebake();
|
presets = prebake();
|
||||||
drawContext = getDrawContext();
|
drawContext = getDrawContext();
|
||||||
@@ -163,6 +159,20 @@ export function useReplContext() {
|
|||||||
editorRef.current?.updateSettings(editorSettings);
|
editorRef.current?.updateSettings(editorSettings);
|
||||||
}, [_settings]);
|
}, [_settings]);
|
||||||
|
|
||||||
|
// on first load, set stored audio device if possible
|
||||||
|
useEffect(() => {
|
||||||
|
const { audioDeviceName } = _settings;
|
||||||
|
if (audioDeviceName !== defaultAudioDeviceName) {
|
||||||
|
getAudioDevices().then((devices) => {
|
||||||
|
const deviceID = devices.get(audioDeviceName);
|
||||||
|
if (deviceID == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAudioDevice(deviceID);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
//
|
//
|
||||||
// UI Actions
|
// UI Actions
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { evalScope, hash2code, logger } from '@strudel/core';
|
import { evalScope, hash2code, logger } from '@strudel/core';
|
||||||
import { settingPatterns } from '../settings.mjs';
|
import { settingPatterns, defaultAudioDeviceName } from '../settings.mjs';
|
||||||
import { setVersionDefaults } from '@strudel/webaudio';
|
import { getAudioContext, initializeAudioOutput, setDefaultAudioContext, setVersionDefaults } from '@strudel/webaudio';
|
||||||
import { getMetadata } from '../metadata_parser';
|
import { getMetadata } from '../metadata_parser';
|
||||||
import { isTauri } from '../tauri.mjs';
|
import { isTauri } from '../tauri.mjs';
|
||||||
import './Repl.css';
|
import './Repl.css';
|
||||||
@@ -159,6 +159,38 @@ export const isUdels = () => {
|
|||||||
return window.top?.location?.pathname.includes('udels');
|
return window.top?.location?.pathname.includes('udels');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAudioDevices = async () => {
|
||||||
|
await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
|
let mediaDevices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default');
|
||||||
|
const devicesMap = new Map();
|
||||||
|
devicesMap.set(defaultAudioDeviceName, '');
|
||||||
|
mediaDevices.forEach((device) => {
|
||||||
|
devicesMap.set(device.label, device.deviceId);
|
||||||
|
});
|
||||||
|
return devicesMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setAudioDevice = async (id) => {
|
||||||
|
let audioCtx = getAudioContext();
|
||||||
|
if (audioCtx.sinkId === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await audioCtx.suspend();
|
||||||
|
await audioCtx.close();
|
||||||
|
audioCtx = setDefaultAudioContext();
|
||||||
|
await audioCtx.resume();
|
||||||
|
const isValidID = (id ?? '').length > 0;
|
||||||
|
if (isValidID) {
|
||||||
|
try {
|
||||||
|
await audioCtx.setSinkId(id);
|
||||||
|
} catch {
|
||||||
|
logger('failed to set audio interface', 'warning');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initializeAudioOutput();
|
||||||
|
};
|
||||||
|
|
||||||
export function setVersionDefaultsFrom(code) {
|
export function setVersionDefaultsFrom(code) {
|
||||||
try {
|
try {
|
||||||
const metadata = getMetadata(code);
|
const metadata = getMetadata(code);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { useStore } from '@nanostores/react';
|
|||||||
import { register } from '@strudel/core';
|
import { register } from '@strudel/core';
|
||||||
import { isUdels } from './repl/util.mjs';
|
import { isUdels } from './repl/util.mjs';
|
||||||
|
|
||||||
|
export const defaultAudioDeviceName = 'System Standard';
|
||||||
|
|
||||||
export const audioEngineTargets = {
|
export const audioEngineTargets = {
|
||||||
webaudio: 'webaudio',
|
webaudio: 'webaudio',
|
||||||
osc: 'osc',
|
osc: 'osc',
|
||||||
@@ -34,11 +36,10 @@ export const defaultSettings = {
|
|||||||
isPanelOpen: true,
|
isPanelOpen: true,
|
||||||
togglePanelTrigger: 'click', //click | hover
|
togglePanelTrigger: 'click', //click | hover
|
||||||
userPatterns: '{}',
|
userPatterns: '{}',
|
||||||
|
audioDeviceName: defaultAudioDeviceName,
|
||||||
audioEngineTarget: audioEngineTargets.webaudio,
|
audioEngineTarget: audioEngineTargets.webaudio,
|
||||||
isButtonRowHidden: false,
|
isButtonRowHidden: false,
|
||||||
isCSSAnimationDisabled: false,
|
isCSSAnimationDisabled: false,
|
||||||
maxPolyphony: 128,
|
|
||||||
multiChannelOrbits: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let search = null;
|
let search = null;
|
||||||
@@ -51,7 +52,7 @@ const settings_key = `strudel-settings${instance > 0 ? instance : ''}`;
|
|||||||
|
|
||||||
export const settingsMap = persistentMap(settings_key, defaultSettings);
|
export const settingsMap = persistentMap(settings_key, defaultSettings);
|
||||||
|
|
||||||
export const parseBoolean = (booleanlike) => ([true, 'true'].includes(booleanlike) ? true : false);
|
const parseBoolean = (booleanlike) => ([true, 'true'].includes(booleanlike) ? true : false);
|
||||||
|
|
||||||
export function useSettings() {
|
export function useSettings() {
|
||||||
const state = useStore(settingsMap);
|
const state = useStore(settingsMap);
|
||||||
@@ -82,7 +83,6 @@ export function useSettings() {
|
|||||||
isPanelPinned: parseBoolean(state.isPanelPinned),
|
isPanelPinned: parseBoolean(state.isPanelPinned),
|
||||||
isPanelOpen: parseBoolean(state.isPanelOpen),
|
isPanelOpen: parseBoolean(state.isPanelOpen),
|
||||||
userPatterns: userPatterns,
|
userPatterns: userPatterns,
|
||||||
multiChannelOrbits: parseBoolean(state.multiChannelOrbits),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user