From 49fc723e668a47e5b4b0411016b1b94599ad4c00 Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Mon, 26 Sep 2016 14:50:15 +0900 Subject: [PATCH] antialiased squarewave generator Former-commit-id: c7d69891460f8f04681c28e1d05421ec1a93379d Former-commit-id: 5767a006dd9221f1839eec3b1b0223670262f8fd --- .../torvald/terrarum/StateTestingSandbox.kt | 127 ++++++++++++++++-- src/net/torvald/terrarum/Terrarum.kt | 4 +- 2 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/net/torvald/terrarum/StateTestingSandbox.kt b/src/net/torvald/terrarum/StateTestingSandbox.kt index b1102618d..114a86428 100644 --- a/src/net/torvald/terrarum/StateTestingSandbox.kt +++ b/src/net/torvald/terrarum/StateTestingSandbox.kt @@ -1,11 +1,24 @@ package net.torvald.terrarum +import com.jme3.math.FastMath +import net.torvald.terrarum.gameactors.floorInt +import net.torvald.terrarum.gameactors.roundInt +import net.torvald.terrarum.virtualcomputer.terminal.ALException import org.apache.commons.csv.CSVRecord +import org.lwjgl.BufferUtils +import org.lwjgl.openal.AL +import org.lwjgl.openal.AL10 import org.newdawn.slick.GameContainer import org.newdawn.slick.Graphics import org.newdawn.slick.state.BasicGameState import org.newdawn.slick.state.StateBasedGame +import java.io.ByteArrayInputStream +import java.nio.ByteBuffer +import java.util.* +import javax.sound.sampled.AudioFormat +import javax.sound.sampled.AudioInputStream +import javax.sound.sampled.AudioSystem /** * Created by minjaesong on 16-09-05. @@ -13,28 +26,118 @@ import org.newdawn.slick.state.StateBasedGame class StateTestingSandbox : BasicGameState() { - override fun init(container: GameContainer?, game: StateBasedGame?) { + playTone() + } + private val sampleRate = 22050 + private var beepSource: Int? = null + private var beepBuffer: Int? = null + + /** + * @param duration : milliseconds + */ + private fun makeAudioData(duration: Int, freq: Float): ByteBuffer { + val audioData = BufferUtils.createByteBuffer(duration.times(sampleRate).div(1000)) + + val realDuration = duration * sampleRate / 1000 + val chopSize = freq * 2f / sampleRate + + val amp = Math.max(4600f / freq, 1f) + val nHarmonics = 4 + + val transitionThre = 1150f + + if (freq < transitionThre) { // chopper generator (for low freq) + for (x in 0..realDuration - 1) { + var sine: Float = amp * FastMath.cos(FastMath.PI * x * chopSize) + if (sine > 1f) sine = 1f + else if (sine < -1f) sine = -1f + audioData.put( + (0.5f + 0.5f * sine).times(0xFF).toByte() + ) + } + } + else { // harmonics generator (for high freq) + for (x in 0..realDuration - 1) { + var sine: Float = 0f + for (k in 0..nHarmonics) { // mix only odd harmonics to make squarewave + sine += (1f / (2*k + 1)) * + FastMath.sin((2*k + 1) * FastMath.PI * x * chopSize) + } + audioData.put( + (0.5f + 0.5f * sine).times(0xFF).toByte() + ) + } + } + + audioData.rewind() + + return audioData + } + + var audioData: ByteBuffer? = null + + private fun playTone() { + if (audioData == null) audioData = makeAudioData(5000, 27.5f) + + + if (!AL.isCreated()) AL.create() + + + // Clear error stack. + AL10.alGetError() + + beepBuffer = AL10.alGenBuffers() + checkALError() + + try { + AL10.alBufferData(beepBuffer!!, AL10.AL_FORMAT_MONO8, audioData, sampleRate) + checkALError() + + beepSource = AL10.alGenSources() + checkALError() + + try { + AL10.alSourceQueueBuffers(beepSource!!, beepBuffer!!) + checkALError() + + AL10.alSource3f(beepSource!!, AL10.AL_POSITION, 0f, 0f, 1f) + AL10.alSourcef(beepSource!!, AL10.AL_REFERENCE_DISTANCE, 1f) + AL10.alSourcef(beepSource!!, AL10.AL_MAX_DISTANCE, 1f) + AL10.alSourcef(beepSource!!, AL10.AL_GAIN, 0.3f) + checkALError() + + AL10.alSourcePlay(beepSource!!) + checkALError() + + } + catch (e: ALException) { + AL10.alDeleteSources(beepSource!!) + } + } + catch (e: ALException) { + if (beepSource != null) AL10.alDeleteSources(beepSource!!) + } } override fun update(container: GameContainer?, game: StateBasedGame?, delta: Int) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + // Custom implementation of Util.checkALError() that uses our custom exception. + private fun checkALError() { + val errorCode = AL10.alGetError() + if (errorCode != AL10.AL_NO_ERROR) { + throw ALException(errorCode) + } + } + + override fun getID() = Terrarum.STATE_ID_TEST_SHIT override fun render(container: GameContainer?, game: StateBasedGame?, g: Graphics?) { + } - private fun intVal(rec: CSVRecord, s: String): Int { - var ret = -1 - try { - ret = Integer.decode(rec.get(s))!! - } - catch (e: NullPointerException) { - } - - return ret - } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 88dcd9242..662a5af96 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -100,8 +100,8 @@ constructor(gamename: String) : StateBasedGame(gamename) { gc.graphics.clear() // clean up any 'dust' in the buffer - addState(StateVTTest()) - //addState(StateTestingSandbox()) + //addState(StateVTTest()) + addState(StateTestingSandbox()) //addState(StateSplash()) //addState(StateMonitorCheck()) //addState(StateFontTester())