new engine
This commit is contained in:
@ -14,6 +14,10 @@ enum PDAlgorithm {
|
|||||||
Dual, // Two oscillators, different waveforms
|
Dual, // Two oscillators, different waveforms
|
||||||
Detune, // Two slightly detuned oscillators
|
Detune, // Two slightly detuned oscillators
|
||||||
Octave, // Oscillator + octave up
|
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 {
|
enum LFOWaveform {
|
||||||
@ -30,6 +34,7 @@ interface PDOscillatorParams {
|
|||||||
level: number;
|
level: number;
|
||||||
detune: number; // in cents
|
detune: number; // in cents
|
||||||
dcw: number; // Digitally Controlled Waveshaping (0-1, controls brightness)
|
dcw: number; // Digitally Controlled Waveshaping (0-1, controls brightness)
|
||||||
|
pulseWidth: number; // 0-1, controls pulse wave width
|
||||||
attack: number;
|
attack: number;
|
||||||
decay: number;
|
decay: number;
|
||||||
sustain: number;
|
sustain: number;
|
||||||
@ -227,48 +232,104 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
switch (algorithm) {
|
switch (algorithm) {
|
||||||
case PDAlgorithm.Single: {
|
case PDAlgorithm.Single: {
|
||||||
// Single oscillator
|
// 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;
|
* 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;
|
* envelopes[0] * oscillators[0].level;
|
||||||
return [outL, outR];
|
return [outL, outR];
|
||||||
}
|
}
|
||||||
|
|
||||||
case PDAlgorithm.Dual: {
|
case PDAlgorithm.Dual: {
|
||||||
// Two oscillators with different waveforms
|
// 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;
|
* 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;
|
* 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;
|
* 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;
|
* envelopes[1] * oscillators[1].level;
|
||||||
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
|
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
|
||||||
}
|
}
|
||||||
|
|
||||||
case PDAlgorithm.Detune: {
|
case PDAlgorithm.Detune: {
|
||||||
// Two slightly detuned oscillators (chorus effect)
|
// 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;
|
* 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;
|
* 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;
|
* 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;
|
* envelopes[0] * oscillators[1].level;
|
||||||
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
|
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
|
||||||
}
|
}
|
||||||
|
|
||||||
case PDAlgorithm.Octave: {
|
case PDAlgorithm.Octave: {
|
||||||
// Oscillator + octave up
|
// 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;
|
* 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;
|
* 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;
|
* 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;
|
* envelopes[1] * oscillators[1].level;
|
||||||
return [(osc1L + osc2L) * 0.707, (osc1R + osc2R) * 0.707];
|
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;
|
const TAU = Math.PI * 2;
|
||||||
let normalizedPhase = phase / TAU;
|
let normalizedPhase = phase / TAU;
|
||||||
normalizedPhase = normalizedPhase - Math.floor(normalizedPhase);
|
normalizedPhase = normalizedPhase - Math.floor(normalizedPhase);
|
||||||
@ -297,7 +358,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
return 1 - distortedPhase * 2;
|
return 1 - distortedPhase * 2;
|
||||||
|
|
||||||
case PDWaveform.Pulse:
|
case PDWaveform.Pulse:
|
||||||
return distortedPhase < 0.5 ? 1 : -1;
|
return distortedPhase < pulseWidth ? 1 : -1;
|
||||||
|
|
||||||
case PDWaveform.DoubleSine:
|
case PDWaveform.DoubleSine:
|
||||||
return Math.sin(distortedPhase * TAU * 2);
|
return Math.sin(distortedPhase * TAU * 2);
|
||||||
@ -417,7 +478,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
randomParams(pitchLock?: PitchLock): PhaseDistortionFMParams {
|
randomParams(pitchLock?: PitchLock): PhaseDistortionFMParams {
|
||||||
const algorithm = this.randomInt(0, 3) as PDAlgorithm;
|
const algorithm = this.randomInt(0, 7) as PDAlgorithm;
|
||||||
|
|
||||||
let baseFreq: number;
|
let baseFreq: number;
|
||||||
if (pitchLock?.enabled) {
|
if (pitchLock?.enabled) {
|
||||||
@ -450,6 +511,8 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
let detune = 0;
|
let detune = 0;
|
||||||
if (algorithm === PDAlgorithm.Detune) {
|
if (algorithm === PDAlgorithm.Detune) {
|
||||||
detune = this.randomRange(-10, 10);
|
detune = this.randomRange(-10, 10);
|
||||||
|
} else if (algorithm === PDAlgorithm.Wide) {
|
||||||
|
detune = this.randomRange(-30, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -457,6 +520,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
level: this.randomRange(0.5, 0.9),
|
level: this.randomRange(0.5, 0.9),
|
||||||
detune,
|
detune,
|
||||||
dcw: this.randomRange(0.2, 0.8),
|
dcw: this.randomRange(0.2, 0.8),
|
||||||
|
pulseWidth: this.randomRange(0.2, 0.8),
|
||||||
attack: this.randomRange(0.001, 0.1),
|
attack: this.randomRange(0.001, 0.1),
|
||||||
decay: this.randomRange(0.02, 0.2),
|
decay: this.randomRange(0.02, 0.2),
|
||||||
sustain: this.randomRange(0.3, 0.8),
|
sustain: this.randomRange(0.3, 0.8),
|
||||||
@ -473,7 +537,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
baseFreq,
|
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 =>
|
oscillators: params.oscillators.map(osc =>
|
||||||
this.mutateOscillator(osc, mutationAmount)
|
this.mutateOscillator(osc, mutationAmount)
|
||||||
) as [PDOscillatorParams, PDOscillatorParams],
|
) as [PDOscillatorParams, PDOscillatorParams],
|
||||||
@ -493,6 +557,7 @@ export class PhaseDistortionFM implements SynthEngine<PhaseDistortionFMParams> {
|
|||||||
level: this.mutateValue(osc.level, amount, 0.3, 1.0),
|
level: this.mutateValue(osc.level, amount, 0.3, 1.0),
|
||||||
detune: this.mutateValue(osc.detune, amount, -20, 20),
|
detune: this.mutateValue(osc.detune, amount, -20, 20),
|
||||||
dcw: this.mutateValue(osc.dcw, amount, 0, 1),
|
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),
|
attack: this.mutateValue(osc.attack, amount, 0.001, 0.2),
|
||||||
decay: this.mutateValue(osc.decay, amount, 0.01, 0.3),
|
decay: this.mutateValue(osc.decay, amount, 0.01, 0.3),
|
||||||
sustain: this.mutateValue(osc.sustain, amount, 0.1, 0.95),
|
sustain: this.mutateValue(osc.sustain, amount, 0.1, 0.95),
|
||||||
|
|||||||
Reference in New Issue
Block a user