Merge branch 'main' into origin/daslyfe/subcycle
This commit is contained in:
commit
6e5d8eed19
7 changed files with 130 additions and 13 deletions
|
|
@ -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`.
|
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.
|
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
|
## Have Fun
|
||||||
|
|
||||||
Remember to have fun, and that this project is driven by the passion of volunteers!
|
Remember to have fun, and that this project is driven by the passion of volunteers!
|
||||||
|
|
|
||||||
|
|
@ -1491,6 +1491,14 @@ function _sequenceCount(x) {
|
||||||
export const mask = curry((a, b) => reify(b).mask(a));
|
export const mask = curry((a, b) => reify(b).mask(a));
|
||||||
export const struct = curry((a, b) => reify(b).struct(a));
|
export const struct = curry((a, b) => reify(b).struct(a));
|
||||||
export const superimpose = curry((a, b) => reify(b).superimpose(...a));
|
export const superimpose = curry((a, b) => reify(b).superimpose(...a));
|
||||||
|
export const withValue = curry((a, b) => reify(b).withValue(a));
|
||||||
|
|
||||||
|
export const bind = curry((a, b) => reify(b).bind(a));
|
||||||
|
export const innerBind = curry((a, b) => reify(b).innerBind(a));
|
||||||
|
export const outerBind = curry((a, b) => reify(b).outerBind(a));
|
||||||
|
export const squeezeBind = curry((a, b) => reify(b).squeezeBind(a));
|
||||||
|
export const stepBind = curry((a, b) => reify(b).stepBind(a));
|
||||||
|
export const polyBind = curry((a, b) => reify(b).polyBind(a));
|
||||||
|
|
||||||
// operators
|
// operators
|
||||||
export const set = curry((a, b) => reify(b).set(a));
|
export const set = curry((a, b) => reify(b).set(a));
|
||||||
|
|
@ -2539,6 +2547,10 @@ Pattern.prototype.stepJoin = function () {
|
||||||
return new Pattern(q, first_t);
|
return new Pattern(q, first_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Pattern.prototype.stepBind = function (func) {
|
||||||
|
return this.fmap(func).stepJoin();
|
||||||
|
};
|
||||||
|
|
||||||
export function _retime(timedHaps) {
|
export function _retime(timedHaps) {
|
||||||
const occupied_perc = timedHaps.filter((t, pat) => pat.hasTactus).reduce((a, b) => a.add(b), Fraction(0));
|
const occupied_perc = timedHaps.filter((t, pat) => pat.hasTactus).reduce((a, b) => a.add(b), Fraction(0));
|
||||||
const occupied_tactus = removeUndefineds(timedHaps.map((t, pat) => pat.tactus)).reduce(
|
const occupied_tactus = removeUndefineds(timedHaps.map((t, pat) => pat.tactus)).reduce(
|
||||||
|
|
|
||||||
|
|
@ -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 { Hap } from './hap.mjs';
|
||||||
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
|
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
|
||||||
import Fraction from './fraction.mjs';
|
import Fraction from './fraction.mjs';
|
||||||
import { id, _mod } from './util.mjs';
|
|
||||||
|
import { id, keyAlias, getCurrentKeyboardState } from './util.mjs';
|
||||||
|
|
||||||
export function steady(value) {
|
export function steady(value) {
|
||||||
// A continuous value
|
// A continuous value
|
||||||
|
|
@ -639,3 +640,48 @@ export const never = register('never', function (_, pat) {
|
||||||
export const always = register('always', function (func, pat) {
|
export const always = register('always', function (func, pat) {
|
||||||
return func(pat);
|
return func(pat);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//keyname: string | Array<string>
|
||||||
|
//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);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -434,6 +434,38 @@ function getUnixTimeSeconds() {
|
||||||
return Date.now() * 0.001;
|
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
|
// 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) {
|
||||||
|
|
|
||||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
|
|
@ -331,6 +331,19 @@ importers:
|
||||||
specifier: ^2.1.3
|
specifier: ^2.1.3
|
||||||
version: 2.1.3(@types/node@22.7.6)(@vitest/ui@2.1.3)(terser@5.36.0)
|
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:
|
packages/osc:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@strudel/core':
|
'@strudel/core':
|
||||||
|
|
@ -623,6 +636,9 @@ importers:
|
||||||
'@strudel/mini':
|
'@strudel/mini':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../packages/mini
|
version: link:../packages/mini
|
||||||
|
'@strudel/mqtt':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../packages/mqtt
|
||||||
'@strudel/osc':
|
'@strudel/osc':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../packages/osc
|
version: link:../packages/osc
|
||||||
|
|
@ -5991,6 +6007,9 @@ packages:
|
||||||
engines: {node: ^16.14.0 || >=18.0.0}
|
engines: {node: ^16.14.0 || >=18.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
paho-mqtt@1.1.0:
|
||||||
|
resolution: {integrity: sha512-KPbL9KAB0ASvhSDbOrZBaccXS+/s7/LIofbPyERww8hM5Ko71GUJQ6Nmg0BWqj8phAIT8zdf/Sd/RftHU9i2HA==}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -7740,6 +7759,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==}
|
||||||
|
|
@ -14686,6 +14706,8 @@ snapshots:
|
||||||
- bluebird
|
- bluebird
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
paho-mqtt@1.1.0: {}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|
|
||||||
|
|
@ -4095,6 +4095,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`] = `
|
exports[`runs examples > example "lastOf" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | note:c3 ]",
|
"[ 0/1 → 1/4 | note:c3 ]",
|
||||||
|
|
@ -8862,6 +8864,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`] = `
|
exports[`runs examples > example "withValue" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/3 | 10 ]",
|
"[ 0/1 → 1/3 | 10 ]",
|
||||||
|
|
|
||||||
|
|
@ -82,18 +82,7 @@ To modify a parameter value, you can either:
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note("50 60 70").room(.1).add(note("<0 1 2>")).log()`} />
|
<MiniRepl client:only="react" tune={`note("50 60 70").room(.1).add(note("<0 1 2>")).log()`} />
|
||||||
|
|
||||||
- Modify _all_ numeral params:
|
Remember the execution of the chained functions goes from left to right.
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note("50 60 70").room(.1).add("<0 1 2>").log()`} />
|
|
||||||
|
|
||||||
Which of these 3 ways to use strongly depends on the context!
|
|
||||||
Note that the order of chaining param functions also matters!
|
|
||||||
In the last example, the `room` value would not have changed if it was applied later:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note("50 60 70").add("<0 1 2>").room(.1).log()`} />
|
|
||||||
|
|
||||||
This shows how the execution of the chained functions goes from left to right.
|
|
||||||
In this case, the `.add` will only modify what's on the left side.
|
|
||||||
|
|
||||||
# Operators
|
# Operators
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue