reset canvas when pattern changes

+ rename noteroll -> punchcard
This commit is contained in:
Felix Roos 2023-01-13 12:57:35 +01:00
parent 1ac784dc7a
commit ea0e0b4396
8 changed files with 32 additions and 19 deletions

View File

@ -284,12 +284,14 @@ export function pianoroll({
return this; return this;
} }
Pattern.prototype.noteroll = function (options = { fold: 1 }) { function getOptions(drawTime, options) {
return this.onPaint((ctx, time, haps, drawTime) => { let [lookbehind, lookahead] = drawTime;
let [lookbehind, lookahead] = drawTime; lookbehind = Math.abs(lookbehind);
lookbehind = Math.abs(lookbehind); const cycles = lookahead + lookbehind;
const cycles = lookahead + lookbehind; const playhead = lookbehind / cycles;
const playhead = lookbehind / cycles; return { ...options, cycles, playhead };
pianoroll({ ctx, time, haps, ...options, cycles, playhead }); }
});
Pattern.prototype.punchcard = function (options = { fold: 1 }) {
return this.onPaint((ctx, time, haps, drawTime) => pianoroll({ ctx, time, haps, ...getOptions(drawTime, options) }));
}; };

File diff suppressed because one or more lines are too long

View File

@ -178,12 +178,16 @@ function Me({ pattern: e, started: r, getTime: t, onDraw: o, drawTime: u = [-2,
return; return;
} }
const p = e.queryArc(Math.max(c.current, n - 1 / 10), n); const p = e.queryArc(Math.max(c.current, n - 1 / 10), n);
c.current = n, a.current = (a.current || []).filter((b) => b.whole.end > n - m - d).concat(p.filter((b) => b.hasOnset())), o(e, n - d, a.current, u); c.current = n, a.current = (a.current || []).filter((b) => b.whole.end >= n - m - d).concat(p.filter((b) => b.hasOnset())), o(e, n - d, a.current, u);
}, [e]) }, [e])
); );
k(() => { return k(() => {
r ? h() : (a.current = [], g()); r ? h() : (a.current = [], g());
}, [r]); }, [r]), {
clear: () => {
a.current = [];
}
};
} }
function Ae(e) { function Ae(e) {
return k(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), w((r) => window.postMessage(r, "*"), []); return k(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), w((r) => window.postMessage(r, "*"), []);

View File

@ -25,7 +25,7 @@ function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2]
const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase); const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase);
lastFrame.current = phase; lastFrame.current = phase;
visibleHaps.current = (visibleHaps.current || []) visibleHaps.current = (visibleHaps.current || [])
.filter((h) => h.whole.end > phase - lookbehind - lookahead) // in frame .filter((h) => h.whole.end >= phase - lookbehind - lookahead) // in frame
.concat(haps.filter((h) => h.hasOnset())); .concat(haps.filter((h) => h.hasOnset()));
onDraw(pattern, phase - lookahead, visibleHaps.current, drawTime); onDraw(pattern, phase - lookahead, visibleHaps.current, drawTime);
}, [pattern]), }, [pattern]),
@ -38,6 +38,11 @@ function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2]
stopFrame(); stopFrame();
} }
}, [started]); }, [started]);
return {
clear: () => {
visibleHaps.current = [];
},
};
} }
export default usePatternFrame; export default usePatternFrame;

View File

@ -28,7 +28,7 @@ Strudel however runs directly in your web browser, does not require any custom s
The main place to actually make music with Strudel is the [Strudel REPL](https://strudel.tidalcycles.org/) ([what is a REPL?](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)), but in these pages you will also encounter interactive "MiniREPLs" where you can listen to and edit Strudel patterns. The main place to actually make music with Strudel is the [Strudel REPL](https://strudel.tidalcycles.org/) ([what is a REPL?](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)), but in these pages you will also encounter interactive "MiniREPLs" where you can listen to and edit Strudel patterns.
Try clicking the play icon below: Try clicking the play icon below:
<MiniRepl client:idle tune={`s("bd sd").noteroll()`} drawTime={[0, 2]} /> <MiniRepl client:idle tune={`s("bd sd").punchcard()`} drawTime={[0, 2]} />
Then edit the text so it reads `s("bd sd cp hh")` and click the refresh icon. Then edit the text so it reads `s("bd sd cp hh")` and click the refresh icon.
Congratulations, you have now live coded your first Strudel pattern! Congratulations, you have now live coded your first Strudel pattern!

View File

@ -51,12 +51,12 @@ If you do just want to get a regular string that is _not_ parsed as mini-notatio
We can play more notes by separating them with spaces: We can play more notes by separating them with spaces:
<MiniRepl client:idle tune={`note("c e g").noteroll()`} drawTime={[0, 4]} /> <MiniRepl client:idle tune={`note("c e g").punchcard()`} drawTime={[0, 4]} />
Here, those four notes are squashed into one cycle, so each note is a quarter second long. Here, those four notes are squashed into one cycle, so each note is a quarter second long.
Try adding or removing notes and notice how the tempo changes! Try adding or removing notes and notice how the tempo changes!
<MiniRepl client:idle tune={`note("c d e f g a b").noteroll()`} drawTime={[0, 4]} /> <MiniRepl client:idle tune={`note("c d e f g a b").punchcard()`} drawTime={[0, 4]} />
Note that the overall duration of time does not change, and instead each note length descreases. Note that the overall duration of time does not change, and instead each note length descreases.
This is a key idea, as it illustrates the 'Cycle' in TidalCycles! This is a key idea, as it illustrates the 'Cycle' in TidalCycles!

View File

@ -29,10 +29,10 @@ add a mini repl with
- `client:idle` is required to tell astro that the repl should be interactive, see [Client Directive](https://docs.astro.build/en/reference/directives-reference/#client-directives) - `client:idle` is required to tell astro that the repl should be interactive, see [Client Directive](https://docs.astro.build/en/reference/directives-reference/#client-directives)
- `tune`: be any valid pattern code - `tune`: be any valid pattern code
- `drawTime`: time window for drawing. Use together with `.noteroll()`. Example: - `drawTime`: time window for drawing. Use together with `.punchcard()`. Example:
```jsx ```jsx
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4").noteroll()`} drawTime={[0, 2]} /> <MiniRepl client:idle tune={`note("a3 c#4 e4 a4").punchcard()`} drawTime={[0, 2]} />
``` ```
## In-Source Documentation ## In-Source Documentation

View File

@ -54,9 +54,10 @@ evalScope(
export let loadedSamples = []; export let loadedSamples = [];
const presets = prebake(); const presets = prebake();
let drawContext; let drawContext, clearCanvas;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
drawContext = getDrawContext(); drawContext = getDrawContext();
clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width);
} }
Promise.all([...modules, presets]).then((data) => { Promise.all([...modules, presets]).then((data) => {
@ -209,6 +210,7 @@ export function Repl({ embedded = false }) {
const handleShuffle = async () => { const handleShuffle = async () => {
const { code, name } = getRandomTune(); const { code, name } = getRandomTune();
logger(`[repl] ✨ loading random tune "${name}"`); logger(`[repl] ✨ loading random tune "${name}"`);
clearCanvas();
resetLoadedSamples(); resetLoadedSamples();
await prebake(); // declare default samples await prebake(); // declare default samples
await evaluate(code, false); await evaluate(code, false);