Merge pull request #649 from MihailRis/generated-pcm-stream

Generated PCM stream (part 1)
This commit is contained in:
MihailRis
2025-11-10 19:12:48 +03:00
committed by GitHub
50 changed files with 2118 additions and 1013 deletions
+63 -597
View File
@@ -1,201 +1,8 @@
local breakpoints = {}
local dbg_steps_mode = false
local dbg_step_into_func = false
local hook_lock = false
local current_func
local current_func_stack_size
local _debug_getinfo = debug.getinfo
local _debug_getlocal = debug.getlocal
local __pause = debug.pause
local __error = error
local __sethook = debug.sethook
-- 'return' hook not called for some functions
-- todo: speedup
local function calc_stack_size()
local s = debug.traceback("", 2)
local count = 0
for i in s:gmatch("\n") do
count = count + 1
end
return count
end
local is_debugging = debug.is_debugging()
if is_debugging then
__sethook(function (e, line)
if e == "return" then
local info = _debug_getinfo(2)
if info.func == current_func then
current_func = nil
end
end
if dbg_steps_mode and not hook_lock then
hook_lock = true
if not dbg_step_into_func then
local func = _debug_getinfo(2).func
if func ~= current_func then
return
end
if current_func_stack_size ~= calc_stack_size() then
return
end
end
current_func = func
__pause("step")
debug.pull_events()
end
hook_lock = false
local bps = breakpoints[line]
if not bps then
return
end
local source = _debug_getinfo(2).source
if not bps[source] then
return
end
current_func = _debug_getinfo(2).func
current_func_stack_size = calc_stack_size()
__pause("breakpoint")
debug.pull_events()
end, "lr")
end
local DBG_EVENT_SET_BREAKPOINT = 1
local DBG_EVENT_RM_BREAKPOINT = 2
local DBG_EVENT_STEP = 3
local DBG_EVENT_STEP_INTO_FUNCTION = 4
local DBG_EVENT_RESUME = 5
local DBG_EVENT_GET_VALUE = 6
local __pull_events = debug.__pull_events
local __sendvalue = debug.__sendvalue
debug.__pull_events = nil
debug.__sendvalue = nil
function debug.pull_events()
if not is_debugging then
return
end
if not debug.is_debugging() then
is_debugging = false
__sethook()
end
local events = __pull_events()
if not events then
return
end
for i, event in ipairs(events) do
if event[1] == DBG_EVENT_SET_BREAKPOINT then
debug.set_breakpoint(event[2], event[3])
elseif event[1] == DBG_EVENT_RM_BREAKPOINT then
debug.remove_breakpoint(event[2], event[3])
elseif event[1] == DBG_EVENT_STEP then
dbg_steps_mode = true
dbg_step_into_func = false
elseif event[1] == DBG_EVENT_STEP_INTO_FUNCTION then
dbg_steps_mode = true
dbg_step_into_func = true
elseif event[1] == DBG_EVENT_RESUME then
dbg_steps_mode = false
dbg_step_into_func = false
elseif event[1] == DBG_EVENT_GET_VALUE then
local _, value = _debug_getlocal(event[2] + 3, event[3])
for _, key in ipairs(event[4]) do
if value == nil then
value = "error: index nil value"
break
end
value = value[key]
end
__sendvalue(value, event[2], event[3], event[4])
__pause()
end
end
end
function debug.set_breakpoint(source, line)
local bps = breakpoints[line]
if not bps then
bps = {}
breakpoints[line] = bps
end
bps[source] = true
end
function debug.remove_breakpoint(source, line)
local bps = breakpoints[line]
if not bps then
return
end
bps[source] = nil
end
function error(message, level)
if is_debugging then
__pause("exception", message)
end
__error(message, level)
end
-- Lua has no parallelizm, also _set_data does not call any lua functions so
-- may be reused one global ffi buffer per lua_State
local canvas_ffi_buffer
local canvas_ffi_buffer_size = 0
local ipairs_mt_supported = false
for i, _ in ipairs(setmetatable({l={1}}, {
__ipairs=function(self) return ipairs(self.l) end})) do
ipairs_mt_supported = true
end
if not ipairs_mt_supported then
local raw_ipairs = ipairs
ipairs = function(t)
local metatable = getmetatable(t)
if metatable and metatable.__ipairs then
return metatable.__ipairs(t)
end
return raw_ipairs(t)
end
end
function await(co)
local res, err
while coroutine.status(co) ~= 'dead' do
coroutine.yield()
res, err = coroutine.resume(co)
if err then
return res, err
end
end
return res, err
end
local _ffi = ffi
function __vc_Canvas_set_data(self, data)
if type(data) == "cdata" then
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
end
local width = self.width
local height = self.height
local size = width * height * 4
if size > canvas_ffi_buffer_size then
canvas_ffi_buffer = _ffi.new(
string.format("unsigned char[%s]", size)
)
canvas_ffi_buffer_size = size
end
for i=0, size - 1 do
canvas_ffi_buffer[i] = data[i + 1]
end
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
end
local _debug_getinfo = debug.getinfo
function crc32(bytes, chksum)
local chksum = chksum or 0
chksum = chksum or 0
local length = #bytes
if type(bytes) == "table" then
@@ -231,20 +38,59 @@ function parse_path(path)
return string.sub(path, 1, index-1), string.sub(path, index+1, -1)
end
function pack.is_installed(packid)
return file.isfile(packid..":package.json")
-- Lua has no parallelizm, also _set_data does not call any lua functions so
-- may be reused one global ffi buffer per lua_State
local canvas_ffi_buffer
local canvas_ffi_buffer_size = 0
local _ffi = ffi
function __vc_Canvas_set_data(self, data)
if type(data) == "cdata" then
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
end
local width = self.width
local height = self.height
local size = width * height * 4
if size > canvas_ffi_buffer_size then
canvas_ffi_buffer = _ffi.new(
string.format("unsigned char[%s]", size)
)
canvas_ffi_buffer_size = size
end
for i=0, size - 1 do
canvas_ffi_buffer[i] = data[i + 1]
end
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
end
function pack.data_file(packid, name)
file.mkdirs("world:data/"..packid)
return "world:data/"..packid.."/"..name
local ipairs_mt_supported = false
for i, _ in ipairs(setmetatable({l={1}}, {
__ipairs=function(self) return ipairs(self.l) end})) do
ipairs_mt_supported = true
end
function pack.shared_file(packid, name)
file.mkdirs("config:"..packid)
return "config:"..packid.."/"..name
if not ipairs_mt_supported then
local raw_ipairs = ipairs
ipairs = function(t)
local metatable = getmetatable(t)
if metatable and metatable.__ipairs then
return metatable.__ipairs(t)
end
return raw_ipairs(t)
end
end
function await(co)
local res, err
while coroutine.status(co) ~= 'dead' do
coroutine.yield()
res, err = coroutine.resume(co)
if err then
return res, err
end
end
return res, err
end
function timeit(iters, func, ...)
local tm = os.clock()
@@ -256,364 +102,6 @@ end
----------------------------------------------
function math.clamp(_in, low, high)
return math.min(math.max(_in, low), high)
end
function math.rand(low, high)
return low + (high - low) * math.random()
end
function math.normalize(num, conf)
conf = conf or 1
return (num / conf) % 1
end
function math.round(num, places)
places = places or 0
local mult = 10 ^ places
return math.floor(num * mult + 0.5) / mult
end
function math.sum(...)
local numbers = nil
local sum = 0
if type(...) == "table" then
numbers = ...
else
numbers = {...}
end
for _, v in ipairs(numbers) do
sum = sum + v
end
return sum
end
----------------------------------------------
function table.copy(t)
local copied = {}
for k, v in pairs(t) do
copied[k] = v
end
return copied
end
function table.deep_copy(t)
local copied = {}
for k, v in pairs(t) do
if type(v) == "table" then
copied[k] = table.deep_copy(v)
else
copied[k] = v
end
end
return setmetatable(copied, getmetatable(t))
end
function table.count_pairs(t)
local count = 0
for k, v in pairs(t) do
count = count + 1
end
return count
end
function table.random(t)
return t[math.random(1, #t)]
end
function table.shuffle(t)
for i = #t, 2, -1 do
local j = math.random(i)
t[i], t[j] = t[j], t[i]
end
return t
end
function table.merge(t1, t2)
for i, v in pairs(t2) do
if type(i) == "number" then
t1[#t1 + 1] = v
elseif t1[i] == nil then
t1[i] = v
end
end
return t1
end
function table.map(t, func)
for i, v in pairs(t) do
t[i] = func(i, v)
end
return t
end
function table.filter(t, func)
for i = #t, 1, -1 do
if not func(i, t[i]) then
table.remove(t, i)
end
end
local size = #t
for i, v in pairs(t) do
local i_type = type(i)
if i_type == "number" then
if i < 1 or i > size then
if not func(i, v) then
t[i] = nil
end
end
else
if not func(i, v) then
t[i] = nil
end
end
end
return t
end
function table.set_default(t, key, default)
if t[key] == nil then
t[key] = default
return default
end
return t[key]
end
function table.flat(t)
local flat = {}
for _, v in pairs(t) do
if type(v) == "table" then
table.merge(flat, v)
else
table.insert(flat, v)
end
end
return flat
end
function table.deep_flat(t)
local flat = {}
for _, v in pairs(t) do
if type(v) == "table" then
table.merge(flat, table.deep_flat(v))
else
table.insert(flat, v)
end
end
return flat
end
function table.sub(arr, start, stop)
local res = {}
start = start or 1
stop = stop or #arr
for i = start, stop do
table.insert(res, arr[i])
end
return res
end
----------------------------------------------
local pattern_escape_replacements = {
["("] = "%(",
[")"] = "%)",
["."] = "%.",
["%"] = "%%",
["+"] = "%+",
["-"] = "%-",
["*"] = "%*",
["?"] = "%?",
["["] = "%[",
["]"] = "%]",
["^"] = "%^",
["$"] = "%$",
["\0"] = "%z"
}
function string.pattern_safe(str)
return string.gsub(str, ".", pattern_escape_replacements)
end
local string_sub = string.sub
local string_find = string.find
local string_len = string.len
function string.explode(separator, str, withpattern)
if (withpattern == nil) then withpattern = false end
local ret = {}
local current_pos = 1
for i = 1, string_len(str) do
local start_pos, end_pos = string_find(
str, separator, current_pos, not withpattern)
if (not start_pos) then break end
ret[i] = string_sub(str, current_pos, start_pos - 1)
current_pos = end_pos + 1
end
ret[#ret + 1] = string_sub(str, current_pos)
return ret
end
function string.split(str, delimiter)
return string.explode(delimiter, str)
end
function string.formatted_time(seconds, format)
if (not seconds) then seconds = 0 end
local hours = math.floor(seconds / 3600)
local minutes = math.floor((seconds / 60) % 60)
local millisecs = (seconds - math.floor(seconds)) * 1000
seconds = math.floor(seconds % 60)
if (format) then
return string.format(format, minutes, seconds, millisecs)
else
return { h = hours, m = minutes, s = seconds, ms = millisecs }
end
end
function string.replace(str, tofind, toreplace)
local tbl = string.explode(tofind, str)
if (tbl[1]) then return table.concat(tbl, toreplace) end
return str
end
function string.trim(s, char)
if char then char = string.pattern_safe(char) else char = "%s" end
return string.match(s, "^" .. char .. "*(.-)" .. char .. "*$") or s
end
function string.trim_right(s, char)
if char then char = string.pattern_safe(char) else char = "%s" end
return string.match(s, "^(.-)" .. char .. "*$") or s
end
function string.trim_left(s, char)
if char then char = string.pattern_safe(char) else char = "%s" end
return string.match(s, "^" .. char .. "*(.+)$") or s
end
function string.pad(str, size, char)
char = char == nil and " " or char
local padding = math.floor((size - #str) / 2)
local extra_padding = (size - #str) % 2
return string.rep(char, padding) .. str .. string.rep(char, padding + extra_padding)
end
function string.left_pad(str, size, char)
char = char == nil and " " or char
local left_padding = size - #str
return string.rep(char, left_padding) .. str
end
function string.right_pad(str, size, char)
char = char == nil and " " or char
local right_padding = size - #str
return str .. string.rep(char, right_padding)
end
string.lower = utf8.lower
string.upper = utf8.upper
string.escape = utf8.escape
local meta = getmetatable("")
function meta:__index(key)
local val = string[key]
if (val ~= nil) then
return val
elseif (tonumber(key)) then
return string.sub(self, key, key)
end
end
function string.starts_with(str, start)
return string.sub(str, 1, string.len(start)) == start
end
function string.ends_with(str, endStr)
return endStr == "" or string.sub(str, -string.len(endStr)) == endStr
end
function table.has(t, x)
for i,v in ipairs(t) do
if v == x then
return true
end
end
return false
end
function table.index(t, x)
for i,v in ipairs(t) do
if v == x then
return i
end
end
return -1
end
function table.remove_value(t, x)
local index = table.index(t, x)
if index ~= -1 then
table.remove(t, index)
end
end
function table.tostring(t)
local s = '['
for i,v in ipairs(t) do
s = s..tostring(v)
if i < #t then
s = s..', '
end
end
return s..']'
end
function file.readlines(path)
local str = file.read(path)
local lines = {}
for s in str:gmatch("[^\r\n]+") do
table.insert(lines, s)
end
return lines
end
function debug.count_frames()
local frames = 1
while true do
@@ -768,7 +256,7 @@ function __vc__error(msg, frame, n, lastn)
if events then
local frames = debug.get_traceback(1)
events.emit(
"core:error", msg,
"core:error", msg,
table.sub(frames, 1 + (n or 0), lastn and #frames-lastn)
)
end
@@ -782,42 +270,20 @@ function __vc_warning(msg, detail, n)
end
end
function file.name(path)
return path:match("([^:/\\]+)$")
end
require "core:internal/extensions/pack"
require "core:internal/extensions/math"
require "core:internal/extensions/file"
require "core:internal/extensions/table"
require "core:internal/extensions/string"
function file.stem(path)
local name = file.name(path)
return name:match("(.+)%.[^%.]+$") or name
end
function file.ext(path)
return path:match("%.([^:/\\]+)$")
end
function file.prefix(path)
return path:match("^([^:]+)")
end
function file.parent(path)
local dir = path:match("(.*)/")
if not dir then
return file.prefix(path)..":"
end
return dir
end
function file.path(path)
local pos = path:find(':')
return path:sub(pos + 1)
end
function file.join(a, b)
if a[#a] == ':' then
return a .. b
end
return a .. "/" .. b
end
local bytearray = require "core:internal/bytearray"
Bytearray = bytearray.FFIBytearray
Bytearray_as_string = bytearray.FFIBytearray_as_string
U16view = bytearray.FFIU16view
I16view = bytearray.FFII16view
U32view = bytearray.FFIU32view
I32view = bytearray.FFII32view
Bytearray_construct = function(...) return Bytearray(...) end
bit.compile = require "core:bitwise/compiler"
bit.execute = require "core:bitwise/executor"