Quite a big update
This commit is contained in:
334
src/lib/audio/engines/Dripwater.ts
Normal file
334
src/lib/audio/engines/Dripwater.ts
Normal file
@ -0,0 +1,334 @@
|
||||
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
|
||||
import type { PitchLock } from './base/SynthEngine';
|
||||
|
||||
interface DripwaterParams {
|
||||
baseFreq: number;
|
||||
dropRate: number;
|
||||
numElements: number;
|
||||
damping: number;
|
||||
shakeIntensity: number;
|
||||
freqRatio1: number;
|
||||
freqRatio2: number;
|
||||
freqRatio3: number;
|
||||
attack: number;
|
||||
release: number;
|
||||
amplitude: number;
|
||||
delayTime1: number;
|
||||
delayTime2: number;
|
||||
delayTime3: number;
|
||||
delayTime4: number;
|
||||
delayGain1: number;
|
||||
delayGain2: number;
|
||||
delayGain3: number;
|
||||
delayGain4: number;
|
||||
feedbackDelayTime: number;
|
||||
feedbackAmount: number;
|
||||
modDepth: number;
|
||||
modRate: number;
|
||||
stereoSpread: number;
|
||||
combDelayTime: number;
|
||||
combFeedback: number;
|
||||
reverseDelayTime: number;
|
||||
reverseGain: number;
|
||||
cascadeDelay1: number;
|
||||
cascadeDelay2: number;
|
||||
cascadeFeedback: number;
|
||||
}
|
||||
|
||||
export class Dripwater extends CsoundEngine<DripwaterParams> {
|
||||
getName(): string {
|
||||
return 'Weird Waters';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Physical model of water droplets through cascading delays, comb filters, and reverse echoes';
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 'generative' as const;
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return 'Noise' as const;
|
||||
}
|
||||
|
||||
protected getOrchestra(): string {
|
||||
return `
|
||||
instr 1
|
||||
iBaseFreq chnget "baseFreq"
|
||||
iDropRate chnget "dropRate"
|
||||
iNumElements chnget "numElements"
|
||||
iDamping chnget "damping"
|
||||
iShakeIntensity chnget "shakeIntensity"
|
||||
iFreqRatio1 chnget "freqRatio1"
|
||||
iFreqRatio2 chnget "freqRatio2"
|
||||
iFreqRatio3 chnget "freqRatio3"
|
||||
iAttack chnget "attack"
|
||||
iRelease chnget "release"
|
||||
iAmplitude chnget "amplitude"
|
||||
iDelayTime1 chnget "delayTime1"
|
||||
iDelayTime2 chnget "delayTime2"
|
||||
iDelayTime3 chnget "delayTime3"
|
||||
iDelayTime4 chnget "delayTime4"
|
||||
iDelayGain1 chnget "delayGain1"
|
||||
iDelayGain2 chnget "delayGain2"
|
||||
iDelayGain3 chnget "delayGain3"
|
||||
iDelayGain4 chnget "delayGain4"
|
||||
iFeedbackDelayTime chnget "feedbackDelayTime"
|
||||
iFeedbackAmount chnget "feedbackAmount"
|
||||
iModDepth chnget "modDepth"
|
||||
iModRate chnget "modRate"
|
||||
iStereoSpread chnget "stereoSpread"
|
||||
iCombDelayTime chnget "combDelayTime"
|
||||
iCombFeedback chnget "combFeedback"
|
||||
iReverseDelayTime chnget "reverseDelayTime"
|
||||
iReverseGain chnget "reverseGain"
|
||||
iCascadeDelay1 chnget "cascadeDelay1"
|
||||
iCascadeDelay2 chnget "cascadeDelay2"
|
||||
iCascadeFeedback chnget "cascadeFeedback"
|
||||
|
||||
idur = p3
|
||||
iAttackTime = iAttack * idur
|
||||
iReleaseTime = iRelease * idur
|
||||
|
||||
; Scale delay times by duration so they fill the entire sound
|
||||
iScaledDelayTime1 = iDelayTime1 * idur
|
||||
iScaledDelayTime2 = iDelayTime2 * idur
|
||||
iScaledDelayTime3 = iDelayTime3 * idur
|
||||
iScaledDelayTime4 = iDelayTime4 * idur
|
||||
iScaledFeedbackDelay = iFeedbackDelayTime * idur
|
||||
iModDepthScaled = iModDepth * idur * 0.1
|
||||
iScaledCombDelay = iCombDelayTime * idur
|
||||
iScaledReverseDelay = iReverseDelayTime * idur
|
||||
iScaledCascade1 = iCascadeDelay1 * idur
|
||||
iScaledCascade2 = iCascadeDelay2 * idur
|
||||
|
||||
; Calculate resonant frequencies from base frequency and ratios
|
||||
iFreq1 = iBaseFreq * iFreqRatio1
|
||||
iFreq2 = iBaseFreq * iFreqRatio2
|
||||
iFreq3 = iBaseFreq * iFreqRatio3
|
||||
|
||||
; Generate dripwater sound
|
||||
; Signature: ares dripwater kamp, idettack, inum, idamp, imaxshake, ifreq, ifreq1, ifreq2
|
||||
; Reduce amplitude to prevent clipping with all the delays
|
||||
kAmp = iAmplitude * 0.08
|
||||
aDrip dripwater kAmp, iDropRate, iNumElements, iDamping, iShakeIntensity, iFreq1, iFreq2, iFreq3
|
||||
|
||||
; Apply AR envelope
|
||||
kEnv linsegr 0, iAttackTime, 1, iReleaseTime, 0
|
||||
|
||||
aDry = aDrip * kEnv
|
||||
|
||||
; Apply multitap delay with duration-scaled times
|
||||
; Signature: a1 [, a2, a3, ...] multitap asource [, itime1, igain1, itime2, igain2, ...]
|
||||
aMultitap multitap aDry, iScaledDelayTime1, iDelayGain1, iScaledDelayTime2, iDelayGain2, iScaledDelayTime3, iDelayGain3, iScaledDelayTime4, iDelayGain4
|
||||
|
||||
; Add modulated feedback delay
|
||||
; LFO modulates delay time
|
||||
kLFO oscili iModDepthScaled, iModRate
|
||||
kDelayTime = iScaledFeedbackDelay + kLFO
|
||||
kDelayTime = max(kDelayTime, 0.001)
|
||||
|
||||
; Variable delay with feedback
|
||||
aBuf delayr idur
|
||||
aTapL deltapi kDelayTime
|
||||
delayw aDry + (aTapL * iFeedbackAmount)
|
||||
|
||||
; Comb filter delay (creates resonance)
|
||||
aComb comb aDry, iScaledCombDelay, iCombFeedback
|
||||
|
||||
; Reverse delay using delayr/delayw with interpolation
|
||||
aBufRev delayr iScaledReverseDelay
|
||||
aReverseTap deltapi iScaledReverseDelay * (1 - (line:a(0, idur, 1)))
|
||||
delayw aDry
|
||||
aReverse = aReverseTap * iReverseGain
|
||||
|
||||
; Cascaded delays (two delays feeding into each other)
|
||||
aBuf1 delayr idur
|
||||
aCascade1 deltapi iScaledCascade1
|
||||
aBuf2 delayr idur
|
||||
aCascade2 deltapi iScaledCascade2
|
||||
delayw aCascade1 * iCascadeFeedback
|
||||
delayw aDry + (aCascade2 * iCascadeFeedback)
|
||||
|
||||
aOut = aDry + aMultitap + aTapL + aComb + aReverse + aCascade1 + aCascade2
|
||||
|
||||
; Create stereo width with slightly different parameters for right channel
|
||||
; Slightly detune the resonant frequencies for right channel
|
||||
iFreq1_R = iFreq1 * 1.003
|
||||
iFreq2_R = iFreq2 * 0.998
|
||||
iFreq3_R = iFreq3 * 1.002
|
||||
|
||||
; Use slightly different initial values for variation
|
||||
iDropRate_R = iDropRate * 1.02
|
||||
iShakeIntensity_R = iShakeIntensity * 0.98
|
||||
|
||||
kAmp_R = iAmplitude * 0.08
|
||||
aDrip_R dripwater kAmp_R, iDropRate_R, iNumElements, iDamping, iShakeIntensity_R, iFreq1_R, iFreq2_R, iFreq3_R
|
||||
|
||||
aDry_R = aDrip_R * kEnv
|
||||
|
||||
; Apply multitap delay to right channel with slightly different times for stereo width
|
||||
iScaledDelayTime1_R = iScaledDelayTime1 * 1.03
|
||||
iScaledDelayTime2_R = iScaledDelayTime2 * 0.97
|
||||
iScaledDelayTime3_R = iScaledDelayTime3 * 1.05
|
||||
iScaledDelayTime4_R = iScaledDelayTime4 * 0.95
|
||||
|
||||
aMultitap_R multitap aDry_R, iScaledDelayTime1_R, iDelayGain1, iScaledDelayTime2_R, iDelayGain2, iScaledDelayTime3_R, iDelayGain3, iScaledDelayTime4_R, iDelayGain4
|
||||
|
||||
; Right channel modulated delay with stereo spread
|
||||
kLFO_R oscili iModDepthScaled, iModRate * 1.07
|
||||
kDelayTime_R = (iScaledFeedbackDelay * iStereoSpread) + kLFO_R
|
||||
kDelayTime_R = max(kDelayTime_R, 0.001)
|
||||
|
||||
aBuf_R delayr idur
|
||||
aTapR deltapi kDelayTime_R
|
||||
delayw aDry_R + (aTapR * iFeedbackAmount)
|
||||
|
||||
; Comb filter for right channel
|
||||
iScaledCombDelay_R = iScaledCombDelay * 1.07
|
||||
aComb_R comb aDry_R, iScaledCombDelay_R, iCombFeedback
|
||||
|
||||
; Reverse delay for right channel
|
||||
iScaledReverseDelay_R = iScaledReverseDelay * 0.93
|
||||
aBufRev_R delayr iScaledReverseDelay_R
|
||||
aReverseTap_R deltapi iScaledReverseDelay_R * (1 - (line:a(0, idur, 1)))
|
||||
delayw aDry_R
|
||||
aReverse_R = aReverseTap_R * iReverseGain
|
||||
|
||||
; Cascaded delays for right channel
|
||||
iScaledCascade1_R = iScaledCascade1 * 1.05
|
||||
iScaledCascade2_R = iScaledCascade2 * 0.95
|
||||
aBuf1_R delayr idur
|
||||
aCascade1_R deltapi iScaledCascade1_R
|
||||
aBuf2_R delayr idur
|
||||
aCascade2_R deltapi iScaledCascade2_R
|
||||
delayw aCascade1_R * iCascadeFeedback
|
||||
delayw aDry_R + (aCascade2_R * iCascadeFeedback)
|
||||
|
||||
aOut_R = aDry_R + aMultitap_R + aTapR + aComb_R + aReverse_R + aCascade1_R + aCascade2_R
|
||||
|
||||
outs aOut, aOut_R
|
||||
endin
|
||||
`;
|
||||
}
|
||||
|
||||
protected getParametersForCsound(params: DripwaterParams): CsoundParameter[] {
|
||||
return [
|
||||
{ channelName: 'baseFreq', value: params.baseFreq },
|
||||
{ channelName: 'dropRate', value: params.dropRate },
|
||||
{ channelName: 'numElements', value: params.numElements },
|
||||
{ channelName: 'damping', value: params.damping },
|
||||
{ channelName: 'shakeIntensity', value: params.shakeIntensity },
|
||||
{ channelName: 'freqRatio1', value: params.freqRatio1 },
|
||||
{ channelName: 'freqRatio2', value: params.freqRatio2 },
|
||||
{ channelName: 'freqRatio3', value: params.freqRatio3 },
|
||||
{ channelName: 'attack', value: params.attack },
|
||||
{ channelName: 'release', value: params.release },
|
||||
{ channelName: 'amplitude', value: params.amplitude },
|
||||
{ channelName: 'delayTime1', value: params.delayTime1 },
|
||||
{ channelName: 'delayTime2', value: params.delayTime2 },
|
||||
{ channelName: 'delayTime3', value: params.delayTime3 },
|
||||
{ channelName: 'delayTime4', value: params.delayTime4 },
|
||||
{ channelName: 'delayGain1', value: params.delayGain1 },
|
||||
{ channelName: 'delayGain2', value: params.delayGain2 },
|
||||
{ channelName: 'delayGain3', value: params.delayGain3 },
|
||||
{ channelName: 'delayGain4', value: params.delayGain4 },
|
||||
{ channelName: 'feedbackDelayTime', value: params.feedbackDelayTime },
|
||||
{ channelName: 'feedbackAmount', value: params.feedbackAmount },
|
||||
{ channelName: 'modDepth', value: params.modDepth },
|
||||
{ channelName: 'modRate', value: params.modRate },
|
||||
{ channelName: 'stereoSpread', value: params.stereoSpread },
|
||||
{ channelName: 'combDelayTime', value: params.combDelayTime },
|
||||
{ channelName: 'combFeedback', value: params.combFeedback },
|
||||
{ channelName: 'reverseDelayTime', value: params.reverseDelayTime },
|
||||
{ channelName: 'reverseGain', value: params.reverseGain },
|
||||
{ channelName: 'cascadeDelay1', value: params.cascadeDelay1 },
|
||||
{ channelName: 'cascadeDelay2', value: params.cascadeDelay2 },
|
||||
{ channelName: 'cascadeFeedback', value: params.cascadeFeedback },
|
||||
];
|
||||
}
|
||||
|
||||
randomParams(pitchLock?: PitchLock): DripwaterParams {
|
||||
const baseFreqChoices = [55, 82.4, 110, 146.8, 220, 293.7, 440, 587.3, 880];
|
||||
const baseFreq = pitchLock?.enabled
|
||||
? pitchLock.frequency
|
||||
: this.randomChoice(baseFreqChoices) * this.randomRange(0.95, 1.05);
|
||||
|
||||
return {
|
||||
baseFreq,
|
||||
dropRate: this.randomRange(2, 40),
|
||||
numElements: this.randomInt(4, 48),
|
||||
damping: this.randomRange(0.1, 0.85),
|
||||
shakeIntensity: this.randomRange(0.2, 0.9),
|
||||
freqRatio1: this.randomRange(1.0, 3.5),
|
||||
freqRatio2: this.randomRange(2.5, 7.0),
|
||||
freqRatio3: this.randomRange(5.0, 14.0),
|
||||
attack: this.randomRange(0.001, 0.04),
|
||||
release: this.randomRange(0.15, 0.9),
|
||||
amplitude: this.randomRange(0.6, 0.95),
|
||||
delayTime1: this.randomRange(0.1, 0.3),
|
||||
delayTime2: this.randomRange(0.25, 0.5),
|
||||
delayTime3: this.randomRange(0.45, 0.7),
|
||||
delayTime4: this.randomRange(0.65, 0.95),
|
||||
delayGain1: this.randomRange(0.4, 0.7),
|
||||
delayGain2: this.randomRange(0.3, 0.55),
|
||||
delayGain3: this.randomRange(0.2, 0.4),
|
||||
delayGain4: this.randomRange(0.1, 0.25),
|
||||
feedbackDelayTime: this.randomRange(0.15, 0.5),
|
||||
feedbackAmount: this.randomRange(0.3, 0.7),
|
||||
modDepth: this.randomRange(0.02, 0.12),
|
||||
modRate: this.randomRange(0.1, 3.0),
|
||||
stereoSpread: this.randomRange(0.85, 1.15),
|
||||
combDelayTime: this.randomRange(0.05, 0.25),
|
||||
combFeedback: this.randomRange(0.4, 0.8),
|
||||
reverseDelayTime: this.randomRange(0.3, 0.8),
|
||||
reverseGain: this.randomRange(0.15, 0.4),
|
||||
cascadeDelay1: this.randomRange(0.2, 0.45),
|
||||
cascadeDelay2: this.randomRange(0.35, 0.65),
|
||||
cascadeFeedback: this.randomRange(0.3, 0.65),
|
||||
};
|
||||
}
|
||||
|
||||
mutateParams(
|
||||
params: DripwaterParams,
|
||||
mutationAmount: number = 0.15,
|
||||
pitchLock?: PitchLock
|
||||
): DripwaterParams {
|
||||
const baseFreq = pitchLock?.enabled ? pitchLock.frequency : params.baseFreq;
|
||||
|
||||
return {
|
||||
baseFreq,
|
||||
dropRate: this.mutateValue(params.dropRate, mutationAmount, 1, 50),
|
||||
numElements: Math.round(this.mutateValue(params.numElements, mutationAmount, 2, 64)),
|
||||
damping: this.mutateValue(params.damping, mutationAmount, 0.0, 0.95),
|
||||
shakeIntensity: this.mutateValue(params.shakeIntensity, mutationAmount, 0.1, 1.0),
|
||||
freqRatio1: this.mutateValue(params.freqRatio1, mutationAmount, 1.0, 4.0),
|
||||
freqRatio2: this.mutateValue(params.freqRatio2, mutationAmount, 2.0, 8.0),
|
||||
freqRatio3: this.mutateValue(params.freqRatio3, mutationAmount, 4.0, 16.0),
|
||||
attack: this.mutateValue(params.attack, mutationAmount, 0.001, 0.05),
|
||||
release: this.mutateValue(params.release, mutationAmount, 0.1, 0.95),
|
||||
amplitude: this.mutateValue(params.amplitude, mutationAmount, 0.5, 1.0),
|
||||
delayTime1: this.mutateValue(params.delayTime1, mutationAmount, 0.05, 0.35),
|
||||
delayTime2: this.mutateValue(params.delayTime2, mutationAmount, 0.2, 0.6),
|
||||
delayTime3: this.mutateValue(params.delayTime3, mutationAmount, 0.4, 0.8),
|
||||
delayTime4: this.mutateValue(params.delayTime4, mutationAmount, 0.6, 0.98),
|
||||
delayGain1: this.mutateValue(params.delayGain1, mutationAmount, 0.3, 0.8),
|
||||
delayGain2: this.mutateValue(params.delayGain2, mutationAmount, 0.2, 0.65),
|
||||
delayGain3: this.mutateValue(params.delayGain3, mutationAmount, 0.1, 0.5),
|
||||
delayGain4: this.mutateValue(params.delayGain4, mutationAmount, 0.05, 0.35),
|
||||
feedbackDelayTime: this.mutateValue(params.feedbackDelayTime, mutationAmount, 0.1, 0.6),
|
||||
feedbackAmount: this.mutateValue(params.feedbackAmount, mutationAmount, 0.2, 0.8),
|
||||
modDepth: this.mutateValue(params.modDepth, mutationAmount, 0.01, 0.15),
|
||||
modRate: this.mutateValue(params.modRate, mutationAmount, 0.05, 4.0),
|
||||
stereoSpread: this.mutateValue(params.stereoSpread, mutationAmount, 0.7, 1.3),
|
||||
combDelayTime: this.mutateValue(params.combDelayTime, mutationAmount, 0.03, 0.3),
|
||||
combFeedback: this.mutateValue(params.combFeedback, mutationAmount, 0.3, 0.85),
|
||||
reverseDelayTime: this.mutateValue(params.reverseDelayTime, mutationAmount, 0.2, 0.9),
|
||||
reverseGain: this.mutateValue(params.reverseGain, mutationAmount, 0.1, 0.5),
|
||||
cascadeDelay1: this.mutateValue(params.cascadeDelay1, mutationAmount, 0.15, 0.5),
|
||||
cascadeDelay2: this.mutateValue(params.cascadeDelay2, mutationAmount, 0.3, 0.7),
|
||||
cascadeFeedback: this.mutateValue(params.cascadeFeedback, mutationAmount, 0.2, 0.75),
|
||||
};
|
||||
}
|
||||
}
|
||||
296
src/lib/audio/engines/Form1.ts
Normal file
296
src/lib/audio/engines/Form1.ts
Normal file
@ -0,0 +1,296 @@
|
||||
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
|
||||
import type { PitchLock } from './base/SynthEngine';
|
||||
|
||||
interface Form1Params {
|
||||
frequency: number;
|
||||
vibratoRate: number;
|
||||
vibratoDepth: number;
|
||||
formant1Freq: number;
|
||||
formant1BW: number;
|
||||
formant1Amp: number;
|
||||
formant2Freq: number;
|
||||
formant2BW: number;
|
||||
formant2Amp: number;
|
||||
formant3Freq: number;
|
||||
formant3BW: number;
|
||||
formant3Amp: number;
|
||||
formant4Freq: number;
|
||||
formant4BW: number;
|
||||
formant4Amp: number;
|
||||
formant5Freq: number;
|
||||
formant5BW: number;
|
||||
formant5Amp: number;
|
||||
attack: number;
|
||||
decay: number;
|
||||
sustain: number;
|
||||
release: number;
|
||||
}
|
||||
|
||||
export class Form1 extends CsoundEngine<Form1Params> {
|
||||
getName(): string {
|
||||
return 'Throat';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Multi-voice formant vocal synthesizer with deep sub-bass and wide stereo field';
|
||||
}
|
||||
|
||||
getType(): 'generative' | 'sample' | 'input' {
|
||||
return 'generative';
|
||||
}
|
||||
|
||||
getCategory(): 'Additive' | 'Subtractive' | 'FM' | 'Percussion' | 'Noise' | 'Physical' | 'Modulation' | 'Experimental' | 'Utility' {
|
||||
return 'Physical';
|
||||
}
|
||||
|
||||
getOrchestra(): string {
|
||||
return `
|
||||
instr 1
|
||||
ifreq = chnget("frequency")
|
||||
iamp = 0.3
|
||||
ivibratoRate = chnget("vibratoRate")
|
||||
ivibratoDepth = chnget("vibratoDepth")
|
||||
|
||||
if1freq = chnget("formant1Freq")
|
||||
if1bw = chnget("formant1BW")
|
||||
if1amp = chnget("formant1Amp")
|
||||
if2freq = chnget("formant2Freq")
|
||||
if2bw = chnget("formant2BW")
|
||||
if2amp = chnget("formant2Amp")
|
||||
if3freq = chnget("formant3Freq")
|
||||
if3bw = chnget("formant3BW")
|
||||
if3amp = chnget("formant3Amp")
|
||||
if4freq = chnget("formant4Freq")
|
||||
if4bw = chnget("formant4BW")
|
||||
if4amp = chnget("formant4Amp")
|
||||
if5freq = chnget("formant5Freq")
|
||||
if5bw = chnget("formant5BW")
|
||||
if5amp = chnget("formant5Amp")
|
||||
|
||||
iatt = chnget("attack") * p3
|
||||
idec = chnget("decay") * p3
|
||||
isus = chnget("sustain")
|
||||
irel = chnget("release") * p3
|
||||
|
||||
; Amplitude envelope
|
||||
aenv = madsr(iatt, idec, isus, irel)
|
||||
|
||||
; Dual vibrato LFOs for stereo width
|
||||
kvibL = lfo(ivibratoDepth, ivibratoRate)
|
||||
kvibR = lfo(ivibratoDepth, ivibratoRate * 1.03)
|
||||
|
||||
; === LEFT CHANNEL VOICES ===
|
||||
; Voice 1L: Main voice
|
||||
kfreq1L = ifreq * (1 + kvibL)
|
||||
inumharm1L = int(sr / (2 * ifreq))
|
||||
asig1L = buzz(1, kfreq1L, inumharm1L, -1)
|
||||
|
||||
; Voice 2L: Slightly detuned (0.987x for beating)
|
||||
kfreq2L = ifreq * 0.987 * (1 + kvibL * 1.02)
|
||||
inumharm2L = int(sr / (2 * ifreq * 0.987))
|
||||
asig2L = buzz(0.85, kfreq2L, inumharm2L, -1)
|
||||
|
||||
; Voice 3L: Slightly detuned up (1.013x for beating)
|
||||
kfreq3L = ifreq * 1.013 * (1 + kvibL * 0.98)
|
||||
inumharm3L = int(sr / (2 * ifreq * 1.013))
|
||||
asig3L = buzz(0.8, kfreq3L, inumharm3L, -1)
|
||||
|
||||
; Voice 4L: Octave down
|
||||
kfreq4L = ifreq * 0.5 * (1 + kvibL * 1.04)
|
||||
inumharm4L = int(sr / (2 * ifreq * 0.5))
|
||||
asig4L = buzz(0.65, kfreq4L, inumharm4L, -1)
|
||||
|
||||
; Voice 5L: Octave up
|
||||
kfreq5L = ifreq * 2 * (1 + kvibL * 0.96)
|
||||
inumharm5L = int(sr / (2 * ifreq * 2))
|
||||
asig5L = buzz(0.45, kfreq5L, inumharm5L, -1)
|
||||
|
||||
; === RIGHT CHANNEL VOICES (different detuning) ===
|
||||
; Voice 1R: Main voice slightly different
|
||||
kfreq1R = ifreq * 1.002 * (1 + kvibR)
|
||||
inumharm1R = int(sr / (2 * ifreq * 1.002))
|
||||
asig1R = buzz(1, kfreq1R, inumharm1R, -1)
|
||||
|
||||
; Voice 2R: Different detuning (0.993x)
|
||||
kfreq2R = ifreq * 0.993 * (1 + kvibR * 1.01)
|
||||
inumharm2R = int(sr / (2 * ifreq * 0.993))
|
||||
asig2R = buzz(0.85, kfreq2R, inumharm2R, -1)
|
||||
|
||||
; Voice 3R: Different detuning up (1.007x)
|
||||
kfreq3R = ifreq * 1.007 * (1 + kvibR * 0.99)
|
||||
inumharm3R = int(sr / (2 * ifreq * 1.007))
|
||||
asig3R = buzz(0.8, kfreq3R, inumharm3R, -1)
|
||||
|
||||
; Voice 4R: Octave down different phase
|
||||
kfreq4R = ifreq * 0.501 * (1 + kvibR * 1.06)
|
||||
inumharm4R = int(sr / (2 * ifreq * 0.501))
|
||||
asig4R = buzz(0.6, kfreq4R, inumharm4R, -1)
|
||||
|
||||
; Voice 5R: Octave up different phase
|
||||
kfreq5R = ifreq * 1.998 * (1 + kvibR * 0.94)
|
||||
inumharm5R = int(sr / (2 * ifreq * 1.998))
|
||||
asig5R = buzz(0.4, kfreq5R, inumharm5R, -1)
|
||||
|
||||
; Sub-bass: Buzz oscillator one octave below with harmonic content
|
||||
kfreqSubL = ifreq * 0.5 * (1 + kvibL * 0.5)
|
||||
kfreqSubR = ifreq * 0.502 * (1 + kvibR * 0.5)
|
||||
inumharmSubL = int(sr / (2 * ifreq * 0.5))
|
||||
inumharmSubR = int(sr / (2 * ifreq * 0.502))
|
||||
aSubRawL = buzz(1, kfreqSubL, inumharmSubL, -1)
|
||||
aSubRawR = buzz(1, kfreqSubR, inumharmSubR, -1)
|
||||
|
||||
; Apply very wide formants for vocal character with depth
|
||||
aSubF1L = butterbp(aSubRawL, if1freq * 0.6, if1bw * 8)
|
||||
aSubF2L = butterbp(aSubRawL, if2freq * 0.7, if2bw * 10)
|
||||
aSubF3L = butterbp(aSubRawL, if3freq * 0.65, if3bw * 9)
|
||||
aSubF1R = butterbp(aSubRawR, if1freq * 0.62, if1bw * 8.5)
|
||||
aSubF2R = butterbp(aSubRawR, if2freq * 0.68, if2bw * 10.5)
|
||||
aSubF3R = butterbp(aSubRawR, if3freq * 0.67, if3bw * 9.5)
|
||||
|
||||
; Mix sub with formants for depth and complexity
|
||||
aSubMixL = (aSubRawL * 0.3) + (aSubF1L * 0.3) + (aSubF2L * 0.25) + (aSubF3L * 0.15)
|
||||
aSubMixR = (aSubRawR * 0.3) + (aSubF1R * 0.3) + (aSubF2R * 0.25) + (aSubF3R * 0.15)
|
||||
|
||||
; Add gentle low-pass for warmth and smooth out harsh frequencies
|
||||
aSubL = butterlp(aSubMixL, 1200)
|
||||
aSubR = butterlp(aSubMixR, 1250)
|
||||
|
||||
; Scale sub-bass
|
||||
aSubL = aSubL * 0.8
|
||||
aSubR = aSubR * 0.8
|
||||
|
||||
; Mix voices per channel (sub added separately later)
|
||||
asigMixL = asig1L + asig2L + asig3L + asig4L + asig5L
|
||||
asigMixR = asig1R + asig2R + asig3R + asig4R + asig5R
|
||||
|
||||
; === LEFT CHANNEL FORMANTS ===
|
||||
; Main formants
|
||||
a1L = butterbp(asigMixL * if1amp, if1freq, if1bw)
|
||||
a2L = butterbp(asigMixL * if2amp, if2freq, if2bw)
|
||||
a3L = butterbp(asigMixL * if3amp, if3freq, if3bw)
|
||||
a4L = butterbp(asigMixL * if4amp, if4freq, if4bw)
|
||||
a5L = butterbp(asigMixL * if5amp, if5freq, if5bw)
|
||||
|
||||
; Additional formant layers
|
||||
a1bL = butterbp(asigMixL * if1amp * 0.35, if1freq * 1.025, if1bw * 1.12)
|
||||
a2bL = butterbp(asigMixL * if2amp * 0.3, if2freq * 0.975, if2bw * 1.18)
|
||||
a3bL = butterbp(asigMixL * if3amp * 0.25, if3freq * 1.035, if3bw * 1.1)
|
||||
|
||||
; === RIGHT CHANNEL FORMANTS (different shifts) ===
|
||||
; Main formants
|
||||
a1R = butterbp(asigMixR * if1amp, if1freq * 1.005, if1bw * 1.02)
|
||||
a2R = butterbp(asigMixR * if2amp, if2freq * 0.995, if2bw * 1.03)
|
||||
a3R = butterbp(asigMixR * if3amp, if3freq * 1.008, if3bw * 1.01)
|
||||
a4R = butterbp(asigMixR * if4amp, if4freq * 0.997, if4bw * 1.04)
|
||||
a5R = butterbp(asigMixR * if5amp, if5freq * 1.003, if5bw * 1.02)
|
||||
|
||||
; Additional formant layers
|
||||
a1bR = butterbp(asigMixR * if1amp * 0.3, if1freq * 0.98, if1bw * 1.15)
|
||||
a2bR = butterbp(asigMixR * if2amp * 0.28, if2freq * 1.022, if2bw * 1.2)
|
||||
a3bR = butterbp(asigMixR * if3amp * 0.22, if3freq * 0.97, if3bw * 1.12)
|
||||
|
||||
; Combine formants per channel
|
||||
asigOutL = a1L + a2L + a3L + a4L + a5L + a1bL + a2bL + a3bL
|
||||
asigOutR = a1R + a2R + a3R + a4R + a5R + a1bR + a2bR + a3bR
|
||||
|
||||
; Apply envelope and level
|
||||
asigFormantL = asigOutL * aenv * iamp * 4
|
||||
asigFormantR = asigOutR * aenv * iamp * 4
|
||||
|
||||
; Add sub-bass directly (bypassing formants)
|
||||
aL = asigFormantL + (aSubL * aenv)
|
||||
aR = asigFormantR + (aSubR * aenv)
|
||||
|
||||
outs aL, aR
|
||||
endin
|
||||
`;
|
||||
}
|
||||
|
||||
getParametersForCsound(params: Form1Params): CsoundParameter[] {
|
||||
return [
|
||||
{ channelName: 'frequency', value: params.frequency },
|
||||
{ channelName: 'vibratoRate', value: params.vibratoRate },
|
||||
{ channelName: 'vibratoDepth', value: params.vibratoDepth },
|
||||
{ channelName: 'formant1Freq', value: params.formant1Freq },
|
||||
{ channelName: 'formant1BW', value: params.formant1BW },
|
||||
{ channelName: 'formant1Amp', value: params.formant1Amp },
|
||||
{ channelName: 'formant2Freq', value: params.formant2Freq },
|
||||
{ channelName: 'formant2BW', value: params.formant2BW },
|
||||
{ channelName: 'formant2Amp', value: params.formant2Amp },
|
||||
{ channelName: 'formant3Freq', value: params.formant3Freq },
|
||||
{ channelName: 'formant3BW', value: params.formant3BW },
|
||||
{ channelName: 'formant3Amp', value: params.formant3Amp },
|
||||
{ channelName: 'formant4Freq', value: params.formant4Freq },
|
||||
{ channelName: 'formant4BW', value: params.formant4BW },
|
||||
{ channelName: 'formant4Amp', value: params.formant4Amp },
|
||||
{ channelName: 'formant5Freq', value: params.formant5Freq },
|
||||
{ channelName: 'formant5BW', value: params.formant5BW },
|
||||
{ channelName: 'formant5Amp', value: params.formant5Amp },
|
||||
{ channelName: 'attack', value: params.attack },
|
||||
{ channelName: 'decay', value: params.decay },
|
||||
{ channelName: 'sustain', value: params.sustain },
|
||||
{ channelName: 'release', value: params.release }
|
||||
];
|
||||
}
|
||||
|
||||
randomParams(pitchLock?: PitchLock): Form1Params {
|
||||
const frequency = pitchLock?.enabled ? pitchLock.frequency : 55 * Math.pow(2, Math.random() * 4);
|
||||
|
||||
return {
|
||||
frequency,
|
||||
vibratoRate: 2 + Math.random() * 6,
|
||||
vibratoDepth: 0.001 + Math.random() * 0.008,
|
||||
formant1Freq: 400 + Math.random() * 800,
|
||||
formant1BW: 40 + Math.random() * 100,
|
||||
formant1Amp: 0.8 + Math.random() * 0.2,
|
||||
formant2Freq: 800 + Math.random() * 600,
|
||||
formant2BW: 50 + Math.random() * 100,
|
||||
formant2Amp: 0.4 + Math.random() * 0.4,
|
||||
formant3Freq: 2000 + Math.random() * 1500,
|
||||
formant3BW: 80 + Math.random() * 120,
|
||||
formant3Amp: 0.05 + Math.random() * 0.15,
|
||||
formant4Freq: 3000 + Math.random() * 1500,
|
||||
formant4BW: 100 + Math.random() * 100,
|
||||
formant4Amp: 0.1 + Math.random() * 0.2,
|
||||
formant5Freq: 4000 + Math.random() * 1500,
|
||||
formant5BW: 100 + Math.random() * 150,
|
||||
formant5Amp: 0.01 + Math.random() * 0.1,
|
||||
attack: Math.random() * 0.15,
|
||||
decay: 0.05 + Math.random() * 0.3,
|
||||
sustain: 0.3 + Math.random() * 0.5,
|
||||
release: 0.05 + Math.random() * 0.4
|
||||
};
|
||||
}
|
||||
|
||||
mutateParams(params: Form1Params, mutationAmount = 0.2, pitchLock?: PitchLock): Form1Params {
|
||||
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, 4)),
|
||||
vibratoRate: mutate(params.vibratoRate, 2, 8),
|
||||
vibratoDepth: mutate(params.vibratoDepth, 0.001, 0.009),
|
||||
formant1Freq: mutate(params.formant1Freq, 400, 1200),
|
||||
formant1BW: mutate(params.formant1BW, 40, 140),
|
||||
formant1Amp: mutate(params.formant1Amp, 0.8, 1.0),
|
||||
formant2Freq: mutate(params.formant2Freq, 800, 1400),
|
||||
formant2BW: mutate(params.formant2BW, 50, 150),
|
||||
formant2Amp: mutate(params.formant2Amp, 0.4, 0.8),
|
||||
formant3Freq: mutate(params.formant3Freq, 2000, 3500),
|
||||
formant3BW: mutate(params.formant3BW, 80, 200),
|
||||
formant3Amp: mutate(params.formant3Amp, 0.05, 0.2),
|
||||
formant4Freq: mutate(params.formant4Freq, 3000, 4500),
|
||||
formant4BW: mutate(params.formant4BW, 100, 200),
|
||||
formant4Amp: mutate(params.formant4Amp, 0.1, 0.3),
|
||||
formant5Freq: mutate(params.formant5Freq, 4000, 5500),
|
||||
formant5BW: mutate(params.formant5BW, 100, 250),
|
||||
formant5Amp: mutate(params.formant5Amp, 0.01, 0.11),
|
||||
attack: mutate(params.attack, 0, 0.15),
|
||||
decay: mutate(params.decay, 0.05, 0.35),
|
||||
sustain: mutate(params.sustain, 0.3, 0.8),
|
||||
release: mutate(params.release, 0.05, 0.45)
|
||||
};
|
||||
}
|
||||
}
|
||||
244
src/lib/audio/engines/LaserSweep.ts
Normal file
244
src/lib/audio/engines/LaserSweep.ts
Normal file
@ -0,0 +1,244 @@
|
||||
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
|
||||
|
||||
interface LaserSweepParams {
|
||||
startFreq: number;
|
||||
endFreq: number;
|
||||
sweepCurve: number;
|
||||
resonance: number;
|
||||
brightness: number;
|
||||
delayTime: number;
|
||||
delayFeedback: number;
|
||||
reverbMix: number;
|
||||
reverbSize: number;
|
||||
noiseAmount: number;
|
||||
decayShape: number;
|
||||
waveformMix: number;
|
||||
mainWaveType: number;
|
||||
secondWaveType: number;
|
||||
pulseWidth: number;
|
||||
filterTracking: number;
|
||||
subOctave: number;
|
||||
subOctaveLevel: number;
|
||||
}
|
||||
|
||||
export class LaserSweep extends CsoundEngine<LaserSweepParams> {
|
||||
getName(): string {
|
||||
return 'Laser Sweep';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Sweeping laser beam with delay and reverb';
|
||||
}
|
||||
|
||||
getType(): 'generative' | 'sample' | 'input' {
|
||||
return 'generative';
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return 'Experimental' as const;
|
||||
}
|
||||
|
||||
getOrchestra(): string {
|
||||
return `
|
||||
sr = 44100
|
||||
ksmps = 64
|
||||
nchnls = 2
|
||||
0dbfs = 1
|
||||
|
||||
instr 1
|
||||
iDur = p3
|
||||
|
||||
iStartFreq chnget "startFreq"
|
||||
iEndFreq chnget "endFreq"
|
||||
iSweepCurve chnget "sweepCurve"
|
||||
iResonance chnget "resonance"
|
||||
iBrightness chnget "brightness"
|
||||
iDelayTime chnget "delayTime"
|
||||
iDelayFeedback chnget "delayFeedback"
|
||||
iReverbMix chnget "reverbMix"
|
||||
iReverbSize chnget "reverbSize"
|
||||
iNoiseAmount chnget "noiseAmount"
|
||||
iDecayShape chnget "decayShape"
|
||||
iWaveformMix chnget "waveformMix"
|
||||
iMainWaveType chnget "mainWaveType"
|
||||
iSecondWaveType chnget "secondWaveType"
|
||||
iPulseWidth chnget "pulseWidth"
|
||||
iFilterTracking chnget "filterTracking"
|
||||
iSubOctave chnget "subOctave"
|
||||
iSubOctaveLevel chnget "subOctaveLevel"
|
||||
|
||||
if iDecayShape < 0.33 then
|
||||
kEnv expseg 1, iDur * 0.85, 1, iDur * 0.15, 0.001
|
||||
elseif iDecayShape < 0.67 then
|
||||
kEnv linseg 1, iDur * 0.85, 1, iDur * 0.15, 0
|
||||
else
|
||||
kEnv expseg 1, iDur * 0.8, 1, iDur * 0.18, 0.001
|
||||
endif
|
||||
|
||||
kFreq expseg iStartFreq, iDur * iSweepCurve, iEndFreq
|
||||
kFreqLimited limit kFreq, 40, 16000
|
||||
|
||||
if iMainWaveType < 0.2 then
|
||||
aMain vco2 0.6, kFreqLimited, 10
|
||||
elseif iMainWaveType < 0.4 then
|
||||
aMain vco2 0.6, kFreqLimited, 0
|
||||
elseif iMainWaveType < 0.6 then
|
||||
aMain vco2 0.6, kFreqLimited, 2, iPulseWidth
|
||||
elseif iMainWaveType < 0.8 then
|
||||
aMain vco2 0.6, kFreqLimited, 12
|
||||
else
|
||||
aMain oscili 0.6, kFreqLimited
|
||||
endif
|
||||
|
||||
if iSecondWaveType < 0.2 then
|
||||
aSecond vco2 0.5, kFreqLimited, 2, iPulseWidth
|
||||
elseif iSecondWaveType < 0.4 then
|
||||
aSecond vco2 0.5, kFreqLimited, 0
|
||||
elseif iSecondWaveType < 0.6 then
|
||||
aSecond vco2 0.5, kFreqLimited, 12
|
||||
elseif iSecondWaveType < 0.8 then
|
||||
aSecond vco2 0.5, kFreqLimited, 10
|
||||
else
|
||||
aSecond oscili 0.5, kFreqLimited * 1.5
|
||||
endif
|
||||
|
||||
aNoise rand 0.3
|
||||
kNoiseFiltFreq limit kFreqLimited * 0.5, 50, 12000
|
||||
aNoise butterbp aNoise, kNoiseFiltFreq, kNoiseFiltFreq * 0.5
|
||||
|
||||
if iSubOctaveLevel > 0.01 then
|
||||
if iSubOctave < 0.5 then
|
||||
kSubFreq = kFreqLimited * 0.5
|
||||
else
|
||||
kSubFreq = kFreqLimited * 0.25
|
||||
endif
|
||||
aSub oscili iSubOctaveLevel * 0.7, kSubFreq
|
||||
else
|
||||
aSub = 0
|
||||
endif
|
||||
|
||||
aOsc = aMain + aSecond * iWaveformMix + aSecond * iBrightness * 0.3 + aNoise * iNoiseAmount + aSub
|
||||
|
||||
kFilterFreq = kFreqLimited * (1 + (1 - iFilterTracking) * 2)
|
||||
kFilterFreq limit kFilterFreq, 50, 15000
|
||||
|
||||
kBandwidth = kFilterFreq * (0.1 + iResonance * 0.3)
|
||||
aFilt butterbp aOsc, kFilterFreq, kBandwidth
|
||||
|
||||
aFilt = aFilt * kEnv * 0.8
|
||||
|
||||
if iDelayTime > 0.01 then
|
||||
aDlyL vdelay3 aFilt, iDelayTime * 1000, 2000
|
||||
aDlyR vdelay3 aFilt, iDelayTime * 1000 * 1.1, 2000
|
||||
|
||||
aDlyL = aDlyL * iDelayFeedback * kEnv
|
||||
aDlyR = aDlyR * iDelayFeedback * kEnv
|
||||
else
|
||||
aDlyL = 0
|
||||
aDlyR = 0
|
||||
endif
|
||||
|
||||
if iReverbMix > 0.01 then
|
||||
aWetL, aWetR reverbsc aFilt + aDlyL * 0.5, aFilt + aDlyR * 0.5, iReverbSize, 8000
|
||||
else
|
||||
aWetL = 0
|
||||
aWetR = 0
|
||||
endif
|
||||
|
||||
aOutL = aFilt + aDlyL * 0.4 + aWetL * iReverbMix
|
||||
aOutR = aFilt + aDlyR * 0.4 + aWetR * iReverbMix
|
||||
|
||||
outs aOutL, aOutR
|
||||
endin
|
||||
`;
|
||||
}
|
||||
|
||||
getParametersForCsound(params: LaserSweepParams): CsoundParameter[] {
|
||||
return [
|
||||
{ channelName: 'startFreq', value: params.startFreq },
|
||||
{ channelName: 'endFreq', value: params.endFreq },
|
||||
{ channelName: 'sweepCurve', value: params.sweepCurve },
|
||||
{ channelName: 'resonance', value: params.resonance },
|
||||
{ channelName: 'brightness', value: params.brightness },
|
||||
{ channelName: 'delayTime', value: params.delayTime },
|
||||
{ channelName: 'delayFeedback', value: params.delayFeedback },
|
||||
{ channelName: 'reverbMix', value: params.reverbMix },
|
||||
{ channelName: 'reverbSize', value: params.reverbSize },
|
||||
{ channelName: 'noiseAmount', value: params.noiseAmount },
|
||||
{ channelName: 'decayShape', value: params.decayShape },
|
||||
{ channelName: 'waveformMix', value: params.waveformMix },
|
||||
{ channelName: 'mainWaveType', value: params.mainWaveType },
|
||||
{ channelName: 'secondWaveType', value: params.secondWaveType },
|
||||
{ channelName: 'pulseWidth', value: params.pulseWidth },
|
||||
{ channelName: 'filterTracking', value: params.filterTracking },
|
||||
{ channelName: 'subOctave', value: params.subOctave },
|
||||
{ channelName: 'subOctaveLevel', value: params.subOctaveLevel }
|
||||
];
|
||||
}
|
||||
|
||||
randomParams(pitchLock?: number): LaserSweepParams {
|
||||
const isUpward = Math.random() > 0.3;
|
||||
|
||||
const startFreq = isUpward
|
||||
? 60 + Math.random() * 200
|
||||
: 1000 + Math.random() * 10000;
|
||||
|
||||
const endFreq = isUpward
|
||||
? 1000 + Math.random() * 10000
|
||||
: 60 + Math.random() * 200;
|
||||
|
||||
return {
|
||||
startFreq,
|
||||
endFreq,
|
||||
sweepCurve: 0.2 + Math.random() * 0.7,
|
||||
resonance: 0.2 + Math.random() * 0.7,
|
||||
brightness: 0.1 + Math.random() * 0.8,
|
||||
delayTime: Math.random() < 0.3 ? 0 : 0.05 + Math.random() * 0.5,
|
||||
delayFeedback: 0.1 + Math.random() * 0.7,
|
||||
reverbMix: Math.random() < 0.2 ? 0 : 0.1 + Math.random() * 0.5,
|
||||
reverbSize: 0.4 + Math.random() * 0.5,
|
||||
noiseAmount: 0.05 + Math.random() * 0.5,
|
||||
decayShape: Math.random(),
|
||||
waveformMix: 0.3 + Math.random() * 0.7,
|
||||
mainWaveType: Math.random(),
|
||||
secondWaveType: Math.random(),
|
||||
pulseWidth: 0.1 + Math.random() * 0.8,
|
||||
filterTracking: 0.3 + Math.random() * 0.7,
|
||||
subOctave: Math.random(),
|
||||
subOctaveLevel: Math.random() < 0.4 ? 0 : 0.3 + Math.random() * 0.7
|
||||
};
|
||||
}
|
||||
|
||||
mutateParams(
|
||||
params: LaserSweepParams,
|
||||
mutationAmount: number = 0.3,
|
||||
pitchLock?: number
|
||||
): LaserSweepParams {
|
||||
const mutate = (value: number, min: number, max: number): number => {
|
||||
const range = max - min;
|
||||
const change = (Math.random() - 0.5) * range * mutationAmount;
|
||||
return Math.max(min, Math.min(max, value + change));
|
||||
};
|
||||
|
||||
return {
|
||||
startFreq: mutate(params.startFreq, 40, 12000),
|
||||
endFreq: mutate(params.endFreq, 40, 12000),
|
||||
sweepCurve: mutate(params.sweepCurve, 0.1, 0.95),
|
||||
resonance: mutate(params.resonance, 0.1, 1.0),
|
||||
brightness: mutate(params.brightness, 0.0, 1.0),
|
||||
delayTime: mutate(params.delayTime, 0.0, 0.6),
|
||||
delayFeedback: mutate(params.delayFeedback, 0.0, 0.85),
|
||||
reverbMix: mutate(params.reverbMix, 0.0, 0.7),
|
||||
reverbSize: mutate(params.reverbSize, 0.3, 0.95),
|
||||
noiseAmount: mutate(params.noiseAmount, 0.0, 0.7),
|
||||
decayShape: mutate(params.decayShape, 0.0, 1.0),
|
||||
waveformMix: mutate(params.waveformMix, 0.0, 1.0),
|
||||
mainWaveType: mutate(params.mainWaveType, 0.0, 1.0),
|
||||
secondWaveType: mutate(params.secondWaveType, 0.0, 1.0),
|
||||
pulseWidth: mutate(params.pulseWidth, 0.05, 0.95),
|
||||
filterTracking: mutate(params.filterTracking, 0.0, 1.0),
|
||||
subOctave: mutate(params.subOctave, 0.0, 1.0),
|
||||
subOctaveLevel: mutate(params.subOctaveLevel, 0.0, 1.0)
|
||||
};
|
||||
}
|
||||
}
|
||||
149
src/lib/audio/engines/Squine1.ts
Normal file
149
src/lib/audio/engines/Squine1.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
|
||||
import type { PitchLock } from './base/SynthEngine';
|
||||
|
||||
interface Squine1Params {
|
||||
frequency: number;
|
||||
shapeAmount1: number;
|
||||
shapeAmount2: number;
|
||||
modulationDepth1: number;
|
||||
modulationDepth2: number;
|
||||
osc2Detune: number;
|
||||
osc2Level: number;
|
||||
hpfCutoff: number;
|
||||
attack: number;
|
||||
decay: number;
|
||||
sustain: number;
|
||||
release: number;
|
||||
}
|
||||
|
||||
export class Squine1 extends CsoundEngine<Squine1Params> {
|
||||
getName(): string {
|
||||
return 'Squine1';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Squinewave synthesizer with waveshaping and dual oscillators';
|
||||
}
|
||||
|
||||
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
|
||||
ishape1 = chnget("shapeAmount1")
|
||||
ishape2 = chnget("shapeAmount2")
|
||||
imod1 = chnget("modulationDepth1")
|
||||
imod2 = chnget("modulationDepth2")
|
||||
idetune = chnget("osc2Detune")
|
||||
iosc2Level = chnget("osc2Level")
|
||||
ihpf = chnget("hpfCutoff")
|
||||
iatt = chnget("attack") * p3
|
||||
idec = chnget("decay") * p3
|
||||
isus = chnget("sustain")
|
||||
irel = chnget("release") * p3
|
||||
|
||||
; First oscillator with waveshaping (squine approximation)
|
||||
aosc1 = poscil(1, ifreq)
|
||||
|
||||
; Create dynamic shape envelope for osc1
|
||||
kshape1 = expseg(ishape1, p3, ishape1 * imod1)
|
||||
|
||||
; Waveshaping: blend between sine and square-like
|
||||
asig1 = tanh(aosc1 * kshape1 * 5) / tanh(kshape1 * 5)
|
||||
|
||||
; Second oscillator slightly detuned
|
||||
aosc2 = poscil(1, ifreq * (1 + idetune))
|
||||
|
||||
; Create dynamic shape envelope for osc2
|
||||
kshape2 = expseg(ishape2, p3, ishape2 * imod2)
|
||||
|
||||
; Waveshaping for second oscillator
|
||||
asig2 = tanh(aosc2 * kshape2 * 5) / tanh(kshape2 * 5)
|
||||
|
||||
; Mix oscillators
|
||||
asig = asig1 + (asig2 * iosc2Level)
|
||||
|
||||
; High-pass filter to remove DC and rumble
|
||||
kcutoff = max(ihpf, 20)
|
||||
asig = butterhp(asig, kcutoff)
|
||||
|
||||
; Amplitude envelope
|
||||
aenv = madsr(iatt, idec, isus, irel)
|
||||
asig = asig * aenv * iamp
|
||||
|
||||
; DC blocker
|
||||
asig = dcblock2(asig)
|
||||
|
||||
outs asig, asig
|
||||
endin
|
||||
`;
|
||||
}
|
||||
|
||||
getParametersForCsound(params: Squine1Params): CsoundParameter[] {
|
||||
return [
|
||||
{ channelName: 'frequency', value: params.frequency },
|
||||
{ channelName: 'shapeAmount1', value: params.shapeAmount1 },
|
||||
{ channelName: 'shapeAmount2', value: params.shapeAmount2 },
|
||||
{ channelName: 'modulationDepth1', value: params.modulationDepth1 },
|
||||
{ channelName: 'modulationDepth2', value: params.modulationDepth2 },
|
||||
{ channelName: 'osc2Detune', value: params.osc2Detune },
|
||||
{ channelName: 'osc2Level', value: params.osc2Level },
|
||||
{ channelName: 'hpfCutoff', value: params.hpfCutoff },
|
||||
{ channelName: 'attack', value: params.attack },
|
||||
{ channelName: 'decay', value: params.decay },
|
||||
{ channelName: 'sustain', value: params.sustain },
|
||||
{ channelName: 'release', value: params.release }
|
||||
];
|
||||
}
|
||||
|
||||
randomParams(pitchLock?: PitchLock): Squine1Params {
|
||||
const frequency = pitchLock?.enabled ? pitchLock.frequency : 55 * Math.pow(2, Math.random() * 5);
|
||||
|
||||
return {
|
||||
frequency,
|
||||
shapeAmount1: 0.2 + Math.random() * 0.8,
|
||||
shapeAmount2: 0.2 + Math.random() * 0.8,
|
||||
modulationDepth1: 0.05 + Math.random() * 0.7,
|
||||
modulationDepth2: 0.05 + Math.random() * 0.7,
|
||||
osc2Detune: 0.0001 + Math.random() * 0.005,
|
||||
osc2Level: 0.01 + Math.random() * 0.15,
|
||||
hpfCutoff: frequency * 0.5 + Math.random() * frequency,
|
||||
attack: 0.001 + Math.random() * 0.05,
|
||||
decay: 0.05 + Math.random() * 0.3,
|
||||
sustain: 0.2 + Math.random() * 0.6,
|
||||
release: 0.01 + Math.random() * 0.3
|
||||
};
|
||||
}
|
||||
|
||||
mutateParams(params: Squine1Params, mutationAmount = 0.2, pitchLock?: PitchLock): Squine1Params {
|
||||
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));
|
||||
};
|
||||
|
||||
const newFreq = pitchLock?.enabled ? pitchLock.frequency : mutate(params.frequency, 55, 55 * Math.pow(2, 5));
|
||||
|
||||
return {
|
||||
frequency: newFreq,
|
||||
shapeAmount1: mutate(params.shapeAmount1, 0.2, 1.0),
|
||||
shapeAmount2: mutate(params.shapeAmount2, 0.2, 1.0),
|
||||
modulationDepth1: mutate(params.modulationDepth1, 0.05, 0.75),
|
||||
modulationDepth2: mutate(params.modulationDepth2, 0.05, 0.75),
|
||||
osc2Detune: mutate(params.osc2Detune, 0.0001, 0.0051),
|
||||
osc2Level: mutate(params.osc2Level, 0.01, 0.16),
|
||||
hpfCutoff: mutate(params.hpfCutoff, newFreq * 0.5, newFreq * 1.5),
|
||||
attack: mutate(params.attack, 0.001, 0.051),
|
||||
decay: mutate(params.decay, 0.05, 0.35),
|
||||
sustain: mutate(params.sustain, 0.2, 0.8),
|
||||
release: mutate(params.release, 0.01, 0.31)
|
||||
};
|
||||
}
|
||||
}
|
||||
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)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -45,11 +45,17 @@ export abstract class CsoundEngine<T = any> implements SynthEngine<T> {
|
||||
|
||||
await csound.terminateInstance();
|
||||
|
||||
const leftChannel = new Float32Array(audioBuffer.leftChannel);
|
||||
const rightChannel = new Float32Array(audioBuffer.rightChannel);
|
||||
let leftChannel = new Float32Array(audioBuffer.leftChannel);
|
||||
let rightChannel = new Float32Array(audioBuffer.rightChannel);
|
||||
|
||||
this.removeDCOffset(leftChannel, rightChannel);
|
||||
|
||||
const trimmed = this.trimToZeroCrossing(leftChannel, rightChannel, sampleRate);
|
||||
leftChannel = trimmed.left;
|
||||
rightChannel = trimmed.right;
|
||||
|
||||
// Apply short fade-in to prevent click at start
|
||||
this.applyFadeIn(leftChannel, rightChannel, sampleRate);
|
||||
this.applyFadeOut(leftChannel, rightChannel, sampleRate);
|
||||
|
||||
const peak = this.findPeak(leftChannel, rightChannel);
|
||||
if (peak > 0.001) {
|
||||
@ -218,22 +224,95 @@ e
|
||||
}
|
||||
}
|
||||
|
||||
private removeDCOffset(leftChannel: Float32Array, rightChannel: Float32Array): void {
|
||||
let leftSum = 0;
|
||||
let rightSum = 0;
|
||||
const length = leftChannel.length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
leftSum += leftChannel[i];
|
||||
rightSum += rightChannel[i];
|
||||
}
|
||||
|
||||
const leftDC = leftSum / length;
|
||||
const rightDC = rightSum / length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
leftChannel[i] -= leftDC;
|
||||
rightChannel[i] -= rightDC;
|
||||
}
|
||||
}
|
||||
|
||||
private trimToZeroCrossing(
|
||||
leftChannel: Float32Array,
|
||||
rightChannel: Float32Array,
|
||||
sampleRate: number
|
||||
): { left: Float32Array; right: Float32Array } {
|
||||
const maxSearchSamples = Math.min(Math.floor(sampleRate * 0.01), leftChannel.length);
|
||||
let trimIndex = 0;
|
||||
|
||||
for (let i = 1; i < maxSearchSamples; i++) {
|
||||
const prevL = leftChannel[i - 1];
|
||||
const currL = leftChannel[i];
|
||||
const prevR = rightChannel[i - 1];
|
||||
const currR = rightChannel[i];
|
||||
|
||||
if (
|
||||
(prevL <= 0 && currL >= 0) || (prevL >= 0 && currL <= 0) ||
|
||||
(prevR <= 0 && currR >= 0) || (prevR >= 0 && currR <= 0)
|
||||
) {
|
||||
trimIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (trimIndex > 0) {
|
||||
const newLeft = new Float32Array(leftChannel.length - trimIndex);
|
||||
const newRight = new Float32Array(rightChannel.length - trimIndex);
|
||||
newLeft.set(leftChannel.subarray(trimIndex));
|
||||
newRight.set(rightChannel.subarray(trimIndex));
|
||||
return { left: newLeft, right: newRight };
|
||||
}
|
||||
|
||||
return { left: leftChannel, right: rightChannel };
|
||||
}
|
||||
|
||||
private applyFadeIn(
|
||||
leftChannel: Float32Array,
|
||||
rightChannel: Float32Array,
|
||||
sampleRate: number
|
||||
): void {
|
||||
const fadeInMs = 5; // 5ms fade-in to prevent clicks
|
||||
const fadeInMs = 5;
|
||||
const fadeSamples = Math.floor((fadeInMs / 1000) * sampleRate);
|
||||
const actualFadeSamples = Math.min(fadeSamples, leftChannel.length);
|
||||
|
||||
for (let i = 0; i < actualFadeSamples; i++) {
|
||||
const gain = i / actualFadeSamples;
|
||||
const phase = i / actualFadeSamples;
|
||||
const gain = 0.5 - 0.5 * Math.cos(phase * Math.PI);
|
||||
leftChannel[i] *= gain;
|
||||
rightChannel[i] *= gain;
|
||||
}
|
||||
}
|
||||
|
||||
private applyFadeOut(
|
||||
leftChannel: Float32Array,
|
||||
rightChannel: Float32Array,
|
||||
sampleRate: number
|
||||
): void {
|
||||
const fadeOutMs = 5;
|
||||
const fadeSamples = Math.floor((fadeOutMs / 1000) * sampleRate);
|
||||
const actualFadeSamples = Math.min(fadeSamples, leftChannel.length);
|
||||
const startSample = leftChannel.length - actualFadeSamples;
|
||||
|
||||
for (let i = 0; i < actualFadeSamples; i++) {
|
||||
const sampleIndex = startSample + i;
|
||||
const phase = i / actualFadeSamples;
|
||||
const gain = 0.5 + 0.5 * Math.cos(phase * Math.PI);
|
||||
leftChannel[sampleIndex] *= gain;
|
||||
rightChannel[sampleIndex] *= gain;
|
||||
}
|
||||
}
|
||||
|
||||
protected randomRange(min: number, max: number): number {
|
||||
return min + Math.random() * (max - min);
|
||||
}
|
||||
|
||||
@ -26,6 +26,11 @@ import { RingCymbal } from './RingCymbal';
|
||||
import { AdditiveBass } from './AdditiveBass';
|
||||
import { FeedbackSnare } from './FeedbackSnare';
|
||||
import { CombResonator } from './CombResonator';
|
||||
import { LaserSweep } from './LaserSweep';
|
||||
import { Dripwater } from './Dripwater';
|
||||
import { Sub8 } from './Sub8';
|
||||
import { Form1 } from './Form1';
|
||||
import { Squine1 } from './Squine1';
|
||||
|
||||
export const engines: SynthEngine[] = [
|
||||
new Sample(),
|
||||
@ -55,4 +60,9 @@ export const engines: SynthEngine[] = [
|
||||
new SubtractiveThreeOsc(),
|
||||
new CombResonator(),
|
||||
new MassiveAdditive(),
|
||||
new LaserSweep(),
|
||||
new Dripwater(),
|
||||
new Sub8(),
|
||||
new Form1(),
|
||||
new Squine1(),
|
||||
];
|
||||
|
||||
27
src/lib/audio/processors/ExpFadeIn.ts
Normal file
27
src/lib/audio/processors/ExpFadeIn.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { AudioProcessor } from './AudioProcessor';
|
||||
|
||||
export class ExpFadeIn implements AudioProcessor {
|
||||
getName(): string {
|
||||
return 'Fade In (Exp)';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Applies an exponential fade from silence to current level';
|
||||
}
|
||||
|
||||
process(leftIn: Float32Array, rightIn: Float32Array): [Float32Array, Float32Array] {
|
||||
const length = leftIn.length;
|
||||
|
||||
const leftOut = new Float32Array(length);
|
||||
const rightOut = new Float32Array(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const t = i / length;
|
||||
const gain = 1.0 - Math.exp(-5.0 * t);
|
||||
leftOut[i] = leftIn[i] * gain;
|
||||
rightOut[i] = rightIn[i] * gain;
|
||||
}
|
||||
|
||||
return [leftOut, rightOut];
|
||||
}
|
||||
}
|
||||
27
src/lib/audio/processors/ExpFadeOut.ts
Normal file
27
src/lib/audio/processors/ExpFadeOut.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { AudioProcessor } from './AudioProcessor';
|
||||
|
||||
export class ExpFadeOut implements AudioProcessor {
|
||||
getName(): string {
|
||||
return 'Fade Out (Exp)';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Applies an exponential fade from current level to silence';
|
||||
}
|
||||
|
||||
process(leftIn: Float32Array, rightIn: Float32Array): [Float32Array, Float32Array] {
|
||||
const length = leftIn.length;
|
||||
|
||||
const leftOut = new Float32Array(length);
|
||||
const rightOut = new Float32Array(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const t = i / length;
|
||||
const gain = Math.exp(-5.0 * t);
|
||||
leftOut[i] = leftIn[i] * gain;
|
||||
rightOut[i] = rightIn[i] * gain;
|
||||
}
|
||||
|
||||
return [leftOut, rightOut];
|
||||
}
|
||||
}
|
||||
26
src/lib/audio/processors/LinearFadeIn.ts
Normal file
26
src/lib/audio/processors/LinearFadeIn.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import type { AudioProcessor } from './AudioProcessor';
|
||||
|
||||
export class LinearFadeIn implements AudioProcessor {
|
||||
getName(): string {
|
||||
return 'Fade In';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Applies a linear fade from silence to current level';
|
||||
}
|
||||
|
||||
process(leftIn: Float32Array, rightIn: Float32Array): [Float32Array, Float32Array] {
|
||||
const length = leftIn.length;
|
||||
|
||||
const leftOut = new Float32Array(length);
|
||||
const rightOut = new Float32Array(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const gain = i / length;
|
||||
leftOut[i] = leftIn[i] * gain;
|
||||
rightOut[i] = rightIn[i] * gain;
|
||||
}
|
||||
|
||||
return [leftOut, rightOut];
|
||||
}
|
||||
}
|
||||
26
src/lib/audio/processors/LinearFadeOut.ts
Normal file
26
src/lib/audio/processors/LinearFadeOut.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import type { AudioProcessor } from './AudioProcessor';
|
||||
|
||||
export class LinearFadeOut implements AudioProcessor {
|
||||
getName(): string {
|
||||
return 'Fade Out';
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'Applies a linear fade from current level to silence';
|
||||
}
|
||||
|
||||
process(leftIn: Float32Array, rightIn: Float32Array): [Float32Array, Float32Array] {
|
||||
const length = leftIn.length;
|
||||
|
||||
const leftOut = new Float32Array(length);
|
||||
const rightOut = new Float32Array(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const gain = 1.0 - i / length;
|
||||
leftOut[i] = leftIn[i] * gain;
|
||||
rightOut[i] = rightIn[i] * gain;
|
||||
}
|
||||
|
||||
return [leftOut, rightOut];
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,10 @@ import { Waveshaper } from './Waveshaper';
|
||||
import { DCOffsetRemover } from './DCOffsetRemover';
|
||||
import { TrimSilence } from './TrimSilence';
|
||||
import { Resonator } from './Resonator';
|
||||
import { LinearFadeOut } from './LinearFadeOut';
|
||||
import { ExpFadeOut } from './ExpFadeOut';
|
||||
import { LinearFadeIn } from './LinearFadeIn';
|
||||
import { ExpFadeIn } from './ExpFadeIn';
|
||||
|
||||
const processors: AudioProcessor[] = [
|
||||
new SegmentShuffler(),
|
||||
@ -53,6 +57,10 @@ const processors: AudioProcessor[] = [
|
||||
new DCOffsetRemover(),
|
||||
new TrimSilence(),
|
||||
new Resonator(),
|
||||
new LinearFadeOut(),
|
||||
new ExpFadeOut(),
|
||||
new LinearFadeIn(),
|
||||
new ExpFadeIn(),
|
||||
];
|
||||
|
||||
export function getRandomProcessor(): AudioProcessor {
|
||||
|
||||
Reference in New Issue
Block a user