mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 14:26:58 -04:00
Working version of bus
This commit is contained in:
@@ -2046,6 +2046,28 @@ export const { octave, oct } = registerControl('octave', 'oct');
|
||||
* )
|
||||
*/
|
||||
export const { orbit } = registerControl('orbit', 'o');
|
||||
|
||||
/**
|
||||
* A `bus` is a send which can be used for mixing patterns. It combines with..
|
||||
* s("bus") to play that bus through another pattern (for, say, applying non-linear
|
||||
* effects like distortion to multiple signals)
|
||||
*
|
||||
* otherPat.bmod(..) (to modulate another pattern with the bus)
|
||||
*
|
||||
* @name bus
|
||||
* @param {number | Pattern} number
|
||||
*/
|
||||
export const { bus } = registerControl('bus');
|
||||
|
||||
/**
|
||||
* Postgain multiplier prior to sending the signal to the audio bus.
|
||||
*
|
||||
* @name busgain
|
||||
* @synonyms bgain
|
||||
* @param {number | Pattern} number
|
||||
*/
|
||||
export const { busgain, bgain } = registerControl('busgain', 'bgain');
|
||||
|
||||
// TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that
|
||||
export const { overgain } = registerControl('overgain');
|
||||
// TODO: what is this? not found in tidal doc. Similar to above, but limited to 1
|
||||
|
||||
+17
-15
@@ -3743,18 +3743,18 @@ addConfigAlias('env', 'depthabs', 'da');
|
||||
addConfigAlias('env', 'acurve', 'ac');
|
||||
addConfigAlias('env', 'dcurve', 'dc');
|
||||
addConfigAlias('env', 'rcurve', 'rc');
|
||||
addConfigAlias('omod', 'orbit', 'o');
|
||||
addConfigAlias('omod', 'target', 't');
|
||||
addConfigAlias('omod', 'depth', 'dep', 'dr');
|
||||
addConfigAlias('omod', 'depthabs', 'da');
|
||||
addConfigAlias('omod', 'dc');
|
||||
addConfigAlias('bmod', 'orbit', 'o');
|
||||
addConfigAlias('bmod', 'target', 't');
|
||||
addConfigAlias('bmod', 'depth', 'dep', 'dr');
|
||||
addConfigAlias('bmod', 'depthabs', 'da');
|
||||
addConfigAlias('bmod', 'dc');
|
||||
|
||||
Pattern.prototype.modulate = function (type, config, idx) {
|
||||
if (config == null || typeof config !== 'object') {
|
||||
return this;
|
||||
}
|
||||
if (!['lfo', 'env', 'omod'].includes(type)) {
|
||||
logger(`[core] Modulation type ${type} not found. Please use one of 'lfo', 'env', 'omod'`);
|
||||
if (!['lfo', 'env', 'bmod'].includes(type)) {
|
||||
logger(`[core] Modulation type ${type} not found. Please use one of 'lfo', 'env', 'bmod'`);
|
||||
return this;
|
||||
}
|
||||
let output = this;
|
||||
@@ -3828,13 +3828,15 @@ Pattern.prototype.env = function (config, idx) {
|
||||
export const env = (config) => pure({}).env(config);
|
||||
|
||||
/**
|
||||
* Modulates with the output from a given `orbit`
|
||||
* Can be called in sequence like pat.obus(...).obus(...) to set up multiple modulators
|
||||
* Modulates with the output from a given `bus`.
|
||||
* Can be called in sequence like pat.bmod(...).bmod(...) to set up multiple modulators
|
||||
*
|
||||
* @name omod
|
||||
* Send to an audio bus with `otherPat.bus(..)`.
|
||||
*
|
||||
* @name bmod
|
||||
* @memberof Pattern
|
||||
* @param {Object} config Orbit bus configuration.
|
||||
* @param {string | Pattern} [config.orbit] Orbit to get modulation signal from
|
||||
* @param {Object} config Bus modulation configuration.
|
||||
* @param {string | Pattern} [config.bus] Bus to get modulation signal from
|
||||
* @param {string | Pattern} [config.target] Node (and parameter if specified like `lpf.frequency`) to modulate. Aliases: t
|
||||
* @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr
|
||||
* @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da
|
||||
@@ -3842,7 +3844,7 @@ export const env = (config) => pure({}).env(config);
|
||||
* @param {number | Pattern} [config.dc] DC offset prior to application
|
||||
* @returns Pattern
|
||||
*/
|
||||
Pattern.prototype.omod = function (config, idx) {
|
||||
return this.modulate('omod', config, idx);
|
||||
Pattern.prototype.bmod = function (config, idx) {
|
||||
return this.modulate('bmod', config, idx);
|
||||
};
|
||||
export const omod = (config) => pure({}).omod(config);
|
||||
export const bmod = (config) => pure({}).bmod(config);
|
||||
|
||||
@@ -163,6 +163,7 @@ let defaultDefaultValues = {
|
||||
distortvol: 1,
|
||||
distorttype: 0,
|
||||
delay: 0,
|
||||
busgain: 1,
|
||||
byteBeatExpression: '0',
|
||||
delayfeedback: 0.5,
|
||||
delaysync: 3 / 16,
|
||||
@@ -521,10 +522,10 @@ function connectEnvelope(idx, params, nodeTracker, value) {
|
||||
return envNode;
|
||||
}
|
||||
|
||||
function connectOrbitModulator(params, nodeTracker, value) {
|
||||
function connectBusModulator(params, nodeTracker, value) {
|
||||
const ac = getAudioContext();
|
||||
const { target, depth = 1, depthabs } = params;
|
||||
const signal = controller.getOrbit(params.orbit).output;
|
||||
const signal = controller.getBus(params.bus).output;
|
||||
const dc = new ConstantSourceNode(ac, { offset: params.dc ?? 0 });
|
||||
dc.start(params.begin);
|
||||
const shifted = dc.connect(gainNode(1));
|
||||
@@ -628,6 +629,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
delaysync = getDefaultValue('delaysync'),
|
||||
delaytime,
|
||||
orbit = getDefaultValue('orbit'),
|
||||
bus,
|
||||
busgain = getDefaultValue('busgain'),
|
||||
room,
|
||||
roomfade,
|
||||
roomlp,
|
||||
@@ -666,6 +669,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
delay = applyGainCurve(delay);
|
||||
velocity = applyGainCurve(velocity);
|
||||
tremolodepth = applyGainCurve(tremolodepth);
|
||||
busgain = applyGainCurve(busgain);
|
||||
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
||||
|
||||
const end = t + hapDuration;
|
||||
@@ -970,6 +974,11 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
const reverbSend = orbitBus.sendReverb(post, room);
|
||||
audioNodes.push(reverbSend);
|
||||
}
|
||||
if (bus != null) {
|
||||
const busNode = audioController.getBus(bus);
|
||||
const busSend = effectSend(post, busNode, busgain);
|
||||
audioNodes.push(busSend);
|
||||
}
|
||||
|
||||
if (djf != null) {
|
||||
nodes['djf'] = orbitBus.getDjf(djf, t);
|
||||
@@ -1027,9 +1036,9 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
audioNodes.push(env);
|
||||
}
|
||||
}
|
||||
if (value.omod) {
|
||||
for (const p of value.omod) {
|
||||
const { toCleanup } = connectOrbitModulator({ ...p, begin: t, end: endWithRelease }, nodes, value);
|
||||
if (value.bmod) {
|
||||
for (const p of value.bmod) {
|
||||
const { toCleanup } = connectBusModulator({ ...p, begin: t, end: endWithRelease }, nodes, value);
|
||||
audioNodes.push(...toCleanup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ const CONTROL_DATA = {
|
||||
// roomfade: { param: 'room.fade', min: 0, max: 1 },
|
||||
roomlp: { param: 'room.lp', min: 20, max: 24000 },
|
||||
djf: { param: 'djf.value', min: 0, max: 1 },
|
||||
busgain: { param: 'bus.gain', default: 1, min: 0, max: 10 },
|
||||
bgain: { param: 'bus.gain', default: 1, min: 0, max: 10 },
|
||||
|
||||
// SYNTHS
|
||||
detune: { param: 'source.detune', min: 0, max: 1 },
|
||||
|
||||
@@ -2,7 +2,10 @@ import { effectSend, getWorklet, webAudioTimeout } from './helpers.mjs';
|
||||
import { errorLogger } from './logger.mjs';
|
||||
import { clamp } from './util.mjs';
|
||||
|
||||
let hasChanged = (now, before) => now !== undefined && now !== before;
|
||||
const hasChanged = (now, before) => now !== undefined && now !== before;
|
||||
// Node with fixed stereo channel count to prevent clicking when the input signal
|
||||
// switches from mono to stereo
|
||||
const getStereoNode = (ac) => new GainNode(ac, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
||||
|
||||
export class Orbit {
|
||||
reverbNode;
|
||||
@@ -11,10 +14,11 @@ export class Orbit {
|
||||
summingNode;
|
||||
djfNode;
|
||||
audioContext;
|
||||
|
||||
constructor(audioContext) {
|
||||
this.audioContext = audioContext;
|
||||
this.output = new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
||||
this.summingNode = new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
||||
this.output = getStereoNode(audioContext);
|
||||
this.summingNode = getStereoNode(audioContext);
|
||||
this.summingNode.connect(this.output);
|
||||
}
|
||||
|
||||
@@ -164,6 +168,7 @@ export class SuperdoughAudioController {
|
||||
audioContext;
|
||||
output;
|
||||
nodes = {};
|
||||
buses = {};
|
||||
|
||||
constructor(audioContext) {
|
||||
this.audioContext = audioContext;
|
||||
@@ -171,10 +176,14 @@ export class SuperdoughAudioController {
|
||||
}
|
||||
|
||||
reset() {
|
||||
Array.from(this.nodes).forEach((node) => {
|
||||
Object.values(this.nodes).forEach((node) => {
|
||||
node.disconnect();
|
||||
});
|
||||
Object.values(this.buses).forEach((bus) => {
|
||||
bus.disconnect();
|
||||
});
|
||||
this.nodes = {};
|
||||
this.buses = {};
|
||||
this.output.reset();
|
||||
}
|
||||
|
||||
@@ -206,4 +215,11 @@ export class SuperdoughAudioController {
|
||||
}
|
||||
return this.nodes[orbitNum];
|
||||
}
|
||||
|
||||
getBus(busNum) {
|
||||
if (this.buses[busNum] == null) {
|
||||
this.buses[busNum] = getStereoNode(this.audioContext);
|
||||
}
|
||||
return this.buses[busNum];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { clamp } from './util.mjs';
|
||||
import { registerSound, soundMap } from './superdough.mjs';
|
||||
import { getSuperdoughAudioController, registerSound, soundMap } from './superdough.mjs';
|
||||
import { getAudioContext } from './audioContext.mjs';
|
||||
import {
|
||||
applyFM,
|
||||
@@ -367,6 +367,41 @@ export function registerSynthSounds() {
|
||||
{ prebake: true, type: 'synth' },
|
||||
);
|
||||
|
||||
registerSound(
|
||||
'bus',
|
||||
(begin, value, onended) => {
|
||||
const ac = getAudioContext();
|
||||
const [attack, decay, sustain, release] = getADSRValues(
|
||||
[value.attack, value.decay, value.sustain, value.release],
|
||||
'linear',
|
||||
[0.001, 0.05, 0.6, 0.01],
|
||||
);
|
||||
const holdend = begin + value.duration;
|
||||
const end = holdend + release + 0.01;
|
||||
const bus = getSuperdoughAudioController().getBus(value.n ?? 0);
|
||||
const envGain = bus.connect(gainNode(1));
|
||||
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear');
|
||||
const timeoutNode = webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
bus.disconnect(envGain);
|
||||
onended();
|
||||
},
|
||||
begin,
|
||||
end,
|
||||
);
|
||||
|
||||
return {
|
||||
node: envGain,
|
||||
source: bus,
|
||||
stop: (time) => {
|
||||
timeoutNode.stop(time);
|
||||
},
|
||||
};
|
||||
},
|
||||
{ prebake: true, type: 'input' },
|
||||
);
|
||||
|
||||
[...noises].forEach((s) => {
|
||||
registerSound(
|
||||
s,
|
||||
|
||||
Reference in New Issue
Block a user