Spectral additive

This commit is contained in:
2025-10-13 13:09:59 +02:00
parent 467558efd2
commit 580aa4b96f
3 changed files with 721 additions and 0 deletions

View File

@ -0,0 +1,404 @@
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
import type { PitchLock } from './base/SynthEngine';
enum VowelType {
A,
E,
I,
O,
U,
AE,
OE,
UE,
}
enum ModulationType {
SimpleFM,
DoubleFM,
RingMod,
CrossFM,
}
interface FormantBand {
frequency: number;
bandwidth: number;
amplitude: number;
}
interface FormantFMParams {
baseFreq: number;
vowel: VowelType;
vowelMorph: number;
modulationType: ModulationType;
modIndex: number;
modRatio: number;
attack: number;
decay: number;
sustain: number;
release: number;
brightness: number;
vibrato: number;
vibratoRate: number;
detune: number;
noise: number;
formantLFORate: number;
formantLFODepth: number;
modIndexLFORate: number;
modIndexLFODepth: number;
chaos: number;
}
export class FormantFM extends CsoundEngine<FormantFMParams> {
getName(): string {
return 'Formant FM';
}
getDescription(): string {
return 'FM synthesis with formant filters creating vowel-like sounds and vocal textures';
}
getType() {
return 'generative' as const;
}
protected getOrchestra(): string {
return `
instr 1
iBaseFreq chnget "baseFreq"
iVowel chnget "vowel"
iVowelMorph chnget "vowelMorph"
iModType chnget "modulationType"
iModIndex chnget "modIndex"
iModRatio chnget "modRatio"
iAttack chnget "attack"
iDecay chnget "decay"
iSustain chnget "sustain"
iRelease chnget "release"
iBrightness chnget "brightness"
iVibrato chnget "vibrato"
iVibratoRate chnget "vibratoRate"
iDetune chnget "detune"
iNoise chnget "noise"
iFormantLFORate chnget "formantLFORate"
iFormantLFODepth chnget "formantLFODepth"
iModIndexLFORate chnget "modIndexLFORate"
iModIndexLFODepth chnget "modIndexLFODepth"
iChaos chnget "chaos"
idur = p3
iAttackTime = iAttack * idur
iDecayTime = iDecay * idur
iReleaseTime = iRelease * idur
; Envelope
kEnv madsr iAttackTime, iDecayTime, iSustain, iReleaseTime
; Vibrato LFO with chaos
kVib oscili iVibrato * iBaseFreq * 0.02, iVibratoRate
kChaosLFO1 oscili iChaos * iBaseFreq * 0.01, iVibratoRate * 1.618
kChaosLFO2 oscili iChaos * iBaseFreq * 0.005, iVibratoRate * 2.414
kFreq = iBaseFreq + kVib + kChaosLFO1 + kChaosLFO2
; Get formant parameters based on vowel type
if iVowel == 0 then
; A (as in "father")
iF1 = 730
iF2 = 1090
iF3 = 2440
iA1 = 1.0
iA2 = 0.5
iA3 = 0.25
iBW1 = 80
iBW2 = 90
iBW3 = 120
elseif iVowel == 1 then
; E (as in "bet")
iF1 = 530
iF2 = 1840
iF3 = 2480
iA1 = 1.0
iA2 = 0.6
iA3 = 0.3
iBW1 = 60
iBW2 = 100
iBW3 = 120
elseif iVowel == 2 then
; I (as in "bit")
iF1 = 390
iF2 = 1990
iF3 = 2550
iA1 = 1.0
iA2 = 0.7
iA3 = 0.2
iBW1 = 50
iBW2 = 100
iBW3 = 120
elseif iVowel == 3 then
; O (as in "boat")
iF1 = 570
iF2 = 840
iF3 = 2410
iA1 = 1.0
iA2 = 0.45
iA3 = 0.28
iBW1 = 70
iBW2 = 80
iBW3 = 100
elseif iVowel == 4 then
; U (as in "boot")
iF1 = 440
iF2 = 1020
iF3 = 2240
iA1 = 1.0
iA2 = 0.4
iA3 = 0.2
iBW1 = 70
iBW2 = 80
iBW3 = 100
elseif iVowel == 5 then
; AE (as in "bat")
iF1 = 660
iF2 = 1720
iF3 = 2410
iA1 = 1.0
iA2 = 0.55
iA3 = 0.3
iBW1 = 80
iBW2 = 90
iBW3 = 120
elseif iVowel == 6 then
; OE (as in "bird")
iF1 = 490
iF2 = 1350
iF3 = 1690
iA1 = 1.0
iA2 = 0.5
iA3 = 0.4
iBW1 = 70
iBW2 = 80
iBW3 = 100
else
; UE (as in "about")
iF1 = 520
iF2 = 1190
iF3 = 2390
iA1 = 1.0
iA2 = 0.45
iA3 = 0.25
iBW1 = 70
iBW2 = 80
iBW3 = 110
endif
; Modulate formant frequencies with multiple LFOs
kFormantShift = 1 + (iVowelMorph - 0.5) * 0.4
kFormantLFO oscili iFormantLFODepth * 0.3, iFormantLFORate
kFormantShiftModulated = kFormantShift + kFormantLFO
kF1 = iF1 * kFormantShiftModulated
kF2 = iF2 * kFormantShiftModulated
kF3 = iF3 * kFormantShiftModulated
; Modulation index LFO
kModIndexLFO oscili iModIndexLFODepth * iModIndex, iModIndexLFORate
kModIndexDynamic = iModIndex + kModIndexLFO
; Generate carrier and modulator based on modulation type
kModFreq = kFreq * iModRatio
if iModType == 0 then
; Simple FM with dynamic modulation
aMod oscili kModIndexDynamic * kModFreq, kModFreq
aCarrier oscili 0.5, kFreq + aMod
elseif iModType == 1 then
; Double FM (modulator modulates itself) with chaos
kChaosModDepth = 1 + (iChaos * 0.5)
aMod1 oscili kModIndexDynamic * 0.3 * kModFreq * kChaosModDepth, kModFreq * 0.5
aMod2 oscili kModIndexDynamic * kModFreq, kModFreq + aMod1
aCarrier oscili 0.5, kFreq + aMod2
elseif iModType == 2 then
; Ring modulation with frequency wobble
kRingModWobble oscili iChaos * 0.2, iFormantLFORate * 0.7
aMod oscili 0.5, kModFreq * (1 + kRingModWobble)
aCarrierTemp oscili 0.5, kFreq
aCarrier = aCarrierTemp * aMod
else
; Cross FM with multiple carriers
aMod oscili kModIndexDynamic * kModFreq, kModFreq
aCarrier1 oscili 0.4, kFreq + aMod
aCarrier2 oscili 0.3, kFreq * 0.5 + aMod * 0.5
kThirdCarrierFreq = kFreq * (1.5 + iChaos * 0.3)
aCarrier3 oscili 0.2 * iChaos, kThirdCarrierFreq + aMod * 0.3
aCarrier = aCarrier1 + aCarrier2 + aCarrier3
endif
; Add brightness via high-frequency content
aCarrierBright oscili 0.15 * iBrightness, kFreq * 2
aCarrierMix = aCarrier + aCarrierBright
; Add subtle noise for breathiness
aNoise noise 0.08 * iNoise, 0
aCarrierFinal = aCarrierMix + aNoise
; Apply formant filters (bandpass filters at formant frequencies)
aFormant1 butterbp aCarrierFinal, kF1, iBW1
aFormant1Scaled = aFormant1 * iA1
aFormant2 butterbp aCarrierFinal, kF2, iBW2
aFormant2Scaled = aFormant2 * iA2
aFormant3 butterbp aCarrierFinal, kF3, iBW3
aFormant3Scaled = aFormant3 * iA3
; Mix formants
aMix = (aFormant1Scaled + aFormant2Scaled + aFormant3Scaled) * 0.6
; Apply envelope
aOut = aMix * kEnv
; Stereo - slightly different phase for right channel
iDetuneFactor = 1 + (iDetune * 0.5)
kFreqR = iBaseFreq * iDetuneFactor + kVib
; Regenerate right channel with detuned frequency
kModFreqR = kFreqR * iModRatio
if iModType == 0 then
aModR oscili iModIndex * kModFreqR, kModFreqR
aCarrierR oscili 0.5, kFreqR + aModR
elseif iModType == 1 then
aMod1R oscili iModIndex * 0.3 * kModFreqR, kModFreqR * 0.5
aMod2R oscili iModIndex * kModFreqR, kModFreqR + aMod1R
aCarrierR oscili 0.5, kFreqR + aMod2R
elseif iModType == 2 then
aModR oscili 0.5, kModFreqR
aCarrierTempR oscili 0.5, kFreqR
aCarrierR = aCarrierTempR * aModR
else
aModR oscili iModIndex * kModFreqR, kModFreqR
aCarrier1R oscili 0.4, kFreqR + aModR
aCarrier2R oscili 0.3, kFreqR * 0.5 + aModR * 0.5
aCarrierR = aCarrier1R + aCarrier2R
endif
aCarrierBrightR oscili 0.15 * iBrightness, kFreqR * 2
aCarrierMixR = aCarrierR + aCarrierBrightR
aNoiseR noise 0.08 * iNoise, 0
aCarrierFinalR = aCarrierMixR + aNoiseR
kF1R = iF1 * kFormantShift
kF2R = iF2 * kFormantShift
kF3R = iF3 * kFormantShift
aFormant1R butterbp aCarrierFinalR, kF1R, iBW1
aFormant1ScaledR = aFormant1R * iA1
aFormant2R butterbp aCarrierFinalR, kF2R, iBW2
aFormant2ScaledR = aFormant2R * iA2
aFormant3R butterbp aCarrierFinalR, kF3R, iBW3
aFormant3ScaledR = aFormant3R * iA3
aMixR = (aFormant1ScaledR + aFormant2ScaledR + aFormant3ScaledR) * 0.6
aOutR = aMixR * kEnv
outs aOut, aOutR
endin
`;
}
protected getParametersForCsound(params: FormantFMParams): CsoundParameter[] {
return [
{ channelName: 'baseFreq', value: params.baseFreq },
{ channelName: 'vowel', value: params.vowel },
{ channelName: 'vowelMorph', value: params.vowelMorph },
{ channelName: 'modulationType', value: params.modulationType },
{ channelName: 'modIndex', value: params.modIndex },
{ channelName: 'modRatio', value: params.modRatio },
{ channelName: 'attack', value: params.attack },
{ channelName: 'decay', value: params.decay },
{ channelName: 'sustain', value: params.sustain },
{ channelName: 'release', value: params.release },
{ channelName: 'brightness', value: params.brightness },
{ channelName: 'vibrato', value: params.vibrato },
{ channelName: 'vibratoRate', value: params.vibratoRate },
{ channelName: 'detune', value: params.detune },
{ channelName: 'noise', value: params.noise },
{ channelName: 'formantLFORate', value: params.formantLFORate },
{ channelName: 'formantLFODepth', value: params.formantLFODepth },
{ channelName: 'modIndexLFORate', value: params.modIndexLFORate },
{ channelName: 'modIndexLFODepth', value: params.modIndexLFODepth },
{ channelName: 'chaos', value: params.chaos },
];
}
randomParams(pitchLock?: PitchLock): FormantFMParams {
const baseFreqChoices = [82.4, 110, 146.8, 220, 293.7, 440];
const baseFreq = pitchLock?.enabled
? pitchLock.frequency
: this.randomChoice(baseFreqChoices) * this.randomRange(0.95, 1.05);
const modulationRatios = [0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 7, 9];
return {
baseFreq,
vowel: this.randomInt(0, 7) as VowelType,
vowelMorph: this.randomRange(0.3, 0.7),
modulationType: this.randomInt(0, 3) as ModulationType,
modIndex: this.randomRange(1, 10),
modRatio: this.randomChoice(modulationRatios),
attack: this.randomRange(0.001, 0.15),
decay: this.randomRange(0.05, 0.25),
sustain: this.randomRange(0.3, 0.8),
release: this.randomRange(0.1, 0.4),
brightness: this.randomRange(0, 0.6),
vibrato: this.randomRange(0, 0.5),
vibratoRate: this.randomRange(3, 8),
detune: this.randomRange(0.001, 0.01),
noise: this.randomRange(0, 0.3),
formantLFORate: this.randomRange(0.2, 6),
formantLFODepth: this.randomRange(0, 0.8),
modIndexLFORate: this.randomRange(0.5, 12),
modIndexLFODepth: this.randomRange(0, 0.7),
chaos: this.randomRange(0, 0.8),
};
}
mutateParams(
params: FormantFMParams,
mutationAmount: number = 0.15,
pitchLock?: PitchLock
): FormantFMParams {
const baseFreq = pitchLock?.enabled ? pitchLock.frequency : params.baseFreq;
const modulationRatios = [0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 7, 9];
return {
baseFreq,
vowel: Math.random() < 0.1 ? (this.randomInt(0, 7) as VowelType) : params.vowel,
vowelMorph: this.mutateValue(params.vowelMorph, mutationAmount, 0, 1),
modulationType:
Math.random() < 0.08
? (this.randomInt(0, 3) as ModulationType)
: params.modulationType,
modIndex: this.mutateValue(params.modIndex, mutationAmount, 0.5, 15),
modRatio:
Math.random() < 0.12
? this.randomChoice(modulationRatios)
: params.modRatio,
attack: this.mutateValue(params.attack, mutationAmount, 0.001, 0.25),
decay: this.mutateValue(params.decay, mutationAmount, 0.02, 0.4),
sustain: this.mutateValue(params.sustain, mutationAmount, 0.1, 0.9),
release: this.mutateValue(params.release, mutationAmount, 0.05, 0.6),
brightness: this.mutateValue(params.brightness, mutationAmount, 0, 1),
vibrato: this.mutateValue(params.vibrato, mutationAmount, 0, 0.6),
vibratoRate: this.mutateValue(params.vibratoRate, mutationAmount, 2, 10),
detune: this.mutateValue(params.detune, mutationAmount, 0.0005, 0.02),
noise: this.mutateValue(params.noise, mutationAmount, 0, 0.5),
formantLFORate: this.mutateValue(params.formantLFORate, mutationAmount, 0.1, 8),
formantLFODepth: this.mutateValue(params.formantLFODepth, mutationAmount, 0, 1),
modIndexLFORate: this.mutateValue(params.modIndexLFORate, mutationAmount, 0.3, 15),
modIndexLFODepth: this.mutateValue(params.modIndexLFODepth, mutationAmount, 0, 1),
chaos: this.mutateValue(params.chaos, mutationAmount, 0, 1),
};
}
}

View File

@ -0,0 +1,313 @@
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
import type { PitchLock } from './base/SynthEngine';
interface MassiveAdditiveParams {
baseFreq: number;
numPartials: number;
spectrumShape: number;
harmonicSpread: number;
inharmonicity: number;
attack: number;
decay: number;
sustain: number;
release: number;
ampLFORate: number;
ampLFODepth: number;
ampLFOPhaseSpread: number;
freqLFORate: number;
freqLFODepth: number;
partialDecayRate: number;
partialAttackSpread: number;
oddEvenBalance: number;
shimmer: number;
shimmerRate: number;
stereoSpread: number;
brightness: number;
chaos: number;
}
export class MassiveAdditive extends CsoundEngine<MassiveAdditiveParams> {
getName(): string {
return 'Spectral Add';
}
getDescription(): string {
return 'Spectral additive synthesis with octave doubling, micro-detuned beating, and evolving harmonic content';
}
getType() {
return 'generative' as const;
}
protected getOrchestra(): string {
return `
; Function tables for sine wave
gisine ftgen 0, 0, 16384, 10, 1
instr 1
iBaseFreq chnget "baseFreq"
iNumPartials chnget "numPartials"
iSpectrumShape chnget "spectrumShape"
iHarmonicSpread chnget "harmonicSpread"
iInharmonicity chnget "inharmonicity"
iAttack chnget "attack"
iDecay chnget "decay"
iSustain chnget "sustain"
iRelease chnget "release"
iAmpLFORate chnget "ampLFORate"
iAmpLFODepth chnget "ampLFODepth"
iAmpLFOPhaseSpread chnget "ampLFOPhaseSpread"
iFreqLFORate chnget "freqLFORate"
iFreqLFODepth chnget "freqLFODepth"
iPartialDecayRate chnget "partialDecayRate"
iPartialAttackSpread chnget "partialAttackSpread"
iOddEvenBalance chnget "oddEvenBalance"
iShimmer chnget "shimmer"
iShimmerRate chnget "shimmerRate"
iStereoSpread chnget "stereoSpread"
iBrightness chnget "brightness"
iChaos chnget "chaos"
idur = p3
iAttackTime = iAttack * idur
iDecayTime = iDecay * idur
iReleaseTime = iRelease * idur
; Main envelope
kEnv madsr iAttackTime, iDecayTime, iSustain, iReleaseTime
; Global modulation LFOs
kGlobalAmpLFO oscili 1, iAmpLFORate * 0.5
kShimmerLFO oscili 1, iShimmerRate
kFreqLFO oscili iFreqLFODepth * 0.02, iFreqLFORate
; Chaos LFOs
kChaosLFO1 oscili 1, iAmpLFORate * 1.618
kChaosLFO2 oscili 1, iAmpLFORate * 2.414
; Create frequency and amplitude tables dynamically
iPartialCount = min(iNumPartials, 64)
; Generate tables for partial frequencies and amplitudes
gifreq ftgen 0, 0, 128, -2, 0
giamp ftgen 0, 0, 128, -2, 0
gifreq2 ftgen 0, 0, 128, -2, 0
giamp2 ftgen 0, 0, 128, -2, 0
iPartialIndex = 0
loop_setup:
iN = iPartialIndex + 1
; Calculate frequency ratio with harmonicity and inharmonicity
iHarmonic = iN * iHarmonicSpread
iInharmonicShift = iInharmonicity * iN * iN * 0.001
iFreqRatio = iHarmonic * (1 + iInharmonicShift)
; Calculate amplitude based on spectrum shape
iAmpFalloff = 1 / pow(iN, 1 + iSpectrumShape * 2)
; Brightness boost for higher partials
iBrightnessBoost = 1 + (iBrightness * (iN / iPartialCount) * 2)
; Odd/even balance
iOddEvenFactor = 1
if (iN % 2) == 0 then
iOddEvenFactor = iOddEvenBalance
else
iOddEvenFactor = 2 - iOddEvenBalance
endif
iAmp = iAmpFalloff * iBrightnessBoost * iOddEvenFactor
; Create alternate partial distribution for morphing
iFreqRatio2 = iFreqRatio * (1 + (iChaos * 0.05 * sin(iN * 0.7)))
iAmp2 = iAmp * (1 - (iN / iPartialCount) * 0.3)
; Write to tables
tableiw iFreqRatio, iPartialIndex, gifreq
tableiw iAmp, iPartialIndex, giamp
tableiw iFreqRatio2, iPartialIndex, gifreq2
tableiw iAmp2, iPartialIndex, giamp2
iPartialIndex = iPartialIndex + 1
if iPartialIndex < iPartialCount goto loop_setup
; Generate additive synthesis with heavy modulation and octave doubling
; Slow modulation LFOs for spectral evolution
kLFO1 oscili 1, iAmpLFORate * 0.53
kLFO2 oscili 1, iAmpLFORate * 0.89
kLFO3 oscili 1, iAmpLFORate * 1.37
kShimmerLFO1 oscili 1, iShimmerRate * 0.67
kShimmerLFO2 oscili 1, iShimmerRate * 1.13
; Very slow spectral morphing
kSpectralMorph oscili 1, iAmpLFORate * 0.19
kMorphAmount = (kSpectralMorph + 1) * 0.5
; Minimal frequency wobble for organic feel
kFreqWobble = kFreqLFO * 0.5
; Micro-detuning for beating (0.1 to 0.5 Hz beating)
iMicroDetune = 0.0003 + (iChaos * 0.0005)
; === FUNDAMENTAL OCTAVE ===
; Main voice at fundamental frequency
kcps = iBaseFreq * (1 + kFreqWobble)
kAmpMain = kEnv * (0.6 + kLFO1 * iAmpLFODepth * 0.2)
aMainVoice adsynt2 kAmpMain, kcps, gisine, gifreq, giamp, iPartialCount
; Slightly detuned fundamental for beating
kcpsDetune = iBaseFreq * (1 + iMicroDetune) * (1 + kFreqWobble * 0.93)
kAmpDetune = kEnv * (0.5 + kLFO2 * iAmpLFODepth * 0.25)
aDetuneVoice adsynt2 kAmpDetune, kcpsDetune, gisine, gifreq, giamp, iPartialCount, 0.25
; Morphing spectral variant
kAmpMorph = kEnv * kMorphAmount * (0.4 + kShimmerLFO1 * iShimmer * 0.3)
aMorphVoice adsynt2 kAmpMorph, kcps, gisine, gifreq2, giamp2, iPartialCount, 0.5
; === OCTAVE UP ===
; One octave higher with micro-detune for beating
kcpsOctUp = (iBaseFreq * 2) * (1 + kFreqWobble * 1.07)
kAmpOctUp = kEnv * (0.35 + kLFO3 * iAmpLFODepth * 0.2 + kShimmerLFO2 * iShimmer * 0.25)
aOctaveUp adsynt2 kAmpOctUp, kcpsOctUp, gisine, gifreq, giamp, iPartialCount, 0.33
; Detuned octave up for complex beating
kcpsOctUpDetune = (iBaseFreq * 2) * (1 - iMicroDetune * 0.8) * (1 + kFreqWobble * 1.11)
kAmpOctUpDetune = kEnv * (0.3 + kLFO1 * iAmpLFODepth * 0.25)
aOctaveUpDetune adsynt2 kAmpOctUpDetune, kcpsOctUpDetune, gisine, gifreq, giamp, iPartialCount, 0.67
; === OCTAVE DOWN ===
; One octave lower with micro-detune
kcpsOctDown = (iBaseFreq * 0.5) * (1 + kFreqWobble * 0.97)
kAmpOctDown = kEnv * (0.4 + kShimmerLFO1 * iShimmer * 0.3)
aOctaveDown adsynt2 kAmpOctDown, kcpsOctDown, gisine, gifreq, giamp, iPartialCount, 0.17
; Detuned octave down for sub-harmonic beating
kcpsOctDownDetune = (iBaseFreq * 0.5) * (1 + iMicroDetune * 1.2) * (1 + kFreqWobble * 1.03)
kAmpOctDownDetune = kEnv * (0.35 + kLFO2 * iAmpLFODepth * 0.2)
aOctaveDownDetune adsynt2 kAmpOctDownDetune, kcpsOctDownDetune, gisine, gifreq2, giamp, iPartialCount, 0.83
; === STEREO MIXING ===
; Left channel: emphasize fundamental and octave down
aOutL = aMainVoice * 0.7 + aDetuneVoice * 0.6 + aMorphVoice * 0.5
aOutL = aOutL + aOctaveUp * 0.4 + aOctaveUpDetune * 0.35
aOutL = aOutL + aOctaveDown * 0.55 + aOctaveDownDetune * 0.45
; Right channel: emphasize octaves with different balance
aOutR = aMainVoice * 0.65 + aDetuneVoice * 0.55 + aMorphVoice * 0.6
aOutR = aOutR + aOctaveUp * 0.5 + aOctaveUpDetune * 0.4
aOutR = aOutR + aOctaveDown * 0.45 + aOctaveDownDetune * 0.5
; Subtle stereo width from chaos
kStereoMod = kChaosLFO1 * iChaos * 0.1
aOutL = aOutL * (1 - kStereoMod * iStereoSpread)
aOutR = aOutR * (1 + kStereoMod * iStereoSpread)
; Normalize to prevent clipping
aOutL = aOutL * 0.28
aOutR = aOutR * 0.28
outs aOutL, aOutR
endin
`;
}
protected getParametersForCsound(params: MassiveAdditiveParams): CsoundParameter[] {
return [
{ channelName: 'baseFreq', value: params.baseFreq },
{ channelName: 'numPartials', value: params.numPartials },
{ channelName: 'spectrumShape', value: params.spectrumShape },
{ channelName: 'harmonicSpread', value: params.harmonicSpread },
{ channelName: 'inharmonicity', value: params.inharmonicity },
{ channelName: 'attack', value: params.attack },
{ channelName: 'decay', value: params.decay },
{ channelName: 'sustain', value: params.sustain },
{ channelName: 'release', value: params.release },
{ channelName: 'ampLFORate', value: params.ampLFORate },
{ channelName: 'ampLFODepth', value: params.ampLFODepth },
{ channelName: 'ampLFOPhaseSpread', value: params.ampLFOPhaseSpread },
{ channelName: 'freqLFORate', value: params.freqLFORate },
{ channelName: 'freqLFODepth', value: params.freqLFODepth },
{ channelName: 'partialDecayRate', value: params.partialDecayRate },
{ channelName: 'partialAttackSpread', value: params.partialAttackSpread },
{ channelName: 'oddEvenBalance', value: params.oddEvenBalance },
{ channelName: 'shimmer', value: params.shimmer },
{ channelName: 'shimmerRate', value: params.shimmerRate },
{ channelName: 'stereoSpread', value: params.stereoSpread },
{ channelName: 'brightness', value: params.brightness },
{ channelName: 'chaos', value: params.chaos },
];
}
randomParams(pitchLock?: PitchLock): MassiveAdditiveParams {
const baseFreqChoices = [55, 82.4, 110, 146.8, 220, 293.7, 440];
const baseFreq = pitchLock?.enabled
? pitchLock.frequency
: this.randomChoice(baseFreqChoices) * this.randomRange(0.98, 1.02);
return {
baseFreq,
numPartials: this.randomInt(32, 64),
spectrumShape: this.randomRange(0.3, 0.7),
harmonicSpread: this.randomChoice([1, 1.5, 2, 3]),
inharmonicity: this.randomRange(0, 0.5),
attack: this.randomRange(0.05, 0.3),
decay: this.randomRange(0.15, 0.5),
sustain: this.randomRange(0.5, 0.9),
release: this.randomRange(0.2, 0.6),
ampLFORate: this.randomRange(0.2, 2),
ampLFODepth: this.randomRange(0.3, 0.8),
ampLFOPhaseSpread: this.randomRange(0.5, 1),
freqLFORate: this.randomRange(0.1, 1.5),
freqLFODepth: this.randomRange(0.05, 0.3),
partialDecayRate: this.randomRange(0.3, 0.8),
partialAttackSpread: this.randomRange(0.1, 0.5),
oddEvenBalance: this.randomRange(0.5, 1.5),
shimmer: this.randomRange(0.3, 0.9),
shimmerRate: this.randomRange(0.05, 0.8),
stereoSpread: this.randomRange(0.4, 1),
brightness: this.randomRange(0.3, 0.9),
chaos: this.randomRange(0.1, 0.7),
};
}
mutateParams(
params: MassiveAdditiveParams,
mutationAmount: number = 0.15,
pitchLock?: PitchLock
): MassiveAdditiveParams {
const baseFreq = pitchLock?.enabled ? pitchLock.frequency : params.baseFreq;
return {
baseFreq,
numPartials:
Math.random() < 0.15
? this.randomInt(16, 64)
: Math.round(this.mutateValue(params.numPartials, mutationAmount, 16, 64)),
spectrumShape: this.mutateValue(params.spectrumShape, mutationAmount, 0, 1),
harmonicSpread:
Math.random() < 0.1
? this.randomChoice([0.5, 1, 1.5, 2])
: params.harmonicSpread,
inharmonicity: this.mutateValue(params.inharmonicity, mutationAmount, 0, 0.5),
attack: this.mutateValue(params.attack, mutationAmount, 0.001, 0.4),
decay: this.mutateValue(params.decay, mutationAmount, 0.05, 0.6),
sustain: this.mutateValue(params.sustain, mutationAmount, 0.2, 0.9),
release: this.mutateValue(params.release, mutationAmount, 0.05, 0.8),
ampLFORate: this.mutateValue(params.ampLFORate, mutationAmount, 0.1, 6),
ampLFODepth: this.mutateValue(params.ampLFODepth, mutationAmount, 0, 1),
ampLFOPhaseSpread: this.mutateValue(params.ampLFOPhaseSpread, mutationAmount, 0, 1),
freqLFORate: this.mutateValue(params.freqLFORate, mutationAmount, 0.1, 5),
freqLFODepth: this.mutateValue(params.freqLFODepth, mutationAmount, 0, 0.8),
partialDecayRate: this.mutateValue(params.partialDecayRate, mutationAmount, 0, 1),
partialAttackSpread: this.mutateValue(params.partialAttackSpread, mutationAmount, 0, 0.8),
oddEvenBalance: this.mutateValue(params.oddEvenBalance, mutationAmount, 0.2, 1.8),
shimmer: this.mutateValue(params.shimmer, mutationAmount, 0, 1),
shimmerRate: this.mutateValue(params.shimmerRate, mutationAmount, 0.05, 2),
stereoSpread: this.mutateValue(params.stereoSpread, mutationAmount, 0, 1),
brightness: this.mutateValue(params.brightness, mutationAmount, 0, 1),
chaos: this.mutateValue(params.chaos, mutationAmount, 0, 0.8),
};
}
}

View File

@ -2,6 +2,7 @@ import type { SynthEngine } from './base/SynthEngine';
import { FourOpFM } from './FourOpFM';
import { TwoOpFM } from './TwoOpFM';
import { PhaseDistortionFM } from './PhaseDistortionFM';
import { FormantFM } from './FormantFM';
import { DubSiren } from './DubSiren';
import { Benjolin } from './Benjolin';
import { ZzfxEngine } from './ZzfxEngine';
@ -17,6 +18,7 @@ import { HiHat } from './HiHat';
import { ParticleNoise } from './ParticleNoise';
import { DustNoise } from './DustNoise';
import { SubtractiveThreeOsc } from './SubtractiveThreeOsc';
import { MassiveAdditive } from './MassiveAdditive';
export const engines: SynthEngine[] = [
new Sample(),
@ -24,6 +26,7 @@ export const engines: SynthEngine[] = [
new FourOpFM(),
new TwoOpFM(),
new PhaseDistortionFM(),
new FormantFM(),
new DubSiren(),
new Benjolin(),
new ZzfxEngine(),
@ -37,4 +40,5 @@ export const engines: SynthEngine[] = [
new ParticleNoise(),
new DustNoise(),
new SubtractiveThreeOsc(),
new MassiveAdditive(),
];