Working beeper (it can send morse)

Former-commit-id: 02553a52405ef4bba0d3f8f781f710b6c5b3ae16
Former-commit-id: 6cee7d2b44563a664756651d73a4b1ddfa70cf2f
This commit is contained in:
Song Minjae
2016-09-26 23:28:15 +09:00
parent 49fc723e66
commit bd4c95b321
12 changed files with 242 additions and 101 deletions

View File

@@ -20,13 +20,15 @@ import org.newdawn.slick.state.StateBasedGame
class StateVTTest : BasicGameState() {
// HiRes: 100x64, LoRes: 80x25
val vt = SimpleTextTerminal(SimpleTextTerminal.GREEN, 80, 25, colour = false, hires = false)
val computerInside = BaseTerrarumComputer(vt)
val computerInside = BaseTerrarumComputer()
val vt = SimpleTextTerminal(SimpleTextTerminal.GREEN, 80, 25, computerInside, colour = false, hires = false)
val vtUI = Image(vt.displayW, vt.displayH)
init {
computerInside.attachTerminal(vt)
}
override fun init(container: GameContainer, game: StateBasedGame) {

View File

@@ -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())

View File

@@ -950,7 +950,7 @@ sandbox._G = sandbox
package.path = "/net/torvald/terrarum/virtualcomputer/assets/lua/?.lua;" .. package.path
-- global variables
_G._VERSION = "Luaj-jse 5.2.3"
_G._VERSION = "Luaj-jse 3.0.1 (Lua 5.2.3)"
_G.EMDASH = string.char(0xC4)
_G.UNCHECKED = string.char(0x9C) -- box unchecked
_G.CHECKED = string.char(0x9D) -- box checked
@@ -962,6 +962,8 @@ _G.DC3 = string.char(19) -- dim grey
_G.DC4 = string.char(20) -- light grey
_G.DLE = string.char(16) -- default error colour
_G.runscript = function(s, src, ...)
if s:byte(1) == 27 then error("Bytecode execution is prohibited.") end
local code, reason = load(s, src)
if code then
@@ -971,6 +973,8 @@ _G.runscript = function(s, src, ...)
end
end
_G.__scanMode__ = "UNINIT" -- part of inputstream implementation
_G.bell = function(patn) term.bell(patn or ".") end
_G.beep = _G.bell
local screenbufferdim = term.width() * term.height()
local screencolours = 4
@@ -995,6 +999,9 @@ computer.freeMemory = function() return totalMemory() - getMemory() end
-- load libraries that coded in Lua
require("ROMLIB")
-- POST passed, initialise beeper
beep "."
-- load bios, if any
if fs.exists(computer.bootloader) then shell.run(computer.bootloader) end
-- halt/run luaprompt upon the termination of bios.

View File

@@ -1,9 +1,11 @@
package net.torvald.terrarum.virtualcomputer.computer
import com.jme3.math.FastMath
import li.cil.repack.org.luaj.vm2.Globals
import li.cil.repack.org.luaj.vm2.LuaError
import li.cil.repack.org.luaj.vm2.LuaTable
import li.cil.repack.org.luaj.vm2.LuaValue
import li.cil.repack.org.luaj.vm2.lib.TwoArgFunction
import li.cil.repack.org.luaj.vm2.lib.ZeroArgFunction
import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform
import net.torvald.terrarum.KVHashMap
@@ -12,9 +14,13 @@ import net.torvald.terrarum.virtualcomputer.luaapi.*
import net.torvald.terrarum.virtualcomputer.terminal.*
import net.torvald.terrarum.virtualcomputer.worldobject.ComputerPartsCodex
import net.torvald.terrarum.virtualcomputer.worldobject.FixtureComputerBase
import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL
import org.lwjgl.openal.AL10
import org.newdawn.slick.GameContainer
import org.newdawn.slick.Input
import java.io.*
import java.nio.ByteBuffer
/**
* A part that makes "computer fixture" actually work
@@ -24,11 +30,12 @@ import java.io.*
* @param term : terminal that is connected to the computer fixtures, null if not connected any.
* Created by minjaesong on 16-09-10.
*/
class BaseTerrarumComputer(val term: Teletype? = null) {
class BaseTerrarumComputer() {
val DEBUG_UNLIMITED_MEM = false
val luaJ_globals: Globals = JsePlatform.debugGlobals()
lateinit var luaJ_globals: Globals
private set
var termOut: PrintStream? = null
private set
@@ -59,6 +66,9 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
lateinit var input: Input
private set
lateinit var term: Teletype
private set
init {
computerValue["memslot0"] = 4864 // -1 indicates mem slot is empty
computerValue["memslot1"] = -1 // put index of item here
@@ -82,12 +92,17 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
// boot device
computerValue["boot"] = computerValue.getAsString("hda")!!
if (term != null) initSandbox(term)
}
fun attachTerminal(term: Teletype) {
this.term = term
initSandbox(term)
}
fun initSandbox(term: Teletype) {
luaJ_globals = JsePlatform.debugGlobals()
termOut = TerminalPrintStream(term)
termErr = TerminalPrintStream(term)
termIn = TerminalInputStream(term)
@@ -200,6 +215,7 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
else
throw IllegalArgumentException("Unsupported mode: $mode")
chunk.call()
}
catch (e: LuaError) {
@@ -216,4 +232,119 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
return LuaValue.valueOf(computer.memSize)
}
}
class EmitTone(val computer: BaseTerrarumComputer) : TwoArgFunction() {
override fun call(millisec: LuaValue, freq: LuaValue): LuaValue {
computer.playTone(millisec.toint(), freq.tofloat())
return LuaValue.NONE
}
}
///////////////////
// BEEPER DRIVER //
///////////////////
private val sampleRate = 22050
private var beepSource: Int? = null
private var beepBuffer: Int? = null
var audioData: ByteBuffer? = 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 = 2
val transitionThre = 2300f
if (freq == 0f) {
for (x in 0..realDuration - 1) {
audioData.put(0x00.toByte())
}
}
else 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
}
internal fun playTone(leninmilli: Int, freq: Float) {
audioData = makeAudioData(leninmilli, freq)
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!!)
}
}
// 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)
}
}
}

View File

@@ -22,11 +22,11 @@ internal class HostAccessProvider(globals: Globals, computer: BaseTerrarumComput
globals["native"]["println"] = PrintLn()
globals["native"]["isHalted"] = IsHalted(computer)
globals["native"]["closeInputString"] = NativeCloseInputString(computer.term!!)
globals["native"]["closeInputKey"] = NativeCloseInputKey(computer.term!!)
globals["native"]["openInput"] = NativeOpenInput(computer.term!!)
globals["native"]["getLastStreamInput"] = NativeGetLastStreamInput(computer.term!!)
globals["native"]["getLastKeyPress"] = NativeGetLastKeyPress(computer.term!!)
globals["native"]["closeInputString"] = NativeCloseInputString(computer.term)
globals["native"]["closeInputKey"] = NativeCloseInputKey(computer.term)
globals["native"]["openInput"] = NativeOpenInput(computer.term)
globals["native"]["getLastStreamInput"] = NativeGetLastStreamInput(computer.term)
globals["native"]["getLastKeyPress"] = NativeGetLastKeyPress(computer.term)
// while lua's dofile/require is fixiated to fs, this command allows
// libraries in JAR to be loaded.

View File

@@ -24,6 +24,7 @@ internal class Term(globals: Globals, term: Teletype) {
globals["term"]["width"] = Term.GetWidth(term)
globals["term"]["scroll"] = Term.Scroll(term)
globals["term"]["isTeletype"] = Term.IsTeletype(term)
globals["term"]["bell"] = Term.Bell(term)
if (term is Terminal) {
globals["term"]["emitRaw"] = Term.EmitRaw(term)
@@ -59,7 +60,14 @@ internal class Term(globals: Globals, term: Teletype) {
throw LuaError("bad argument (string expected, got ${this.typename()})")
}
}
class Bell(val tty: Teletype) : OneArgFunction() {
override fun call(pattern: LuaValue): LuaValue {
tty.bell(pattern.checkjstring())
return LuaValue.NONE
}
}
class WriteString(val tty: Teletype) : LuaFunction() {
override fun call(p0: LuaValue): LuaValue {
if (tty is Terminal)

View File

@@ -5,12 +5,14 @@ import net.torvald.aa.ColouredFastFont
import net.torvald.terrarum.blendNormal
import net.torvald.terrarum.blendMul
import net.torvald.terrarum.blendScreen
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL
import org.lwjgl.openal.AL10
import org.lwjgl.openal.AL11
import org.newdawn.slick.*
import java.nio.ByteBuffer
import java.util.*
/**
* Default text terminal, four text colours (black, grey, lgrey, white).
@@ -18,7 +20,7 @@ import java.nio.ByteBuffer
* Created by minjaesong on 16-09-07.
*/
open class SimpleTextTerminal(
phosphorColour: Color, override val width: Int, override val height: Int,
phosphorColour: Color, override val width: Int, override val height: Int, private val host: BaseTerrarumComputer,
colour: Boolean = false, hires: Boolean = false
) : Terminal {
/**
@@ -107,13 +109,31 @@ open class SimpleTextTerminal(
wrap()
// beep AL-related
if (beepSource != null && AL10.alGetSourcei(beepSource!!, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING) {
AL10.alDeleteSources(beepSource!!)
AL10.alDeleteBuffers(beepBuffer!!)
beepSource = null
beepBuffer == null
// start beep queue
if (beepQueue.size > 0 && beepCursor == -1) {
beepCursor = 0
}
// continue beep queue
if (beepCursor >= 0 && beepQueueLineExecTimer >= beepQueueGetLenOfPtn(beepCursor)) {
beepQueueLineExecTimer -= beepQueueGetLenOfPtn(beepCursor)
beepCursor += 1
beepQueueFired = false
}
// complete beep queue
if (beepCursor >= beepQueue.size) {
clearBeepQueue()
println("!! Beep queue clear")
}
// actually play queue
if (beepCursor >= 0 && beepQueue.size > 0 && !beepQueueFired) {
beep(beepQueue[beepCursor].first, beepQueue[beepCursor].second)
beepQueueFired = true
}
if (beepQueueFired) beepQueueLineExecTimer += delta
}
private fun wrap() {
@@ -312,85 +332,48 @@ open class SimpleTextTerminal(
foreColour = foreDefault
}
private val sampleRate = 22050
private val maxDuration = 10000
private val beepSamples = maxDuration.div(1000).times(sampleRate)
private var beepSource: Int? = null
private var beepBuffer: Int? = null
override fun beep(freq: Float, duration: Int) {
throw NotImplementedError("errenous OpenAL behaviour; *grunts*")
// let's regard it as a tracker...
private val beepQueue = ArrayList<Pair<Int, Float>>()
private var beepCursor = -1
private var beepQueueLineExecTimer = 0 // millisec
private var beepQueueFired = false
val audioData = BufferUtils.createByteBuffer(duration.times(sampleRate).div(1000))
var chop = false
val realDuration = Math.min(maxDuration, duration)
for (i in 0..realDuration - 1) {
if (i.mod(freq) < 1.0) chop = !chop
audioData.put(if (chop) 0xFF.toByte() else 0x00.toByte())
}
audioData.rewind()
// 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!!)
}
/*def checkFinished = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING && {
AL10.alDeleteSources(source)
AL10.alDeleteBuffers(buffer)
true
}*/
}
// 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)
}
/**
* @param duration: milliseconds
* @param freg: Frequency (float)
*/
override fun beep(duration: Int, freq: Float) {
println("!! Beep playing row $beepCursor, d ${Math.min(duration, maxDuration)} f $freq")
host.playTone(Math.min(duration, maxDuration), freq)
}
/** for "beep code" on modern BIOS. Pattern: - . */
override fun bell(pattern: String) {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
clearBeepQueue()
for (c in pattern) {
when (c) {
'.' -> { enqueueBeep(80, 1000f); enqueueBeep(80, 0f) }
'-' -> { enqueueBeep(250, 1000f); enqueueBeep(80, 0f) }
' ' -> { enqueueBeep(250, 0f) }
else -> throw IllegalArgumentException("Unacceptable pattern: $c (from '$pattern')")
}
}
}
fun clearBeepQueue() {
beepQueue.clear()
beepCursor = -1
beepQueueLineExecTimer = 0
}
fun enqueueBeep(duration: Int, freq: Float) {
beepQueue.add(Pair(Math.min(duration, maxDuration), freq))
}
fun beepQueueGetLenOfPtn(ptnIndex: Int) = beepQueue[ptnIndex].first
override var lastInputByte: Int = -1
var sb: StringBuilder = StringBuilder()
private var inputOpen = false

View File

@@ -58,11 +58,16 @@ interface Terminal : Teletype {
fun setColour(back: Int, fore: Int)
fun resetColour()
/**
* @param freg: Frequency (float)
* @param duration: milliseconds
* @param freg: Frequency (float)
*/
fun beep(duration: Int = 80, freq: Float = 1000f)
/**
* Pattern: - .
* . 80 ms
* - 200 ms
* ( ) 80 ms
*/
fun beep(freq: Float = 1000f, duration: Int = 200)
/** for "beep code" on modern BIOS. Pattern: - . */
override fun bell(pattern: String)
/** Requires keyPressed() event to be processed.
*

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.virtualcomputer.worldobject
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameactors.FixtureBase
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
import net.torvald.terrarum.virtualcomputer.terminal.SimpleTextTerminal
import net.torvald.terrarum.virtualcomputer.terminal.Terminal
import net.torvald.terrarum.virtualcomputer.worldobject.ui.UITextTerminal
@@ -13,10 +14,13 @@ import java.util.*
*/
class FixtureBasicTerminal(phosphor: Color) : FixtureBase() {
val vt: Terminal = SimpleTextTerminal(phosphor, 80, 25)
val computer = BaseTerrarumComputer()
val vt: Terminal = SimpleTextTerminal(phosphor, 80, 25, computer)
val ui = UITextTerminal(vt)
init {
computer.attachTerminal(vt)
collisionFlag = COLLISION_PLATFORM
actorValue[AVKey.UUID] = UUID.randomUUID().toString()

View File

@@ -33,7 +33,8 @@ open class FixtureComputerBase() : FixtureBase() {
fun attachTerminal(uuid: String) {
val fetchedTerminal = getTerminalByUUID(uuid)
computerInside = BaseTerrarumComputer(fetchedTerminal)
computerInside = BaseTerrarumComputer()
computerInside!!.attachTerminal(fetchedTerminal!!)
actorValue["computerid"] = computerInside!!.UUID
}

View File

@@ -61,9 +61,9 @@
°3C°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±Ý ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ Þ±±±±±±
RecPlay °3D°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±ÝZÞÝXÞÝCÞÝVÞÝBÞÝNÞÝMÞÝ,ÞÝ.ÞÝ/Þ±Oct±±
Trcks 4 °3E°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±Ý ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ Þ± -1±±
Tick 08 °3F°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
Tick 00 °3F°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
RecPlay RecPlay
Trcks 4 Trcks 4  Editable
Tick 08 Tick 08  Fxx eff
SAUCE00 20160924~dEIBM VGA50
Tick 00 Tick 00  Current play tick
SAUCE00 20160924u~dEIBM VGA50

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB