testing
This commit is contained in:
parent
47b65846e9
commit
496d60dc1c
7 changed files with 98 additions and 9 deletions
|
|
@ -430,6 +430,17 @@ export const { crush } = registerControl('crush');
|
||||||
*/
|
*/
|
||||||
export const { coarse } = registerControl('coarse');
|
export const { coarse } = registerControl('coarse');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modulate the output gain of a sound with a continuous wave
|
||||||
|
*
|
||||||
|
* @name gainmod
|
||||||
|
* @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on.
|
||||||
|
* @example
|
||||||
|
* s("triangle").gainmod("2:1:0")
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const { gainmod } = registerControl('gainmod');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows you to set the output channels on the interface
|
* Allows you to set the output channels on the interface
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export class Cyclist {
|
||||||
// the following line is dumb and only here for backwards compatibility
|
// the following line is dumb and only here for backwards compatibility
|
||||||
// see https://github.com/tidalcycles/strudel/pull/1004
|
// see https://github.com/tidalcycles/strudel/pull/1004
|
||||||
const deadline = targetTime - phase;
|
const deadline = targetTime - phase;
|
||||||
onTrigger?.(hap, deadline, duration, this.cps, targetTime);
|
onTrigger?.(hap, deadline, duration, this.cps, targetTime, end);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Fraction from 'fraction.js';
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
|
|
||||||
export class NeoCyclist {
|
export class NeoCyclist {
|
||||||
|
|
@ -86,7 +87,15 @@ export class NeoCyclist {
|
||||||
this.latency +
|
this.latency +
|
||||||
this.worker_time_dif;
|
this.worker_time_dif;
|
||||||
const duration = hap.duration / this.cps;
|
const duration = hap.duration / this.cps;
|
||||||
onTrigger?.(hap, 0, duration, this.cps, targetTime);
|
onTrigger?.(
|
||||||
|
hap,
|
||||||
|
0,
|
||||||
|
duration,
|
||||||
|
this.cps,
|
||||||
|
targetTime,
|
||||||
|
this.cycle,
|
||||||
|
// + Fraction(this.latency).div(this.cps)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -178,15 +178,15 @@ export function repl({
|
||||||
|
|
||||||
export const getTrigger =
|
export const getTrigger =
|
||||||
({ getTime, defaultOutput }) =>
|
({ getTime, defaultOutput }) =>
|
||||||
async (hap, deadline, duration, cps, t) => {
|
async (hap, deadline, duration, cps, t, cycle = 0) => {
|
||||||
// TODO: get rid of deadline after https://github.com/tidalcycles/strudel/pull/1004
|
// TODO: get rid of deadline after https://github.com/tidalcycles/strudel/pull/1004
|
||||||
try {
|
try {
|
||||||
if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
|
if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
|
||||||
await defaultOutput(hap, deadline, duration, cps, t);
|
await defaultOutput(hap, deadline, duration, cps, t, cycle);
|
||||||
}
|
}
|
||||||
if (hap.context.onTrigger) {
|
if (hap.context.onTrigger) {
|
||||||
// call signature of output / onTrigger is different...
|
// call signature of output / onTrigger is different...
|
||||||
await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t);
|
await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t, cycle);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger(`[cyclist] error: ${err.message}`, 'error');
|
logger(`[cyclist] error: ${err.message}`, 'error');
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,8 @@ export function resetGlobalEffects() {
|
||||||
analysersData = {};
|
analysersData = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const superdough = async (value, t, hapDuration) => {
|
export const superdough = async (value, t, hapDuration, cps, cycle) => {
|
||||||
|
console.log({ cps, cycle });
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
if (typeof value !== 'object') {
|
if (typeof value !== 'object') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -322,6 +323,7 @@ export const superdough = async (value, t, hapDuration) => {
|
||||||
phasercenter,
|
phasercenter,
|
||||||
//
|
//
|
||||||
coarse,
|
coarse,
|
||||||
|
gainmod,
|
||||||
crush,
|
crush,
|
||||||
shape,
|
shape,
|
||||||
shapevol = 1,
|
shapevol = 1,
|
||||||
|
|
@ -470,6 +472,7 @@ export const superdough = async (value, t, hapDuration) => {
|
||||||
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
||||||
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol }));
|
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol }));
|
||||||
distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol }));
|
distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol }));
|
||||||
|
gainmod !== undefined && chain.push(getWorklet(ac, 'gainmod-processor', { speed: gainmod, cps, cycle }));
|
||||||
|
|
||||||
compressorThreshold !== undefined &&
|
compressorThreshold !== undefined &&
|
||||||
chain.push(
|
chain.push(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,36 @@
|
||||||
// coarse, crush, and shape processors adapted from dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js
|
// coarse, crush, and shape processors adapted from dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js
|
||||||
// LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE
|
// LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE
|
||||||
|
|
||||||
|
const linearEnvelope = (startVal, EndVal, startTime, endTime, currentTime, hold = 0) => {
|
||||||
|
let min = 0.001;
|
||||||
|
currentTime = currentTime - hold;
|
||||||
|
if (startTime > currentTime) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (currentTime > endTime) {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
// change relative start time to 0 to prevent numeric overflow
|
||||||
|
currentTime = currentTime - startTime;
|
||||||
|
endTime = endTime - startTime;
|
||||||
|
startTime = 0;
|
||||||
|
|
||||||
|
let x1 = startTime;
|
||||||
|
let y1 = startVal;
|
||||||
|
let x2 = endTime;
|
||||||
|
let y2 = EndVal;
|
||||||
|
|
||||||
|
// Calculate the growth or decay rate (b)
|
||||||
|
let b = y1 / y2 / (x1 - x2);
|
||||||
|
|
||||||
|
// Calculate the initial value (a)
|
||||||
|
let a = y1 / (b * x1);
|
||||||
|
|
||||||
|
let x = currentTime;
|
||||||
|
// calculate y for any x
|
||||||
|
return a * (b * x);
|
||||||
|
};
|
||||||
|
|
||||||
class CoarseProcessor extends AudioWorkletProcessor {
|
class CoarseProcessor extends AudioWorkletProcessor {
|
||||||
static get parameterDescriptors() {
|
static get parameterDescriptors() {
|
||||||
return [{ name: 'coarse', defaultValue: 1 }];
|
return [{ name: 'coarse', defaultValue: 1 }];
|
||||||
|
|
@ -62,6 +92,41 @@ class CrushProcessor extends AudioWorkletProcessor {
|
||||||
}
|
}
|
||||||
registerProcessor('crush-processor', CrushProcessor);
|
registerProcessor('crush-processor', CrushProcessor);
|
||||||
|
|
||||||
|
class GainModProcessor extends AudioWorkletProcessor {
|
||||||
|
static get parameterDescriptors() {
|
||||||
|
return [
|
||||||
|
{ name: 'cps', defaultValue: 0.5 },
|
||||||
|
{ name: 'speed', defaultValue: 0.5 },
|
||||||
|
{ name: 'cycle', defaultValue: 0 },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
process(inputs, outputs, parameters) {
|
||||||
|
const input = inputs[0];
|
||||||
|
const output = outputs[0];
|
||||||
|
const blockSize = 128;
|
||||||
|
|
||||||
|
let crush = parameters.crush[0] ?? 8;
|
||||||
|
crush = Math.max(1, crush);
|
||||||
|
|
||||||
|
if (input[0] == null || output[0] == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let n = 0; n < blockSize; n++) {
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
const x = Math.pow(2, crush - 1);
|
||||||
|
output[i][n] = Math.round(input[i][n] * x) / x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerProcessor('gainmod-processor', GainModProcessor);
|
||||||
|
|
||||||
class ShapeProcessor extends AudioWorkletProcessor {
|
class ShapeProcessor extends AudioWorkletProcessor {
|
||||||
static get parameterDescriptors() {
|
static get parameterDescriptors() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ const hap2value = (hap) => {
|
||||||
|
|
||||||
export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps);
|
export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps);
|
||||||
// uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004
|
// uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004
|
||||||
export const webaudioOutput = (hap, deadline, hapDuration, cps, t) =>
|
export const webaudioOutput = (hap, deadline, hapDuration, cps, t, cycle) => {
|
||||||
superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration);
|
return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps, cycle);
|
||||||
|
};
|
||||||
Pattern.prototype.webaudio = function () {
|
Pattern.prototype.webaudio = function () {
|
||||||
return this.onTrigger(webaudioOutputTrigger);
|
return this.onTrigger(webaudioOutputTrigger);
|
||||||
};
|
};
|
||||||
|
|
@ -30,6 +30,7 @@ export function webaudioScheduler(options = {}) {
|
||||||
defaultOutput: webaudioOutput,
|
defaultOutput: webaudioOutput,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { defaultOutput, getTime } = options;
|
const { defaultOutput, getTime } = options;
|
||||||
return new strudel.Cyclist({
|
return new strudel.Cyclist({
|
||||||
...options,
|
...options,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue