slightly better
This commit is contained in:
@ -12,6 +12,7 @@ class BytebeatProcessor extends AudioWorkletProcessor {
|
||||
this.sampleRate = 8000
|
||||
this.duration = 4
|
||||
this.loopLength = this.sampleRate * this.duration
|
||||
this.playbackRate = 1.0
|
||||
this.error = false
|
||||
|
||||
this.port.onmessage = (event) => {
|
||||
@ -32,6 +33,9 @@ class BytebeatProcessor extends AudioWorkletProcessor {
|
||||
case 'loopLength':
|
||||
this.loopLength = value
|
||||
break
|
||||
case 'playbackRate':
|
||||
this.playbackRate = value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +75,7 @@ class BytebeatProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
this.t++
|
||||
this.t += this.playbackRate
|
||||
if (this.loopLength > 0 && this.t >= this.loopLength) {
|
||||
this.t = 0
|
||||
}
|
||||
|
||||
60
public/worklets/output-limiter.js
Normal file
60
public/worklets/output-limiter.js
Normal file
@ -0,0 +1,60 @@
|
||||
class OutputLimiter extends AudioWorkletProcessor {
|
||||
static get parameterDescriptors() {
|
||||
return [
|
||||
{
|
||||
name: 'threshold',
|
||||
defaultValue: 0.8,
|
||||
minValue: 0.1,
|
||||
maxValue: 1.0,
|
||||
automationRate: 'k-rate'
|
||||
},
|
||||
{
|
||||
name: 'makeup',
|
||||
defaultValue: 1.5,
|
||||
minValue: 1.0,
|
||||
maxValue: 3.0,
|
||||
automationRate: 'k-rate'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
softClip(x, threshold) {
|
||||
if (Math.abs(x) < threshold) {
|
||||
return x
|
||||
}
|
||||
const sign = x < 0 ? -1 : 1
|
||||
const scaled = (Math.abs(x) - threshold) / (1 - threshold)
|
||||
return sign * (threshold + (1 - threshold) * Math.tanh(scaled))
|
||||
}
|
||||
|
||||
process(inputs, outputs, parameters) {
|
||||
const input = inputs[0]
|
||||
const output = outputs[0]
|
||||
|
||||
if (input.length === 0 || output.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
const threshold = parameters.threshold[0]
|
||||
const makeup = parameters.makeup[0]
|
||||
|
||||
for (let channel = 0; channel < input.length; channel++) {
|
||||
const inputChannel = input[channel]
|
||||
const outputChannel = output[channel]
|
||||
|
||||
for (let i = 0; i < inputChannel.length; i++) {
|
||||
let sample = inputChannel[i] * makeup
|
||||
sample = this.softClip(sample, threshold)
|
||||
outputChannel[i] = sample
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('output-limiter', OutputLimiter)
|
||||
103
public/worklets/svf-processor.js
Normal file
103
public/worklets/svf-processor.js
Normal file
@ -0,0 +1,103 @@
|
||||
class SVFProcessor extends AudioWorkletProcessor {
|
||||
static get parameterDescriptors() {
|
||||
return [
|
||||
{
|
||||
name: 'frequency',
|
||||
defaultValue: 1000,
|
||||
minValue: 20,
|
||||
maxValue: 20000,
|
||||
automationRate: 'k-rate'
|
||||
},
|
||||
{
|
||||
name: 'resonance',
|
||||
defaultValue: 0.707,
|
||||
minValue: 0.05,
|
||||
maxValue: 10,
|
||||
automationRate: 'k-rate'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.mode = 'lowpass'
|
||||
this.ic1eq = 0
|
||||
this.ic2eq = 0
|
||||
|
||||
this.port.onmessage = (event) => {
|
||||
const { type, value } = event.data
|
||||
if (type === 'mode') {
|
||||
this.mode = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process(inputs, outputs, parameters) {
|
||||
const input = inputs[0]
|
||||
const output = outputs[0]
|
||||
|
||||
if (input.length === 0 || output.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
const inputChannel = input[0]
|
||||
const outputChannel = output[0]
|
||||
const frequency = parameters.frequency
|
||||
const resonance = parameters.resonance
|
||||
|
||||
const sampleRate = globalThis.sampleRate || 44100
|
||||
const isFreqArray = frequency.length > 1
|
||||
const isResArray = resonance.length > 1
|
||||
|
||||
for (let i = 0; i < inputChannel.length; i++) {
|
||||
const freq = isFreqArray ? frequency[i] : frequency[0]
|
||||
const res = isResArray ? resonance[i] : resonance[0]
|
||||
|
||||
const g = Math.tan(Math.PI * Math.min(freq, sampleRate * 0.49) / sampleRate)
|
||||
const k = 1 / Math.max(0.05, res)
|
||||
|
||||
const inputSample = inputChannel[i]
|
||||
|
||||
const a1 = 1 / (1 + g * (g + k))
|
||||
const a2 = g * a1
|
||||
const a3 = g * a2
|
||||
|
||||
const v3 = inputSample - this.ic2eq
|
||||
const v1 = a1 * this.ic1eq + a2 * v3
|
||||
const v2 = this.ic2eq + a2 * this.ic1eq + a3 * v3
|
||||
|
||||
this.ic1eq = 2 * v1 - this.ic1eq
|
||||
this.ic2eq = 2 * v2 - this.ic2eq
|
||||
|
||||
const lp = v2
|
||||
const bp = v1
|
||||
const hp = inputSample - k * v1 - v2
|
||||
const notch = inputSample - k * v1
|
||||
|
||||
let outSample
|
||||
switch (this.mode) {
|
||||
case 'lowpass':
|
||||
outSample = lp
|
||||
break
|
||||
case 'highpass':
|
||||
outSample = hp
|
||||
break
|
||||
case 'bandpass':
|
||||
outSample = bp
|
||||
break
|
||||
case 'notch':
|
||||
outSample = notch
|
||||
break
|
||||
default:
|
||||
outSample = lp
|
||||
}
|
||||
|
||||
outputChannel[i] = outSample
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('svf-processor', SVFProcessor)
|
||||
Reference in New Issue
Block a user