new engine
This commit is contained in:
@ -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),
|
||||
|
||||
Reference in New Issue
Block a user