Spectral additive
This commit is contained in:
404
src/lib/audio/engines/FormantFM.ts
Normal file
404
src/lib/audio/engines/FormantFM.ts
Normal 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
313
src/lib/audio/engines/MassiveAdditive.ts
Normal file
313
src/lib/audio/engines/MassiveAdditive.ts
Normal 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import type { SynthEngine } from './base/SynthEngine';
|
|||||||
import { FourOpFM } from './FourOpFM';
|
import { FourOpFM } from './FourOpFM';
|
||||||
import { TwoOpFM } from './TwoOpFM';
|
import { TwoOpFM } from './TwoOpFM';
|
||||||
import { PhaseDistortionFM } from './PhaseDistortionFM';
|
import { PhaseDistortionFM } from './PhaseDistortionFM';
|
||||||
|
import { FormantFM } from './FormantFM';
|
||||||
import { DubSiren } from './DubSiren';
|
import { DubSiren } from './DubSiren';
|
||||||
import { Benjolin } from './Benjolin';
|
import { Benjolin } from './Benjolin';
|
||||||
import { ZzfxEngine } from './ZzfxEngine';
|
import { ZzfxEngine } from './ZzfxEngine';
|
||||||
@ -17,6 +18,7 @@ import { HiHat } from './HiHat';
|
|||||||
import { ParticleNoise } from './ParticleNoise';
|
import { ParticleNoise } from './ParticleNoise';
|
||||||
import { DustNoise } from './DustNoise';
|
import { DustNoise } from './DustNoise';
|
||||||
import { SubtractiveThreeOsc } from './SubtractiveThreeOsc';
|
import { SubtractiveThreeOsc } from './SubtractiveThreeOsc';
|
||||||
|
import { MassiveAdditive } from './MassiveAdditive';
|
||||||
|
|
||||||
export const engines: SynthEngine[] = [
|
export const engines: SynthEngine[] = [
|
||||||
new Sample(),
|
new Sample(),
|
||||||
@ -24,6 +26,7 @@ export const engines: SynthEngine[] = [
|
|||||||
new FourOpFM(),
|
new FourOpFM(),
|
||||||
new TwoOpFM(),
|
new TwoOpFM(),
|
||||||
new PhaseDistortionFM(),
|
new PhaseDistortionFM(),
|
||||||
|
new FormantFM(),
|
||||||
new DubSiren(),
|
new DubSiren(),
|
||||||
new Benjolin(),
|
new Benjolin(),
|
||||||
new ZzfxEngine(),
|
new ZzfxEngine(),
|
||||||
@ -37,4 +40,5 @@ export const engines: SynthEngine[] = [
|
|||||||
new ParticleNoise(),
|
new ParticleNoise(),
|
||||||
new DustNoise(),
|
new DustNoise(),
|
||||||
new SubtractiveThreeOsc(),
|
new SubtractiveThreeOsc(),
|
||||||
|
new MassiveAdditive(),
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user