volume randomiser for smelter and furnace

This commit is contained in:
minjaesong
2024-02-12 16:13:23 +09:00
parent a14f2697fb
commit 5f1e63e370
7 changed files with 83 additions and 31 deletions

View File

@@ -27,6 +27,7 @@ private data class Frac(var nom: Int, val denom: Int) {
*/ */
class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray) -> Int?, val onAudioFinished: () -> Unit) { class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray) -> Int?, val onAudioFinished: () -> Unit) {
var pitch: Float = 1f
var playbackSpeed = 1f var playbackSpeed = 1f
var jitterMode = 0 // 0: none, 1: phono, 2: tape var jitterMode = 0 // 0: none, 1: phono, 2: tape
var jitterIntensity = 0f var jitterIntensity = 0f
@@ -47,8 +48,8 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
else 1f / (1f - this) else 1f / (1f - this)
} }
private val playRate: Float internal val playRate: Float
get() = playbackSpeed.coerceIn(0.5f, 2f)/*(playbackSpeed * when (jitterMode) { // disabled until arraycopy negative length bug is resolved get() = (playbackSpeed * pitch).coerceIn(0.5f, 2f)/*(playbackSpeed * when (jitterMode) { // disabled until arraycopy negative length bug is resolved
1 -> jitterMode1(totalSamplesPlayed.toFloat()) 1 -> jitterMode1(totalSamplesPlayed.toFloat())
else -> 0f else -> 0f
} * jitterIntensity).coerceIn(0.5f, 2f)*/ } * jitterIntensity).coerceIn(0.5f, 2f)*/
@@ -133,9 +134,9 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
} }
} }
var validSamplesInBuf = 0 var validSamplesInBuf = 0; private set
var totalSamplesPlayed = 0L private var totalSamplesPlayed = 0L
private val finL = FloatArray(fetchSize + 2 * PADSIZE) private val finL = FloatArray(fetchSize + 2 * PADSIZE)
private val finR = FloatArray(fetchSize + 2 * PADSIZE) private val finR = FloatArray(fetchSize + 2 * PADSIZE)
@@ -155,7 +156,8 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
} }
fun fetchBytes() { fun fetchBytes() {
val readCount = if (validSamplesInBuf < App.audioBufferSize) fetchSize else 0 val samplesInBuf = validSamplesInBuf
val readCount = if (samplesInBuf < App.audioBufferSize) fetchSize else 0
val writeCount = (readCount / q).roundToInt() val writeCount = (readCount / q).roundToInt()
fun getFromReadBuf(i: Int, bytesRead: Int) = if (i < bytesRead) readBuf[i].toUint() else 0 fun getFromReadBuf(i: Int, bytesRead: Int) = if (i < bytesRead) readBuf[i].toUint() else 0
@@ -195,16 +197,16 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
resampleBlock(finL, finR, fmidL, fmidR, writeCount) resampleBlock(finL, finR, fmidL, fmidR, writeCount)
// fill in the output buffers // fill in the output buffers
System.arraycopy(fmidL, 0, foutL, validSamplesInBuf, writeCount) System.arraycopy(fmidL, 0, foutL, samplesInBuf, writeCount)
System.arraycopy(fmidR, 0, foutR, validSamplesInBuf, writeCount) System.arraycopy(fmidR, 0, foutR, samplesInBuf, writeCount)
} }
else { else {
// fill in the output buffers // fill in the output buffers
System.arraycopy(finL, 0, foutL, validSamplesInBuf, writeCount) System.arraycopy(finL, 0, foutL, samplesInBuf, writeCount)
System.arraycopy(finR, 0, foutR, validSamplesInBuf, writeCount) System.arraycopy(finR, 0, foutR, samplesInBuf, writeCount)
} }
validSamplesInBuf += writeCount validSamplesInBuf = samplesInBuf + writeCount
totalSamplesPlayed += writeCount totalSamplesPlayed += writeCount
} }
} }
@@ -216,20 +218,22 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
} }
fun getLR(): Pair<FloatArray, FloatArray> { fun getLR(): Pair<FloatArray, FloatArray> {
val samplesInBuf = validSamplesInBuf
// copy into the out // copy into the out
val outL = FloatArray(App.audioBufferSize) val outL = FloatArray(App.audioBufferSize)
val outR = FloatArray(App.audioBufferSize) val outR = FloatArray(App.audioBufferSize)
System.arraycopy(foutL, 0, outL, 0, App.audioBufferSize) System.arraycopy(foutL, 0, outL, 0, App.audioBufferSize)
System.arraycopy(foutR, 0, outR, 0, App.audioBufferSize) System.arraycopy(foutR, 0, outR, 0, App.audioBufferSize)
// shift bytes in the fout // shift bytes in the fout
System.arraycopy(foutL, App.audioBufferSize, foutL, 0, validSamplesInBuf - App.audioBufferSize) System.arraycopy(foutL, App.audioBufferSize, foutL, 0, samplesInBuf - App.audioBufferSize)
System.arraycopy(foutR, App.audioBufferSize, foutR, 0, validSamplesInBuf - App.audioBufferSize) System.arraycopy(foutR, App.audioBufferSize, foutR, 0, samplesInBuf - App.audioBufferSize)
for (i in validSamplesInBuf until App.audioBufferSize) { for (i in samplesInBuf until App.audioBufferSize) {
foutL[i] = 0f foutL[i] = 0f
foutR[i] = 0f foutR[i] = 0f
} }
// decrement necessary variables // decrement necessary variables
validSamplesInBuf -= App.audioBufferSize validSamplesInBuf = samplesInBuf - App.audioBufferSize
return outL to outR return outL to outR
} }

View File

@@ -36,8 +36,8 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
internal var streamBuf: AudioProcessBuf? = null internal var streamBuf: AudioProcessBuf? = null
internal var jitterMode = 0 // internal var jitterMode = 0
internal var jitterIntensity = 0f // internal var jitterIntensity = 0f
private var fout1 = listOf(emptyBuf, emptyBuf) private var fout1 = listOf(emptyBuf, emptyBuf)
@@ -88,8 +88,8 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
bytesRead bytesRead
}, { purgeStreamBuf() }).also { }, { purgeStreamBuf() }).also {
it.jitterMode = jitterMode // it.jitterMode = jitterMode
it.jitterIntensity = jitterIntensity // it.jitterIntensity = jitterIntensity
} }
} }
@@ -168,7 +168,7 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
track.processor.streamBuf?.playbackSpeed = dopplerFactor.toFloat() track.processor.streamBuf?.playbackSpeed = dopplerFactor.toFloat()
// printdbg("dist=$distFromActor\tvol=${fullscaleToDecibels(vol)}\tcutoff=${(track.filters[1] as Lowpass).cutoff}\tdopplerFactor=$dopplerFactor") // printdbg("dist=$distFromActor\tdopplerFactor=$dopplerFactor")
} }
} }
else { else {
@@ -208,7 +208,7 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
} }
// source channel: skip processing if there's no active input // source channel: skip processing if there's no active input
// else if (track.getSidechains().any { it != null && !it.isBus && !it.isMaster && !it.streamPlaying } && !track.streamPlaying) { // else if (track.getSidechains().any { it != null && !it.isBus && !it.isMaster && !it.streamPlaying } && !track.streamPlaying) {
else if (!track.streamPlaying || streamBuf == null) { else if (!track.streamPlaying || streamBuf == null || streamBuf!!.validSamplesInBuf < App.audioBufferSize) {
samplesL1 = emptyBuf samplesL1 = emptyBuf
samplesR1 = emptyBuf samplesR1 = emptyBuf

View File

@@ -108,6 +108,7 @@ abstract class Actor : Comparable<Actor>, Runnable {
it.stop() it.stop()
it.filters[0] = NullFilter it.filters[0] = NullFilter
it.filters[1] = NullFilter it.filters[1] = NullFilter
it.processor.streamBuf?.pitch = 1f
} }
despawnHook(this) despawnHook(this)

View File

@@ -106,6 +106,10 @@ class FixtureFurnaceAndAnvil : FixtureBase, CraftingStation {
spawnTimer += delta spawnTimer += delta
// update sound randomiser
volRand.update(delta)
// manage audio // manage audio
getTrackByAudio(static).let { getTrackByAudio(static).let {
if (it != null && !it.isPlaying) { if (it != null && !it.isPlaying) {
@@ -118,11 +122,12 @@ class FixtureFurnaceAndAnvil : FixtureBase, CraftingStation {
if (it.filters[filterIndex] !is Gain) // just in case... if (it.filters[filterIndex] !is Gain) // just in case...
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = Gain(0f)
(it.filters[filterIndex] as Gain).gain = 0.4f // TODO randomsied undulation (it.filters[filterIndex] as Gain).gain = 0.4f * volRand.get()
} }
} }
@Transient private val filterIndex = 0 @Transient private val filterIndex = 0
@Transient private val volRand = ParamRandomiser(0.8f, 0.4f)
override fun dispose() { override fun dispose() {
super.dispose() super.dispose()

View File

@@ -248,10 +248,10 @@ class FixtureJukebox : Electric, PlaysMusic {
} }
fun setJitter(it: TerrarumAudioMixerTrack?, mode: Int, intensity: Float) { fun setJitter(it: TerrarumAudioMixerTrack?, mode: Int, intensity: Float) {
it?.let { // it?.let {
it.processor.jitterMode = mode // it.processor.jitterMode = mode
it.processor.jitterIntensity = intensity // it.processor.jitterIntensity = intensity
} // }
} }
fun unloadConvolver(actor: Actor, filterIndex: Int, music: MusicContainer?) { fun unloadConvolver(actor: Actor, filterIndex: Int, music: MusicContainer?) {
@@ -263,10 +263,10 @@ class FixtureJukebox : Electric, PlaysMusic {
} }
fun unsetJitter(actor: Actor, music: MusicContainer?) { fun unsetJitter(actor: Actor, music: MusicContainer?) {
actor.musicTracks[music]?.let { // actor.musicTracks[music]?.let {
it.processor.jitterMode = 0 // it.processor.jitterMode = 0
it.processor.jitterIntensity = 0f // it.processor.jitterIntensity = 0f
} // }
} }
} }
} }

View File

@@ -4,11 +4,11 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.jme3.math.FastMath
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.spriteanimation.SheetSpriteAnimation import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.audio.MusicContainer import net.torvald.terrarum.audio.MusicContainer
import net.torvald.terrarum.audio.dsp.Gain import net.torvald.terrarum.audio.dsp.Gain
@@ -289,6 +289,9 @@ class FixtureSmelterBasic : FixtureBase, CraftingStation {
spawnTimer = 0f spawnTimer = 0f
// update sound randomiser
volRand.update(delta)
// manage audio // manage audio
getTrackByAudio(static).let { getTrackByAudio(static).let {
@@ -307,12 +310,13 @@ class FixtureSmelterBasic : FixtureBase, CraftingStation {
if (it.filters[filterIndex] !is Gain) // just in case... if (it.filters[filterIndex] !is Gain) // just in case...
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = Gain(0f)
(it.filters[filterIndex] as Gain).gain = (it.maxVolume * temperature).toFloat() // TODO randomsied undulation (it.filters[filterIndex] as Gain).gain = (it.maxVolume * temperature * volRand.get()).toFloat()
} }
} }
@Transient private val filterIndex = 0 @Transient private val filterIndex = 0
@Transient private val volRand = ParamRandomiser(0.8f, 0.4f)
override fun dispose() { override fun dispose() {
super.dispose() super.dispose()

View File

@@ -0,0 +1,38 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.Gdx
import com.jme3.math.FastMath
/**
* Created by minjaesong on 2024-02-12.
*/
class ParamRandomiser(val base: Float, val mult: Float) {
@Transient private var rngBase0 = Math.random().toFloat() // initial cycle phase (xxxxFuncX)
@Transient private var rngBase1 = getNewRandom() // flicker P0, etc
@Transient private var rngBase2 = getNewRandom() // flicker P1, etc
@Transient private val domain = 18f/64f
private fun getNewRandom() = base + Math.random().toFloat() * mult
fun update(delta: Float) {
// FPS-time compensation
if (Gdx.graphics.framesPerSecond > 0) {
rngBase0 += delta
}
// reset timer
if (rngBase0 > domain) {
rngBase0 -= domain
// flicker related
rngBase1 = rngBase2
rngBase2 = getNewRandom()
}
}
fun get(): Float {
val funcY = FastMath.interpolateLinear(rngBase0 / domain, rngBase1, rngBase2)
return funcY
}
}