Quite a big update
This commit is contained in:
135
src/lib/audio/engines/Sub8.ts
Normal file
135
src/lib/audio/engines/Sub8.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
|
||||
import type { PitchLock } from './base/SynthEngine';
|
||||
|
||||
interface Sub8Params {
|
||||
frequency: number;
|
||||
filterCutoff: number;
|
||||
filterEnvAmount: number;
|
||||
resonance: number;
|
||||
saturation: number;
|
||||
osc1Level: number;
|
||||
osc2Level: number;
|
||||
osc3Level: number;
|
||||
attack: number;
|
||||
decay: number;
|
||||
sustain: number;
|
||||
release: number;
|
||||
}
|
||||
|
||||
export class Sub8 extends CsoundEngine<Sub8Params> {
|
||||
getName(): string {
|
||||
return 'Sub8';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Multi-oscillator subtractive synth with diode ladder filter and saturation';
|
||||
}
|
||||
|
||||
getType(): 'generative' | 'sample' | 'input' {
|
||||
return 'generative';
|
||||
}
|
||||
|
||||
getCategory(): 'Additive' | 'Subtractive' | 'FM' | 'Percussion' | 'Noise' | 'Physical' | 'Modulation' | 'Experimental' | 'Utility' {
|
||||
return 'Subtractive';
|
||||
}
|
||||
|
||||
getOrchestra(): string {
|
||||
return `
|
||||
instr 1
|
||||
ifreq = chnget("frequency")
|
||||
iamp = 0.5
|
||||
ifilterCutoff = chnget("filterCutoff")
|
||||
ifilterEnvAmount = chnget("filterEnvAmount")
|
||||
ireso = chnget("resonance")
|
||||
isat = chnget("saturation")
|
||||
iosc1Level = chnget("osc1Level")
|
||||
iosc2Level = chnget("osc2Level")
|
||||
iosc3Level = chnget("osc3Level")
|
||||
iatt = chnget("attack") * p3
|
||||
idec = chnget("decay") * p3
|
||||
isus = chnget("sustain")
|
||||
irel = chnget("release") * p3
|
||||
|
||||
; Three detuned oscillators
|
||||
asig1 = vco2(iamp * iosc1Level, ifreq, 10)
|
||||
asig2 = vco2(iamp * iosc2Level, ifreq * 2, 10)
|
||||
asig3 = vco2(iamp * iosc3Level, ifreq * 3.5, 12)
|
||||
asig = asig1 + asig2 + asig3
|
||||
|
||||
; Saturation
|
||||
asig = tanh(asig * isat) / tanh(isat)
|
||||
|
||||
; Filter envelope
|
||||
aenv = madsr(iatt, idec, isus, irel)
|
||||
kcutoff = ifilterCutoff + (aenv * ifilterEnvAmount)
|
||||
kcutoff = limit(kcutoff, 20, 18000)
|
||||
|
||||
; Diode ladder filter
|
||||
asig = diode_ladder(asig, kcutoff, ireso)
|
||||
|
||||
; Final amplitude envelope
|
||||
asig = asig * aenv * 0.7
|
||||
|
||||
outs asig, asig
|
||||
endin
|
||||
`;
|
||||
}
|
||||
|
||||
getParametersForCsound(params: Sub8Params): CsoundParameter[] {
|
||||
return [
|
||||
{ channelName: 'frequency', value: params.frequency },
|
||||
{ channelName: 'filterCutoff', value: params.filterCutoff },
|
||||
{ channelName: 'filterEnvAmount', value: params.filterEnvAmount },
|
||||
{ channelName: 'resonance', value: params.resonance },
|
||||
{ channelName: 'saturation', value: params.saturation },
|
||||
{ channelName: 'osc1Level', value: params.osc1Level },
|
||||
{ channelName: 'osc2Level', value: params.osc2Level },
|
||||
{ channelName: 'osc3Level', value: params.osc3Level },
|
||||
{ channelName: 'attack', value: params.attack },
|
||||
{ channelName: 'decay', value: params.decay },
|
||||
{ channelName: 'sustain', value: params.sustain },
|
||||
{ channelName: 'release', value: params.release }
|
||||
];
|
||||
}
|
||||
|
||||
randomParams(pitchLock?: PitchLock): Sub8Params {
|
||||
const frequency = pitchLock?.enabled ? pitchLock.frequency : 55 * Math.pow(2, Math.random() * 5);
|
||||
|
||||
return {
|
||||
frequency,
|
||||
filterCutoff: 200 + Math.random() * 3800,
|
||||
filterEnvAmount: Math.random() * 6000,
|
||||
resonance: 0.5 + Math.random() * 14.5,
|
||||
saturation: 1 + Math.random() * 9,
|
||||
osc1Level: 0.5 + Math.random() * 0.5,
|
||||
osc2Level: Math.random() * 0.5,
|
||||
osc3Level: Math.random() * 0.3,
|
||||
attack: Math.random() * 0.1,
|
||||
decay: 0.05 + Math.random() * 0.3,
|
||||
sustain: 0.2 + Math.random() * 0.6,
|
||||
release: 0.05 + Math.random() * 0.4
|
||||
};
|
||||
}
|
||||
|
||||
mutateParams(params: Sub8Params, mutationAmount = 0.2, pitchLock?: PitchLock): Sub8Params {
|
||||
const mutate = (value: number, min: number, max: number) => {
|
||||
const change = (Math.random() - 0.5) * 2 * mutationAmount * (max - min);
|
||||
return Math.max(min, Math.min(max, value + change));
|
||||
};
|
||||
|
||||
return {
|
||||
frequency: pitchLock?.enabled ? pitchLock.frequency : mutate(params.frequency, 55, 55 * Math.pow(2, 5)),
|
||||
filterCutoff: mutate(params.filterCutoff, 200, 4000),
|
||||
filterEnvAmount: mutate(params.filterEnvAmount, 0, 6000),
|
||||
resonance: mutate(params.resonance, 0.5, 15),
|
||||
saturation: mutate(params.saturation, 1, 10),
|
||||
osc1Level: mutate(params.osc1Level, 0.5, 1),
|
||||
osc2Level: mutate(params.osc2Level, 0, 0.5),
|
||||
osc3Level: mutate(params.osc3Level, 0, 0.3),
|
||||
attack: mutate(params.attack, 0, 0.1),
|
||||
decay: mutate(params.decay, 0.05, 0.35),
|
||||
sustain: mutate(params.sustain, 0.2, 0.8),
|
||||
release: mutate(params.release, 0.05, 0.45)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user