implemented modulators
This commit is contained in:
parent
bfcb2070d1
commit
c0b00339c5
3 changed files with 9 additions and 137 deletions
|
|
@ -202,7 +202,7 @@ const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') =>
|
||||||
const modgain = modfreq * modulationIndex;
|
const modgain = modfreq * modulationIndex;
|
||||||
return mod(modfreq, modgain, wave);
|
return mod(modfreq, modgain, wave);
|
||||||
};
|
};
|
||||||
export function applyFM(param, value) {
|
export function applyFM(param, value, begin) {
|
||||||
const {
|
const {
|
||||||
fmh: fmHarmonicity = 1,
|
fmh: fmHarmonicity = 1,
|
||||||
fmi: fmModulationIndex,
|
fmi: fmModulationIndex,
|
||||||
|
|
@ -214,16 +214,15 @@ export function applyFM(param, value) {
|
||||||
fmvelocity: fmVelocity,
|
fmvelocity: fmVelocity,
|
||||||
fmwave: fmWaveform = 'sine',
|
fmwave: fmWaveform = 'sine',
|
||||||
duration,
|
duration,
|
||||||
begin,
|
|
||||||
} = value;
|
} = value;
|
||||||
|
|
||||||
const ac = getAudioContext();
|
|
||||||
if (fmModulationIndex) {
|
if (fmModulationIndex) {
|
||||||
|
const ac = getAudioContext();
|
||||||
const envGain = ac.createGain();
|
const envGain = ac.createGain();
|
||||||
const { node: modulator } = fm(param, fmHarmonicity, fmModulationIndex, fmWaveform);
|
const { node: modulator } = fm(param, fmHarmonicity, fmModulationIndex, fmWaveform);
|
||||||
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
||||||
// no envelope by default
|
// no envelope by default
|
||||||
modulator.connect(param.frequency);
|
modulator.connect(param);
|
||||||
} else {
|
} else {
|
||||||
const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]);
|
const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]);
|
||||||
const holdEnd = begin + duration;
|
const holdEnd = begin + duration;
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,6 @@ import { registerSound, getAudioContext, getWorklet } from './superdough.mjs';
|
||||||
import { applyFM, 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 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 = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => {
|
|
||||||
const carrfreq = osc.frequency.value;
|
|
||||||
const modfreq = carrfreq * harmonicityRatio;
|
|
||||||
const modgain = modfreq * modulationIndex;
|
|
||||||
return mod(modfreq, modgain, wave);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFrequencyFromValue = (value) => {
|
const getFrequencyFromValue = (value) => {
|
||||||
let { note, freq } = value;
|
let { note, freq } = value;
|
||||||
note = note || 36;
|
note = note || 36;
|
||||||
|
|
@ -38,59 +20,6 @@ const getFrequencyFromValue = (value) => {
|
||||||
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
||||||
const noises = ['pink', 'white', 'brown', 'crackle'];
|
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(
|
||||||
|
|
@ -166,7 +95,7 @@ export function registerSynthSounds() {
|
||||||
// console.log(node.parameters.get('frequency'));
|
// console.log(node.parameters.get('frequency'));
|
||||||
getPitchEnvelope(node.parameters.get('detune'), value, begin, holdend);
|
getPitchEnvelope(node.parameters.get('detune'), value, begin, holdend);
|
||||||
getVibratoOscillator(node.parameters.get('detune'), value, begin);
|
getVibratoOscillator(node.parameters.get('detune'), value, begin);
|
||||||
// applyFM(node.parameters.get('frequency'));
|
applyFM(node.parameters.get('frequency'), value, begin);
|
||||||
const envGain = gainNode(1);
|
const envGain = gainNode(1);
|
||||||
node = node.connect(envGain);
|
node = node.connect(envGain);
|
||||||
|
|
||||||
|
|
@ -260,24 +189,7 @@ export function waveformN(partials, type) {
|
||||||
|
|
||||||
// expects one of waveforms as s
|
// expects one of waveforms as s
|
||||||
export function getOscillator(s, t, value) {
|
export function getOscillator(s, t, value) {
|
||||||
let {
|
let { n: partials, duration, noise = 0 } = value;
|
||||||
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,
|
|
||||||
} = value;
|
|
||||||
let ac = getAudioContext();
|
|
||||||
let o;
|
let o;
|
||||||
// If no partials are given, use stock waveforms
|
// If no partials are given, use stock waveforms
|
||||||
if (!partials || s === 'sine') {
|
if (!partials || s === 'sine') {
|
||||||
|
|
@ -288,55 +200,15 @@ export function getOscillator(s, t, value) {
|
||||||
else {
|
else {
|
||||||
o = waveformN(partials, s);
|
o = waveformN(partials, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get frequency from note...
|
|
||||||
note = note || 36;
|
|
||||||
if (typeof note === 'string') {
|
|
||||||
note = noteToMidi(note); // e.g. c3 => 48
|
|
||||||
}
|
|
||||||
// get frequency
|
|
||||||
if (!freq && typeof note === 'number') {
|
|
||||||
freq = midiToFreq(note); // + 48);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set frequency
|
// set frequency
|
||||||
o.frequency.value = Number(freq);
|
o.frequency.value = getFrequencyFromValue(value);
|
||||||
o.start(t);
|
o.start(t);
|
||||||
|
|
||||||
// FM
|
|
||||||
let stopFm;
|
|
||||||
let envGain = ac.createGain();
|
|
||||||
if (fmModulationIndex) {
|
|
||||||
const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform);
|
|
||||||
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
|
||||||
// no envelope by default
|
|
||||||
modulator.connect(o.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(o.frequency);
|
|
||||||
}
|
|
||||||
stopFm = stop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additional oscillator for vibrato effect
|
|
||||||
let vibratoOscillator = getVibratoOscillator(o.detune, value, t);
|
let vibratoOscillator = getVibratoOscillator(o.detune, value, t);
|
||||||
|
|
||||||
// pitch envelope
|
// pitch envelope
|
||||||
getPitchEnvelope(o.detune, value, t, t + duration);
|
getPitchEnvelope(o.detune, value, t, t + duration);
|
||||||
|
applyFM(o.frequency, value, t);
|
||||||
|
|
||||||
let noiseMix;
|
let noiseMix;
|
||||||
if (noise) {
|
if (noise) {
|
||||||
|
|
@ -348,7 +220,7 @@ export function getOscillator(s, t, value) {
|
||||||
stop: (time) => {
|
stop: (time) => {
|
||||||
vibratoOscillator?.stop(time);
|
vibratoOscillator?.stop(time);
|
||||||
noiseMix?.stop(time);
|
noiseMix?.stop(time);
|
||||||
stopFm?.(time);
|
// stopFm?.(time);
|
||||||
o.stop(time);
|
o.stop(time);
|
||||||
},
|
},
|
||||||
triggerRelease: (time) => {
|
triggerRelease: (time) => {
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ class DistortProcessor extends AudioWorkletProcessor {
|
||||||
registerProcessor('distort-processor', DistortProcessor);
|
registerProcessor('distort-processor', DistortProcessor);
|
||||||
|
|
||||||
// adjust waveshape to remove frequencies above nyquist to prevent aliasing
|
// adjust waveshape to remove frequencies above nyquist to prevent aliasing
|
||||||
|
// referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517
|
||||||
const polyBlep = (phase, dt) => {
|
const polyBlep = (phase, dt) => {
|
||||||
// 0 <= phase < 1
|
// 0 <= phase < 1
|
||||||
if (phase < dt) {
|
if (phase < dt) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue