diff --git a/assets/mods/basegame/audio/convolution/EchoThief - CedarCreekWinery.bin b/assets/mods/basegame/audio/convolution/EchoThief - CedarCreekWinery.bin
deleted file mode 100644
index c630ccbf1..000000000
Binary files a/assets/mods/basegame/audio/convolution/EchoThief - CedarCreekWinery.bin and /dev/null differ
diff --git a/assets/mods/basegame/audio/convolution/EchoThief - Cranbrook Art Museum.bin b/assets/mods/basegame/audio/convolution/EchoThief - Cranbrook Art Museum.bin
deleted file mode 100644
index 4133b39e6..000000000
--- a/assets/mods/basegame/audio/convolution/EchoThief - Cranbrook Art Museum.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2008760876a186741e201c752beb957b9e1e91f219e455adf72557abe09c600a
-size 578760
diff --git a/assets/mods/basegame/audio/convolution/EchoThief - PurgatoryChasm.bin b/assets/mods/basegame/audio/convolution/EchoThief - PurgatoryChasm.bin
new file mode 100644
index 000000000..ef2c85262
--- /dev/null
+++ b/assets/mods/basegame/audio/convolution/EchoThief - PurgatoryChasm.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:046106dc38f21455ad5f8101f748385901fb25546c9c93ce8ad1f58332bd0684
+size 234672
diff --git a/assets/mods/basegame/audio/convolution/EchoThief - WaterplacePark-trimmed.bin b/assets/mods/basegame/audio/convolution/EchoThief - WaterplacePark-trimmed.bin
new file mode 100644
index 000000000..0d93912d8
--- /dev/null
+++ b/assets/mods/basegame/audio/convolution/EchoThief - WaterplacePark-trimmed.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:69791eadb78c723dab7d732a2acfe6af1edaaa55d70290406640332d72677180
+size 524288
diff --git a/assets/mods/basegame/audio/convolution/EchoThief - WoodruffLane.bin b/assets/mods/basegame/audio/convolution/EchoThief - WoodruffLane.bin
deleted file mode 100644
index d4d2f1564..000000000
Binary files a/assets/mods/basegame/audio/convolution/EchoThief - WoodruffLane.bin and /dev/null differ
diff --git a/src/net/torvald/terrarum/CreditSingleton.kt b/src/net/torvald/terrarum/CreditSingleton.kt
index 25c9bfe6e..ed941118d 100644
--- a/src/net/torvald/terrarum/CreditSingleton.kt
+++ b/src/net/torvald/terrarum/CreditSingleton.kt
@@ -198,22 +198,6 @@ SOFTWARE.
-$BULLET Ambient sound recordings:
-
- - ambient_forest_01.ogg
- - ambient_meadow_01.ogg
- - ambient_windy_01.ogg
- - ambient_woods_01.ogg
- - crickets_01.ogg
- - crickets_02.ogg
-
-Copyright (C) 2012, 2013, 2015, 2016, 2017 Klankbeeld
-Sound from
-
-
-
-
-
$BULLET GraalVM Community Edition
GraalVM Community Edition consists of multiple modules. The software as a whole,
@@ -253,6 +237,7 @@ SOFTWARE.
$BULLET Apache Commons Codec
+
Copyright 2002-2023 The Apache Software Foundation
This product includes software developed at
@@ -263,6 +248,7 @@ The Apache Software Foundation (https://www.apache.org/).
$BULLET Apache Commons CSV
+
Copyright 2005-2023 The Apache Software Foundation
This product includes software developed at
@@ -282,6 +268,30 @@ The Apache Software Foundation (http://www.apache.org/).
This product includes software developed for Orekit by
CS Systèmes d'Information (http://www.c-s.fr/)
Copyright 2010-2012 CS Systèmes d'Information
+
+
+
+
+
+$BULLET Ambient sound recordings:
+
+ - ambient_forest_01.ogg
+ - ambient_meadow_01.ogg
+ - ambient_windy_01.ogg
+ - ambient_woods_01.ogg
+ - crickets_01.ogg
+ - crickets_02.ogg
+
+Copyright (C) 2012, 2013, 2015, 2016, 2017 Klankbeeld
+Sound from
+
+
+
+
+
+$BULLET EchoThief Impulse Response Library
+
+Copyright 2013-2023 Chris Warren
""").split('\n')
diff --git a/src/net/torvald/terrarum/audio/AudioMixer.kt b/src/net/torvald/terrarum/audio/AudioMixer.kt
index 3e9db0e1c..5b3dea110 100644
--- a/src/net/torvald/terrarum/audio/AudioMixer.kt
+++ b/src/net/torvald/terrarum/audio/AudioMixer.kt
@@ -131,14 +131,14 @@ object AudioMixer: Disposable {
}
convolveBusOpen.filters[0] = Highpass(80f)
- convolveBusOpen.filters[1] = Convolv(ModMgr.getFile("basegame", "audio/convolution/EchoThief - Cranbrook Art Museum.bin"))
- convolveBusOpen.filters[2] = Gain(decibelsToFullscale(18.0).toFloat())
- convolveBusOpen.volume = 0.5
+ convolveBusOpen.filters[1] = Convolv(ModMgr.getFile("basegame", "audio/convolution/EchoThief - PurgatoryChasm.bin"))
+ convolveBusOpen.filters[2] = Gain(decibelsToFullscale(18.0).toFloat()) // don't make it too loud; it'll sound like a shit
+ convolveBusOpen.volume = 0.5 // will be controlled by the other updater which surveys the world
convolveBusCave.filters[0] = Highpass(80f)
- convolveBusCave.filters[1] = Convolv(ModMgr.getFile("basegame", "audio/convolution/EchoThief - CedarCreekWinery.bin"))
+ convolveBusCave.filters[1] = Convolv(ModMgr.getFile("basegame", "audio/convolution/EchoThief - WaterplacePark-trimmed.bin"))
convolveBusCave.filters[2] = Gain(decibelsToFullscale(18.0).toFloat())
- convolveBusCave.volume = 0.5
+ convolveBusCave.volume = 0.5 // will be controlled by the other updater which surveys the world
fadeBus.addSidechainInput(sumBus, 1.0 / 3.0)
fadeBus.addSidechainInput(convolveBusOpen, 2.0 / 3.0)
diff --git a/src/net/torvald/terrarum/audio/FFT.kt b/src/net/torvald/terrarum/audio/FFT.kt
index c0fe64e8d..204071495 100644
--- a/src/net/torvald/terrarum/audio/FFT.kt
+++ b/src/net/torvald/terrarum/audio/FFT.kt
@@ -1,15 +1,20 @@
package net.torvald.terrarum.audio
-import org.apache.commons.math3.exception.MathIllegalStateException
import org.apache.commons.math3.transform.DftNormalization
import org.apache.commons.math3.transform.TransformType
-import org.apache.commons.math3.util.FastMath
-data class FComplex(var re: Float = 0f, var im: Float = 0f) {
- operator fun times(other: FComplex) = FComplex(
- this.re * other.re - this.im * other.im,
- this.re * other.im + this.im * other.re
- )
+class ComplexArray(val res: FloatArray, val ims: FloatArray) {
+ val indices: IntProgression
+ get() = 0 until size
+ val size: Int
+ get() = res.size
+
+ operator fun times(other: ComplexArray): ComplexArray {
+ val l = size
+ val re = FloatArray(l) { res[it] * other.res[it] - ims[it] * other.ims[it] }
+ val im = FloatArray(l) { res[it] * other.ims[it] + ims[it] * other.res[it] }
+ return ComplexArray(re, im)
+ }
}
/**
@@ -20,53 +25,16 @@ data class FComplex(var re: Float = 0f, var im: Float = 0f) {
object FFT {
// org.apache.commons.math3.transform.FastFouriesTransformer.java:370
- fun fft(signal: FloatArray): Array {
- val dataRI = arrayOf(signal.copyOf(), FloatArray(signal.size))
-
+ fun fft(signal: FloatArray): ComplexArray {
+ val dataRI = ComplexArray(signal.copyOf(), FloatArray(signal.size))
transformInPlace(dataRI, DftNormalization.STANDARD, TransformType.FORWARD)
-
- val output = dataRI.toComplexArray()
-
- return getComplex(output, false)
+ return dataRI
}
// org.apache.commons.math3.transform.FastFouriesTransformer.java:404
- fun ifftAndGetReal(y: Array): FloatArray {
- val dataRI = Array(2) { FloatArray(y.size) }
- for (i in y.indices) {
- dataRI[0][i] = y[i].re
- dataRI[1][i] = y[i].im
- }
-
- transformInPlace(dataRI, DftNormalization.STANDARD, TransformType.INVERSE)
-
- return dataRI[0]
- }
-
- private fun Array.toComplexArray(): Array {
- return Array(this[0].size) {
- FComplex(this[0][it], this[1][it])
- }
- }
-
- // com.github.psambit9791.jdsp.transform.FastFourier.java:190
- /**
- * Returns the complex value of the fast fourier transformed sequence
- * @param onlyPositive Set to True if non-mirrored output is required
- * @throws java.lang.ExceptionInInitializerError if called before executing transform() method
- * @return Complex[] The complex FFT output
- */
- @Throws(ExceptionInInitializerError::class)
- fun getComplex(output: Array, onlyPositive: Boolean): Array {
- val dftout: Array = if (onlyPositive) {
- val numBins: Int = output.size / 2 + 1
- Array(numBins) { FComplex() }
- }
- else {
- Array(output.size) { FComplex() }
- }
- System.arraycopy(output, 0, dftout, 0, dftout.size)
- return dftout
+ fun ifftAndGetReal(y: ComplexArray): FloatArray {
+ transformInPlace(y, DftNormalization.STANDARD, TransformType.INVERSE)
+ return y.res
}
// org.apache.commons.math3.transform.FastFouriesTransformer.java:214
@@ -86,12 +54,12 @@ object FFT {
* @throws MathIllegalArgumentException if the number of data points is not
* a power of two
*/
- private fun transformInPlace(dataRI: Array, normalization: DftNormalization, type: TransformType) {
- val dataR = dataRI[0]
- val dataI = dataRI[1]
+ private fun transformInPlace(dataRI: ComplexArray, normalization: DftNormalization, type: TransformType) {
+ val dataR = dataRI.res
+ val dataI = dataRI.ims
val n = dataR.size
- if (n == 1) {
+ /*if (n == 1) {
return
}
else if (n == 2) {
@@ -108,7 +76,7 @@ object FFT {
dataI[1] = srcI0 - srcI1
normalizeTransformedData(dataRI, normalization, type)
return
- }
+ }*/
bitReversalShuffle2(dataR, dataI)
@@ -230,25 +198,26 @@ object FFT {
* @param type the type of transform (forward, inverse) which resulted in the specified data
*/
private fun normalizeTransformedData(
- dataRI: Array,
+ dataRI: ComplexArray,
normalization: DftNormalization, type: TransformType
) {
- val dataR = dataRI[0]
- val dataI = dataRI[1]
+ val dataR = dataRI.res
+ val dataI = dataRI.ims
val n = dataR.size
- assert(dataI.size == n)
- when (normalization) {
- DftNormalization.STANDARD -> if (type == TransformType.INVERSE) {
- val scaleFactor = 1f / n.toFloat()
- var i = 0
- while (i < n) {
- dataR[i] *= scaleFactor
- dataI[i] *= scaleFactor
- i++
+// assert(dataI.size == n)
+// when (normalization) {
+// DftNormalization.STANDARD ->
+ if (type == TransformType.INVERSE) {
+ val scaleFactor = 1f / n.toFloat()
+ var i = 0
+ while (i < n) {
+ dataR[i] *= scaleFactor
+ dataI[i] *= scaleFactor
+ i++
+ }
}
- }
- DftNormalization.UNITARY -> {
+ /* DftNormalization.UNITARY -> {
val scaleFactor = (1.0 / FastMath.sqrt(n.toDouble())).toFloat()
var i = 0
while (i < n) {
@@ -259,7 +228,7 @@ object FFT {
}
else -> throw MathIllegalStateException()
- }
+ }*/
}
/**
diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt
index 9b9861bab..9e9898d1d 100644
--- a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt
+++ b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt
@@ -313,30 +313,29 @@ class Reverb(val delayMS: Float = 36f, var feedback: Float = 0.92f, var lowpass:
class Convolv(ir: File, val gain: Float = 1f / 256f): TerrarumAudioFilter() {
private val fftLen: Int
- private val convFFT: Array>
- private val convFFTpartd: Array>> // index: Channel, partition, frequencies
+ private val convFFT: Array
+// private val convFFTpartd: Array> // index: Channel, partition, frequencies
private val inbuf: Array
private val BLOCKSIZE = BUFFER_SIZE / 4
var processingSpeed = 1f; private set
- private val partSizes: IntArray
- private val partOffsets: IntArray
+// private val partSizes: IntArray
+// private val partOffsets: IntArray
init {
if (!ir.exists()) {
throw IllegalArgumentException("Impulse Response file '${ir.path}' does not exist.")
}
- val sampleCount = ir.length().toInt() / 8
+ val sampleCount = (ir.length().toInt() / 8)//.coerceAtMost(65536)
fftLen = FastMath.nextPowerOfTwo(sampleCount)
-// println("IR Sample Count = $sampleCount; FFT Length = $fftLen")
+ println("IR '${ir.path}' Sample Count = $sampleCount; FFT Length = $fftLen")
val conv = Array(2) { FloatArray(fftLen) }
inbuf = Array(2) { FloatArray(fftLen) }
-// outbuf = Array(2) { DoubleArray(fftLen) }
ir.inputStream().let {
for (i in 0 until sampleCount) {
@@ -362,31 +361,10 @@ class Convolv(ir: File, val gain: Float = 1f / 256f): TerrarumAudioFilter() {
// println("convFFT Length = ${convFFT[0].size}")
- if (fftLen < BUFFER_SIZE) // buffer size is always 4x the samples in the buffer
- throw Error("FIR size is too small (minimum: $BUFFER_SIZE samples)")
-
- val partitions = ArrayList()
- var cnt = fftLen
- while (cnt > BUFFER_SIZE / 4) {
- cnt /= 2
- partitions.add(cnt)
- }
- partitions.add(cnt)
-
- partSizes = partitions.reversed().toIntArray()
- partOffsets = partSizes.clone().also { it[0] = 0 }
-
- // allocate arrays
- convFFTpartd = Array(2) { ch ->
- Array(partSizes.size) { partNo ->
- Array(partSizes[partNo]) {
- convFFT[ch][partOffsets[partNo] + it]
- }
- }
- }
}
+
/**
* https://thewolfsound.com/fast-convolution-fft-based-overlap-add-overlap-save-partitioned/
*/
@@ -398,12 +376,19 @@ class Convolv(ir: File, val gain: Float = 1f / 256f): TerrarumAudioFilter() {
push(inbuf[ch].applyGain(gain), this.inbuf[ch])
val inputFFT = FFT.fft(this.inbuf[ch])
-
- val Y = multiply(inputFFT, convFFT[ch])
+ val Y = inputFFT * convFFT[ch]
val y = FFT.ifftAndGetReal(Y)
-
val u = y.takeLast(BLOCKSIZE).toFloatArray()
+
+ /*val inputFFTs = FFT.fft(this.inbuf[ch]).sliceUnevenly()
+ val u = convFFTpartd[ch].zip(inputFFTs).map { (convFFT, inputFFT) ->
+ val Y = multiply(inputFFT, convFFT)
+ FFT.ifftAndGetReal(Y)
+ }.last().takeLast(BLOCKSIZE).toFloatArray()*/
+
+
+
System.arraycopy(u, 0, outbuf[ch], 0, BLOCKSIZE)
}
val t2 = System.nanoTime()
@@ -412,10 +397,6 @@ class Convolv(ir: File, val gain: Float = 1f / 256f): TerrarumAudioFilter() {
processingSpeed = realtime / ptime
}
- private fun multiply(X: Array, H: Array): Array {
- return Array(X.size) { X[it] * H[it] }
- }
-
}
object XYtoMS: TerrarumAudioFilter() {
diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt
index 1aa622f8d..dee69c3d3 100644
--- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt
+++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt
@@ -20,7 +20,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, v
const val SAMPLING_RATE = 48000
const val SAMPLING_RATEF = 48000f
const val SAMPLING_RATED = 48000.0
- const val BUFFER_SIZE = 65536 // n ms -> 384 * n
+ const val BUFFER_SIZE = 512*4 // n ms -> 384 * n
}
val hash = getHashStr()