BEEPs are now played limitness, without killing AL

Former-commit-id: 627561820d469f0b563832f01ee37a8b04bd6935
Former-commit-id: 16f01413bc1c6809f3402d87ebffac8731338562
This commit is contained in:
Song Minjae
2016-09-30 01:06:03 +09:00
parent 1b4aca320f
commit 20d5636b4b
2 changed files with 588 additions and 578 deletions

View File

@@ -47,7 +47,7 @@ _G.bell = function(patn) term.bell(patn or ".") end
_G.beep = _G.bell
if totalMemory() == 0 then
bell "="
bell "==="
print("no RAM installed")
__haltsystemexplicit__()
return
@@ -457,9 +457,9 @@ do
::brk::
end
return s
end
end
local function push_onecapture(ms, i, s, e)
local function push_onecapture(ms, i, s, e)
if i >= ms.level then
if i == 0 then -- ms->level == 0, too
return s:head(e - s) -- add whole match
@@ -475,19 +475,19 @@ local function push_onecapture(ms, i, s, e)
return ms.capture[i].init:head(l)
end
end
end
end
local function push_captures(ms, s, e)
local function push_captures(ms, s, e)
local nlevels = (ms.level == 0 and s) and 1 or ms.level
local captures = {}
for i = 0, nlevels - 1 do
table.insert(captures, push_onecapture(ms, i, s, e))
end
return table.unpack(captures)
end
end
-- check whether pattern has no special characters
local function nospecials(p)
-- check whether pattern has no special characters
local function nospecials(p)
for i = 1, #p do
for j = 1, #SPECIALS do
if p:sub(i, i) == SPECIALS:sub(j, j) then
@@ -496,9 +496,9 @@ local function nospecials(p)
end
end
return true
end
end
local function str_find_aux(str, pattern, init, plain, find)
local function str_find_aux(str, pattern, init, plain, find)
checkArg(1, str, "string")
checkArg(2, pattern, "string")
checkArg(3, init, "number", "nil")
@@ -544,17 +544,17 @@ local function str_find_aux(str, pattern, init, plain, find)
until s1:step() > ms.src_end or anchor
end
return nil -- not found
end
end
local function str_find(s, pattern, init, plain)
local function str_find(s, pattern, init, plain)
return str_find_aux(s, pattern, init, plain, true)
end
end
local function str_match(s, pattern, init)
local function str_match(s, pattern, init)
return str_find_aux(s, pattern, init, false, false)
end
end
local function str_gmatch(s, pattern)
local function str_gmatch(s, pattern)
checkArg(1, s, "string")
checkArg(2, pattern, "string")
@@ -585,9 +585,9 @@ local function str_gmatch(s, pattern)
end
return nil -- not found
end
end
end
local function add_s(ms, b, s, e, r)
local function add_s(ms, b, s, e, r)
local news = tostring(r)
local i = 1
while i <= #news do
@@ -606,9 +606,9 @@ local function add_s(ms, b, s, e, r)
i = i + 1
end
return b
end
end
local function add_value(ms, b, s, e, r, tr)
local function add_value(ms, b, s, e, r, tr)
local res
if tr == "function" then
res = r(push_captures(ms, s, e))
@@ -623,9 +623,9 @@ local function add_value(ms, b, s, e, r, tr)
error("invalid replacement value (a "..type(res)..")")
end
return b .. res -- add result to accumulator
end
end
local function str_gsub(s, pattern, repl, n)
local function str_gsub(s, pattern, repl, n)
checkArg(1, s, "string")
checkArg(2, pattern, "string")
checkArg(3, repl, "number", "string", "function", "table")
@@ -669,55 +669,55 @@ local function str_gsub(s, pattern, repl, n)
end
b = b .. src:head()
return b, n -- number of substitutions
end
end
string.find = str_find
string.match = str_match
string.gmatch = str_gmatch
string.gsub = str_gsub
string.find = str_find
string.match = str_match
string.gmatch = str_gmatch
string.gsub = str_gsub
end
-------------------------------------------------------------------------------
local function spcall(...)
local result = table.pack(pcall(...))
if not result[1] then
error(tostring(result[2]), 0)
else
return table.unpack(result, 2, result.n)
end
local result = table.pack(pcall(...))
if not result[1] then
error(tostring(result[2]), 0)
else
return table.unpack(result, 2, result.n)
end
end
local sgcco
local function sgcf(self, gc)
while true do
self, gc = coroutine.yield(pcall(gc, self))
end
while true do
self, gc = coroutine.yield(pcall(gc, self))
end
end
local function sgc(self)
local oldDeadline, oldHitDeadline = deadline, hitDeadline
local mt = debug.getmetatable(self)
mt = rawget(mt, "mt")
local gc = rawget(mt, "__gc")
if type(gc) ~= "function" then
return
end
if not sgcco then
sgcco = coroutine.create(sgcf)
end
debug.sethook(sgcco, checkDeadline, "", hookInterval)
deadline, hitDeadline = math.min(oldDeadline, computer.realTime() + 0.5), true
local _, result, reason = coroutine.resume(sgcco, self, gc)
debug.sethook(sgcco)
if coroutine.status(sgcco) == "dead" then
sgcco = nil
end
deadline, hitDeadline = oldDeadline, oldHitDeadline
if not result then
error(reason, 0)
end
local oldDeadline, oldHitDeadline = deadline, hitDeadline
local mt = debug.getmetatable(self)
mt = rawget(mt, "mt")
local gc = rawget(mt, "__gc")
if type(gc) ~= "function" then
return
end
if not sgcco then
sgcco = coroutine.create(sgcf)
end
debug.sethook(sgcco, checkDeadline, "", hookInterval)
deadline, hitDeadline = math.min(oldDeadline, computer.realTime() + 0.5), true
local _, result, reason = coroutine.resume(sgcco, self, gc)
debug.sethook(sgcco)
if coroutine.status(sgcco) == "dead" then
sgcco = nil
end
deadline, hitDeadline = oldDeadline, oldHitDeadline
if not result then
error(reason, 0)
end
end
--[[ This is the global environment we make available to userland programs. ]]
@@ -727,262 +727,262 @@ end
-- persisted.
local sandbox, libprocess
sandbox = {
assert = assert,
dofile = nil, -- in boot/*_base.lua
error = error,
_G = nil, -- see below
getmetatable = function(t)
if type(t) == "string" then -- don't allow messing with the string mt
return nil
end
local result = getmetatable(t)
-- check if we have a wrapped __gc using mt
if type(result) == "table" and system.allowGC() and rawget(result, "__gc") == sgc then
result = rawget(result, "mt")
end
return result
end,
ipairs = ipairs,
load = function(ld, source, mode, env)
if not system.allowBytecode() then
mode = "t"
end
return load(ld, source, mode, env or sandbox)
end,
loadfile = nil, -- in boot/*_base.lua
next = next,
pairs = pairs,
pcall = function(...)
local result = table.pack(pcall(...))
checkDeadline()
return table.unpack(result, 1, result.n)
end,
print = nil, -- in boot/*_base.lua
rawequal = rawequal,
rawget = rawget,
rawlen = rawlen,
rawset = rawset,
select = select,
setmetatable = function(t, mt)
if type(mt) ~= "table" then
return setmetatable(t, mt)
end
if rawget(mt, "__gc") ~= nil then -- If __gc is set to ANYTHING not `nil`, we're gonna have issues
-- Garbage collector callbacks apparently can't be sandboxed after
-- all, because hooks are disabled while they're running. So we just
-- disable them altogether by default.
if system.allowGC() then
-- For all user __gc functions we enforce a much tighter deadline.
-- This is because these functions may be called from the main
-- thread under certain circumstanced (such as when saving the world),
-- which can lead to noticeable lag if the __gc function behaves badly.
local sbmt = {} -- sandboxed metatable. only for __gc stuff, so it's
-- kinda ok to have a shallow copy instead... meh.
for k, v in next, mt do
sbmt[k] = v
end
sbmt.__gc = sgc
sbmt.mt = mt
mt = sbmt
else
-- Don't allow marking for finalization, but use the raw metatable.
local gc = rawget(mt, "__gc")
rawset(mt, "__gc", nil) -- remove __gc
local ret = table.pack(pcall(setmetatable, t, mt))
rawset(mt, "__gc", gc) -- restore __gc
if not ret[1] then error(ret[2], 0) end
return table.unpack(ret, 2, ret.n)
end
end
return setmetatable(t, mt)
end,
tonumber = tonumber,
tostring = tostring,
type = type,
_VERSION = _VERSION:match("5.3") and "Lua 5.3" or "Lua 5.2",
xpcall = function(f, msgh, ...)
local handled = false
local result = table.pack(xpcall(f, function(...)
if handled then
return ...
else
handled = true
return msgh(...)
end
end, ...))
checkDeadline()
return table.unpack(result, 1, result.n)
end,
assert = assert,
dofile = nil, -- in boot/*_base.lua
error = error,
_G = nil, -- see below
getmetatable = function(t)
if type(t) == "string" then -- don't allow messing with the string mt
return nil
end
local result = getmetatable(t)
-- check if we have a wrapped __gc using mt
if type(result) == "table" and system.allowGC() and rawget(result, "__gc") == sgc then
result = rawget(result, "mt")
end
return result
end,
ipairs = ipairs,
load = function(ld, source, mode, env)
if not system.allowBytecode() then
mode = "t"
end
return load(ld, source, mode, env or sandbox)
end,
loadfile = nil, -- in boot/*_base.lua
next = next,
pairs = pairs,
pcall = function(...)
local result = table.pack(pcall(...))
checkDeadline()
return table.unpack(result, 1, result.n)
end,
print = nil, -- in boot/*_base.lua
rawequal = rawequal,
rawget = rawget,
rawlen = rawlen,
rawset = rawset,
select = select,
setmetatable = function(t, mt)
if type(mt) ~= "table" then
return setmetatable(t, mt)
end
if rawget(mt, "__gc") ~= nil then -- If __gc is set to ANYTHING not `nil`, we're gonna have issues
-- Garbage collector callbacks apparently can't be sandboxed after
-- all, because hooks are disabled while they're running. So we just
-- disable them altogether by default.
if system.allowGC() then
-- For all user __gc functions we enforce a much tighter deadline.
-- This is because these functions may be called from the main
-- thread under certain circumstanced (such as when saving the world),
-- which can lead to noticeable lag if the __gc function behaves badly.
local sbmt = {} -- sandboxed metatable. only for __gc stuff, so it's
-- kinda ok to have a shallow copy instead... meh.
for k, v in next, mt do
sbmt[k] = v
end
sbmt.__gc = sgc
sbmt.mt = mt
mt = sbmt
else
-- Don't allow marking for finalization, but use the raw metatable.
local gc = rawget(mt, "__gc")
rawset(mt, "__gc", nil) -- remove __gc
local ret = table.pack(pcall(setmetatable, t, mt))
rawset(mt, "__gc", gc) -- restore __gc
if not ret[1] then error(ret[2], 0) end
return table.unpack(ret, 2, ret.n)
end
end
return setmetatable(t, mt)
end,
tonumber = tonumber,
tostring = tostring,
type = type,
_VERSION = _VERSION:match("5.3") and "Lua 5.3" or "Lua 5.2",
xpcall = function(f, msgh, ...)
local handled = false
local result = table.pack(xpcall(f, function(...)
if handled then
return ...
else
handled = true
return msgh(...)
end
end, ...))
checkDeadline()
return table.unpack(result, 1, result.n)
end,
coroutine = {
create = coroutine.create,
resume = function(co, ...) -- custom resume part for bubbling sysyields
checkArg(1, co, "thread")
local args = table.pack(...)
while true do -- for consecutive sysyields
debug.sethook(co, checkDeadline, "", hookInterval)
local result = table.pack(
coroutine.resume(co, table.unpack(args, 1, args.n)))
debug.sethook(co) -- avoid gc issues
checkDeadline()
if result[1] then -- success: (true, sysval?, ...?)
if coroutine.status(co) == "dead" then -- return: (true, ...)
return true, table.unpack(result, 2, result.n)
elseif result[2] ~= nil then -- yield: (true, sysval)
args = table.pack(coroutine.yield(result[2]))
else -- yield: (true, nil, ...)
return true, table.unpack(result, 3, result.n)
end
else -- error: result = (false, string)
return false, result[2]
end
end
end,
running = coroutine.running,
status = coroutine.status,
wrap = function(f) -- for bubbling coroutine.resume
local co = coroutine.create(f)
return function(...)
local result = table.pack(sandbox.coroutine.resume(co, ...))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2], 0)
end
end
end,
yield = function(...) -- custom yield part for bubbling sysyields
return coroutine.yield(nil, ...)
end
},
coroutine = {
create = coroutine.create,
resume = function(co, ...) -- custom resume part for bubbling sysyields
checkArg(1, co, "thread")
local args = table.pack(...)
while true do -- for consecutive sysyields
debug.sethook(co, checkDeadline, "", hookInterval)
local result = table.pack(
coroutine.resume(co, table.unpack(args, 1, args.n)))
debug.sethook(co) -- avoid gc issues
checkDeadline()
if result[1] then -- success: (true, sysval?, ...?)
if coroutine.status(co) == "dead" then -- return: (true, ...)
return true, table.unpack(result, 2, result.n)
elseif result[2] ~= nil then -- yield: (true, sysval)
args = table.pack(coroutine.yield(result[2]))
else -- yield: (true, nil, ...)
return true, table.unpack(result, 3, result.n)
end
else -- error: result = (false, string)
return false, result[2]
end
end
end,
running = coroutine.running,
status = coroutine.status,
wrap = function(f) -- for bubbling coroutine.resume
local co = coroutine.create(f)
return function(...)
local result = table.pack(sandbox.coroutine.resume(co, ...))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2], 0)
end
end
end,
yield = function(...) -- custom yield part for bubbling sysyields
return coroutine.yield(nil, ...)
end
},
string = {
byte = string.byte,
char = string.char,
dump = string.dump,
find = string.find,
format = string.format,
gmatch = string.gmatch,
gsub = string.gsub,
len = string.len,
lower = string.lower,
match = string.match,
rep = string.rep,
reverse = string.reverse,
sub = string.sub,
upper = string.upper
},
string = {
byte = string.byte,
char = string.char,
dump = string.dump,
find = string.find,
format = string.format,
gmatch = string.gmatch,
gsub = string.gsub,
len = string.len,
lower = string.lower,
match = string.match,
rep = string.rep,
reverse = string.reverse,
sub = string.sub,
upper = string.upper
},
table = {
concat = table.concat,
insert = table.insert,
pack = table.pack,
remove = table.remove,
sort = table.sort,
unpack = table.unpack
},
table = {
concat = table.concat,
insert = table.insert,
pack = table.pack,
remove = table.remove,
sort = table.sort,
unpack = table.unpack
},
math = {
abs = math.abs,
acos = math.acos,
asin = math.asin,
atan = math.atan,
atan2 = math.atan2,
ceil = math.ceil,
cos = math.cos,
cosh = math.cosh,
deg = math.deg,
exp = math.exp,
floor = math.floor,
fmod = math.fmod,
frexp = math.frexp,
huge = math.huge,
ldexp = math.ldexp,
log = math.log,
max = math.max,
min = math.min,
modf = math.modf,
pi = math.pi,
pow = math.pow or function(a, b) -- Deprecated in Lua 5.3
return a^b
end,
rad = math.rad,
random = function(...)
return spcall(math.random, ...)
end,
randomseed = function(seed)
spcall(math.randomseed, seed)
end,
sin = math.sin,
sinh = math.sinh,
sqrt = math.sqrt,
tan = math.tan,
tanh = math.tanh
},
math = {
abs = math.abs,
acos = math.acos,
asin = math.asin,
atan = math.atan,
atan2 = math.atan2,
ceil = math.ceil,
cos = math.cos,
cosh = math.cosh,
deg = math.deg,
exp = math.exp,
floor = math.floor,
fmod = math.fmod,
frexp = math.frexp,
huge = math.huge,
ldexp = math.ldexp,
log = math.log,
max = math.max,
min = math.min,
modf = math.modf,
pi = math.pi,
pow = math.pow or function(a, b) -- Deprecated in Lua 5.3
return a^b
end,
rad = math.rad,
random = function(...)
return spcall(math.random, ...)
end,
randomseed = function(seed)
spcall(math.randomseed, seed)
end,
sin = math.sin,
sinh = math.sinh,
sqrt = math.sqrt,
tan = math.tan,
tanh = math.tanh
},
-- Deprecated in Lua 5.3.
bit32 = bit32 and {
arshift = bit32.arshift,
band = bit32.band,
bnot = bit32.bnot,
bor = bit32.bor,
btest = bit32.btest,
bxor = bit32.bxor,
extract = bit32.extract,
replace = bit32.replace,
lrotate = bit32.lrotate,
lshift = bit32.lshift,
rrotate = bit32.rrotate,
rshift = bit32.rshift
},
-- Deprecated in Lua 5.3.
bit32 = bit32 and {
arshift = bit32.arshift,
band = bit32.band,
bnot = bit32.bnot,
bor = bit32.bor,
btest = bit32.btest,
bxor = bit32.bxor,
extract = bit32.extract,
replace = bit32.replace,
lrotate = bit32.lrotate,
lshift = bit32.lshift,
rrotate = bit32.rrotate,
rshift = bit32.rshift
},
io = nil, -- in lib/io.lua
io = nil, -- in lib/io.lua
os = {
clock = os.clock,
date = function(format, time)
return spcall(os.date, format, time)
end,
difftime = function(t2, t1)
return t2 - t1
end,
execute = nil, -- in boot/*_os.lua
exit = nil, -- in boot/*_os.lua
remove = nil, -- in boot/*_os.lua
rename = nil, -- in boot/*_os.lua
time = function(table)
checkArg(1, table, "table", "nil")
return os.time(table)
end,
tmpname = nil, -- in boot/*_os.lua
},
os = {
clock = os.clock,
date = function(format, time)
return spcall(os.date, format, time)
end,
difftime = function(t2, t1)
return t2 - t1
end,
execute = nil, -- in boot/*_os.lua
exit = nil, -- in boot/*_os.lua
remove = nil, -- in boot/*_os.lua
rename = nil, -- in boot/*_os.lua
time = function(table)
checkArg(1, table, "table", "nil")
return os.time(table)
end,
tmpname = nil, -- in boot/*_os.lua
},
debug = {
getinfo = function(...)
local result = debug.getinfo(...)
if result then
-- Only make primitive information available in the sandbox.
return {
source = result.source,
short_src = result.short_src,
linedefined = result.linedefined,
lastlinedefined = result.lastlinedefined,
what = result.what,
currentline = result.currentline,
nups = result.nups,
nparams = result.nparams,
isvararg = result.isvararg,
name = result.name,
namewhat = result.namewhat,
istailcall = result.istailcall
}
end
end,
traceback = debug.traceback
},
debug = {
getinfo = function(...)
local result = debug.getinfo(...)
if result then
-- Only make primitive information available in the sandbox.
return {
source = result.source,
short_src = result.short_src,
linedefined = result.linedefined,
lastlinedefined = result.lastlinedefined,
what = result.what,
currentline = result.currentline,
nups = result.nups,
nparams = result.nparams,
isvararg = result.isvararg,
name = result.name,
namewhat = result.namewhat,
istailcall = result.istailcall
}
end
end,
traceback = debug.traceback
},
checkArg = checkArg
checkArg = checkArg
}
sandbox._G = sandbox

View File

@@ -311,7 +311,7 @@ class BaseTerrarumComputer(peripheralSlots: Int) {
beepCursor = 0
}
// continue emitTone queue
// advance emitTone queue
if (beepCursor >= 0 && beepQueueLineExecTimer >= beepQueueGetLenOfPtn(beepCursor)) {
beepQueueLineExecTimer -= beepQueueGetLenOfPtn(beepCursor)
beepCursor += 1
@@ -321,7 +321,6 @@ class BaseTerrarumComputer(peripheralSlots: Int) {
// complete emitTone queue
if (beepCursor >= beepQueue.size) {
clearBeepQueue()
AL.destroy()
if (DEBUG) println("[BaseTerrarumComputer] !! Beep queue clear")
}
@@ -329,6 +328,12 @@ class BaseTerrarumComputer(peripheralSlots: Int) {
if (beepCursor >= 0 && beepQueue.size > 0 && !beepQueueFired) {
playTone(beepQueue[beepCursor].first, beepQueue[beepCursor].second)
beepQueueFired = true
// delete sources that is finished. AL is limited to 256 sources. If you exceed it,
// we won't get any more sounds played.
AL10.alSourcei(oldBeepSource, AL10.AL_BUFFER, 0)
AL10.alDeleteSources(oldBeepSource)
AL10.alDeleteBuffers(oldBeepBuffer)
}
if (beepQueueFired) beepQueueLineExecTimer += delta
@@ -338,6 +343,8 @@ class BaseTerrarumComputer(peripheralSlots: Int) {
beepQueue.clear()
beepCursor = -1
beepQueueLineExecTimer = 0
//AL.destroy()
}
fun enqueueBeep(duration: Int, freq: Double) {
@@ -352,8 +359,10 @@ class BaseTerrarumComputer(peripheralSlots: Int) {
////////////////////
private val sampleRate = 44100
private var beepSource: Int? = null
private var beepBuffer: Int? = null
private var beepSource: Int = -1
private var beepBuffer: Int = -1
private var oldBeepSource: Int = -1
private var oldBeepBuffer: Int = -1
var audioData: ByteBuffer? = null
/**
@@ -424,36 +433,37 @@ class BaseTerrarumComputer(peripheralSlots: Int) {
// Clear error stack.
AL10.alGetError()
oldBeepBuffer = beepBuffer
beepBuffer = AL10.alGenBuffers()
checkALError()
try {
AL10.alBufferData(beepBuffer!!, AL10.AL_FORMAT_MONO8, audioData, sampleRate)
AL10.alBufferData(beepBuffer, AL10.AL_FORMAT_MONO8, audioData, sampleRate)
checkALError()
oldBeepSource = beepSource
beepSource = AL10.alGenSources()
checkALError()
try {
AL10.alSourceQueueBuffers(beepSource!!, beepBuffer!!)
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)
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!!)
AL10.alSourcePlay(beepSource)
checkALError()
}
catch (e: ALException) {
AL10.alDeleteSources(beepSource!!)
AL10.alDeleteSources(beepSource)
}
}
catch (e: ALException) {
if (beepSource != null) AL10.alDeleteSources(beepSource!!)
AL10.alDeleteSources(beepSource)
}
}