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 { 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(),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user