diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 2b43cf0b..37fe6b6c 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -67,13 +67,14 @@ export const getFreq = (noteOrMidi) => { return midiToFreq(noteToMidi(noteOrMidi)); }; +const pcs = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; /** * @deprecated does not appear to be referenced or invoked anywhere in the codebase * @noAutocomplete */ export const midi2note = (n) => { const oct = Math.floor(n / 12) - 1; - const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12]; + const pc = pcs[n % 12]; return pc + oct; }; diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx index 9c30dc29..8ff738e6 100644 --- a/packages/react/src/components/MiniRepl.jsx +++ b/packages/react/src/components/MiniRepl.jsx @@ -18,18 +18,21 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, + onTrigger, drawTime, punchcard, + onPaint, canvasHeight = 200, fontSize = 18, hideHeader = false, theme, + keybindings, }) { drawTime = drawTime || (punchcard ? [0, 4] : undefined); const evalOnMount = !!drawTime; const drawContext = useCallback( - !!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, - [drawTime], + punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, + [punchcard], ); const { code, @@ -51,7 +54,15 @@ export function MiniRepl({ defaultOutput: webaudioOutput, editPattern: (pat, id) => { //pat = pat.withContext((ctx) => ({ ...ctx, id })); - return punchcard ? pat.punchcard() : pat; + if (onTrigger) { + pat = pat.onTrigger(onTrigger, false); + } + if (onPaint) { + pat = pat.onPaint(onPaint); + } else if (punchcard) { + pat = pat.punchcard(); + } + return pat; }, getTime, evalOnMount, @@ -87,7 +98,7 @@ export function MiniRepl({ e.preventDefault(); flash(view); await activateCode(); - } else if (e.key === '.') { + } else if (e.key === '.' || e.code === 'Period') { stop(); e.preventDefault(); } @@ -140,11 +151,18 @@ export function MiniRepl({ )}
{show && ( - + )} {error &&
{error.message}
}
- {drawTime && ( + {punchcard && ( !!(pat?.context?.onPaint && drawContext), [drawContext]); + //const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]); + const shouldPaint = useCallback((pat) => !!pat?.context?.onPaint, []); // TODO: make sure this hook reruns when scheduler.started changes const { scheduler, evaluate, start, stop, pause, setCps } = useMemo( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a135d89..6542a707 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,7 +92,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/core: dependencies: @@ -102,7 +102,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -127,7 +127,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/core/examples/vite-vanilla-repl-cm6: dependencies: @@ -155,7 +155,7 @@ importers: devDependencies: vite: specifier: ^4.3.2 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/csound: dependencies: @@ -171,7 +171,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/embed: {} @@ -204,7 +204,7 @@ importers: version: link:../mini vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -223,7 +223,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/mini: dependencies: @@ -236,7 +236,7 @@ importers: version: 3.0.2 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -255,7 +255,7 @@ importers: version: 5.8.1 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/react: dependencies: @@ -325,7 +325,7 @@ importers: version: 3.3.2 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/react/examples/nano-repl: dependencies: @@ -380,7 +380,7 @@ importers: version: 3.3.2 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/serial: dependencies: @@ -390,7 +390,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/soundfonts: dependencies: @@ -412,7 +412,7 @@ importers: version: 3.3.1 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/tonal: dependencies: @@ -431,7 +431,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -447,7 +447,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -469,7 +469,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -494,7 +494,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/web/examples/repl-example: dependencies: @@ -504,7 +504,7 @@ importers: devDependencies: vite: specifier: ^4.3.2 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/webaudio: dependencies: @@ -517,7 +517,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/webdirt: dependencies: @@ -533,7 +533,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -546,7 +546,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -646,6 +646,9 @@ importers: canvas: specifier: ^2.11.2 version: 2.11.2 + claviature: + specifier: ^0.1.0 + version: 0.1.0 fraction.js: specifier: ^4.2.0 version: 4.2.0 @@ -4502,7 +4505,7 @@ packages: '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5) '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5) react-refresh: 0.14.0 - vite: 4.3.3(@types/node@18.16.3) + vite: 4.3.3(@types/node@18.11.18) transitivePeerDependencies: - supports-color dev: true @@ -5413,6 +5416,10 @@ packages: resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} engines: {node: '>=8'} + /claviature@0.1.0: + resolution: {integrity: sha512-Ai12axNwQ7x/F9QAj64RYKsgvi5Y33+X3GUSKAC/9s/adEws8TSSc0efeiqhKNGKBo6rT/c+CSCwSXzXxwxZzQ==} + dev: false + /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} diff --git a/website/package.json b/website/package.json index 6b8b7a6c..d6100625 100644 --- a/website/package.json +++ b/website/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@algolia/client-search": "^4.17.0", - "@astrojs/mdx": "^0.19.0", + "@astrojs/mdx": "^0.19.0", "@astrojs/react": "^2.1.1", "@astrojs/tailwind": "^3.1.1", "@docsearch/css": "^3.3.4", @@ -43,6 +43,7 @@ "@uiw/codemirror-themes-all": "^4.19.16", "astro": "^2.3.2", "canvas": "^2.11.2", + "claviature": "^0.1.0", "fraction.js": "^4.2.0", "nanoid": "^4.0.2", "nanostores": "^0.8.1", diff --git a/website/src/components/Claviature.jsx b/website/src/components/Claviature.jsx new file mode 100644 index 00000000..e97facbc --- /dev/null +++ b/website/src/components/Claviature.jsx @@ -0,0 +1,24 @@ +import { getClaviature } from 'claviature'; +import React from 'react'; + +export default function Claviature({ options, onClick, onMouseDown, onMouseUp, onMouseLeave }) { + const svg = getClaviature({ + options, + onClick, + onMouseDown, + onMouseUp, + onMouseLeave, + }); + return ( + + {svg.children.map((el, i) => { + const TagName = el.name; + return ( + + {el.value} + + ); + })} + + ); +} diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 6b434353..651241a9 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,10 +1,11 @@ -import { evalScope, controls } from '@strudel.cycles/core'; +import { evalScope, controls, noteToMidi } from '@strudel.cycles/core'; import { initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { useEffect, useState } from 'react'; import { prebake } from '../repl/prebake'; import { themes, settings } from '../repl/themes.mjs'; import './MiniRepl.css'; import { useSettings } from '../settings.mjs'; +import Claviature from '@components/Claviature'; let modules; if (typeof window !== 'undefined') { @@ -27,9 +28,19 @@ if (typeof window !== 'undefined') { prebake(); } -export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeight = 100, hideHeader }) { +export function MiniRepl({ + tune, + drawTime, + punchcard, + span = [0, 4], + canvasHeight = 100, + hideHeader, + claviature, + claviatureLabels, +}) { const [Repl, setRepl] = useState(); - const { theme } = useSettings(); + const { theme, keybindings, fontSize, fontFamily } = useSettings(); + const [activeNotes, setActiveNotes] = useState([]); useEffect(() => { // we have to load this package on the client // because codemirror throws an error on the server @@ -42,13 +53,35 @@ export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeigh { + const active = haps + .map((hap) => hap.value.note) + .filter(Boolean) + .map((n) => (typeof n === 'string' ? noteToMidi(n) : n)); + setActiveNotes(active); + } + : undefined + } /> + {claviature && ( + + )} ) : (
{tune}
diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 4ad387fe..1db8f8dd 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -157,7 +157,7 @@ export function Repl({ embedded = false }) { e.preventDefault(); flash(view); await activateCode(); - } else if (e.key === '.') { + } else if (e.key === '.' || e.keyCode === 'Period') { stop(); e.preventDefault(); }