factoring a bunch of math outside the SawOscillatorDevice class
This commit is contained in:
parent
578005f41f
commit
9dbf5000a7
3 changed files with 46 additions and 32 deletions
|
|
@ -0,0 +1,17 @@
|
|||
package net.liquidev.dawd3.audio.devices.oscillator
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
/** Utility for computing the derivative of a phase without artifacting. */
|
||||
class PhaseDerivative {
|
||||
private var previousPhase = 0f
|
||||
private var previousDeltaPhase = 0f
|
||||
|
||||
fun stepNextDerivative(phase: Float): Float {
|
||||
val deltaPhase = phase - previousPhase
|
||||
val smoothDeltaPhase = if (abs(deltaPhase) > 0.999) previousDeltaPhase else deltaPhase
|
||||
previousPhase = phase
|
||||
previousDeltaPhase = deltaPhase
|
||||
return smoothDeltaPhase
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package net.liquidev.dawd3.audio.devices.oscillator
|
|||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.*
|
||||
import net.minecraft.util.Identifier
|
||||
import kotlin.math.abs
|
||||
|
||||
class SawOscillatorDevice : Device<NoControls> {
|
||||
companion object : DeviceDescriptor {
|
||||
|
|
@ -16,12 +15,10 @@ class SawOscillatorDevice : Device<NoControls> {
|
|||
const val oversampling = 4
|
||||
}
|
||||
|
||||
|
||||
private val phase = InputPort()
|
||||
private val output = OutputPort(bufferCount = 1)
|
||||
|
||||
private var previousPhase = 0f
|
||||
private var previousDeltaPhase = 0f
|
||||
private val phaseDerivative = PhaseDerivative()
|
||||
|
||||
override fun process(sampleCount: Int, controls: NoControls) {
|
||||
val phaseBuffer = phase.getConnectedOutputBuffer(0, sampleCount)
|
||||
|
|
@ -29,23 +26,8 @@ class SawOscillatorDevice : Device<NoControls> {
|
|||
val outputBuffer = output.buffers[0].getOrReallocate(sampleCount)
|
||||
for (i in 0 until sampleCount) {
|
||||
val phase = phaseBuffer[i]
|
||||
val deltaPhase = phase - previousPhase
|
||||
|
||||
// Smooth out the delta phase at spikes, as then our PolyBLEP dt is no longer accurate.
|
||||
val smoothDeltaPhase = if (abs(deltaPhase) > 0.999) previousDeltaPhase else deltaPhase
|
||||
|
||||
previousPhase = phase
|
||||
previousDeltaPhase = deltaPhase
|
||||
|
||||
var accumulator = 0f
|
||||
for (sample in 0 until oversampling) {
|
||||
val t = sample.toFloat() / oversampling.toFloat()
|
||||
accumulator += saw(
|
||||
phase + smoothDeltaPhase * t,
|
||||
smoothDeltaPhase
|
||||
)
|
||||
}
|
||||
outputBuffer[i] = accumulator / oversampling
|
||||
val deltaPhase = phaseDerivative.stepNextDerivative(phase)
|
||||
outputBuffer[i] = oversample(phase, deltaPhase, oversampling, ::saw)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,17 +40,6 @@ class SawOscillatorDevice : Device<NoControls> {
|
|||
}
|
||||
}
|
||||
|
||||
private fun polyBLEP(t: Float, dt: Float) =
|
||||
if (t < dt) {
|
||||
val tt = t / dt
|
||||
tt + tt - tt * tt - 1f
|
||||
} else if (t > 1f - dt) {
|
||||
val tt = (t - 1f) / dt
|
||||
tt * tt + tt + tt + 1f
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
|
||||
override fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {
|
||||
visit(phasePort, phase)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package net.liquidev.dawd3.audio.devices.oscillator
|
||||
|
||||
inline fun oversample(
|
||||
phase: Float,
|
||||
deltaPhase: Float,
|
||||
sampleCount: Int,
|
||||
oscillator: (Float, Float) -> Float,
|
||||
): Float {
|
||||
var accumulator = 0f
|
||||
for (sample in 0 until sampleCount) {
|
||||
val t = sample.toFloat() / sampleCount.toFloat()
|
||||
accumulator += oscillator(phase + deltaPhase * t, deltaPhase)
|
||||
}
|
||||
return accumulator / sampleCount.toFloat()
|
||||
}
|
||||
|
||||
fun polyBLEP(t: Float, dt: Float) =
|
||||
if (t < dt) {
|
||||
val tt = t / dt
|
||||
tt + tt - tt * tt - 1f
|
||||
} else if (t > 1f - dt) {
|
||||
val tt = (t - 1f) / dt
|
||||
tt * tt + tt + tt + 1f
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
Reference in a new issue