@jadujoel/web-audio-clip-node 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/audio/ClipNode.js +312 -0
- package/dist/audio/processor-code.js +2 -0
- package/dist/audio/processor-kernel.js +861 -0
- package/dist/audio/processor.js +80 -0
- package/dist/audio/types.js +9 -0
- package/dist/audio/utils.js +128 -0
- package/dist/audio/version.d.ts +1 -0
- package/dist/audio/version.js +2 -0
- package/dist/audio/workletUrl.js +17 -0
- package/dist/components/AudioControl.js +99 -0
- package/dist/components/ContextMenu.js +73 -0
- package/dist/components/ControlSection.js +74 -0
- package/dist/components/DetuneControl.js +44 -0
- package/dist/components/DisplayPanel.js +6 -0
- package/dist/components/FilterControl.js +48 -0
- package/dist/components/GainControl.js +44 -0
- package/dist/components/PanControl.js +50 -0
- package/dist/components/PlaybackRateControl.js +44 -0
- package/dist/components/PlayheadSlider.js +20 -0
- package/dist/components/SnappableSlider.js +174 -0
- package/dist/components/TransportButtons.js +9 -0
- package/dist/controls/controlDefs.js +211 -0
- package/dist/controls/formatValueText.js +80 -0
- package/dist/controls/linkedControlPairs.js +51 -0
- package/dist/data/cache.js +17 -0
- package/dist/data/fileStore.js +39 -0
- package/dist/hooks/useClipNode.js +338 -0
- package/dist/lib-react.js +17 -19
- package/dist/lib.bundle.js +6 -0
- package/dist/lib.bundle.js.map +7 -0
- package/dist/lib.js +16 -44
- package/dist/store/clipStore.js +71 -0
- package/examples/README.md +10 -0
- package/examples/cdn-vanilla/README.md +13 -0
- package/examples/cdn-vanilla/index.html +69 -0
- package/examples/esm-bundler/README.md +8 -0
- package/examples/esm-bundler/index.html +12 -0
- package/examples/esm-bundler/node_modules/.package-lock.json +1168 -0
- package/examples/esm-bundler/node_modules/@esbuild/darwin-arm64/README.md +3 -0
- package/examples/esm-bundler/node_modules/@esbuild/darwin-arm64/bin/esbuild +0 -0
- package/examples/esm-bundler/node_modules/@esbuild/darwin-arm64/package.json +20 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/LICENSE +21 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/README.md +91 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/ClipNode.d.ts +86 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/ClipNode.js +312 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/processor-code.d.ts +1 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/processor-code.js +2 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/processor-kernel.d.ts +43 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/processor-kernel.js +861 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/processor.d.ts +1 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/processor.js +80 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/types.d.ts +192 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/types.js +9 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/utils.d.ts +22 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/utils.js +128 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/version.d.ts +1 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/version.js +2 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/workletUrl.d.ts +6 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/audio/workletUrl.js +17 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/AudioControl.d.ts +28 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/AudioControl.js +99 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/ContextMenu.d.ts +17 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/ContextMenu.js +73 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/ControlSection.d.ts +26 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/ControlSection.js +74 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/DetuneControl.d.ts +10 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/DetuneControl.js +44 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/DisplayPanel.d.ts +14 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/DisplayPanel.js +6 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/FilterControl.d.ts +12 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/FilterControl.js +48 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/GainControl.d.ts +10 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/GainControl.js +44 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/PanControl.d.ts +10 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/PanControl.js +50 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/PlaybackRateControl.d.ts +10 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/PlaybackRateControl.js +44 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/PlayheadSlider.d.ts +13 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/PlayheadSlider.js +20 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/SnappableSlider.d.ts +18 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/SnappableSlider.js +174 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/TransportButtons.d.ts +14 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/components/TransportButtons.js +9 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/controls/controlDefs.d.ts +31 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/controls/controlDefs.js +211 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/controls/formatValueText.d.ts +2 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/controls/formatValueText.js +80 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/controls/linkedControlPairs.d.ts +20 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/controls/linkedControlPairs.js +51 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/data/cache.d.ts +1 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/data/cache.js +17 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/data/fileStore.d.ts +6 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/data/fileStore.js +39 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/hooks/useClipNode.d.ts +31 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/hooks/useClipNode.js +338 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/lib-react.d.ts +15 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/lib-react.js +17 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/lib.d.ts +16 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/lib.js +16 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/processor.js +4 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/processor.js.map +12 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/store/clipStore.d.ts +63 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/store/clipStore.js +71 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/dist/styles.css +739 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/README.md +10 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/cdn-vanilla/README.md +13 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/cdn-vanilla/index.html +61 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/esm-bundler/README.md +8 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/esm-bundler/index.html +12 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/esm-bundler/package.json +15 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/esm-bundler/src/main.ts +43 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/react/README.md +10 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/react/index.html +12 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/react/package.json +21 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/react/src/App.tsx +20 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/react/src/main.tsx +9 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/react/vite.config.ts +6 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/self-hosted/README.md +11 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/self-hosted/index.html +12 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/self-hosted/package.json +16 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/self-hosted/public/.gitkeep +1 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/examples/self-hosted/src/main.ts +46 -0
- package/examples/esm-bundler/node_modules/@jadujoel/web-audio-clip-node/package.json +88 -0
- package/examples/esm-bundler/node_modules/@rollup/rollup-darwin-arm64/README.md +3 -0
- package/examples/esm-bundler/node_modules/@rollup/rollup-darwin-arm64/package.json +22 -0
- package/examples/esm-bundler/node_modules/@rollup/rollup-darwin-arm64/rollup.darwin-arm64.node +0 -0
- package/examples/esm-bundler/node_modules/@types/estree/LICENSE +21 -0
- package/examples/esm-bundler/node_modules/@types/estree/README.md +15 -0
- package/examples/esm-bundler/node_modules/@types/estree/flow.d.ts +167 -0
- package/examples/esm-bundler/node_modules/@types/estree/index.d.ts +694 -0
- package/examples/esm-bundler/node_modules/@types/estree/package.json +27 -0
- package/examples/esm-bundler/node_modules/esbuild/LICENSE.md +21 -0
- package/examples/esm-bundler/node_modules/esbuild/README.md +3 -0
- package/examples/esm-bundler/node_modules/esbuild/bin/esbuild +0 -0
- package/examples/esm-bundler/node_modules/esbuild/install.js +289 -0
- package/examples/esm-bundler/node_modules/esbuild/lib/main.d.ts +716 -0
- package/examples/esm-bundler/node_modules/esbuild/lib/main.js +2242 -0
- package/examples/esm-bundler/node_modules/esbuild/package.json +49 -0
- package/examples/esm-bundler/node_modules/fdir/LICENSE +7 -0
- package/examples/esm-bundler/node_modules/fdir/README.md +91 -0
- package/examples/esm-bundler/node_modules/fdir/dist/index.cjs +588 -0
- package/examples/esm-bundler/node_modules/fdir/dist/index.d.cts +155 -0
- package/examples/esm-bundler/node_modules/fdir/dist/index.d.mts +155 -0
- package/examples/esm-bundler/node_modules/fdir/dist/index.mjs +570 -0
- package/examples/esm-bundler/node_modules/fdir/package.json +103 -0
- package/examples/esm-bundler/node_modules/nanoid/LICENSE +20 -0
- package/examples/esm-bundler/node_modules/nanoid/README.md +39 -0
- package/examples/esm-bundler/node_modules/nanoid/async/index.browser.cjs +69 -0
- package/examples/esm-bundler/node_modules/nanoid/async/index.browser.js +34 -0
- package/examples/esm-bundler/node_modules/nanoid/async/index.cjs +71 -0
- package/examples/esm-bundler/node_modules/nanoid/async/index.d.ts +56 -0
- package/examples/esm-bundler/node_modules/nanoid/async/index.js +35 -0
- package/examples/esm-bundler/node_modules/nanoid/async/index.native.js +26 -0
- package/examples/esm-bundler/node_modules/nanoid/async/package.json +12 -0
- package/examples/esm-bundler/node_modules/nanoid/bin/nanoid.cjs +55 -0
- package/examples/esm-bundler/node_modules/nanoid/index.browser.cjs +72 -0
- package/examples/esm-bundler/node_modules/nanoid/index.browser.js +34 -0
- package/examples/esm-bundler/node_modules/nanoid/index.cjs +85 -0
- package/examples/esm-bundler/node_modules/nanoid/index.d.cts +91 -0
- package/examples/esm-bundler/node_modules/nanoid/index.d.ts +91 -0
- package/examples/esm-bundler/node_modules/nanoid/index.js +45 -0
- package/examples/esm-bundler/node_modules/nanoid/nanoid.js +1 -0
- package/examples/esm-bundler/node_modules/nanoid/non-secure/index.cjs +34 -0
- package/examples/esm-bundler/node_modules/nanoid/non-secure/index.d.ts +33 -0
- package/examples/esm-bundler/node_modules/nanoid/non-secure/index.js +21 -0
- package/examples/esm-bundler/node_modules/nanoid/non-secure/package.json +6 -0
- package/examples/esm-bundler/node_modules/nanoid/package.json +89 -0
- package/examples/esm-bundler/node_modules/nanoid/url-alphabet/index.cjs +7 -0
- package/examples/esm-bundler/node_modules/nanoid/url-alphabet/index.js +3 -0
- package/examples/esm-bundler/node_modules/nanoid/url-alphabet/package.json +6 -0
- package/examples/esm-bundler/node_modules/picocolors/LICENSE +15 -0
- package/examples/esm-bundler/node_modules/picocolors/README.md +21 -0
- package/examples/esm-bundler/node_modules/picocolors/package.json +25 -0
- package/examples/esm-bundler/node_modules/picocolors/picocolors.browser.js +4 -0
- package/examples/esm-bundler/node_modules/picocolors/picocolors.d.ts +5 -0
- package/examples/esm-bundler/node_modules/picocolors/picocolors.js +75 -0
- package/examples/esm-bundler/node_modules/picocolors/types.d.ts +51 -0
- package/examples/esm-bundler/node_modules/picomatch/LICENSE +21 -0
- package/examples/esm-bundler/node_modules/picomatch/README.md +749 -0
- package/examples/esm-bundler/node_modules/picomatch/index.js +17 -0
- package/examples/esm-bundler/node_modules/picomatch/lib/constants.js +184 -0
- package/examples/esm-bundler/node_modules/picomatch/lib/parse.js +1386 -0
- package/examples/esm-bundler/node_modules/picomatch/lib/picomatch.js +349 -0
- package/examples/esm-bundler/node_modules/picomatch/lib/scan.js +391 -0
- package/examples/esm-bundler/node_modules/picomatch/lib/utils.js +72 -0
- package/examples/esm-bundler/node_modules/picomatch/package.json +82 -0
- package/examples/esm-bundler/node_modules/picomatch/posix.js +3 -0
- package/examples/esm-bundler/node_modules/postcss/LICENSE +20 -0
- package/examples/esm-bundler/node_modules/postcss/README.md +28 -0
- package/examples/esm-bundler/node_modules/postcss/lib/at-rule.d.ts +139 -0
- package/examples/esm-bundler/node_modules/postcss/lib/at-rule.js +25 -0
- package/examples/esm-bundler/node_modules/postcss/lib/comment.d.ts +67 -0
- package/examples/esm-bundler/node_modules/postcss/lib/comment.js +13 -0
- package/examples/esm-bundler/node_modules/postcss/lib/container.d.ts +478 -0
- package/examples/esm-bundler/node_modules/postcss/lib/container.js +447 -0
- package/examples/esm-bundler/node_modules/postcss/lib/css-syntax-error.d.ts +247 -0
- package/examples/esm-bundler/node_modules/postcss/lib/css-syntax-error.js +133 -0
- package/examples/esm-bundler/node_modules/postcss/lib/declaration.d.ts +150 -0
- package/examples/esm-bundler/node_modules/postcss/lib/declaration.js +24 -0
- package/examples/esm-bundler/node_modules/postcss/lib/document.d.ts +68 -0
- package/examples/esm-bundler/node_modules/postcss/lib/document.js +33 -0
- package/examples/esm-bundler/node_modules/postcss/lib/fromJSON.d.ts +9 -0
- package/examples/esm-bundler/node_modules/postcss/lib/fromJSON.js +54 -0
- package/examples/esm-bundler/node_modules/postcss/lib/input.d.ts +226 -0
- package/examples/esm-bundler/node_modules/postcss/lib/input.js +273 -0
- package/examples/esm-bundler/node_modules/postcss/lib/lazy-result.d.ts +189 -0
- package/examples/esm-bundler/node_modules/postcss/lib/lazy-result.js +550 -0
- package/examples/esm-bundler/node_modules/postcss/lib/list.d.ts +60 -0
- package/examples/esm-bundler/node_modules/postcss/lib/list.js +58 -0
- package/examples/esm-bundler/node_modules/postcss/lib/map-generator.js +376 -0
- package/examples/esm-bundler/node_modules/postcss/lib/no-work-result.d.ts +45 -0
- package/examples/esm-bundler/node_modules/postcss/lib/no-work-result.js +137 -0
- package/examples/esm-bundler/node_modules/postcss/lib/node.d.ts +555 -0
- package/examples/esm-bundler/node_modules/postcss/lib/node.js +449 -0
- package/examples/esm-bundler/node_modules/postcss/lib/parse.d.ts +9 -0
- package/examples/esm-bundler/node_modules/postcss/lib/parse.js +42 -0
- package/examples/esm-bundler/node_modules/postcss/lib/parser.js +611 -0
- package/examples/esm-bundler/node_modules/postcss/lib/postcss.d.mts +66 -0
- package/examples/esm-bundler/node_modules/postcss/lib/postcss.d.ts +456 -0
- package/examples/esm-bundler/node_modules/postcss/lib/postcss.js +101 -0
- package/examples/esm-bundler/node_modules/postcss/lib/postcss.mjs +30 -0
- package/examples/esm-bundler/node_modules/postcss/lib/previous-map.d.ts +80 -0
- package/examples/esm-bundler/node_modules/postcss/lib/previous-map.js +145 -0
- package/examples/esm-bundler/node_modules/postcss/lib/processor.d.ts +114 -0
- package/examples/esm-bundler/node_modules/postcss/lib/processor.js +67 -0
- package/examples/esm-bundler/node_modules/postcss/lib/result.d.ts +204 -0
- package/examples/esm-bundler/node_modules/postcss/lib/result.js +42 -0
- package/examples/esm-bundler/node_modules/postcss/lib/root.d.ts +86 -0
- package/examples/esm-bundler/node_modules/postcss/lib/root.js +61 -0
- package/examples/esm-bundler/node_modules/postcss/lib/rule.d.ts +126 -0
- package/examples/esm-bundler/node_modules/postcss/lib/rule.js +27 -0
- package/examples/esm-bundler/node_modules/postcss/lib/stringifier.d.ts +45 -0
- package/examples/esm-bundler/node_modules/postcss/lib/stringifier.js +353 -0
- package/examples/esm-bundler/node_modules/postcss/lib/stringify.d.ts +9 -0
- package/examples/esm-bundler/node_modules/postcss/lib/stringify.js +11 -0
- package/examples/esm-bundler/node_modules/postcss/lib/symbols.js +5 -0
- package/examples/esm-bundler/node_modules/postcss/lib/terminal-highlight.js +70 -0
- package/examples/esm-bundler/node_modules/postcss/lib/tokenize.js +266 -0
- package/examples/esm-bundler/node_modules/postcss/lib/warn-once.js +13 -0
- package/examples/esm-bundler/node_modules/postcss/lib/warning.d.ts +146 -0
- package/examples/esm-bundler/node_modules/postcss/lib/warning.js +37 -0
- package/examples/esm-bundler/node_modules/postcss/package.json +88 -0
- package/examples/esm-bundler/node_modules/rollup/LICENSE.md +679 -0
- package/examples/esm-bundler/node_modules/rollup/README.md +134 -0
- package/examples/esm-bundler/node_modules/rollup/dist/bin/rollup +1912 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/getLogFilter.js +64 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/package.json +1 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/parseAst.js +12 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/rollup.js +17 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/shared/node-entry.js +24463 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/shared/parseAst.js +2124 -0
- package/examples/esm-bundler/node_modules/rollup/dist/es/shared/watch.js +9909 -0
- package/examples/esm-bundler/node_modules/rollup/dist/getLogFilter.d.ts +5 -0
- package/examples/esm-bundler/node_modules/rollup/dist/getLogFilter.js +69 -0
- package/examples/esm-bundler/node_modules/rollup/dist/loadConfigFile.d.ts +20 -0
- package/examples/esm-bundler/node_modules/rollup/dist/loadConfigFile.js +29 -0
- package/examples/esm-bundler/node_modules/rollup/dist/native.js +161 -0
- package/examples/esm-bundler/node_modules/rollup/dist/parseAst.d.ts +4 -0
- package/examples/esm-bundler/node_modules/rollup/dist/parseAst.js +22 -0
- package/examples/esm-bundler/node_modules/rollup/dist/rollup.d.ts +1225 -0
- package/examples/esm-bundler/node_modules/rollup/dist/rollup.js +127 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/fsevents-importer.js +37 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/index.js +9615 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/loadConfigFile.js +572 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/parseAst.js +2361 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/rollup.js +24385 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/watch-cli.js +542 -0
- package/examples/esm-bundler/node_modules/rollup/dist/shared/watch.js +324 -0
- package/examples/esm-bundler/node_modules/rollup/package.json +289 -0
- package/examples/esm-bundler/node_modules/source-map-js/LICENSE +28 -0
- package/examples/esm-bundler/node_modules/source-map-js/README.md +765 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/array-set.js +121 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/base64-vlq.js +140 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/base64.js +67 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/binary-search.js +111 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/mapping-list.js +79 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/quick-sort.js +132 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/source-map-consumer.d.ts +1 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/source-map-consumer.js +1188 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/source-map-generator.d.ts +1 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/source-map-generator.js +444 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/source-node.d.ts +1 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/source-node.js +413 -0
- package/examples/esm-bundler/node_modules/source-map-js/lib/util.js +594 -0
- package/examples/esm-bundler/node_modules/source-map-js/package.json +71 -0
- package/examples/esm-bundler/node_modules/source-map-js/source-map.d.ts +104 -0
- package/examples/esm-bundler/node_modules/source-map-js/source-map.js +8 -0
- package/examples/esm-bundler/node_modules/tinyglobby/LICENSE +21 -0
- package/examples/esm-bundler/node_modules/tinyglobby/README.md +25 -0
- package/examples/esm-bundler/node_modules/tinyglobby/dist/index.cjs +334 -0
- package/examples/esm-bundler/node_modules/tinyglobby/dist/index.d.cts +148 -0
- package/examples/esm-bundler/node_modules/tinyglobby/dist/index.d.mts +148 -0
- package/examples/esm-bundler/node_modules/tinyglobby/dist/index.mjs +306 -0
- package/examples/esm-bundler/node_modules/tinyglobby/package.json +73 -0
- package/examples/esm-bundler/node_modules/vite/LICENSE.md +2406 -0
- package/examples/esm-bundler/node_modules/vite/README.md +20 -0
- package/examples/esm-bundler/node_modules/vite/bin/openChrome.applescript +95 -0
- package/examples/esm-bundler/node_modules/vite/bin/vite.js +79 -0
- package/examples/esm-bundler/node_modules/vite/client.d.ts +279 -0
- package/examples/esm-bundler/node_modules/vite/dist/client/client.mjs +1134 -0
- package/examples/esm-bundler/node_modules/vite/dist/client/env.mjs +24 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/chunks/dep-3RmXg9uo.js +553 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/chunks/dep-4-IQbZQm.js +822 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/chunks/dep-CvfTChi5.js +8218 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/chunks/dep-Dq2t6Dq0.js +49565 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/chunks/dep-lpEPC2f9.js +7113 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/cli.js +949 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/constants.js +149 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/index.d.ts +4227 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/index.js +194 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/module-runner.d.ts +290 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/module-runner.js +1311 -0
- package/examples/esm-bundler/node_modules/vite/dist/node/moduleRunnerTransport.d-DJ_mE5sf.d.ts +87 -0
- package/examples/esm-bundler/node_modules/vite/dist/node-cjs/publicUtils.cjs +3987 -0
- package/examples/esm-bundler/node_modules/vite/index.cjs +96 -0
- package/examples/esm-bundler/node_modules/vite/index.d.cts +6 -0
- package/examples/esm-bundler/node_modules/vite/misc/false.js +1 -0
- package/examples/esm-bundler/node_modules/vite/misc/true.js +1 -0
- package/examples/esm-bundler/node_modules/vite/package.json +204 -0
- package/examples/esm-bundler/node_modules/vite/types/customEvent.d.ts +45 -0
- package/examples/esm-bundler/node_modules/vite/types/hmrPayload.d.ts +74 -0
- package/examples/esm-bundler/node_modules/vite/types/hot.d.ts +39 -0
- package/examples/esm-bundler/node_modules/vite/types/import-meta.d.ts +5 -0
- package/examples/esm-bundler/node_modules/vite/types/importGlob.d.ts +75 -0
- package/examples/esm-bundler/node_modules/vite/types/importMeta.d.ts +31 -0
- package/examples/esm-bundler/node_modules/vite/types/internal/cssPreprocessorOptions.d.ts +63 -0
- package/examples/esm-bundler/node_modules/vite/types/internal/lightningcssOptions.d.ts +18 -0
- package/examples/esm-bundler/node_modules/vite/types/metadata.d.ts +35 -0
- package/examples/esm-bundler/node_modules/vite/types/package.json +4 -0
- package/examples/esm-bundler/package-lock.json +1127 -0
- package/examples/esm-bundler/package.json +15 -0
- package/examples/esm-bundler/src/main.ts +43 -0
- package/examples/react/README.md +10 -0
- package/examples/react/index.html +12 -0
- package/examples/react/package.json +19 -0
- package/examples/react/src/App.tsx +20 -0
- package/examples/react/src/main.tsx +9 -0
- package/examples/self-hosted/README.md +11 -0
- package/examples/self-hosted/index.html +12 -0
- package/examples/self-hosted/package.json +16 -0
- package/examples/self-hosted/public/.gitkeep +1 -0
- package/examples/self-hosted/src/main.ts +46 -0
- package/package.json +4 -2
- package/dist/lib-react.js.map +0 -9
- package/dist/lib.js.map +0 -9
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// AudioWorklet processor — runs in AudioWorkletGlobalScope
|
|
2
|
+
// Bundled separately and served at /processor.js
|
|
3
|
+
// This is a thin shell — all DSP logic lives in processor-kernel.ts
|
|
4
|
+
import { createFilterState, getProperties, handleProcessorMessage, processBlock, } from "./processor-kernel";
|
|
5
|
+
import { State } from "./types";
|
|
6
|
+
class ClipProcessor extends AudioWorkletProcessor {
|
|
7
|
+
static get parameterDescriptors() {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
name: "playbackRate",
|
|
11
|
+
automationRate: "a-rate",
|
|
12
|
+
defaultValue: 1.0,
|
|
13
|
+
},
|
|
14
|
+
{ name: "detune", automationRate: "a-rate", defaultValue: 0 },
|
|
15
|
+
{
|
|
16
|
+
name: "gain",
|
|
17
|
+
automationRate: "a-rate",
|
|
18
|
+
defaultValue: 1,
|
|
19
|
+
minValue: 0,
|
|
20
|
+
},
|
|
21
|
+
{ name: "pan", automationRate: "a-rate", defaultValue: 0 },
|
|
22
|
+
{
|
|
23
|
+
name: "highpass",
|
|
24
|
+
automationRate: "a-rate",
|
|
25
|
+
defaultValue: 20,
|
|
26
|
+
minValue: 20,
|
|
27
|
+
maxValue: 20000,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "lowpass",
|
|
31
|
+
automationRate: "a-rate",
|
|
32
|
+
defaultValue: 20000,
|
|
33
|
+
minValue: 20,
|
|
34
|
+
maxValue: 20000,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
properties;
|
|
39
|
+
filterState = {
|
|
40
|
+
lowpass: createFilterState(),
|
|
41
|
+
highpass: createFilterState(),
|
|
42
|
+
};
|
|
43
|
+
lastFrameTime = 0;
|
|
44
|
+
constructor(options) {
|
|
45
|
+
super(options);
|
|
46
|
+
this.properties = getProperties(options?.processorOptions, sampleRate);
|
|
47
|
+
this.port.onmessage = (ev) => {
|
|
48
|
+
const messages = handleProcessorMessage(this.properties, ev.data, currentTime, sampleRate);
|
|
49
|
+
for (const msg of messages)
|
|
50
|
+
this.port.postMessage(msg);
|
|
51
|
+
if (this.properties.state === State.Disposed)
|
|
52
|
+
this.port.close();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
process(_inputs, outputs, parameters) {
|
|
56
|
+
try {
|
|
57
|
+
const result = processBlock(this.properties, outputs, parameters, { currentTime, currentFrame, sampleRate }, this.filterState);
|
|
58
|
+
for (const msg of result.messages)
|
|
59
|
+
this.port.postMessage(msg);
|
|
60
|
+
// Frame reporting
|
|
61
|
+
const timeTaken = currentTime - this.lastFrameTime;
|
|
62
|
+
this.lastFrameTime = currentTime;
|
|
63
|
+
this.port.postMessage({
|
|
64
|
+
type: "frame",
|
|
65
|
+
data: [
|
|
66
|
+
currentTime,
|
|
67
|
+
currentFrame,
|
|
68
|
+
Math.floor(this.properties.playhead),
|
|
69
|
+
timeTaken * 1000,
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
return result.keepAlive;
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.log(e);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
registerProcessor("ClipProcessor", ClipProcessor);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export function dbFromLin(lin) {
|
|
2
|
+
return Math.max(20 * Math.log10(lin), -1000);
|
|
3
|
+
}
|
|
4
|
+
export function linFromDb(db) {
|
|
5
|
+
return 10 ** (db / 20);
|
|
6
|
+
}
|
|
7
|
+
const TEMPO_RELATIVE_SNAPS = ["beat", "bar", "8th", "16th"];
|
|
8
|
+
function clamp(value, min, max) {
|
|
9
|
+
return Math.min(Math.max(value, min), max);
|
|
10
|
+
}
|
|
11
|
+
export function isTempoRelativeSnap(snap) {
|
|
12
|
+
return TEMPO_RELATIVE_SNAPS.includes(snap);
|
|
13
|
+
}
|
|
14
|
+
export function getTempoSnapInterval(snap, tempo) {
|
|
15
|
+
if (!Number.isFinite(tempo) || tempo <= 0)
|
|
16
|
+
return null;
|
|
17
|
+
const secondsPerBeat = 60 / tempo;
|
|
18
|
+
switch (snap) {
|
|
19
|
+
case "beat":
|
|
20
|
+
return secondsPerBeat;
|
|
21
|
+
case "bar":
|
|
22
|
+
return secondsPerBeat * 4;
|
|
23
|
+
case "8th":
|
|
24
|
+
return secondsPerBeat / 2;
|
|
25
|
+
case "16th":
|
|
26
|
+
return secondsPerBeat / 4;
|
|
27
|
+
default:
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function remapTempoRelativeValue(value, snap, oldTempo, newTempo, min, max) {
|
|
32
|
+
if (!isTempoRelativeSnap(snap)) {
|
|
33
|
+
return clamp(value, min, max);
|
|
34
|
+
}
|
|
35
|
+
if (value < 0) {
|
|
36
|
+
return clamp(value, min, max);
|
|
37
|
+
}
|
|
38
|
+
const oldInterval = getTempoSnapInterval(snap, oldTempo);
|
|
39
|
+
const newInterval = getTempoSnapInterval(snap, newTempo);
|
|
40
|
+
if (oldInterval == null || newInterval == null) {
|
|
41
|
+
return clamp(value, min, max);
|
|
42
|
+
}
|
|
43
|
+
const count = Math.round(value / oldInterval);
|
|
44
|
+
return clamp(count * newInterval, min, max);
|
|
45
|
+
}
|
|
46
|
+
export function getSnappedValue(value, snap, tempo) {
|
|
47
|
+
const interval = getTempoSnapInterval(snap, tempo);
|
|
48
|
+
if (interval != null) {
|
|
49
|
+
return Math.round(value / interval) * interval;
|
|
50
|
+
}
|
|
51
|
+
switch (snap) {
|
|
52
|
+
case "int":
|
|
53
|
+
return Math.round(value);
|
|
54
|
+
default:
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export const presets = {
|
|
59
|
+
hertz: {
|
|
60
|
+
snaps: [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384],
|
|
61
|
+
ticks: [64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384],
|
|
62
|
+
min: 32,
|
|
63
|
+
max: 16384,
|
|
64
|
+
logarithmic: true,
|
|
65
|
+
},
|
|
66
|
+
decibel: {
|
|
67
|
+
ticks: [-48, -24, -12, -6, -3, 0],
|
|
68
|
+
min: -60,
|
|
69
|
+
max: 0,
|
|
70
|
+
skew: 1,
|
|
71
|
+
},
|
|
72
|
+
cents: {
|
|
73
|
+
snaps: Array.from({ length: 49 }, (_, i) => (i - 24) * 100), // semitones: -2400..2400 by 100
|
|
74
|
+
ticks: [-2400, -1200, 0, 1200, 2400],
|
|
75
|
+
min: -2400,
|
|
76
|
+
max: 2400,
|
|
77
|
+
skew: 1,
|
|
78
|
+
step: 1,
|
|
79
|
+
},
|
|
80
|
+
playbackRate: {
|
|
81
|
+
snaps: [-2, -1, -0.5, 0, 0.5, 1, 1.5, 2],
|
|
82
|
+
ticks: [-2, -1, 0, 1, 2],
|
|
83
|
+
min: -2,
|
|
84
|
+
max: 2,
|
|
85
|
+
skew: 1,
|
|
86
|
+
},
|
|
87
|
+
gain: {
|
|
88
|
+
snaps: [-60, -48, -36, -24, -18, -12, -9, -6, -3, -1, 0],
|
|
89
|
+
ticks: [-48, -24, -12, -6, -3, 0],
|
|
90
|
+
min: -100,
|
|
91
|
+
max: 0,
|
|
92
|
+
skew: 6,
|
|
93
|
+
},
|
|
94
|
+
pan: {
|
|
95
|
+
snaps: [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1],
|
|
96
|
+
ticks: [-1, -0.5, 0, 0.5, 1],
|
|
97
|
+
min: -1,
|
|
98
|
+
max: 1,
|
|
99
|
+
skew: 1,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
export function float32ArrayFromAudioBuffer(buffer) {
|
|
103
|
+
return buffer.numberOfChannels === 1
|
|
104
|
+
? [buffer.getChannelData(0)]
|
|
105
|
+
: [buffer.getChannelData(0), buffer.getChannelData(1)];
|
|
106
|
+
}
|
|
107
|
+
export function audioBufferFromFloat32Array(context, data) {
|
|
108
|
+
if (!data || data.length === 0)
|
|
109
|
+
return undefined;
|
|
110
|
+
const buffer = context.createBuffer(data.length, data[0].length, context.sampleRate);
|
|
111
|
+
for (let i = 0; i < data.length; i++) {
|
|
112
|
+
buffer.copyToChannel(new Float32Array(data[i]), i);
|
|
113
|
+
}
|
|
114
|
+
return buffer;
|
|
115
|
+
}
|
|
116
|
+
export function generateSnapPoints(snap, tempo, min, max) {
|
|
117
|
+
const interval = getTempoSnapInterval(snap, tempo) ?? (snap === "int" ? 1 : null);
|
|
118
|
+
if (interval == null)
|
|
119
|
+
return [];
|
|
120
|
+
if (interval <= 0)
|
|
121
|
+
return [];
|
|
122
|
+
const points = [];
|
|
123
|
+
const start = Math.ceil(min / interval) * interval;
|
|
124
|
+
for (let v = start; v <= max; v += interval) {
|
|
125
|
+
points.push(Math.round(v * 1e10) / 1e10);
|
|
126
|
+
}
|
|
127
|
+
return points;
|
|
128
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.2";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { processorCode } from "./processor-code";
|
|
2
|
+
import { VERSION } from "./version";
|
|
3
|
+
const PACKAGE_NAME = "@jadujoel/web-audio-clip-node";
|
|
4
|
+
const PACKAGE_VERSION = VERSION;
|
|
5
|
+
/** Blob URL from embedded processor code. Zero-config, default for npm users. */
|
|
6
|
+
export function getProcessorBlobUrl() {
|
|
7
|
+
const blob = new Blob([processorCode], { type: "text/javascript" });
|
|
8
|
+
return URL.createObjectURL(blob);
|
|
9
|
+
}
|
|
10
|
+
/** jsDelivr CDN URL. For script-tag / no-bundler usage. */
|
|
11
|
+
export function getProcessorCdnUrl(version = PACKAGE_VERSION) {
|
|
12
|
+
return `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@${version}/dist/processor.js`;
|
|
13
|
+
}
|
|
14
|
+
/** Custom URL relative to a base. For self-hosted processor.js. */
|
|
15
|
+
export function getProcessorModuleUrl(baseUrl = document.baseURI) {
|
|
16
|
+
return new URL("./processor.js", baseUrl).toString();
|
|
17
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useCallback, useId, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { generateSnapPoints, getSnappedValue, presets } from "../audio/utils";
|
|
4
|
+
import { formatTickLabel, formatValueText } from "../controls/formatValueText";
|
|
5
|
+
import { ContextMenu } from "./ContextMenu";
|
|
6
|
+
import { SnappableSlider } from "./SnappableSlider";
|
|
7
|
+
function AudioControlInner({ label, controlKey, min: propMin, max: propMax, value, defaultValue, step, tempo = 120, snap = "none", preset, title, enabled = true, hasToggle = false, hasSnap = false, hasMaxLock = false, audioDuration, maxLocked = false, onChange, onToggle, onSnapChange, onMinChange, onMaxChange, onMaxLockedChange, }) {
|
|
8
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
9
|
+
const [editText, setEditText] = useState("");
|
|
10
|
+
const [ctxMenu, setCtxMenu] = useState(null);
|
|
11
|
+
const inputRef = useRef(null);
|
|
12
|
+
const labelId = useId();
|
|
13
|
+
const presetConfig = preset ? presets[preset] : undefined;
|
|
14
|
+
const resolvedMin = presetConfig?.min ?? propMin;
|
|
15
|
+
const resolvedMax = presetConfig?.max ?? propMax;
|
|
16
|
+
const tempoSnaps = snap !== "none" && !preset
|
|
17
|
+
? generateSnapPoints(snap, tempo, resolvedMin, resolvedMax)
|
|
18
|
+
: [];
|
|
19
|
+
const resolvedSnaps = presetConfig?.snaps ?? tempoSnaps;
|
|
20
|
+
const resolvedTicks = presetConfig?.ticks ?? [];
|
|
21
|
+
const resolvedSkew = presetConfig?.skew ?? 1;
|
|
22
|
+
const resolvedStep = step ?? presetConfig?.step;
|
|
23
|
+
const resolvedLogarithmic = presetConfig?.logarithmic ?? false;
|
|
24
|
+
const handleSliderChange = useCallback((rawValue) => {
|
|
25
|
+
if (!enabled)
|
|
26
|
+
return;
|
|
27
|
+
const snapped = getSnappedValue(rawValue, snap, tempo);
|
|
28
|
+
onChange?.(snapped);
|
|
29
|
+
}, [snap, tempo, onChange, enabled]);
|
|
30
|
+
const displayValue = formatValueText(value, controlKey, snap, tempo);
|
|
31
|
+
const tickFormatter = useMemo(() => {
|
|
32
|
+
if (preset || !controlKey)
|
|
33
|
+
return undefined;
|
|
34
|
+
return (v) => formatTickLabel(v, controlKey, snap, tempo);
|
|
35
|
+
}, [preset, controlKey, snap, tempo]);
|
|
36
|
+
const startEditing = useCallback(() => {
|
|
37
|
+
setEditText(String(value));
|
|
38
|
+
setIsEditing(true);
|
|
39
|
+
queueMicrotask(() => {
|
|
40
|
+
inputRef.current?.select();
|
|
41
|
+
});
|
|
42
|
+
}, [value]);
|
|
43
|
+
const commitEdit = useCallback(() => {
|
|
44
|
+
setIsEditing(false);
|
|
45
|
+
const parsed = Number.parseFloat(editText);
|
|
46
|
+
if (Number.isFinite(parsed)) {
|
|
47
|
+
const clamped = Math.min(Math.max(parsed, resolvedMin), resolvedMax);
|
|
48
|
+
onChange?.(clamped);
|
|
49
|
+
}
|
|
50
|
+
}, [editText, resolvedMin, resolvedMax, onChange]);
|
|
51
|
+
const cancelEdit = useCallback(() => {
|
|
52
|
+
setIsEditing(false);
|
|
53
|
+
}, []);
|
|
54
|
+
const handleEditKeyDown = useCallback((e) => {
|
|
55
|
+
if (e.key === "Enter") {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
commitEdit();
|
|
58
|
+
}
|
|
59
|
+
else if (e.key === "Escape") {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
cancelEdit();
|
|
62
|
+
}
|
|
63
|
+
}, [commitEdit, cancelEdit]);
|
|
64
|
+
const handleContextMenu = useCallback((e) => {
|
|
65
|
+
if (!hasSnap)
|
|
66
|
+
return;
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
setCtxMenu({ x: e.clientX, y: e.clientY });
|
|
69
|
+
}, [hasSnap]);
|
|
70
|
+
return (_jsxs("div", { className: `audio-control${hasToggle && !enabled ? " audio-control--disabled" : ""}`, title: title, onContextMenu: handleContextMenu, children: [hasToggle && (_jsx("input", { type: "checkbox", className: "control-toggle", checked: enabled, onChange: (e) => onToggle?.(e.target.checked) })), !hasToggle && _jsx("span", { className: "control-toggle-placeholder" }), _jsx("span", { className: "control-label", id: labelId, children: label }), _jsx(SnappableSlider, { min: resolvedMin, max: resolvedMax, value: value, skew: resolvedSkew, step: resolvedStep, defaultValue: defaultValue, enableSnap: snap !== "none", snaps: resolvedSnaps, ticks: resolvedTicks, logarithmic: resolvedLogarithmic, disabled: hasToggle && !enabled, labelId: labelId, valueText: displayValue, formatTick: tickFormatter, onChange: handleSliderChange }), isEditing ? (_jsx("input", { ref: inputRef, type: "text", className: "control-output control-output--editing", value: editText, onChange: (e) => setEditText(e.target.value), onBlur: commitEdit, onKeyDown: handleEditKeyDown })) : (_jsx("button", { type: "button", className: "control-output", onClick: startEditing, children: displayValue })), ctxMenu && (_jsx(ContextMenu, { x: ctxMenu.x, y: ctxMenu.y, snap: snap, snapMode: preset ? "preset" : "tempo", min: propMin, max: propMax, maxLocked: maxLocked, showMaxLock: hasMaxLock, audioDuration: audioDuration ?? null, onSnapChange: (s) => {
|
|
71
|
+
onSnapChange?.(s);
|
|
72
|
+
}, onMinChange: (v) => onMinChange?.(v), onMaxChange: (v) => onMaxChange?.(v), onMaxLockedChange: (locked) => onMaxLockedChange?.(locked), onClose: () => setCtxMenu(null) }))] }));
|
|
73
|
+
}
|
|
74
|
+
function areAudioControlPropsEqual(prev, next) {
|
|
75
|
+
return (prev.label === next.label &&
|
|
76
|
+
prev.controlKey === next.controlKey &&
|
|
77
|
+
prev.min === next.min &&
|
|
78
|
+
prev.max === next.max &&
|
|
79
|
+
prev.value === next.value &&
|
|
80
|
+
prev.defaultValue === next.defaultValue &&
|
|
81
|
+
prev.step === next.step &&
|
|
82
|
+
prev.tempo === next.tempo &&
|
|
83
|
+
prev.snap === next.snap &&
|
|
84
|
+
prev.preset === next.preset &&
|
|
85
|
+
prev.title === next.title &&
|
|
86
|
+
prev.enabled === next.enabled &&
|
|
87
|
+
prev.hasToggle === next.hasToggle &&
|
|
88
|
+
prev.hasSnap === next.hasSnap &&
|
|
89
|
+
prev.hasMaxLock === next.hasMaxLock &&
|
|
90
|
+
prev.audioDuration === next.audioDuration &&
|
|
91
|
+
prev.maxLocked === next.maxLocked &&
|
|
92
|
+
prev.onChange === next.onChange &&
|
|
93
|
+
prev.onToggle === next.onToggle &&
|
|
94
|
+
prev.onSnapChange === next.onSnapChange &&
|
|
95
|
+
prev.onMinChange === next.onMinChange &&
|
|
96
|
+
prev.onMaxChange === next.onMaxChange &&
|
|
97
|
+
prev.onMaxLockedChange === next.onMaxLockedChange);
|
|
98
|
+
}
|
|
99
|
+
export const AudioControl = memo(AudioControlInner, areAudioControlPropsEqual);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
const SNAP_OPTIONS = [
|
|
5
|
+
{ value: "none", label: "None" },
|
|
6
|
+
{ value: "beat", label: "Beat" },
|
|
7
|
+
{ value: "bar", label: "Bar" },
|
|
8
|
+
{ value: "8th", label: "8th" },
|
|
9
|
+
{ value: "16th", label: "16th" },
|
|
10
|
+
{ value: "int", label: "Integer" },
|
|
11
|
+
];
|
|
12
|
+
export function ContextMenu({ x, y, snap, snapMode = "tempo", min, max, maxLocked = false, showMaxLock = true, audioDuration, onSnapChange, onMinChange, onMaxChange, onMaxLockedChange, onClose, }) {
|
|
13
|
+
const menuRef = useRef(null);
|
|
14
|
+
// Close on click outside or Escape
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const handleClick = (e) => {
|
|
17
|
+
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
18
|
+
onClose();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const handleKey = (e) => {
|
|
22
|
+
if (e.key === "Escape")
|
|
23
|
+
onClose();
|
|
24
|
+
};
|
|
25
|
+
document.addEventListener("mousedown", handleClick);
|
|
26
|
+
document.addEventListener("keydown", handleKey);
|
|
27
|
+
return () => {
|
|
28
|
+
document.removeEventListener("mousedown", handleClick);
|
|
29
|
+
document.removeEventListener("keydown", handleKey);
|
|
30
|
+
};
|
|
31
|
+
}, [onClose]);
|
|
32
|
+
// Adjust position to stay within viewport
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const el = menuRef.current;
|
|
35
|
+
if (!el)
|
|
36
|
+
return;
|
|
37
|
+
const rect = el.getBoundingClientRect();
|
|
38
|
+
if (rect.right > window.innerWidth) {
|
|
39
|
+
el.style.left = `${window.innerWidth - rect.width - 8}px`;
|
|
40
|
+
}
|
|
41
|
+
if (rect.bottom > window.innerHeight) {
|
|
42
|
+
el.style.top = `${window.innerHeight - rect.height - 8}px`;
|
|
43
|
+
}
|
|
44
|
+
}, []);
|
|
45
|
+
const handleSnapClick = useCallback((value) => {
|
|
46
|
+
onSnapChange(value);
|
|
47
|
+
}, [onSnapChange]);
|
|
48
|
+
const handleMinCommit = useCallback((e) => {
|
|
49
|
+
const input = e.currentTarget;
|
|
50
|
+
const parsed = Number.parseFloat(input.value);
|
|
51
|
+
if (Number.isFinite(parsed)) {
|
|
52
|
+
onMinChange(parsed);
|
|
53
|
+
}
|
|
54
|
+
}, [onMinChange]);
|
|
55
|
+
const handleMaxCommit = useCallback((e) => {
|
|
56
|
+
const input = e.currentTarget;
|
|
57
|
+
const parsed = Number.parseFloat(input.value);
|
|
58
|
+
if (Number.isFinite(parsed)) {
|
|
59
|
+
onMaxChange(parsed);
|
|
60
|
+
}
|
|
61
|
+
}, [onMaxChange]);
|
|
62
|
+
const handleInputKeyDown = useCallback((commit) => (e) => {
|
|
63
|
+
if (e.key === "Enter") {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
commit(e);
|
|
66
|
+
}
|
|
67
|
+
else if (e.key === "Escape") {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
onClose();
|
|
70
|
+
}
|
|
71
|
+
}, [onClose]);
|
|
72
|
+
return createPortal(_jsxs("div", { ref: menuRef, className: "context-menu", style: { left: x, top: y }, role: "menu", children: [_jsx("div", { className: "context-menu__section-label", children: "Snap" }), snapMode === "preset" ? (_jsxs("label", { className: "context-menu__field", children: [_jsx("input", { type: "checkbox", className: "control-toggle", checked: snap !== "none", onChange: (e) => onSnapChange(e.target.checked ? "preset" : "none") }), "Enable snap"] })) : (SNAP_OPTIONS.map((opt) => (_jsxs("button", { type: "button", className: `context-menu__item${snap === opt.value ? " context-menu__item--active" : ""}`, role: "menuitemradio", "aria-checked": snap === opt.value, onClick: () => handleSnapClick(opt.value), children: [_jsx("span", { className: "context-menu__radio", children: snap === opt.value ? "●" : "○" }), opt.label] }, opt.value)))), _jsx("div", { className: "context-menu__divider" }), _jsx("div", { className: "context-menu__section-label", children: "Range" }), _jsxs("label", { className: "context-menu__field", children: ["Min:", _jsx("input", { type: "number", className: "context-menu__input", defaultValue: min, step: "any", onBlur: handleMinCommit, onKeyDown: handleInputKeyDown(handleMinCommit) })] }), _jsxs("label", { className: "context-menu__field", children: ["Max:", _jsx("input", { type: "number", className: "context-menu__input", defaultValue: max, step: "any", disabled: maxLocked && audioDuration != null, onBlur: handleMaxCommit, onKeyDown: handleInputKeyDown(handleMaxCommit) })] }), showMaxLock && (_jsxs("label", { className: "context-menu__field", children: [_jsx("input", { type: "checkbox", className: "control-toggle", checked: maxLocked, onChange: (e) => onMaxLockedChange?.(e.target.checked) }), "Max = file length"] }))] }), document.body);
|
|
73
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useId } from "react";
|
|
3
|
+
import { AudioControl } from "./AudioControl";
|
|
4
|
+
/** Build a set of control keys that belong to a linked pair,
|
|
5
|
+
* and a map from the first control key to its pair def. */
|
|
6
|
+
function buildPairMaps(pairs) {
|
|
7
|
+
const pairByFirst = new Map();
|
|
8
|
+
const pairedKeys = new Set();
|
|
9
|
+
if (pairs) {
|
|
10
|
+
for (const pair of pairs) {
|
|
11
|
+
pairByFirst.set(pair.controls[0], pair);
|
|
12
|
+
pairedKeys.add(pair.controls[0]);
|
|
13
|
+
pairedKeys.add(pair.controls[1]);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return { pairByFirst, pairedKeys };
|
|
17
|
+
}
|
|
18
|
+
function renderAudioControl(def, props) {
|
|
19
|
+
const { mins, maxs, maxLocked, audioDuration, values, tempo, snaps, enabled, onValueChange, onToggle, onSnapChange, onMinChange, onMaxChange, onMaxLockedChange, } = props;
|
|
20
|
+
return (_jsx(AudioControl, { label: def.label, controlKey: def.key, min: mins[def.key] ?? def.min, max: maxLocked[def.key] && audioDuration != null
|
|
21
|
+
? audioDuration
|
|
22
|
+
: (maxs[def.key] ?? def.max), value: values[def.key], defaultValue: def.defaultValue, tempo: tempo, snap: snaps[def.key], preset: def.preset, title: def.title, enabled: enabled[def.key], hasToggle: def.hasToggle, hasSnap: def.hasSnap, hasMaxLock: def.hasMaxLock, audioDuration: audioDuration, maxLocked: maxLocked[def.key] ?? true, onChange: (v) => onValueChange(def.key, v), onToggle: (on) => onToggle(def.key, on), onSnapChange: (s) => onSnapChange(def.key, s), onMinChange: (v) => onMinChange(def.key, v), onMaxChange: (v) => onMaxChange(def.key, v), onMaxLockedChange: (locked) => onMaxLockedChange(def.key, locked) }, def.key));
|
|
23
|
+
}
|
|
24
|
+
function ControlSectionInner({ legend, defs, linked, linkedPairs, onLinkedChange, ...controlProps }) {
|
|
25
|
+
const sectionId = useId();
|
|
26
|
+
const { pairByFirst, pairedKeys } = buildPairMaps(linkedPairs);
|
|
27
|
+
const items = [];
|
|
28
|
+
let i = 0;
|
|
29
|
+
while (i < defs.length) {
|
|
30
|
+
const def = defs[i];
|
|
31
|
+
const pair = pairByFirst.get(def.key);
|
|
32
|
+
if (pair) {
|
|
33
|
+
// Find the second control in the pair
|
|
34
|
+
const secondDef = defs.find((d) => d.key === pair.controls[1]);
|
|
35
|
+
const isLinked = linked?.[pair.key] ?? false;
|
|
36
|
+
const inputId = `${sectionId}-${pair.key}`;
|
|
37
|
+
items.push(_jsxs("div", { className: `control-link-group${isLinked ? " control-link-group--active" : ""}`, children: [_jsxs("div", { className: "control-link-bracket", children: [_jsx("span", { className: "control-link-line" }), _jsx("button", { type: "button", id: inputId, className: "control-link-btn", "aria-pressed": isLinked, "aria-label": pair.label, title: pair.label, onClick: () => onLinkedChange?.(pair.key, !isLinked), children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M6.5 4.5h-1A2.5 2.5 0 0 0 3 7v2a2.5 2.5 0 0 0 2.5 2.5h1m3-7h1A2.5 2.5 0 0 1 13 7v2a2.5 2.5 0 0 1-2.5 2.5h-1M5.5 8h5", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }) }) }), _jsx("span", { className: "control-link-line" })] }), _jsxs("div", { className: "control-link-controls", children: [renderAudioControl(def, controlProps), secondDef && renderAudioControl(secondDef, controlProps)] })] }, `link-${pair.key}`));
|
|
38
|
+
// Skip past both controls in the pair
|
|
39
|
+
i += 1;
|
|
40
|
+
if (secondDef && defs[i]?.key === secondDef.key) {
|
|
41
|
+
i += 1;
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Not part of a pair (or is a second control already rendered above)
|
|
46
|
+
if (!pairedKeys.has(def.key)) {
|
|
47
|
+
items.push(renderAudioControl(def, controlProps));
|
|
48
|
+
}
|
|
49
|
+
i += 1;
|
|
50
|
+
}
|
|
51
|
+
return (_jsxs("fieldset", { className: "control-group", children: [_jsx("legend", { children: legend }), items] }));
|
|
52
|
+
}
|
|
53
|
+
function areControlSectionPropsEqual(prev, next) {
|
|
54
|
+
return (prev.legend === next.legend &&
|
|
55
|
+
prev.defs === next.defs &&
|
|
56
|
+
prev.values === next.values &&
|
|
57
|
+
prev.snaps === next.snaps &&
|
|
58
|
+
prev.enabled === next.enabled &&
|
|
59
|
+
prev.mins === next.mins &&
|
|
60
|
+
prev.maxs === next.maxs &&
|
|
61
|
+
prev.maxLocked === next.maxLocked &&
|
|
62
|
+
prev.linked === next.linked &&
|
|
63
|
+
prev.linkedPairs === next.linkedPairs &&
|
|
64
|
+
prev.tempo === next.tempo &&
|
|
65
|
+
prev.audioDuration === next.audioDuration &&
|
|
66
|
+
prev.onValueChange === next.onValueChange &&
|
|
67
|
+
prev.onToggle === next.onToggle &&
|
|
68
|
+
prev.onLinkedChange === next.onLinkedChange &&
|
|
69
|
+
prev.onSnapChange === next.onSnapChange &&
|
|
70
|
+
prev.onMinChange === next.onMinChange &&
|
|
71
|
+
prev.onMaxChange === next.onMaxChange &&
|
|
72
|
+
prev.onMaxLockedChange === next.onMaxLockedChange);
|
|
73
|
+
}
|
|
74
|
+
export const ControlSection = memo(ControlSectionInner, areControlSectionPropsEqual);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useCallback, useId, useRef, useState } from "react";
|
|
3
|
+
import { presets } from "../audio/utils";
|
|
4
|
+
import { SnappableSlider } from "./SnappableSlider";
|
|
5
|
+
function DetuneControlInner({ value, defaultValue, enabled, onChange, onToggle, }) {
|
|
6
|
+
const preset = presets.cents;
|
|
7
|
+
const min = preset.min ?? -2400;
|
|
8
|
+
const max = preset.max ?? 2400;
|
|
9
|
+
const labelId = useId();
|
|
10
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
11
|
+
const [editText, setEditText] = useState("");
|
|
12
|
+
const inputRef = useRef(null);
|
|
13
|
+
const displayValue = `${Math.round(value)} cents`;
|
|
14
|
+
const handleChange = useCallback((v) => {
|
|
15
|
+
if (!enabled)
|
|
16
|
+
return;
|
|
17
|
+
onChange(v);
|
|
18
|
+
}, [enabled, onChange]);
|
|
19
|
+
const startEditing = useCallback(() => {
|
|
20
|
+
setEditText(String(Math.round(value)));
|
|
21
|
+
setIsEditing(true);
|
|
22
|
+
queueMicrotask(() => inputRef.current?.select());
|
|
23
|
+
}, [value]);
|
|
24
|
+
const commitEdit = useCallback(() => {
|
|
25
|
+
setIsEditing(false);
|
|
26
|
+
const parsed = Number.parseFloat(editText);
|
|
27
|
+
if (Number.isFinite(parsed)) {
|
|
28
|
+
onChange(Math.min(Math.max(parsed, min), max));
|
|
29
|
+
}
|
|
30
|
+
}, [editText, min, max, onChange]);
|
|
31
|
+
const handleEditKeyDown = useCallback((e) => {
|
|
32
|
+
if (e.key === "Enter") {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
commitEdit();
|
|
35
|
+
}
|
|
36
|
+
else if (e.key === "Escape") {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
setIsEditing(false);
|
|
39
|
+
}
|
|
40
|
+
}, [commitEdit]);
|
|
41
|
+
const disabled = !enabled;
|
|
42
|
+
return (_jsxs("div", { className: `audio-control${disabled ? " audio-control--disabled" : ""}`, title: "Pitch shift in cents.", children: [_jsx("input", { type: "checkbox", className: "control-toggle", checked: enabled, onChange: (e) => onToggle(e.target.checked) }), _jsx("span", { className: "control-label", id: labelId, children: "Detune" }), _jsx(SnappableSlider, { min: min, max: max, value: value, skew: preset.skew ?? 1, step: preset.step ?? 1, defaultValue: defaultValue, ticks: preset.ticks ?? [], disabled: disabled, labelId: labelId, valueText: displayValue, onChange: handleChange }), isEditing ? (_jsx("input", { ref: inputRef, type: "text", className: "control-output control-output--editing", value: editText, onChange: (e) => setEditText(e.target.value), onBlur: commitEdit, onKeyDown: handleEditKeyDown })) : (_jsx("button", { type: "button", className: "control-output", onClick: startEditing, children: displayValue }))] }));
|
|
43
|
+
}
|
|
44
|
+
export const DetuneControl = memo(DetuneControlInner);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo } from "react";
|
|
3
|
+
function DisplayPanelInner({ nodeState, statusMessage, soundName, currentTime, currentFrame, timesLooped, latency, timeTaken, }) {
|
|
4
|
+
return (_jsxs("section", { id: "display", children: [statusMessage && (_jsx("div", { className: "status-message", role: "alert", children: statusMessage })), _jsx("code", { children: "Sound:" }), _jsx("output", { children: soundName ?? "none" }), _jsx("code", { children: "State:" }), _jsx("output", { children: nodeState }), _jsx("code", { children: "Time:" }), _jsx("output", { children: currentTime }), _jsx("code", { children: "Loops:" }), _jsx("output", { children: timesLooped }), _jsxs("details", { className: "display-details", children: [_jsx("summary", { children: "Debug" }), _jsxs("div", { className: "display-details__row", children: [_jsx("code", { children: "Frame:" }), _jsx("output", { children: currentFrame }), _jsx("code", { children: "Latency:" }), _jsx("output", { children: latency }), _jsx("code", { children: "TimeTaken:" }), _jsx("output", { children: timeTaken })] })] })] }));
|
|
5
|
+
}
|
|
6
|
+
export const DisplayPanel = memo(DisplayPanelInner);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useCallback, useId, useRef, useState } from "react";
|
|
3
|
+
import { presets } from "../audio/utils";
|
|
4
|
+
import { SnappableSlider } from "./SnappableSlider";
|
|
5
|
+
function formatHz(value) {
|
|
6
|
+
if (value >= 1000)
|
|
7
|
+
return `${(value / 1000).toFixed(1)} kHz`;
|
|
8
|
+
return `${Math.round(value)} Hz`;
|
|
9
|
+
}
|
|
10
|
+
function FilterControlInner({ label, controlKey: _controlKey, value, defaultValue, enabled, onChange, onToggle, }) {
|
|
11
|
+
const preset = presets.hertz;
|
|
12
|
+
const min = preset.min ?? 32;
|
|
13
|
+
const max = preset.max ?? 16384;
|
|
14
|
+
const labelId = useId();
|
|
15
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
16
|
+
const [editText, setEditText] = useState("");
|
|
17
|
+
const inputRef = useRef(null);
|
|
18
|
+
const handleChange = useCallback((v) => {
|
|
19
|
+
if (!enabled)
|
|
20
|
+
return;
|
|
21
|
+
onChange(v);
|
|
22
|
+
}, [enabled, onChange]);
|
|
23
|
+
const startEditing = useCallback(() => {
|
|
24
|
+
setEditText(String(Math.round(value)));
|
|
25
|
+
setIsEditing(true);
|
|
26
|
+
queueMicrotask(() => inputRef.current?.select());
|
|
27
|
+
}, [value]);
|
|
28
|
+
const commitEdit = useCallback(() => {
|
|
29
|
+
setIsEditing(false);
|
|
30
|
+
const parsed = Number.parseFloat(editText);
|
|
31
|
+
if (Number.isFinite(parsed)) {
|
|
32
|
+
onChange(Math.min(Math.max(parsed, min), max));
|
|
33
|
+
}
|
|
34
|
+
}, [editText, min, max, onChange]);
|
|
35
|
+
const handleEditKeyDown = useCallback((e) => {
|
|
36
|
+
if (e.key === "Enter") {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
commitEdit();
|
|
39
|
+
}
|
|
40
|
+
else if (e.key === "Escape") {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
setIsEditing(false);
|
|
43
|
+
}
|
|
44
|
+
}, [commitEdit]);
|
|
45
|
+
const disabled = !enabled;
|
|
46
|
+
return (_jsxs("div", { className: `audio-control${disabled ? " audio-control--disabled" : ""}`, title: `${label} cutoff frequency.`, children: [_jsx("input", { type: "checkbox", className: "control-toggle", checked: enabled, onChange: (e) => onToggle(e.target.checked) }), _jsx("span", { className: "control-label", id: labelId, children: label }), _jsx(SnappableSlider, { min: min, max: max, value: value, logarithmic: true, defaultValue: defaultValue, enableSnap: true, snaps: preset.snaps ?? [], ticks: preset.ticks ?? [], disabled: disabled, labelId: labelId, valueText: formatHz(value), onChange: handleChange }), isEditing ? (_jsx("input", { ref: inputRef, type: "text", className: "control-output control-output--editing", value: editText, onChange: (e) => setEditText(e.target.value), onBlur: commitEdit, onKeyDown: handleEditKeyDown })) : (_jsx("button", { type: "button", className: "control-output", onClick: startEditing, children: formatHz(value) }))] }));
|
|
47
|
+
}
|
|
48
|
+
export const FilterControl = memo(FilterControlInner);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useCallback, useId, useRef, useState } from "react";
|
|
3
|
+
import { presets } from "../audio/utils";
|
|
4
|
+
import { SnappableSlider } from "./SnappableSlider";
|
|
5
|
+
function GainControlInner({ value, defaultValue, enabled, onChange, onToggle, }) {
|
|
6
|
+
const preset = presets.gain;
|
|
7
|
+
const min = preset.min ?? -100;
|
|
8
|
+
const max = preset.max ?? 0;
|
|
9
|
+
const labelId = useId();
|
|
10
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
11
|
+
const [editText, setEditText] = useState("");
|
|
12
|
+
const inputRef = useRef(null);
|
|
13
|
+
const displayValue = `${value.toFixed(1)} dB`;
|
|
14
|
+
const handleChange = useCallback((v) => {
|
|
15
|
+
if (!enabled)
|
|
16
|
+
return;
|
|
17
|
+
onChange(v);
|
|
18
|
+
}, [enabled, onChange]);
|
|
19
|
+
const startEditing = useCallback(() => {
|
|
20
|
+
setEditText(String(value));
|
|
21
|
+
setIsEditing(true);
|
|
22
|
+
queueMicrotask(() => inputRef.current?.select());
|
|
23
|
+
}, [value]);
|
|
24
|
+
const commitEdit = useCallback(() => {
|
|
25
|
+
setIsEditing(false);
|
|
26
|
+
const parsed = Number.parseFloat(editText);
|
|
27
|
+
if (Number.isFinite(parsed)) {
|
|
28
|
+
onChange(Math.min(Math.max(parsed, min), max));
|
|
29
|
+
}
|
|
30
|
+
}, [editText, min, max, onChange]);
|
|
31
|
+
const handleEditKeyDown = useCallback((e) => {
|
|
32
|
+
if (e.key === "Enter") {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
commitEdit();
|
|
35
|
+
}
|
|
36
|
+
else if (e.key === "Escape") {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
setIsEditing(false);
|
|
39
|
+
}
|
|
40
|
+
}, [commitEdit]);
|
|
41
|
+
const disabled = !enabled;
|
|
42
|
+
return (_jsxs("div", { className: `audio-control${disabled ? " audio-control--disabled" : ""}`, title: "Amplitude in dB.", children: [_jsx("input", { type: "checkbox", className: "control-toggle", checked: enabled, onChange: (e) => onToggle(e.target.checked) }), _jsx("span", { className: "control-label", id: labelId, children: "Gain" }), _jsx(SnappableSlider, { min: min, max: max, value: value, skew: preset.skew ?? 6, defaultValue: defaultValue, ticks: preset.ticks ?? [], disabled: disabled, labelId: labelId, valueText: displayValue, onChange: handleChange }), isEditing ? (_jsx("input", { ref: inputRef, type: "text", className: "control-output control-output--editing", value: editText, onChange: (e) => setEditText(e.target.value), onBlur: commitEdit, onKeyDown: handleEditKeyDown })) : (_jsx("button", { type: "button", className: "control-output", onClick: startEditing, children: displayValue }))] }));
|
|
43
|
+
}
|
|
44
|
+
export const GainControl = memo(GainControlInner);
|