Use pat.withValue and add uniformTrigger

This commit is contained in:
Tristan de Cacqueray
2024-10-20 09:49:46 -04:00
parent 9b4675f1ce
commit b85a5a9179
2 changed files with 87 additions and 52 deletions
+5 -2
View File
@@ -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]);
+82 -50
View File
@@ -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);
});