Add chorus effect implementation to ChipProcessor
Introduced chorus effect parameters (`rate`, `depth`, `mix`) and integrated real-time processing. Updated `PatchDTO` and `MainView` to support chorus controls and visualization. Enhanced MIDI handling for dynamic chorus adjustments.
This commit is contained in:
@@ -5,8 +5,12 @@ package nl.astraeus.vst.chip
|
||||
import nl.astraeus.midi.message.SortedTimedMidiMessageList
|
||||
import nl.astraeus.midi.message.TimedMidiMessage
|
||||
import nl.astraeus.tba.SlicedByteArray
|
||||
import nl.astraeus.vst.*
|
||||
import nl.astraeus.vst.ADSR
|
||||
import nl.astraeus.vst.AudioWorkletProcessor
|
||||
import nl.astraeus.vst.currentTime
|
||||
import nl.astraeus.vst.midi.MidiMessageHandler
|
||||
import nl.astraeus.vst.registerProcessor
|
||||
import nl.astraeus.vst.sampleRate
|
||||
import org.khronos.webgl.Float32Array
|
||||
import org.khronos.webgl.get
|
||||
import org.khronos.webgl.set
|
||||
@@ -17,7 +21,13 @@ import kotlin.math.sin
|
||||
|
||||
val POLYPHONICS = 20
|
||||
val PI2 = PI * 2
|
||||
|
||||
var chorusRate = 0.25
|
||||
var chorusDepth = 0.0
|
||||
var chorusMix = 0.0
|
||||
var chorusPhase = 0.0
|
||||
val leftChorusBuffer = Float32Array(sampleRate)
|
||||
val rightChorusBuffer = Float32Array(sampleRate)
|
||||
var chorusIndex = 0
|
||||
@ExperimentalJsExport
|
||||
@JsExport
|
||||
class PlayingNote(
|
||||
@@ -74,6 +84,14 @@ class VstChipProcessor : AudioWorkletProcessor() {
|
||||
var amFreq = 0.0
|
||||
var amAmp = 0.0
|
||||
|
||||
var chorusRate = 0.25
|
||||
var chorusDepth = 0.0
|
||||
var chorusMix = 0.0
|
||||
var chorusPhase = 0.0
|
||||
val leftChorusBuffer = Float32Array(sampleRate)
|
||||
val rightChorusBuffer = Float32Array(sampleRate)
|
||||
var chorusIndex = 0
|
||||
|
||||
var attack = 0.1
|
||||
var decay = 0.2
|
||||
var sustain = 0.5
|
||||
@@ -159,6 +177,20 @@ class VstChipProcessor : AudioWorkletProcessor() {
|
||||
addHandler(0xb0, 0x50) { b1, b2, b3 ->
|
||||
feedback = b3 / 127.0
|
||||
}
|
||||
addHandler(0xb0, 0x51) { b1, b2, b3 ->
|
||||
chorusRate = 0.01 + (b3 / 127.0) * 99.99
|
||||
}
|
||||
addHandler(0xb0, 0x52) { b1, b2, b3 ->
|
||||
chorusDepth = b3 / 127.0
|
||||
}
|
||||
addHandler(0xb0, 0x53) { b1, b2, b3 ->
|
||||
chorusMix = b3 / 127.0
|
||||
}
|
||||
addHandler(0xb0, 123) { b1, b2, b3 ->
|
||||
for (note in notes) {
|
||||
note?.noteRelease = currentTime
|
||||
}
|
||||
}
|
||||
addHandler(0xb0, 123) { b1, b2, b3 ->
|
||||
for (note in notes) {
|
||||
note?.noteRelease = currentTime
|
||||
@@ -418,6 +450,52 @@ class VstChipProcessor : AudioWorkletProcessor() {
|
||||
}
|
||||
*/
|
||||
|
||||
if (chorusMix > 0.0 && chorusDepth > 0.0) {
|
||||
val baseDelaySamples = (sampleRate * 0.018).toInt()
|
||||
val maxModulationSamples = sampleRate * 0.012 * chorusDepth
|
||||
|
||||
for (i in 0 until samples) {
|
||||
val dryLeft = left[i]
|
||||
val dryRight = right[i]
|
||||
|
||||
val leftLfo = sin(chorusPhase)
|
||||
val rightLfo = sin(chorusPhase + PI)
|
||||
|
||||
val leftDelaySamples = baseDelaySamples + (leftLfo * maxModulationSamples).toInt()
|
||||
val rightDelaySamples = baseDelaySamples + (rightLfo * maxModulationSamples).toInt()
|
||||
|
||||
val leftReadIndex =
|
||||
(chorusIndex + leftChorusBuffer.length - leftDelaySamples) % leftChorusBuffer.length
|
||||
val rightReadIndex =
|
||||
(chorusIndex + rightChorusBuffer.length - rightDelaySamples) % rightChorusBuffer.length
|
||||
|
||||
val wetLeft = leftChorusBuffer[leftReadIndex]
|
||||
val wetRight = rightChorusBuffer[rightReadIndex]
|
||||
|
||||
left[i] = dryLeft * (1f - chorusMix.toFloat()) + wetLeft * chorusMix.toFloat()
|
||||
right[i] = dryRight * (1f - chorusMix.toFloat()) + wetRight * chorusMix.toFloat()
|
||||
|
||||
leftChorusBuffer[chorusIndex] = dryLeft
|
||||
rightChorusBuffer[chorusIndex] = dryRight
|
||||
|
||||
chorusIndex++
|
||||
chorusIndex %= leftChorusBuffer.length
|
||||
|
||||
chorusPhase += PI2 * chorusRate / sampleRate
|
||||
if (chorusPhase > PI2) {
|
||||
chorusPhase -= PI2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i in 0 until samples) {
|
||||
leftChorusBuffer[chorusIndex] = left[i]
|
||||
rightChorusBuffer[chorusIndex] = right[i]
|
||||
|
||||
chorusIndex++
|
||||
chorusIndex %= leftChorusBuffer.length
|
||||
}
|
||||
}
|
||||
|
||||
val delaySamples = (delay * leftDelayBuffer.length).toInt()
|
||||
for (i in 0 until samples) {
|
||||
if (delaySamples > 0) {
|
||||
|
||||
Reference in New Issue
Block a user