mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-25 12:38:35 +00:00
adding modulators
This commit is contained in:
parent
1e3a23e017
commit
bfcb2070d1
@ -186,3 +186,61 @@ export function getVibratoOscillator(param, value, t) {
|
|||||||
return vibratoOscillator;
|
return vibratoOscillator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const mod = (freq, range = 1, type = 'sine') => {
|
||||||
|
const ctx = getAudioContext();
|
||||||
|
const osc = ctx.createOscillator();
|
||||||
|
osc.type = type;
|
||||||
|
osc.frequency.value = freq;
|
||||||
|
osc.start();
|
||||||
|
const g = new GainNode(ctx, { gain: range });
|
||||||
|
osc.connect(g); // -range, range
|
||||||
|
return { node: g, stop: (t) => osc.stop(t) };
|
||||||
|
};
|
||||||
|
const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') => {
|
||||||
|
const carrfreq = frequencyparam.value;
|
||||||
|
const modfreq = carrfreq * harmonicityRatio;
|
||||||
|
const modgain = modfreq * modulationIndex;
|
||||||
|
return mod(modfreq, modgain, wave);
|
||||||
|
};
|
||||||
|
export function applyFM(param, value) {
|
||||||
|
const {
|
||||||
|
fmh: fmHarmonicity = 1,
|
||||||
|
fmi: fmModulationIndex,
|
||||||
|
fmenv: fmEnvelopeType = 'exp',
|
||||||
|
fmattack: fmAttack,
|
||||||
|
fmdecay: fmDecay,
|
||||||
|
fmsustain: fmSustain,
|
||||||
|
fmrelease: fmRelease,
|
||||||
|
fmvelocity: fmVelocity,
|
||||||
|
fmwave: fmWaveform = 'sine',
|
||||||
|
duration,
|
||||||
|
begin,
|
||||||
|
} = value;
|
||||||
|
|
||||||
|
const ac = getAudioContext();
|
||||||
|
if (fmModulationIndex) {
|
||||||
|
const envGain = ac.createGain();
|
||||||
|
const { node: modulator } = fm(param, fmHarmonicity, fmModulationIndex, fmWaveform);
|
||||||
|
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
||||||
|
// no envelope by default
|
||||||
|
modulator.connect(param.frequency);
|
||||||
|
} else {
|
||||||
|
const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]);
|
||||||
|
const holdEnd = begin + duration;
|
||||||
|
getParamADSR(
|
||||||
|
envGain.gain,
|
||||||
|
attack,
|
||||||
|
decay,
|
||||||
|
sustain,
|
||||||
|
release,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
begin,
|
||||||
|
holdEnd,
|
||||||
|
fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
|
||||||
|
);
|
||||||
|
modulator.connect(envGain);
|
||||||
|
envGain.connect(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { clamp, midiToFreq, noteToMidi } from './util.mjs';
|
import { clamp, midiToFreq, noteToMidi } from './util.mjs';
|
||||||
import { registerSound, getAudioContext, getWorklet } from './superdough.mjs';
|
import { registerSound, getAudioContext, getWorklet } from './superdough.mjs';
|
||||||
import { gainNode, getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
import { applyFM, gainNode, getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
||||||
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
||||||
|
|
||||||
const mod = (freq, range = 1, type = 'sine') => {
|
const mod = (freq, range = 1, type = 'sine') => {
|
||||||
@ -21,9 +21,6 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => {
|
|||||||
return mod(modfreq, modgain, wave);
|
return mod(modfreq, modgain, wave);
|
||||||
};
|
};
|
||||||
|
|
||||||
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
|
||||||
const noises = ['pink', 'white', 'brown', 'crackle'];
|
|
||||||
|
|
||||||
const getFrequencyFromValue = (value) => {
|
const getFrequencyFromValue = (value) => {
|
||||||
let { note, freq } = value;
|
let { note, freq } = value;
|
||||||
note = note || 36;
|
note = note || 36;
|
||||||
@ -38,6 +35,62 @@ const getFrequencyFromValue = (value) => {
|
|||||||
return Number(freq);
|
return Number(freq);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
||||||
|
const noises = ['pink', 'white', 'brown', 'crackle'];
|
||||||
|
|
||||||
|
const applyModulators = (node, value) => {
|
||||||
|
let {
|
||||||
|
n: partials,
|
||||||
|
note,
|
||||||
|
freq,
|
||||||
|
noise = 0,
|
||||||
|
// fm
|
||||||
|
fmh: fmHarmonicity = 1,
|
||||||
|
fmi: fmModulationIndex,
|
||||||
|
fmenv: fmEnvelopeType = 'exp',
|
||||||
|
fmattack: fmAttack,
|
||||||
|
fmdecay: fmDecay,
|
||||||
|
fmsustain: fmSustain,
|
||||||
|
fmrelease: fmRelease,
|
||||||
|
fmvelocity: fmVelocity,
|
||||||
|
fmwave: fmWaveform = 'sine',
|
||||||
|
duration,
|
||||||
|
begin: t,
|
||||||
|
} = value;
|
||||||
|
|
||||||
|
const ac = getAudioContext();
|
||||||
|
if (fmModulationIndex) {
|
||||||
|
let envGain = ac.createGain();
|
||||||
|
const { node: modulator, stop } = fm(node, fmHarmonicity, fmModulationIndex, fmWaveform);
|
||||||
|
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
||||||
|
// no envelope by default
|
||||||
|
modulator.connect(node.frequency);
|
||||||
|
} else {
|
||||||
|
const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]);
|
||||||
|
const holdEnd = t + duration;
|
||||||
|
getParamADSR(
|
||||||
|
envGain.gain,
|
||||||
|
attack,
|
||||||
|
decay,
|
||||||
|
sustain,
|
||||||
|
release,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
t,
|
||||||
|
holdEnd,
|
||||||
|
fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
|
||||||
|
);
|
||||||
|
modulator.connect(envGain);
|
||||||
|
envGain.connect(node.frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getVibratoOscillator(node.detune, value, t);
|
||||||
|
getPitchEnvelope(node.detune, value, t, t + duration);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
|
||||||
export function registerSynthSounds() {
|
export function registerSynthSounds() {
|
||||||
[...waveforms].forEach((s) => {
|
[...waveforms].forEach((s) => {
|
||||||
registerSound(
|
registerSound(
|
||||||
@ -102,15 +155,18 @@ export function registerSynthSounds() {
|
|||||||
frequency,
|
frequency,
|
||||||
begin,
|
begin,
|
||||||
end,
|
end,
|
||||||
detune: detune * 0.1,
|
freqspread: detune * 0.1,
|
||||||
voices: clamp(unison, 0, 100),
|
voices: clamp(unison, 0, 100),
|
||||||
spread: clamp(spread, 0, 1),
|
panspread: clamp(spread, 0, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
outputChannelCount: [2],
|
outputChannelCount: [2],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
// console.log(node.parameters.get('frequency'));
|
||||||
|
getPitchEnvelope(node.parameters.get('detune'), value, begin, holdend);
|
||||||
|
getVibratoOscillator(node.parameters.get('detune'), value, begin);
|
||||||
|
// applyFM(node.parameters.get('frequency'));
|
||||||
const envGain = gainNode(1);
|
const envGain = gainNode(1);
|
||||||
node = node.connect(envGain);
|
node = node.connect(envGain);
|
||||||
|
|
||||||
|
|||||||
@ -169,16 +169,21 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'spread',
|
name: 'panspread',
|
||||||
defaultValue: 0.4,
|
defaultValue: 0.4,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'detune',
|
name: 'freqspread',
|
||||||
defaultValue: 0.2,
|
defaultValue: 0.2,
|
||||||
min: 0,
|
min: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'detune',
|
||||||
|
defaultValue: 0,
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'voices',
|
name: 'voices',
|
||||||
@ -194,23 +199,25 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
if (currentTime >= params.end[0]) {
|
if (currentTime >= params.end[0]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const frequency = params.frequency[0];
|
let frequency = params.frequency[0];
|
||||||
|
//apply detune in cents
|
||||||
|
frequency = frequency * Math.pow(2, params.detune[0] / 1200);
|
||||||
|
|
||||||
const output = outputs[0];
|
const output = outputs[0];
|
||||||
const voices = params.voices[0];
|
const voices = params.voices[0];
|
||||||
const detune = params.detune[0];
|
const freqspread = params.freqspread[0];
|
||||||
let spread = params.spread[0];
|
let panspread = params.panspread[0];
|
||||||
spread = spread * 0.5 + 0.5;
|
panspread = panspread * 0.5 + 0.5;
|
||||||
const gainAdjustment = 1;
|
const gainAdjustment = 1;
|
||||||
|
|
||||||
for (let n = 0; n < voices; n++) {
|
for (let n = 0; n < voices; n++) {
|
||||||
let adj = 0;
|
let adj = 0;
|
||||||
const isOdd = n % 2 === 1;
|
const isOdd = n % 2 === 1;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
adj = isOdd ? n * detune : -((n - 1) * detune);
|
adj = isOdd ? n * freqspread : -((n - 1) * freqspread);
|
||||||
}
|
}
|
||||||
const freq = Math.min(16744, Math.max(1, frequency + adj * 0.01 * frequency));
|
const freq = Math.min(16744, Math.max(1, frequency + adj * 0.01 * frequency));
|
||||||
const balance = isOdd ? 1 - spread : spread;
|
const balance = isOdd ? 1 - panspread : panspread;
|
||||||
const dt = freq / sampleRate;
|
const dt = freq / sampleRate;
|
||||||
|
|
||||||
for (let i = 0; i < output[0].length; i++) {
|
for (let i = 0; i < output[0].length; i++) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user