mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 22:35:15 -04:00
wip
This commit is contained in:
+23
-39
@@ -22,44 +22,38 @@ export class Cyclist {
|
||||
this.started = false;
|
||||
this.beforeStart = beforeStart;
|
||||
this.cps = 0.5;
|
||||
this.time_at_last_tick_message = 0;
|
||||
this.cycle = 0;
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
||||
this.lastBegin = 0; // query begin of last tick
|
||||
this.lastEnd = 0; // query end of last tick
|
||||
this.getTime = getTime; // get absolute time
|
||||
this.num_cycles_at_cps_change = 0;
|
||||
this.seconds_at_cps_change; // clock phase when cps was changed
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.onToggle = onToggle;
|
||||
this.latency = latency; // fixed trigger time offset
|
||||
|
||||
this.interval = interval;
|
||||
|
||||
this.clock = createClock(
|
||||
getTime,
|
||||
// called slightly before each cycle
|
||||
(phase, duration, _, time) => {
|
||||
if (this.started === false) {
|
||||
return;
|
||||
(phase, duration, _, t) => {
|
||||
if (this.num_ticks_since_cps_change === 0) {
|
||||
this.num_cycles_at_cps_change = this.lastEnd;
|
||||
this.seconds_at_cps_change = phase;
|
||||
}
|
||||
const num_seconds_since_cps_change = this.num_ticks_since_cps_change * duration;
|
||||
const tickdeadline = phase - time;
|
||||
const lastTick = time + tickdeadline;
|
||||
const num_cycles_since_cps_change = num_seconds_since_cps_change * this.cps;
|
||||
const begin = this.num_cycles_at_cps_change + num_cycles_since_cps_change;
|
||||
const secondsSinceLastTick = time - lastTick - duration;
|
||||
const eventLength = duration * this.cps;
|
||||
const end = begin + eventLength;
|
||||
this.cycle = begin + secondsSinceLastTick * this.cps;
|
||||
this.num_ticks_since_cps_change++;
|
||||
const seconds_since_cps_change = this.num_ticks_since_cps_change * duration;
|
||||
const num_cycles_since_cps_change = seconds_since_cps_change * this.cps;
|
||||
|
||||
//account for latency and tick duration when using cycle calculations for audio downstream
|
||||
const cycle_gap = (this.latency - duration) * this.cps;
|
||||
try {
|
||||
const begin = this.lastEnd;
|
||||
this.lastBegin = begin;
|
||||
const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change;
|
||||
this.lastEnd = end;
|
||||
this.lastTick = phase;
|
||||
|
||||
const haps = this.pattern.queryArc(begin, end, { _cps: this.cps });
|
||||
haps.forEach((hap) => {
|
||||
if (hap.hasOnset()) {
|
||||
let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps;
|
||||
targetTime = targetTime + this.latency + tickdeadline + time - num_seconds_since_cps_change;
|
||||
const duration = hap.duration / this.cps;
|
||||
onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap);
|
||||
if (phase < t) {
|
||||
// avoid querying haps that are in the past anyway
|
||||
console.log(`skip query: too late`);
|
||||
return;
|
||||
}
|
||||
|
||||
// query the pattern for events
|
||||
@@ -96,25 +90,17 @@ export class Cyclist {
|
||||
if (!this.started) {
|
||||
return 0;
|
||||
}
|
||||
const gap = (this.getTime() - this.time_at_last_tick_message) * this.cps;
|
||||
return this.cycle + gap;
|
||||
const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration;
|
||||
return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency;
|
||||
}
|
||||
|
||||
setCycle = (cycle) => {
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.num_cycles_at_cps_change = cycle;
|
||||
};
|
||||
setStarted(v) {
|
||||
this.started = v;
|
||||
|
||||
this.setCycle(0);
|
||||
this.onToggle?.(v);
|
||||
}
|
||||
async start() {
|
||||
await this.beforeStart?.();
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.num_cycles_at_cps_change = 0;
|
||||
|
||||
if (!this.pattern) {
|
||||
throw new Error('Scheduler: no pattern set! call .setPattern first.');
|
||||
}
|
||||
@@ -143,8 +129,6 @@ export class Cyclist {
|
||||
if (this.cps === cps) {
|
||||
return;
|
||||
}
|
||||
const num_seconds_since_cps_change = this.num_ticks_since_cps_change * this.interval;
|
||||
this.num_cycles_at_cps_change = this.num_cycles_at_cps_change + num_seconds_since_cps_change * cps;
|
||||
this.cps = cps;
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
}
|
||||
|
||||
@@ -248,11 +248,11 @@ export const getTrigger =
|
||||
// TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004
|
||||
try {
|
||||
if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
|
||||
await defaultOutput(hap, deadline, duration, cps, t, cycle);
|
||||
await defaultOutput(hap, deadline, duration, cps, t);
|
||||
}
|
||||
if (hap.context.onTrigger) {
|
||||
// call signature of output / onTrigger is different...
|
||||
await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t, cycle);
|
||||
await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t);
|
||||
}
|
||||
} catch (err) {
|
||||
logger(`[cyclist] error: ${err.message}`, 'error');
|
||||
|
||||
@@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
import './feedbackdelay.mjs';
|
||||
import './reverb.mjs';
|
||||
import './vowel.mjs';
|
||||
import { clamp, nanFallback, _mod, cycleToSeconds } from './util.mjs';
|
||||
import { clamp, nanFallback, _mod, cycleToSeconds, secondsToCycle } from './util.mjs';
|
||||
import workletsUrl from './worklets.mjs?audioworklet';
|
||||
import { createFilter, gainNode, getCompressor, getWorklet } from './helpers.mjs';
|
||||
import { map } from 'nanostores';
|
||||
@@ -482,6 +482,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => {
|
||||
amdepth = 1,
|
||||
amskew = 0.5,
|
||||
amphase = 0,
|
||||
amshape = 1,
|
||||
s = getDefaultValue('s'),
|
||||
bank,
|
||||
source,
|
||||
@@ -707,19 +708,35 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => {
|
||||
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
||||
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol }));
|
||||
distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol }));
|
||||
am !== undefined &&
|
||||
chain.push(
|
||||
getWorklet(ac, 'am-processor', {
|
||||
speed: am,
|
||||
depth: amdepth,
|
||||
skew: amskew,
|
||||
phaseoffset: amphase,
|
||||
// shape: amshape,
|
||||
// am !== undefined &&
|
||||
// chain.push(
|
||||
// getWorklet(ac, 'am-processor', {
|
||||
// speed: am,
|
||||
// depth: amdepth,
|
||||
// skew: amskew,
|
||||
// phaseoffset: amphase,
|
||||
// // shape: amshape,
|
||||
|
||||
cps,
|
||||
cycle,
|
||||
}),
|
||||
);
|
||||
// cps,
|
||||
// cycle,
|
||||
// }),
|
||||
// );
|
||||
|
||||
if (am !== undefined) {
|
||||
const amGain = new GainNode(ac, { gain: 1 });
|
||||
const frequency = cycleToSeconds(am, cps)
|
||||
const phaseoffset = cycleToSeconds(amphase, cps)
|
||||
const lfo = getLfo(ac, t, t + hapDuration, {
|
||||
skew: amskew,
|
||||
frequency: am * cps,
|
||||
depth: amdepth,
|
||||
// dcoffset: 0,
|
||||
shape: amshape,
|
||||
phaseoffset
|
||||
})
|
||||
lfo.connect(amGain.gain)
|
||||
chain.push(amGain)
|
||||
}
|
||||
|
||||
compressorThreshold !== undefined &&
|
||||
chain.push(
|
||||
|
||||
@@ -72,3 +72,7 @@ export const getSoundIndex = (n, numSounds) => {
|
||||
export function cycleToSeconds(cycle, cps) {
|
||||
return cycle / cps;
|
||||
}
|
||||
|
||||
export function secondsToCycle(t, cps) {
|
||||
return t * cps;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user