Merge branch 'main' into daslyfe/jade/delaycycle
This commit is contained in:
commit
3acc364a03
214 changed files with 5779 additions and 2322 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
superdough.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/superdough/superdough.mjs>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/packages/superdough/superdough.mjs>
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
|
@ -22,6 +22,12 @@ let maxPolyphony = DEFAULT_MAX_POLYPHONY;
|
|||
export function setMaxPolyphony(polyphony) {
|
||||
maxPolyphony = parseInt(polyphony) ?? DEFAULT_MAX_POLYPHONY;
|
||||
}
|
||||
|
||||
let multiChannelOrbits = false;
|
||||
export function setMultiChannelOrbits(bool) {
|
||||
multiChannelOrbits = bool == true;
|
||||
}
|
||||
|
||||
export const soundMap = map();
|
||||
|
||||
export function registerSound(key, onTrigger, data = {}) {
|
||||
|
|
@ -101,6 +107,10 @@ export async function aliasBank(...args) {
|
|||
}
|
||||
|
||||
export function getSound(s) {
|
||||
if (typeof s !== 'string') {
|
||||
console.warn(`getSound: expected string got "${s}". fall back to triangle`);
|
||||
return soundMap.get().triangle; // is this good?
|
||||
}
|
||||
return soundMap.get()[s.toLowerCase()];
|
||||
}
|
||||
|
||||
|
|
@ -131,8 +141,9 @@ const defaultDefaultValues = {
|
|||
shapevol: 1,
|
||||
distortvol: 1,
|
||||
delay: 0,
|
||||
byteBeatExpression: '0',
|
||||
delayfeedback: 0.5,
|
||||
delaytime: 3 / 16,
|
||||
delaysync: 3 / 16,
|
||||
orbit: 1,
|
||||
i: 1,
|
||||
velocity: 1,
|
||||
|
|
@ -195,8 +206,15 @@ function loadWorklets() {
|
|||
|
||||
// this function should be called on first user interaction (to avoid console warning)
|
||||
export async function initAudio(options = {}) {
|
||||
const { disableWorklets = false, maxPolyphony, audioDeviceName = DEFAULT_AUDIO_DEVICE_NAME } = options;
|
||||
const {
|
||||
disableWorklets = false,
|
||||
maxPolyphony,
|
||||
audioDeviceName = DEFAULT_AUDIO_DEVICE_NAME,
|
||||
multiChannelOrbits = false,
|
||||
} = options;
|
||||
|
||||
setMaxPolyphony(maxPolyphony);
|
||||
setMultiChannelOrbits(multiChannelOrbits);
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
|
@ -277,7 +295,7 @@ export const connectToDestination = (input, channels = [0, 1]) => {
|
|||
});
|
||||
stereoMix.connect(splitter);
|
||||
channels.forEach((ch, i) => {
|
||||
splitter.connect(channelMerger, i % stereoMix.channelCount, clamp(ch, 0, ctx.destination.channelCount - 1));
|
||||
splitter.connect(channelMerger, i % stereoMix.channelCount, ch % ctx.destination.channelCount);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -290,7 +308,7 @@ export const panic = () => {
|
|||
channelMerger == null;
|
||||
};
|
||||
|
||||
function getDelay(orbit, delaytime, delayfeedback, t) {
|
||||
function getDelay(orbit, delaytime, delayfeedback, t, channels) {
|
||||
if (delayfeedback > maxfeedback) {
|
||||
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
||||
}
|
||||
|
|
@ -299,7 +317,7 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
|
|||
const ac = getAudioContext();
|
||||
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||
connectToDestination(dly, [0, 1]);
|
||||
connectToDestination(dly, channels);
|
||||
delays[orbit] = dly;
|
||||
}
|
||||
delays[orbit].delayTime.value !== delaytime && delays[orbit].delayTime.setValueAtTime(delaytime, t);
|
||||
|
|
@ -307,16 +325,9 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
|
|||
return delays[orbit];
|
||||
}
|
||||
|
||||
function getPhaser(time, end, frequency = 1, depth = 0.5, centerFrequency = 1000, sweep = 2000) {
|
||||
//gain
|
||||
const ac = getAudioContext();
|
||||
const lfoGain = ac.createGain();
|
||||
lfoGain.gain.value = sweep * 2;
|
||||
// centerFrequency = centerFrequency * 2;
|
||||
// sweep = sweep * 1.5;
|
||||
|
||||
const lfo = getWorklet(ac, 'lfo-processor', {
|
||||
frequency,
|
||||
export function getLfo(audioContext, time, end, properties = {}) {
|
||||
return getWorklet(audioContext, 'lfo-processor', {
|
||||
frequency: 1,
|
||||
depth: 1,
|
||||
skew: 0,
|
||||
phaseoffset: 0,
|
||||
|
|
@ -324,8 +335,13 @@ function getPhaser(time, end, frequency = 1, depth = 0.5, centerFrequency = 1000
|
|||
end,
|
||||
shape: 1,
|
||||
dcoffset: -0.5,
|
||||
...properties,
|
||||
});
|
||||
lfo.connect(lfoGain);
|
||||
}
|
||||
|
||||
function getPhaser(time, end, frequency = 1, depth = 0.5, centerFrequency = 1000, sweep = 2000) {
|
||||
const ac = getAudioContext();
|
||||
const lfoGain = getLfo(ac, time, end, { frequency, depth: sweep * 2 });
|
||||
|
||||
//filters
|
||||
const numStages = 2; //num of filters in series
|
||||
|
|
@ -356,12 +372,12 @@ function getFilterType(ftype) {
|
|||
|
||||
let reverbs = {};
|
||||
let hasChanged = (now, before) => now !== undefined && now !== before;
|
||||
function getReverb(orbit, duration, fade, lp, dim, ir) {
|
||||
function getReverb(orbit, duration, fade, lp, dim, ir, channels) {
|
||||
// If no reverb has been created for a given orbit, create one
|
||||
if (!reverbs[orbit]) {
|
||||
const ac = getAudioContext();
|
||||
const reverb = ac.createReverb(duration, fade, lp, dim, ir);
|
||||
connectToDestination(reverb, [0, 1]);
|
||||
connectToDestination(reverb, channels);
|
||||
reverbs[orbit] = reverb;
|
||||
}
|
||||
if (
|
||||
|
|
@ -428,8 +444,13 @@ export function resetGlobalEffects() {
|
|||
}
|
||||
|
||||
let activeSoundSources = new Map();
|
||||
//music programs/audio gear usually increments inputs/outputs from 1, we need to subtract 1 from the input because the webaudio API channels start at 0
|
||||
|
||||
export const superdough = async (value, t, hapDuration, cps = 1) => {
|
||||
function mapChannelNumbers(channels) {
|
||||
return (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1);
|
||||
}
|
||||
|
||||
export const superdough = async (value, t, hapDuration, cps = 0.5) => {
|
||||
const ac = getAudioContext();
|
||||
t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t;
|
||||
let { stretch } = value;
|
||||
|
|
@ -490,7 +511,7 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
bpsustain,
|
||||
bprelease,
|
||||
bandq = getDefaultValue('bandq'),
|
||||
channels = getDefaultValue('channels'),
|
||||
|
||||
//phaser
|
||||
phaserrate: phaser,
|
||||
phaserdepth = getDefaultValue('phaserdepth'),
|
||||
|
|
@ -507,7 +528,8 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
vowel,
|
||||
delay = getDefaultValue('delay'),
|
||||
delayfeedback = getDefaultValue('delayfeedback'),
|
||||
delaytime = getDefaultValue('delaytime'),
|
||||
delaysync = getDefaultValue('delaysync'),
|
||||
delaytime,
|
||||
orbit = getDefaultValue('orbit'),
|
||||
room,
|
||||
roomfade,
|
||||
|
|
@ -526,6 +548,13 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
compressorRelease,
|
||||
} = value;
|
||||
|
||||
delaytime = delaytime ?? cycleToSeconds(delaysync)
|
||||
|
||||
const orbitChannels = mapChannelNumbers(
|
||||
multiChannelOrbits && orbit > 0 ? [orbit * 2 - 1, orbit * 2] : getDefaultValue('channels'),
|
||||
);
|
||||
const channels = value.channels != null ? mapChannelNumbers(value.channels) : orbitChannels;
|
||||
|
||||
gain = applyGainCurve(nanFallback(gain, 1));
|
||||
postgain = applyGainCurve(postgain);
|
||||
shapevol = applyGainCurve(shapevol);
|
||||
|
|
@ -547,11 +576,11 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
activeSoundSources.delete(chainID);
|
||||
}
|
||||
|
||||
//music programs/audio gear usually increments inputs/outputs from 1, so imitate that behavior
|
||||
channels = (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1);
|
||||
|
||||
let audioNodes = [];
|
||||
|
||||
if (['-', '~', '_'].includes(s)) {
|
||||
return;
|
||||
}
|
||||
if (bank && s) {
|
||||
s = `${bank}_${s}`;
|
||||
value.s = s;
|
||||
|
|
@ -560,7 +589,7 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
// get source AudioNode
|
||||
let sourceNode;
|
||||
if (source) {
|
||||
sourceNode = source(t, value, hapDuration);
|
||||
sourceNode = source(t, value, hapDuration, cps);
|
||||
} else if (getSound(s)) {
|
||||
const { onTrigger } = getSound(s);
|
||||
const onEnded = () => {
|
||||
|
|
@ -699,8 +728,8 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
// delay
|
||||
let delaySend;
|
||||
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||
const delyNode = getDelay(orbit, cycleToSeconds(delaytime, cps), delayfeedback, t);
|
||||
delaySend = effectSend(post, delyNode, delay);
|
||||
const delayNode = getDelay(orbit, delaytime, delayfeedback, t, orbitChannels);
|
||||
delaySend = effectSend(post, delayNode, delay);
|
||||
audioNodes.push(delaySend);
|
||||
}
|
||||
// reverb
|
||||
|
|
@ -717,7 +746,7 @@ export const superdough = async (value, t, hapDuration, cps = 1) => {
|
|||
}
|
||||
roomIR = await loadBuffer(url, ac, ir, 0);
|
||||
}
|
||||
const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, roomIR);
|
||||
const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, roomIR, orbitChannels);
|
||||
reverbSend = effectSend(post, reverbNode, room);
|
||||
audioNodes.push(reverbSend);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue