diff --git a/README.md b/README.md index 34694bf5..a3c455dd 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,21 @@ the `hydra` target to execute Hydra code. You can also use [p5.js](https://p5js.org/) within a `hydra` target, like you would in the official Hydra editor. +##### `P()` function +The `P()` function allows you to use strudel mini-patterns in Hydra. +It uses the same timing information as Strudel itself, so it will be synchronized with the audio. + +**Note**: It will only work, if strudel is already initialized, because that will load the modules we need. +**Note**: You can not use any strudel functions on the pattern. +**Note**: Currently we do not have mini-highlighting for Hydra panes. + +##### `useStrudelCanvas(source)` + +Will initialise the given source (`s0`, `s1`, etc) to use the strudel canvas as a source. +Will also hide the strudel canvas, so it will not overlap. + +**Note**: Strudel will have to be initialized, otherwise this will not work. + ##### `fft()` function The `fft()` function is a special function that allows you to get the FFT data diff --git a/packages/web/src/lib/hydra-wrapper.ts b/packages/web/src/lib/hydra-wrapper.ts index 6fcfaf41..ae98d1c0 100644 --- a/packages/web/src/lib/hydra-wrapper.ts +++ b/packages/web/src/lib/hydra-wrapper.ts @@ -9,6 +9,8 @@ declare global { H: Function; P5: Function; fft: (index: number, buckets: number) => number; + P: Function; + useStrudelCanvas: Function; } } @@ -74,6 +76,33 @@ export class HydraWrapper { window.H = this._hydra; + // Enable using strudel style mini-patterns for argument control on Hydra. + // strudel needs to be loaded first, otherwise this will cause warnings, and rendering will not + // include the mini-pattern. + // Inspired by + // - https://github.com/atfornes/Hydra-strudel-extension/blob/51a93496b1b05ea00c08d1dec10e046aa3769c93/hydra-strudel.js#L72 + // - https://github.com/tidalcycles/strudel/blob/26cc7e2920e32ec01bf22e1dae8ced716462a158/packages/hydra/hydra.mjs#L50 + window.P = (pattern: any) => { + return () => { + // parse using the strudel mini parser + const reified = window.strudel.mini.minify(pattern) + + const now = window.strudel.core.getTime() + + // query the current value + const arc = reified.queryArc(now, now) + return arc[0].value; + } + } + + // initialized a streaming canvas with the strudel draw context canvas + // this allows us to use the strudel output + window.useStrudelCanvas = (s: any) => { + const canvas = window.strudel.draw.getDrawContext().canvas + canvas.style.display = "none" + s.init({src: canvas}) + } + const clamp = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max); diff --git a/packages/web/src/lib/strudel-wrapper.ts b/packages/web/src/lib/strudel-wrapper.ts index 0f6bea27..ca711b69 100644 --- a/packages/web/src/lib/strudel-wrapper.ts +++ b/packages/web/src/lib/strudel-wrapper.ts @@ -33,6 +33,9 @@ export class StrudelWrapper { protected _docPatterns: any; protected _audioInitialized: boolean; protected framer?: any; + protected mini?: any; + protected core?: any; + protected draw?: any; protected webaudio?: any; enableAutoAnalyze = false; @@ -63,12 +66,17 @@ export class StrudelWrapper { async importModules() { // import desired modules and add them to the eval scope + this.mini = await import("@strudel/mini"); + this.core = await import("@strudel/core"); + this.draw = await import("@strudel/draw"); + this.webaudio = await import("@strudel/webaudio"); await evalScope( - import("@strudel/core"), + this.core, import("@strudel/midi"), - import("@strudel/mini"), + this.mini, + this.draw, import("@strudel/tonal"), import("@strudel/osc"), import("@strudel/serial"),