diff --git a/assets/mods/basegame/items/itemid.csv b/assets/mods/basegame/items/itemid.csv index adb51f928..607cc3d0b 100644 --- a/assets/mods/basegame/items/itemid.csv +++ b/assets/mods/basegame/items/itemid.csv @@ -52,6 +52,7 @@ id;classname;tags 51;net.torvald.terrarum.modulebasegame.gameitems.ItemElectricWorkbench;FIXTURE,CRAFTING 52;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalSevenSeg;FIXTURE,SIGNAL 53;net.torvald.terrarum.modulebasegame.gameitems.ItemEngravingWorkbench;FIXTURE,CRAFTING +54;net.torvald.terrarum.modulebasegame.gameitems.ItemMechanicalTines;FIXTURE,MUSIC,SIGNAL # ingots 112;net.torvald.terrarum.modulebasegame.gameitems.IngotCopper;INGOT diff --git a/assets/mods/basegame/sprites/fixtures/mechanical_tines.tga b/assets/mods/basegame/sprites/fixtures/mechanical_tines.tga new file mode 100644 index 000000000..916a3bc87 --- /dev/null +++ b/assets/mods/basegame/sprites/fixtures/mechanical_tines.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fb1032052d51b08dd7351f6c9e5519a9772d3a62704193fccd34d0cade25e40 +size 4114 diff --git a/src/net/torvald/terrarum/audio/AudioBank.kt b/src/net/torvald/terrarum/audio/AudioBank.kt index d038d0788..93a025c24 100644 --- a/src/net/torvald/terrarum/audio/AudioBank.kt +++ b/src/net/torvald/terrarum/audio/AudioBank.kt @@ -7,6 +7,8 @@ import com.badlogic.gdx.utils.Disposable */ abstract class AudioBank : Disposable { + open val notCopyable: Boolean = false + protected val hash = System.nanoTime() abstract fun makeCopy(): AudioBank diff --git a/src/net/torvald/terrarum/audio/MusicCache.kt b/src/net/torvald/terrarum/audio/MusicCache.kt index cefa72186..e593ce90e 100644 --- a/src/net/torvald/terrarum/audio/MusicCache.kt +++ b/src/net/torvald/terrarum/audio/MusicCache.kt @@ -8,8 +8,10 @@ class MusicCache(val trackName: String) : Disposable { private val cache = HashMap() fun getOrPut(music: AudioBank?): AudioBank? { - if (music != null) + if (music != null && !music.notCopyable) return cache.getOrPut(music.name) { music.makeCopy() } + else if (music != null) + return music return null } diff --git a/src/net/torvald/terrarum/modulebasegame/audio/audiobank/AudioBankMusicBox.kt b/src/net/torvald/terrarum/modulebasegame/audio/audiobank/AudioBankMusicBox.kt index 33308548f..43a4e2520 100644 --- a/src/net/torvald/terrarum/modulebasegame/audio/audiobank/AudioBankMusicBox.kt +++ b/src/net/torvald/terrarum/modulebasegame/audio/audiobank/AudioBankMusicBox.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.modulebasegame.audio.audiobank import net.torvald.terrarum.App +import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.INGAME import net.torvald.terrarum.audio.AudioBank @@ -9,14 +10,21 @@ import net.torvald.terrarum.audio.AudioBank */ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) : AudioBank() { - private data class Msg(val tick: Long, val samplesL: FloatArray, val samplesR: FloatArray, var samplesDispatched: Int = 0) // in many cases, samplesL and samplesR will point to the same object + override val notCopyable = true + + // TODO don't store samples (1MB each!), store numbers instead and synthesize on readSamples() + internal data class Msg(val tick: Long, val samplesL: FloatArray, val samplesR: FloatArray, var samplesDispatched: Int = 0) { // in many cases, samplesL and samplesR will point to the same object + override fun toString(): String { + return "Msg(tick=$tick, samplesDispatched=$samplesDispatched)" + } + } override val name = "spieluhr" override val samplingRate = 48000 // 122880 // use 122880 to make each tick is 2048 samples override val channels = 1 private val getSample = // usage: getSample(noteNum 0..60) - InstrumentLoader.load("spieluhr", "basegame", "audio/effects/notes/spieluhr.ogg", 29) + InstrumentLoader.load("spieluhr", "basegame", "audio/effects/notes/spieluhr.ogg", 41) private val SAMPLES_PER_TICK = samplingRate / App.TICK_SPEED // should be 800 on default setting @@ -34,16 +42,26 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) return result } + init { + printdbg(this, "Note 0 length: ${getSample(0).first.size}") + printdbg(this, "Note 12 length: ${getSample(12).first.size}") + printdbg(this, "Note 24 length: ${getSample(24).first.size}") + printdbg(this, "Note 48 length: ${getSample(48).first.size}") + } + /** * Queues the notes such that they are played on the next tick */ override fun sendMessage(noteBits: Long) { + queue(INGAME.WORLD_UPDATE_TIMER + 1, noteBits) + } + + private fun queue(tick: Long, noteBits: Long) { if (noteBits == 0L) return - val tick = INGAME.WORLD_UPDATE_TIMER + 1 val notes = findSetBits(noteBits) - val buf = FloatArray(getSample(notes.first()).first.size) + val buf = FloatArray(getSample(0).first.size) // combine all those samples notes.forEach { note -> @@ -53,7 +71,8 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) } // actually queue it - messageQueue.add(Msg(tick, buf, buf)) + val msg = Msg(tick, buf, buf) + messageQueue.add(messageQueue.size, msg) } override fun currentPositionInSamples() = 0L @@ -62,6 +81,9 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) val tickCount = INGAME.WORLD_UPDATE_TIMER val bufferSize = bufferL.size + bufferL.fill(0f) + bufferR.fill(0f) + // only copy over the past and current messages messageQueue.filter { it.tick <= tickCount }.forEach { // copy over the samples @@ -73,44 +95,33 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) it.samplesDispatched += bufferSize } - // dequeue the finished messages - val messagesToKill = ArrayList(messageQueue.filter { it.samplesDispatched >= it.samplesL.size }) - if (messagesToKill.isNotEmpty()) messagesToKill.forEach { - messageQueue.remove(it) + var rc = 0 + while (rc < messageQueue.size) { + val it = messageQueue[rc] + if (it.samplesDispatched >= it.samplesL.size) { + messageQueue.removeAt(rc) + rc -= 1 + } + + rc += 1 } + printdbg(this, "Queuelen: ${messageQueue.size}") + return bufferSize } override fun reset() { - TODO("Not yet implemented") + messageQueue.clear() } override fun makeCopy(): AudioBank { - TODO("Not yet implemented") + throw UnsupportedOperationException() } override fun dispose() { - TODO("Not yet implemented") } - private fun prel(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): List { - return listOf( - n1, n2, n3, n4, n5, n3, n4, n5, - n1, n2, n3, n4, n5, n3, n4, n5 - ) - } - - private val testNotes = prel(0,0,0,0,0)+ - prel(24,28,31,36,40) + - prel(24, 26,33,28,41) + - prel(23,26,31,38,41) + - prel(24,28,31,36,40) + - prel(24,28,33,40,45) + - prel(24,26,30,33,38) + - prel(23,26,31,38,43) + - prel(23,24,28,31,36) + - prel(21,24,28,31,36) } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/audio/audiobank/InstrumentLoader.kt b/src/net/torvald/terrarum/modulebasegame/audio/audiobank/InstrumentLoader.kt index bee3d903f..f4edf8682 100644 --- a/src/net/torvald/terrarum/modulebasegame/audio/audiobank/InstrumentLoader.kt +++ b/src/net/torvald/terrarum/modulebasegame/audio/audiobank/InstrumentLoader.kt @@ -6,8 +6,12 @@ import net.torvald.terrarum.audio.AudioProcessBuf import net.torvald.terrarum.audio.audiobank.MusicContainer import net.torvald.terrarum.ceilToInt import net.torvald.terrarum.floorToInt +import org.dyn4j.Epsilon import java.lang.Math.pow +import kotlin.math.PI +import kotlin.math.absoluteValue import kotlin.math.roundToInt +import kotlin.math.sin /** * Creates 61-note (C1 to C5) pack of samples from a single file. Tuning system is 12-note Equal Temperament. @@ -17,7 +21,7 @@ import kotlin.math.roundToInt object InstrumentLoader { // 0 is C0 - private fun getStretch(noteNum: Int) = pow(2.0, (noteNum - 29) / 12.0) + private fun getRate(noteNum: Int) = pow(2.0, noteNum / 12.0) /** @@ -51,8 +55,8 @@ object InstrumentLoader { for (j in 0 until 61) { val i = j - initialNote - val rate = getStretch(i) - val sampleCount = (masterFile.totalSizeInSamples * rate).roundToInt() + val rate = getRate(i) + val sampleCount = (masterFile.totalSizeInSamples * (1.0 / rate)).roundToInt() val samplesL = FloatArray(sampleCount) val samplesR = if (isDualMono) samplesL else FloatArray(sampleCount) @@ -92,7 +96,7 @@ object InstrumentLoader { var weightedSum = 0.0 for (j in leftBound..rightBound) { - val w = AudioProcessBuf.L(t - j.toDouble()) + val w = L(t - j.toDouble()) akkuL += input[j] * w weightedSum += w } @@ -101,4 +105,14 @@ object InstrumentLoader { } } + + fun L(x: Double): Double { + return if (x.absoluteValue < Epsilon.E) + 1.0 + else if (-TAPS <= x && x < TAPS) + (TAPS * sin(PI * x) * sin(PI * x / TAPS)) / (PI * PI * x * x) + else + 0.0 + } + } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureMechanicalTines.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureMechanicalTines.kt new file mode 100644 index 000000000..afd9b0236 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureMechanicalTines.kt @@ -0,0 +1,132 @@ +package net.torvald.terrarum.modulebasegame.gameactors + +import net.torvald.terrarum.App +import net.torvald.terrarum.App.printdbg +import net.torvald.terrarum.INGAME +import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE +import net.torvald.terrarum.gameactors.AVKey +import net.torvald.terrarum.langpack.Lang +import net.torvald.terrarum.modulebasegame.audio.audiobank.AudioBankMusicBox +import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack + +/** + * Created by minjaesong on 2024-04-15. + */ +class FixtureMechanicalTines : Electric { + + constructor() : super( + BlockBox(BlockBox.NO_COLLISION, 2, 2), + nameFun = { Lang["ITEM_MECHANICAL_TINES"] } + ) + + @Transient private val audioBank = AudioBankMusicBox() + + @Transient private val track = App.audioMixer.getFreeTrackNoMatterWhat() + + init { + track.trackingTarget = this + track.currentTrack = audioBank + track.play() + + + val itemImage = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/mechanical_tines.tga") + density = 1400.0 + setHitboxDimension(TILE_SIZE * 2, TILE_SIZE * 2, 0, 0) + + makeNewSprite(TextureRegionPack(itemImage.texture, TILE_SIZE * 2, TILE_SIZE * 2)).let { + it.setRowsAndFrames(1,1) + } + + actorValue[AVKey.BASEMASS] = 20.0 + + + setWireSinkAt(0, 1, "digital_bit") + setWireSinkAt(1, 1, "network") + + despawnHook = { + track.stop() + track.currentTrack = null + track.trackingTarget = null + audioBank.dispose() + } + } + + @Transient private var testRollCursor = 0 + @Transient private val TICK_DIVISOR = 12 + + override fun updateSignal() { + // TODO update using network port + + + if (isSignalHigh(0, 1)) { + if (INGAME.WORLD_UPDATE_TIMER % TICK_DIVISOR == 0L) { + audioBank.sendMessage(testNotes[testRollCursor]) + testRollCursor = (testRollCursor + 1) % testNotes.size + } + } + } + + + + companion object { + @Transient val testNotes = + prel(24,28,31,36,40) + + prel(24,26,33,38,41) + + prel(23,26,31,38,41) + + prel(24,28,31,36,40) + + prel(24,28,33,40,45) + + prel(24,26,30,33,38) + + prel(23,26,31,38,43) + + prel(23,24,28,31,36) + + prel(21,24,28,31,36) + + prel(14,21,26,30,36) + + prel(19,23,26,31,35) + + prel(19,22,28,31,37) + + prel(17,21,26,33,38) + + prel(17,20,26,29,35) + + prel(16,19,24,31,36) + + prel(16,17,21,24,29) + + prel(14,17,21,24,29) + + prel( 7,14,19,23,29) + + prel(12,16,19,24,28) + + prel(12,19,22,24,28) + + prel( 5,17,21,24,28) + + prel( 6,12,21,24,27) + + prel( 8,17,23,24,26) + + prel( 7,17,19,23,26) + + prel( 7,16,19,24,28) + + prel( 7,14,19,24,29) + + prel( 7,14,19,23,29) + + prel( 7,15,21,24,30) + + prel( 7,16,19,24,31) + + prel( 7,14,19,24,29) + + prel( 7,14,19,23,29) + + prel( 0,12,19,22,28) + + end1( 0,12,17,21,24,29,21,17,14) + + end2( 0,11,31,35,38,41,26,29,28) + + end3( 0,12,28,31,36) + + private fun prel(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): List { + return listOf( + 1L shl n1, 1L shl n2, 1L shl n3, 1L shl n4, 1L shl n5, 1L shl n3, 1L shl n4, 1L shl n5, + 1L shl n1, 1L shl n2, 1L shl n3, 1L shl n4, 1L shl n5, 1L shl n3, 1L shl n4, 1L shl n5) + } + + private fun end1(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, n6: Int, n7: Int, n8: Int, n9: Int): List { + return listOf( + 1L shl n1, 1L shl n2, 1L shl n3, 1L shl n4, 1L shl n5, 1L shl n6, 1L shl n5, 1L shl n4, + 1L shl n5, 1L shl n7, 1L shl n8, 1L shl n7, 1L shl n8, 1L shl n9, 1L shl n8, 1L shl n9) + } + + private fun end2(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, n6: Int, n7: Int, n8: Int, n9: Int): List { + return listOf( + 1L shl n1, 1L shl n2, 1L shl n3, 1L shl n4, 1L shl n5, 1L shl n6, 1L shl n5, 1L shl n4, + 1L shl n5, 1L shl n4, 1L shl n3, 1L shl n4, 1L shl n7, 1L shl n8, 1L shl n9, 1L shl n7) + } + + private fun end3(vararg ns: Int): List { + return listOf(ns.map { 1L shl it }.fold(0L) { acc, note -> acc or note }) + List(15) { 0L } + } + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/ItemJukebox.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/ItemJukebox.kt index e44df35b4..120040118 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/ItemJukebox.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/ItemJukebox.kt @@ -36,4 +36,22 @@ class ItemMusicalTurntable(originalID: ItemID) : FixtureItemBase(originalID, "ne override var baseToolSize: Double? = baseMass override var originalName = "ITEM_TURNTABLE" +} + + +/** + * Created by minjaesong on 2024-04-15. + */ +class ItemMechanicalTines(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.FixtureMechanicalTines") { + + + override var baseMass = 20.0 + override val canBeDynamic = false + override val materialId = "" + override val itemImage: TextureRegion + get() = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/mechanical_tines.tga") + + override var baseToolSize: Double? = baseMass + override var originalName = "ITEM_MECHANICAL_TINES" + } \ No newline at end of file diff --git a/work_files/graphics/sprites/fixtures/mechanical_tines.kra b/work_files/graphics/sprites/fixtures/mechanical_tines.kra new file mode 100644 index 000000000..e62d0bcd6 --- /dev/null +++ b/work_files/graphics/sprites/fixtures/mechanical_tines.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dee0a2ea3c2e00f18b0a9da01bd3da9c4e1b533cc436fde764ca454c775f8701 +size 93298