OK ready
This commit is contained in:
@ -35,6 +35,16 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
this.pitchLFODepth = 0.1
|
||||
this.pitchLFOBaseRate = 2.0
|
||||
|
||||
this.sampleHoldValue = 0
|
||||
this.sampleHoldCounter = 0
|
||||
this.sampleHoldInterval = 500
|
||||
this.driftValue = 0
|
||||
this.perlinA = Math.random() * 2 - 1
|
||||
this.perlinB = Math.random() * 2 - 1
|
||||
this.perlinPhase = 0
|
||||
this.perlinInterval = 2000
|
||||
this.chaosX = 0.5
|
||||
|
||||
this.port.onmessage = (event) => {
|
||||
const { type, value } = event.data
|
||||
switch (type) {
|
||||
@ -72,6 +82,13 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
this.phase4 = 0
|
||||
this.sampleCount = 0
|
||||
this.feedbackSample = 0
|
||||
this.sampleHoldValue = 0
|
||||
this.sampleHoldCounter = 0
|
||||
this.driftValue = 0
|
||||
this.perlinA = Math.random() * 2 - 1
|
||||
this.perlinB = Math.random() * 2 - 1
|
||||
this.perlinPhase = 0
|
||||
this.chaosX = 0.5
|
||||
break
|
||||
case 'loopLength':
|
||||
this.loopLength = value
|
||||
@ -96,6 +113,32 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
return normalizedPhase % 1 < 0.5 ? 1 : -1
|
||||
case 3: // sawtooth
|
||||
return 2 * (normalizedPhase % 1) - 1
|
||||
case 4: // sample & hold random (glitchy)
|
||||
this.sampleHoldCounter++
|
||||
if (this.sampleHoldCounter >= this.sampleHoldInterval) {
|
||||
this.sampleHoldValue = Math.random() * 2 - 1
|
||||
this.sampleHoldCounter = 0
|
||||
this.sampleHoldInterval = Math.floor(100 + Math.random() * 900)
|
||||
}
|
||||
return this.sampleHoldValue
|
||||
case 5: // drift (random walk)
|
||||
this.driftValue += (Math.random() - 0.5) * 0.002
|
||||
this.driftValue = Math.max(-1, Math.min(1, this.driftValue))
|
||||
return this.driftValue
|
||||
case 6: // perlin noise (smooth random)
|
||||
this.perlinPhase++
|
||||
if (this.perlinPhase >= this.perlinInterval) {
|
||||
this.perlinA = this.perlinB
|
||||
this.perlinB = Math.random() * 2 - 1
|
||||
this.perlinPhase = 0
|
||||
this.perlinInterval = Math.floor(1000 + Math.random() * 2000)
|
||||
}
|
||||
const t = this.perlinPhase / this.perlinInterval
|
||||
const smoothT = t * t * (3 - 2 * t)
|
||||
return this.perlinA + (this.perlinB - this.perlinA) * smoothT
|
||||
case 7: // chaos (logistic map)
|
||||
this.chaosX = 3.9 * this.chaosX * (1 - this.chaosX)
|
||||
return this.chaosX * 2 - 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
@ -103,7 +146,7 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
synthesize(algorithm) {
|
||||
const TWO_PI = Math.PI * 2
|
||||
const sampleRate = 44100
|
||||
const sampleRate = globalThis.sampleRate || 44100
|
||||
|
||||
const avgDiff = (Math.abs(this.opLevel1 - this.opLevel3) + Math.abs(this.opLevel2 - this.opLevel4)) / (2 * 255)
|
||||
const pitchLFORate = this.pitchLFOBaseRate * (0.3 + avgDiff * 1.4)
|
||||
@ -129,6 +172,17 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
const level3 = (this.opLevel3 / 255.0) * (1 + this.lfoDepth * lfo3)
|
||||
const level4 = (this.opLevel4 / 255.0) * (1 + this.lfoDepth * lfo4)
|
||||
|
||||
const nyquist = sampleRate / 2
|
||||
const maxCarrierFreq = Math.max(
|
||||
modulatedBaseFreq * this.frequencyRatios[0],
|
||||
modulatedBaseFreq * this.frequencyRatios[1],
|
||||
modulatedBaseFreq * this.frequencyRatios[2],
|
||||
modulatedBaseFreq * this.frequencyRatios[3]
|
||||
)
|
||||
const antiAliasFactor = Math.min(1.0, nyquist / (maxCarrierFreq * 5))
|
||||
const modDepth = 10 * antiAliasFactor
|
||||
const modDepthLight = 1.5 * antiAliasFactor
|
||||
|
||||
this.lfoPhase1 += (this.lfoRate1 * TWO_PI) / sampleRate
|
||||
this.lfoPhase2 += (this.lfoRate2 * TWO_PI) / sampleRate
|
||||
this.lfoPhase3 += (this.lfoRate3 * TWO_PI) / sampleRate
|
||||
@ -157,12 +211,12 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 1: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod2) * level3
|
||||
const mod3 = op3 * 10
|
||||
const op4 = Math.sin(this.phase4 + mod3 + this.feedbackSample * this.feedback * 10) * level4
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod3 + this.feedbackSample * this.feedback * modDepth) * level4
|
||||
output = op4
|
||||
this.feedbackSample = op4
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -174,10 +228,10 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 2: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const op3 = Math.sin(this.phase3) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod3) * level4
|
||||
output = (op2 + op4) * 0.5
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -189,9 +243,9 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 3: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod2) * level3
|
||||
const op4 = Math.sin(this.phase4) * level4
|
||||
output = (op3 + op4) * 0.5
|
||||
@ -204,11 +258,11 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 4: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod1 + mod2) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod3) * level4
|
||||
output = op4
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -220,11 +274,11 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 5: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod1) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod2 + mod3) * level4
|
||||
output = op4
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -236,7 +290,7 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 6: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const op3 = Math.sin(this.phase3) * level3
|
||||
const op4 = Math.sin(this.phase4) * level4
|
||||
@ -250,11 +304,11 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 7: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const mod2 = op2 * 1.5
|
||||
const mod2 = op2 * modDepthLight
|
||||
const op3 = Math.sin(this.phase3 + mod1) * level3
|
||||
const mod3 = op3 * 1.5
|
||||
const mod3 = op3 * modDepthLight
|
||||
const op4 = Math.sin(this.phase4 + mod2 + mod3) * level4
|
||||
output = op4
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -266,10 +320,10 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 8: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod1) * level3
|
||||
const op2 = Math.sin(this.phase2) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod2) * level4
|
||||
output = (op3 + op4) * 0.5
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -281,10 +335,10 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 9: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod1) * level4
|
||||
const op2 = Math.sin(this.phase2) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod2) * level3
|
||||
output = (op3 + op4) * 0.5
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -296,10 +350,10 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 10: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const op3 = Math.sin(this.phase3) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod3) * level4
|
||||
output = (op2 + op4) * 0.5
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -312,9 +366,9 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
case 11: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const op2 = Math.sin(this.phase2) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod2) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod3) * level4
|
||||
output = (op1 + op4) * 0.5
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -326,9 +380,9 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 12: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod2) * level4
|
||||
const op3 = Math.sin(this.phase3) * level3
|
||||
output = (op3 + op4) * 0.5
|
||||
@ -341,11 +395,11 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 13: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2 + mod1) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3 + mod1) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod2 + mod3) * level4
|
||||
output = op4
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -357,11 +411,11 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 14: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op3 = Math.sin(this.phase3) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod3) * level4
|
||||
const mod4 = op4 * 1.5
|
||||
const mod4 = op4 * modDepthLight
|
||||
const op2 = Math.sin(this.phase2 + mod1 + mod4) * level2
|
||||
output = op2
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -373,11 +427,11 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
|
||||
case 15: {
|
||||
const op1 = Math.sin(this.phase1) * level1
|
||||
const mod1 = op1 * 10
|
||||
const mod1 = op1 * modDepth
|
||||
const op2 = Math.sin(this.phase2) * level2
|
||||
const mod2 = op2 * 10
|
||||
const mod2 = op2 * modDepth
|
||||
const op3 = Math.sin(this.phase3) * level3
|
||||
const mod3 = op3 * 10
|
||||
const mod3 = op3 * modDepth
|
||||
const op4 = Math.sin(this.phase4 + mod1 + mod2 + mod3) * level4
|
||||
output = op4
|
||||
this.phase1 += freq1 * this.playbackRate
|
||||
@ -391,11 +445,10 @@ class FMProcessor extends AudioWorkletProcessor {
|
||||
output = 0
|
||||
}
|
||||
|
||||
const TWO_PI_LIMIT = TWO_PI * 10
|
||||
if (this.phase1 > TWO_PI_LIMIT) this.phase1 -= TWO_PI_LIMIT
|
||||
if (this.phase2 > TWO_PI_LIMIT) this.phase2 -= TWO_PI_LIMIT
|
||||
if (this.phase3 > TWO_PI_LIMIT) this.phase3 -= TWO_PI_LIMIT
|
||||
if (this.phase4 > TWO_PI_LIMIT) this.phase4 -= TWO_PI_LIMIT
|
||||
this.phase1 = this.phase1 % TWO_PI
|
||||
this.phase2 = this.phase2 % TWO_PI
|
||||
this.phase3 = this.phase3 % TWO_PI
|
||||
this.phase4 = this.phase4 % TWO_PI
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user