Working version of bus
This commit is contained in:
parent
82ca77be9d
commit
2aeba2e53f
6 changed files with 111 additions and 25 deletions
|
|
@ -2046,6 +2046,28 @@ export const { octave, oct } = registerControl('octave', 'oct');
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
export const { orbit } = registerControl('orbit', 'o');
|
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
|
// 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');
|
export const { overgain } = registerControl('overgain');
|
||||||
// TODO: what is this? not found in tidal doc. Similar to above, but limited to 1
|
// TODO: what is this? not found in tidal doc. Similar to above, but limited to 1
|
||||||
|
|
|
||||||
|
|
@ -3743,18 +3743,18 @@ addConfigAlias('env', 'depthabs', 'da');
|
||||||
addConfigAlias('env', 'acurve', 'ac');
|
addConfigAlias('env', 'acurve', 'ac');
|
||||||
addConfigAlias('env', 'dcurve', 'dc');
|
addConfigAlias('env', 'dcurve', 'dc');
|
||||||
addConfigAlias('env', 'rcurve', 'rc');
|
addConfigAlias('env', 'rcurve', 'rc');
|
||||||
addConfigAlias('omod', 'orbit', 'o');
|
addConfigAlias('bmod', 'orbit', 'o');
|
||||||
addConfigAlias('omod', 'target', 't');
|
addConfigAlias('bmod', 'target', 't');
|
||||||
addConfigAlias('omod', 'depth', 'dep', 'dr');
|
addConfigAlias('bmod', 'depth', 'dep', 'dr');
|
||||||
addConfigAlias('omod', 'depthabs', 'da');
|
addConfigAlias('bmod', 'depthabs', 'da');
|
||||||
addConfigAlias('omod', 'dc');
|
addConfigAlias('bmod', 'dc');
|
||||||
|
|
||||||
Pattern.prototype.modulate = function (type, config, idx) {
|
Pattern.prototype.modulate = function (type, config, idx) {
|
||||||
if (config == null || typeof config !== 'object') {
|
if (config == null || typeof config !== 'object') {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (!['lfo', 'env', 'omod'].includes(type)) {
|
if (!['lfo', 'env', 'bmod'].includes(type)) {
|
||||||
logger(`[core] Modulation type ${type} not found. Please use one of 'lfo', 'env', 'omod'`);
|
logger(`[core] Modulation type ${type} not found. Please use one of 'lfo', 'env', 'bmod'`);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
let output = this;
|
let output = this;
|
||||||
|
|
@ -3828,13 +3828,15 @@ Pattern.prototype.env = function (config, idx) {
|
||||||
export const env = (config) => pure({}).env(config);
|
export const env = (config) => pure({}).env(config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modulates with the output from a given `orbit`
|
* Modulates with the output from a given `bus`.
|
||||||
* Can be called in sequence like pat.obus(...).obus(...) to set up multiple modulators
|
* 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
|
* @memberof Pattern
|
||||||
* @param {Object} config Orbit bus configuration.
|
* @param {Object} config Bus modulation configuration.
|
||||||
* @param {string | Pattern} [config.orbit] Orbit to get modulation signal from
|
* @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 {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.depth] Relative modulation depth. Aliases: dep, dr
|
||||||
* @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da
|
* @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
|
* @param {number | Pattern} [config.dc] DC offset prior to application
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.omod = function (config, idx) {
|
Pattern.prototype.bmod = function (config, idx) {
|
||||||
return this.modulate('omod', 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,
|
distortvol: 1,
|
||||||
distorttype: 0,
|
distorttype: 0,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
|
busgain: 1,
|
||||||
byteBeatExpression: '0',
|
byteBeatExpression: '0',
|
||||||
delayfeedback: 0.5,
|
delayfeedback: 0.5,
|
||||||
delaysync: 3 / 16,
|
delaysync: 3 / 16,
|
||||||
|
|
@ -521,10 +522,10 @@ function connectEnvelope(idx, params, nodeTracker, value) {
|
||||||
return envNode;
|
return envNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectOrbitModulator(params, nodeTracker, value) {
|
function connectBusModulator(params, nodeTracker, value) {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
const { target, depth = 1, depthabs } = params;
|
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 });
|
const dc = new ConstantSourceNode(ac, { offset: params.dc ?? 0 });
|
||||||
dc.start(params.begin);
|
dc.start(params.begin);
|
||||||
const shifted = dc.connect(gainNode(1));
|
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'),
|
delaysync = getDefaultValue('delaysync'),
|
||||||
delaytime,
|
delaytime,
|
||||||
orbit = getDefaultValue('orbit'),
|
orbit = getDefaultValue('orbit'),
|
||||||
|
bus,
|
||||||
|
busgain = getDefaultValue('busgain'),
|
||||||
room,
|
room,
|
||||||
roomfade,
|
roomfade,
|
||||||
roomlp,
|
roomlp,
|
||||||
|
|
@ -666,6 +669,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||||
delay = applyGainCurve(delay);
|
delay = applyGainCurve(delay);
|
||||||
velocity = applyGainCurve(velocity);
|
velocity = applyGainCurve(velocity);
|
||||||
tremolodepth = applyGainCurve(tremolodepth);
|
tremolodepth = applyGainCurve(tremolodepth);
|
||||||
|
busgain = applyGainCurve(busgain);
|
||||||
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
||||||
|
|
||||||
const end = t + hapDuration;
|
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);
|
const reverbSend = orbitBus.sendReverb(post, room);
|
||||||
audioNodes.push(reverbSend);
|
audioNodes.push(reverbSend);
|
||||||
}
|
}
|
||||||
|
if (bus != null) {
|
||||||
|
const busNode = audioController.getBus(bus);
|
||||||
|
const busSend = effectSend(post, busNode, busgain);
|
||||||
|
audioNodes.push(busSend);
|
||||||
|
}
|
||||||
|
|
||||||
if (djf != null) {
|
if (djf != null) {
|
||||||
nodes['djf'] = orbitBus.getDjf(djf, t);
|
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);
|
audioNodes.push(env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value.omod) {
|
if (value.bmod) {
|
||||||
for (const p of value.omod) {
|
for (const p of value.bmod) {
|
||||||
const { toCleanup } = connectOrbitModulator({ ...p, begin: t, end: endWithRelease }, nodes, value);
|
const { toCleanup } = connectBusModulator({ ...p, begin: t, end: endWithRelease }, nodes, value);
|
||||||
audioNodes.push(...toCleanup);
|
audioNodes.push(...toCleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ const CONTROL_DATA = {
|
||||||
// roomfade: { param: 'room.fade', min: 0, max: 1 },
|
// roomfade: { param: 'room.fade', min: 0, max: 1 },
|
||||||
roomlp: { param: 'room.lp', min: 20, max: 24000 },
|
roomlp: { param: 'room.lp', min: 20, max: 24000 },
|
||||||
djf: { param: 'djf.value', min: 0, max: 1 },
|
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
|
// SYNTHS
|
||||||
detune: { param: 'source.detune', min: 0, max: 1 },
|
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 { errorLogger } from './logger.mjs';
|
||||||
import { clamp } from './util.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 {
|
export class Orbit {
|
||||||
reverbNode;
|
reverbNode;
|
||||||
|
|
@ -11,10 +14,11 @@ export class Orbit {
|
||||||
summingNode;
|
summingNode;
|
||||||
djfNode;
|
djfNode;
|
||||||
audioContext;
|
audioContext;
|
||||||
|
|
||||||
constructor(audioContext) {
|
constructor(audioContext) {
|
||||||
this.audioContext = audioContext;
|
this.audioContext = audioContext;
|
||||||
this.output = new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
this.output = getStereoNode(audioContext);
|
||||||
this.summingNode = new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
this.summingNode = getStereoNode(audioContext);
|
||||||
this.summingNode.connect(this.output);
|
this.summingNode.connect(this.output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +168,7 @@ export class SuperdoughAudioController {
|
||||||
audioContext;
|
audioContext;
|
||||||
output;
|
output;
|
||||||
nodes = {};
|
nodes = {};
|
||||||
|
buses = {};
|
||||||
|
|
||||||
constructor(audioContext) {
|
constructor(audioContext) {
|
||||||
this.audioContext = audioContext;
|
this.audioContext = audioContext;
|
||||||
|
|
@ -171,10 +176,14 @@ export class SuperdoughAudioController {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
Array.from(this.nodes).forEach((node) => {
|
Object.values(this.nodes).forEach((node) => {
|
||||||
node.disconnect();
|
node.disconnect();
|
||||||
});
|
});
|
||||||
|
Object.values(this.buses).forEach((bus) => {
|
||||||
|
bus.disconnect();
|
||||||
|
});
|
||||||
this.nodes = {};
|
this.nodes = {};
|
||||||
|
this.buses = {};
|
||||||
this.output.reset();
|
this.output.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,4 +215,11 @@ export class SuperdoughAudioController {
|
||||||
}
|
}
|
||||||
return this.nodes[orbitNum];
|
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 { clamp } from './util.mjs';
|
||||||
import { registerSound, soundMap } from './superdough.mjs';
|
import { getSuperdoughAudioController, registerSound, soundMap } from './superdough.mjs';
|
||||||
import { getAudioContext } from './audioContext.mjs';
|
import { getAudioContext } from './audioContext.mjs';
|
||||||
import {
|
import {
|
||||||
applyFM,
|
applyFM,
|
||||||
|
|
@ -367,6 +367,41 @@ export function registerSynthSounds() {
|
||||||
{ prebake: true, type: 'synth' },
|
{ 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) => {
|
[...noises].forEach((s) => {
|
||||||
registerSound(
|
registerSound(
|
||||||
s,
|
s,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue