new engine

This commit is contained in:
2025-10-12 23:39:14 +02:00
parent d118d3a52b
commit c1f7cc02fd

View File

@ -14,6 +14,10 @@ enum PDAlgorithm {
Dual, // Two oscillators, different waveforms
Detune, // Two slightly detuned oscillators
Octave, // Oscillator + octave up
Fifth, // Base + fifth up (1.5x frequency)
Sub, // Base + octave down
Stack, // Base + fifth + octave
Wide, // Heavily detuned oscillators
}
enum LFOWaveform {
@ -30,6 +34,7 @@ interface PDOscillatorParams {
level: number;
detune: number; // in cents
dcw: number; // Digitally Controlled Waveshaping (0-1, controls brightness)
pulseWidth: number; // 0-1, controls pulse wave width
attack: number;
decay: number;
sustain: number;
@ -227,48 +232,104 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
switch (algorithm) {
case PDAlgorithm.Single: {
// Single oscillator
const outL = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0])
const outL = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const outR = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0])
const outR = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
return [outL, outR];
}
case PDAlgorithm.Dual: {
// Two oscillators with different waveforms
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0])
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0])
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1], oscillators[1].waveform, dcws[1])
const osc2L = this.generatePDWaveform(phasesL[1], oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1], oscillators[1].waveform, dcws[1])
const osc2R = this.generatePDWaveform(phasesR[1], oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
}
case PDAlgorithm.Detune: {
// Two slightly detuned oscillators (chorus effect)
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0])
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0])
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1], oscillators[0].waveform, dcws[0])
const osc2L = this.generatePDWaveform(phasesL[1], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1], oscillators[0].waveform, dcws[0])
const osc2R = this.generatePDWaveform(phasesR[1], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[1].level;
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
}
case PDAlgorithm.Octave: {
// Oscillator + octave up
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0])
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0])
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1] * 2, oscillators[1].waveform, dcws[1])
const osc2L = this.generatePDWaveform(phasesL[1] * 2, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1] * 2, oscillators[1].waveform, dcws[1])
const osc2R = this.generatePDWaveform(phasesR[1] * 2, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
}
case PDAlgorithm.Fifth: {
// Base + fifth up (1.5x frequency)
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1] * 1.5, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1] * 1.5, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
}
case PDAlgorithm.Sub: {
// Base + octave down
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1] * 0.5, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1] * 0.5, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
}
case PDAlgorithm.Stack: {
// Base + fifth + octave - three oscillators!
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1] * 1.5, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1] * 1.5, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc3L = this.generatePDWaveform(phasesL[1] * 2, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level * 0.7;
const osc3R = this.generatePDWaveform(phasesR[1] * 2, oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level * 0.7;
return [(osc1L + osc2L + osc3L) * 0.577, (osc1R + osc2R + osc3R) * 0.577];
}
case PDAlgorithm.Wide: {
// Heavily detuned oscillators for super wide chorus
const osc1L = this.generatePDWaveform(phasesL[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc1R = this.generatePDWaveform(phasesR[0], oscillators[0].waveform, dcws[0], oscillators[0].pulseWidth)
* envelopes[0] * oscillators[0].level;
const osc2L = this.generatePDWaveform(phasesL[1], oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
const osc2R = this.generatePDWaveform(phasesR[1], oscillators[1].waveform, dcws[1], oscillators[1].pulseWidth)
* envelopes[1] * oscillators[1].level;
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
}
@ -278,7 +339,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
}
}
private generatePDWaveform(phase: number, waveform: PDWaveform, dcw: number): number {
private generatePDWaveform(phase: number, waveform: PDWaveform, dcw: number, pulseWidth: number): number {
const TAU = Math.PI * 2;
let normalizedPhase = phase / TAU;
normalizedPhase = normalizedPhase - Math.floor(normalizedPhase);
@ -297,7 +358,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
return 1 - distortedPhase * 2;
case PDWaveform.Pulse:
return distortedPhase < 0.5 ? 1 : -1;
return distortedPhase < pulseWidth ? 1 : -1;
case PDWaveform.DoubleSine:
return Math.sin(distortedPhase * TAU * 2);
@ -417,7 +478,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
}
randomParams(pitchLock?: PitchLock): PhaseDistortionFMParams {
const algorithm = this.randomInt(0, 3) as PDAlgorithm;
const algorithm = this.randomInt(0, 7) as PDAlgorithm;
let baseFreq: number;
if (pitchLock?.enabled) {
@ -450,6 +511,8 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
let detune = 0;
if (algorithm === PDAlgorithm.Detune) {
detune = this.randomRange(-10, 10);
} else if (algorithm === PDAlgorithm.Wide) {
detune = this.randomRange(-30, 30);
}
return {
@ -457,6 +520,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
level: this.randomRange(0.5, 0.9),
detune,
dcw: this.randomRange(0.2, 0.8),
pulseWidth: this.randomRange(0.2, 0.8),
attack: this.randomRange(0.001, 0.1),
decay: this.randomRange(0.02, 0.2),
sustain: this.randomRange(0.3, 0.8),
@ -473,7 +537,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
return {
baseFreq,
algorithm: Math.random() < 0.1 ? this.randomInt(0, 3) as PDAlgorithm : params.algorithm,
algorithm: Math.random() < 0.1 ? this.randomInt(0, 7) as PDAlgorithm : params.algorithm,
oscillators: params.oscillators.map(osc =>
this.mutateOscillator(osc, mutationAmount)
) as [PDOscillatorParams, PDOscillatorParams],
@ -493,6 +557,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
level: this.mutateValue(osc.level, amount, 0.3, 1.0),
detune: this.mutateValue(osc.detune, amount, -20, 20),
dcw: this.mutateValue(osc.dcw, amount, 0, 1),
pulseWidth: this.mutateValue(osc.pulseWidth, amount, 0.1, 0.9),
attack: this.mutateValue(osc.attack, amount, 0.001, 0.2),
decay: this.mutateValue(osc.decay, amount, 0.01, 0.3),
sustain: this.mutateValue(osc.sustain, amount, 0.1, 0.95),