diff --git a/packages/shader/shader.mjs b/packages/shader/shader.mjs index 8d9f0430c..6b1864b38 100644 --- a/packages/shader/shader.mjs +++ b/packages/shader/shader.mjs @@ -24,6 +24,8 @@ out vec4 oColor; uniform float iTime; uniform vec2 iResolution; +#define STRUDEL 1 + ${code} void main(void) { @@ -65,6 +67,7 @@ export function setUniform(instanceName, name, value, position) { return; } + // console.log("setUniform: ", name, value, position) const uniform = instance.uniforms[name]; if (uniform) { if (uniform.count == 0) { @@ -91,6 +94,8 @@ function updateUniforms(drawFrame, elapsed, uniforms) { Object.values(uniforms).forEach((uniform) => { const value = uniform.count == 0 ? uniform.mod.get(elapsed) : uniform.value.map((_, i) => uniform.mod[i].get(elapsed)); + + // console.log("updateUniforms:", uniform.name, value) // Send the value to the GPU drawFrame.uniform(uniform.name, value); }); @@ -138,9 +143,7 @@ function setupCanvas(name) { async function initializeShaderInstance(name, code) { // Setup PicoGL app const ctx = setupCanvas(name); - console.log(ctx); const app = PicoGL.createApp(ctx); - app.resize(400, 300); // Setup buffers const resolution = new Float32Array([ctx.canvas.width, ctx.canvas.height]); diff --git a/packages/shader/uniform.mjs b/packages/shader/uniform.mjs index 903988554..12627aa68 100644 --- a/packages/shader/uniform.mjs +++ b/packages/shader/uniform.mjs @@ -7,72 +7,104 @@ This program is free software: you can redistribute it and/or modify it under th import { register, logger } from '@strudel/core'; import { setUniform } from './shader.mjs'; -/** - * Update a shader. The destination name consist of - * - * - the uniform name - * - optional array mapping, either a number or an assignment mode ('seq' or 'pitch') - * - * @name uniform - * @example - * s("bd").uniform("iColor") - * @example - * s("bd").uniform("iColors:seq") - * @example - * note("c3 e3").uniform("iMorph:pitch") - */ function parseUniformTarget(name) { - if (typeof name === 'string') return { name, mapping: 'single', position: 0 }; + if (typeof name === 'string') return { name, position: 0 }; else if (name.length == 2) { - const mapping = typeof name[1] === 'string' ? name[1] : 'single'; - const position = typeof name[1] === 'string' ? null : name[1]; - return { - name: name[0], - mapping, - position, - }; + let position = null; + if (typeof name[1] === 'number') position = name[1]; + else if (name[1] == 'random') position = Math.floor(Math.random() * 1024); + else if (name[1] != 'seq') throw 'Unknown mapping: ' + name[1]; + return { name: name[0], position }; } } // Keep track of the pitches value per uniform -let _pitches = {}; +let _uniforms = {}; +function getNextPosition(name, value) { + // Initialize uniform state + if (!_uniforms[name]) _uniforms[name] = { _count: 0 }; + const uniform = _uniforms[name]; + + // Set a new position when the value changes + if (uniform._last != value) { + uniform._last = value; + uniform._count++; + } + return uniform._count; +} + +/** + * Update a shader. The destination name consist of + * + * - the uniform name + * - optional array mapping, either a number or an assignment mode ('seq' or 'random') + * + * @name uniform + * @example + * pan(sine.uniform("iColor")) + * @example + * gain("<.5 .3>".uniform("rotations:seq")) + */ export const uniform = register('uniform', (target, pat) => { // TODO: support multiple shader instance const instance = 'default'; - // Decode the uniform defintion + // Decode the uniform target defintion const uniformTarget = parseUniformTarget(target); - // Get the pitches - if (!_pitches[uniformTarget.name]) _pitches[uniformTarget.name] = { _count: 0 }; - const pitches = _pitches[uniformTarget.name]; + return pat.withValue((v) => { + // TODO: figure out why this is called repeatedly when changing values. For example, on first call, the last value is passed. + if (typeof v === 'number') { + const position = + uniformTarget.position === null ? getNextPosition(uniformTarget.name, v) : uniformTarget.position; + setUniform(instance, uniformTarget.name, v, position); + } else { + console.error('Uniform applied to a non number pattern'); + } + return v; + }); +}); + +function getNotePosition(name, value) { + // Initialize uniform state + if (!_uniforms[name]) _uniforms[name] = {}; + const uniform = _uniforms[name]; + + const note = value.note || value.n || value.sound || value.s; + if (uniform[note] === undefined) { + // Assign new value, the first note gets 0, then 1, then 2, ... + uniform[note] = Object.keys(uniform).length; + } + return uniform[note]; +} + +/** + * Update a shader with note-on event. The destination name consist of + * + * - the uniform name + * - optional array position, default to randomly assigned position based on the note or sound. + * + * @name uniformTrigger + * @example + * pan(sine.uniform("iColor")) + * @example + * gain("<.5 .3>".uniform("rotations:seq")) + */ +export const uniformTrigger = register('uniformTrigger', (target, pat) => { + // TODO: support multiple shader instance + const instance = 'default'; + + // Decode the uniform target defintion + const uniformTarget = parseUniformTarget(target); return pat.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { - // TODO: figure out how to get the desired value, e.g. is this pattern for pan, gain, velocity, ... - const value = hap.value ? hap.value.gain || 1.0 : 1.0; + const position = + uniformTarget.position === null ? getNotePosition(uniformTarget.name, hap.value) : uniformTarget.position; - // Get the uniform mapping position - let position = null; - if (uniformTarget.mapping == 'pitch') { - // Assign one position per pitch - const note = hap.value.note || hap.value.s; - if (pitches[note] === undefined) { - // Assign new value, the first note gets 0, then 1, then 2, ... - pitches[note] = Object.keys(pitches).length; - } - position = pitches[note]; - } else if (uniformTarget.mapping == 'seq') { - console.log('HERE', pitches); - // Assign a new position per event - position = pitches._count++; - } else if (uniformTarget.mapping == 'single') { - // Assign a fixed position - position = uniformTarget.position; - } else { - console.error('Unknown uniform target', uniformTarget); - } + // TODO: support custom value + const value = 1.0; // Update the uniform - if (position !== null) setUniform(instance, uniformTarget.name, value, position); + setUniform(instance, uniformTarget.name, value, position); }, false); });