diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index adb883e5..7d8170cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,6 +138,18 @@ pnpm --filter "./packages/**" publish --access public To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`. Important: Always publish with `pnpm`, as `npm` does not support overriding main files in `publishConfig`, which is done in all the packages. + +## useful commands +```sh +#regenerate the test snapshots (ex: when updating or creating new pattern functions) +pnpm snapshot + +#start the OSC server +pnpm run osc + +#build the standalone version +pnpm tauri build +``` ## Have Fun Remember to have fun, and that this project is driven by the passion of volunteers! diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 215eac2f..36f07df5 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -7,7 +7,8 @@ This program is free software: you can redistribute it and/or modify it under th import { Hap } from './hap.mjs'; import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs'; import Fraction from './fraction.mjs'; -import { id, _mod } from './util.mjs'; + +import { id, keyAlias, getCurrentKeyboardState } from './util.mjs'; export function steady(value) { // A continuous value @@ -639,3 +640,48 @@ export const never = register('never', function (_, pat) { export const always = register('always', function (func, pat) { return func(pat); }); + +//keyname: string | Array +//keyname reference: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values +export function _keyDown(keyname) { + if (Array.isArray(keyname) === false) { + keyname = [keyname]; + } + const keyState = getCurrentKeyboardState(); + return keyname.every((x) => { + const keyName = keyAlias.get(x) ?? x; + return keyState[keyName]; + }); +} + +/** + * + * Do something on a keypress, or array of keypresses + * [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) + * + * @name whenKey + * @memberof Pattern + * @returns Pattern + * @example + * s("bd(5,8)").whenKey("Control:j", x => x.segment(16).color("red")).whenKey("Control:i", x => x.fast(2).color("blue")) + */ + +export const whenKey = register('whenKey', function (input, func, pat) { + return pat.when(_keyDown(input), func); +}); + +/** + * + * returns true when a key or array of keys is held + * [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) + * + * @name keyDown + * @memberof Pattern + * @returns Pattern + * @example + * keyDown("Control:j").pick([s("bd(5,8)"), s("cp(3,8)")]) + */ + +export const keyDown = register('keyDown', function (pat) { + return pat.fmap(_keyDown); +}); diff --git a/packages/core/util.mjs b/packages/core/util.mjs index bc844057..b7b1e841 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -434,6 +434,38 @@ function getUnixTimeSeconds() { return Date.now() * 0.001; } +export const keyAlias = new Map([ + ['control', 'Control'], + ['ctrl', 'Control'], + ['alt', 'Alt'], + ['shift', 'Shift'], + ['down', 'ArrowDown'], + ['up', 'ArrowUp'], + ['left', 'ArrowLeft'], + ['right', 'ArrowRight'], +]); +let keyState; + +export function getCurrentKeyboardState() { + if (keyState == null) { + if (typeof window === 'undefined') { + return; + } + keyState = {}; + // Listen for the keydown event to mark the key as pressed + window.addEventListener('keydown', (event) => { + keyState[event.key] = true; // Mark the key as pressed + }); + + // Listen for the keyup event to mark the key as released + window.addEventListener('keyup', (event) => { + keyState[event.key] = false; // Mark the key as released + }); + } + + return { ...keyState }; // Return a shallow copy of the key state object +} + // Floating point versions, see Fraction for rational versions // // greatest common divisor // export const gcd = function (x, y, ...z) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a18f06e7..6cb259a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -331,6 +331,19 @@ importers: specifier: ^2.1.3 version: 2.1.3(@types/node@22.7.6)(@vitest/ui@2.1.3)(terser@5.36.0) + packages/mqtt: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + paho-mqtt: + specifier: ^1.1.0 + version: 1.1.0 + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.4.9(@types/node@22.7.6)(terser@5.36.0) + packages/osc: dependencies: '@strudel/core': @@ -623,6 +636,9 @@ importers: '@strudel/mini': specifier: workspace:* version: link:../packages/mini + '@strudel/mqtt': + specifier: workspace:* + version: link:../packages/mqtt '@strudel/osc': specifier: workspace:* version: link:../packages/osc @@ -5991,6 +6007,9 @@ packages: engines: {node: ^16.14.0 || >=18.0.0} hasBin: true + paho-mqtt@1.1.0: + resolution: {integrity: sha512-KPbL9KAB0ASvhSDbOrZBaccXS+/s7/LIofbPyERww8hM5Ko71GUJQ6Nmg0BWqj8phAIT8zdf/Sd/RftHU9i2HA==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -7740,6 +7759,7 @@ packages: workbox-google-analytics@7.0.0: 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: resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==} @@ -14686,6 +14706,8 @@ snapshots: - bluebird - supports-color + paho-mqtt@1.1.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 7c21d464..761c95fa 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -4065,6 +4065,8 @@ exports[`runs examples > example "juxBy" example index 0 1`] = ` ] `; +exports[`runs examples > example "keyDown" example index 0 1`] = `[]`; + exports[`runs examples > example "lastOf" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c3 ]", @@ -8832,6 +8834,8 @@ exports[`runs examples > example "when" example index 0 1`] = ` ] `; +exports[`runs examples > example "whenKey" example index 0 1`] = `[]`; + exports[`runs examples > example "withValue" example index 0 1`] = ` [ "[ 0/1 → 1/3 | 10 ]",