diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 408b5398..fc9dae04 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -427,7 +427,7 @@ export class Pattern { * @noAutocomplete */ withHaps(func) { - return new Pattern((state) => func(this.query(state))); + return new Pattern((state) => func(this.query(state), state)); } /** @@ -2359,15 +2359,10 @@ export const splice = register( false, // turns off auto-patternification ); -// this function will be redefined in repl.mjs to use the correct cps value. -// It is still here to work in cases where repl.mjs is not used - export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat) { - return _loopAt(factor, pat, 1); + return new Pattern((state) => _loopAt(factor, pat, state.controls._cps).query(state)); }); -// the fit function will be redefined in repl.mjs to use the correct cps value. -// It is still here to work in cases where repl.mjs is not used /** * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. * Similar to `loopAt`. @@ -2377,12 +2372,14 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto * s("rhodes/4").fit() */ export const fit = register('fit', (pat) => - pat.withHap((hap) => - hap.withValue((v) => ({ - ...v, - speed: 1 / hap.whole.duration, - unit: 'c', - })), + pat.withHaps((haps, state) => + haps.map((hap) => + hap.withValue((v) => ({ + ...v, + speed: (state.controls._cps || 1) / hap.whole.duration, + unit: 'c', + })), + ), ), ); diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 9fb6b4b9..490ac98c 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -61,12 +61,63 @@ export function repl({ scheduler.setPattern(pattern, autostart); }; setTime(() => scheduler.now()); // TODO: refactor? + + const stop = () => scheduler.stop(); + const start = () => scheduler.start(); + const pause = () => scheduler.pause(); + const toggle = () => scheduler.toggle(); + const setCps = (cps) => scheduler.setCps(cps); + const setCpm = (cpm) => scheduler.setCps(cpm / 60); + const all = function (transform) { + allTransform = transform; + return silence; + }; + + // set pattern methods that use this repl via closure + const injectPatternMethods = () => { + Pattern.prototype.p = function (id) { + pPatterns[id] = this; + return this; + }; + Pattern.prototype.q = function (id) { + return silence; + }; + try { + for (let i = 1; i < 10; ++i) { + Object.defineProperty(Pattern.prototype, `d${i}`, { + get() { + return this.p(i); + }, + configurable: true, + }); + Object.defineProperty(Pattern.prototype, `p${i}`, { + get() { + return this.p(i); + }, + configurable: true, + }); + Pattern.prototype[`q${i}`] = silence; + } + } catch (err) { + console.warn('injectPatternMethods: error:', err); + } + evalScope({ + all, + hush, + setCps, + setcps: setCps, + setCpm, + setcpm: setCpm, + }); + }; + const evaluate = async (code, autostart = true, shouldHush = true) => { if (!code) { throw new Error('no code to evaluate'); } try { updateState({ code, pending: true }); + injectPatternMethods(); await beforeEval?.({ code }); shouldHush && hush(); let { pattern, meta } = await _evaluate(code, transpiler); @@ -100,68 +151,6 @@ export function repl({ onEvalError?.(err); } }; - const stop = () => scheduler.stop(); - const start = () => scheduler.start(); - const pause = () => scheduler.pause(); - const toggle = () => scheduler.toggle(); - const setCps = (cps) => scheduler.setCps(cps); - const setCpm = (cpm) => scheduler.setCps(cpm / 60); - - // the following functions use the cps value, which is why they are defined here.. - const loopAt = register('loopAt', (cycles, pat) => { - return pat.loopAtCps(cycles, scheduler.cps); - }); - - Pattern.prototype.p = function (id) { - pPatterns[id] = this; - return this; - }; - Pattern.prototype.q = function (id) { - return silence; - }; - - const all = function (transform) { - allTransform = transform; - return silence; - }; - try { - for (let i = 1; i < 10; ++i) { - Object.defineProperty(Pattern.prototype, `d${i}`, { - get() { - return this.p(i); - }, - }); - Object.defineProperty(Pattern.prototype, `p${i}`, { - get() { - return this.p(i); - }, - }); - Pattern.prototype[`q${i}`] = silence; - } - } catch (err) { - // already defined.. - } - - const fit = register('fit', (pat) => - pat.withHap((hap) => - hap.withValue((v) => ({ - ...v, - speed: scheduler.cps / hap.whole.duration, // overwrite speed completely? - unit: 'c', - })), - ), - ); - - evalScope({ - loopAt, - fit, - all, - hush, - setCps, - setcps: setCps, - setCpm, - setcpm: setCpm, - }); const setCode = (code) => updateState({ code }); return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state }; } diff --git a/website/src/components/Oven/Oven.jsx b/website/src/components/Oven/Oven.jsx index 7cb2e7a7..30822d6c 100644 --- a/website/src/components/Oven/Oven.jsx +++ b/website/src/components/Oven/Oven.jsx @@ -6,6 +6,7 @@ import { PatternLabel } from '@src/repl/panel/PatternsTab'; function PatternList({ patterns }) { return (
+ {/* pat.code.trim())} /> */} {patterns.map((pat) => (
@@ -15,8 +16,7 @@ function PatternList({ patterns }) {
- {/* */} - {/*
{JSON.stringify(pat)}
*/} +
))}
diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 10eff483..3afb609e 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -27,6 +27,7 @@ export function MiniRepl({ punchcardLabels = true, claviature, claviatureLabels, + maxHeight, }) { const code = tunes ? tunes[0] : tune; const id = useMemo(() => s4(), []); @@ -154,7 +155,7 @@ export function MiniRepl({ )} )} -
+
{ if (!editorRef.current) {