<audio>
This commit is contained in:
236
flumi/Scripts/Utils/Lua/Audio.gd
Normal file
236
flumi/Scripts/Utils/Lua/Audio.gd
Normal file
@@ -0,0 +1,236 @@
|
||||
class_name LuaAudioUtils
|
||||
extends RefCounted
|
||||
|
||||
static var last_user_event_time: int = 0
|
||||
static var user_event_window_ms: int = 100
|
||||
|
||||
static func setup_audio_api(vm: LuauVM):
|
||||
vm.lua_newtable()
|
||||
vm.lua_pushcallable(_lua_audio_new_handler, "Audio.new")
|
||||
vm.lua_setfield(-2, "new")
|
||||
vm.lua_setglobal("Audio")
|
||||
|
||||
static func mark_user_event():
|
||||
last_user_event_time = Time.get_ticks_msec()
|
||||
|
||||
static func _check_if_likely_user_event(current_time: int) -> bool:
|
||||
var time_since_user_event = current_time - last_user_event_time
|
||||
return time_since_user_event < user_event_window_ms
|
||||
|
||||
static func _defer_audio_setup(audio_node: HTMLAudio):
|
||||
var main_scene = Engine.get_main_loop().current_scene
|
||||
main_scene.add_child(audio_node)
|
||||
audio_node.visible = false
|
||||
|
||||
var element = audio_node.current_element
|
||||
var src = element.get_attribute("src")
|
||||
if not src.is_empty():
|
||||
audio_node.loop = element.has_attribute("loop")
|
||||
if element.has_attribute("muted"):
|
||||
audio_node.muted = true
|
||||
audio_node.load_audio_async(src)
|
||||
|
||||
static func _lua_audio_new_handler(vm: LuauVM) -> int:
|
||||
var url: String = vm.luaL_checkstring(1)
|
||||
|
||||
var audio_scene = preload("res://Scenes/Tags/audio.tscn")
|
||||
var audio_node = audio_scene.instantiate() as HTMLAudio
|
||||
|
||||
var dummy_element = HTMLParser.HTMLElement.new("audio")
|
||||
dummy_element.set_attribute("src", url)
|
||||
dummy_element.set_attribute("controls", "false")
|
||||
|
||||
audio_node.current_element = dummy_element
|
||||
audio_node.current_parser = null
|
||||
audio_node.visible = false
|
||||
|
||||
audio_node.set_meta("deferred_url", url)
|
||||
|
||||
_defer_audio_setup.call_deferred(audio_node)
|
||||
|
||||
vm.lua_newtable()
|
||||
|
||||
vm.lua_pushobject(audio_node)
|
||||
vm.lua_setfield(-2, "_audio_node")
|
||||
|
||||
vm.lua_pushcallable(_lua_audio_play_handler, "Audio.play")
|
||||
vm.lua_setfield(-2, "play")
|
||||
|
||||
vm.lua_pushcallable(_lua_audio_pause_handler, "Audio.pause")
|
||||
vm.lua_setfield(-2, "pause")
|
||||
|
||||
vm.lua_pushcallable(_lua_audio_stop_handler, "Audio.stop")
|
||||
vm.lua_setfield(-2, "stop")
|
||||
|
||||
# Set up metatable for property access
|
||||
vm.lua_newtable()
|
||||
vm.lua_pushcallable(_lua_audio_index_handler, "Audio.__index")
|
||||
vm.lua_setfield(-2, "__index")
|
||||
vm.lua_pushcallable(_lua_audio_newindex_handler, "Audio.__newindex")
|
||||
vm.lua_setfield(-2, "__newindex")
|
||||
vm.lua_setmetatable(-2)
|
||||
|
||||
return 1
|
||||
|
||||
static func _get_audio_node_from_table(vm: LuauVM) -> HTMLAudio:
|
||||
vm.lua_getfield(1, "_audio_node")
|
||||
var audio_node = vm.lua_toobject(-1) as HTMLAudio
|
||||
vm.lua_pop(1)
|
||||
return audio_node
|
||||
|
||||
static func _lua_audio_play_handler(vm: LuauVM) -> int:
|
||||
var audio_node = _get_audio_node_from_table(vm)
|
||||
if audio_node:
|
||||
var current_time = Time.get_ticks_msec()
|
||||
var is_likely_user_event = _check_if_likely_user_event(current_time)
|
||||
audio_node.call_deferred("_deferred_play_with_user_context", is_likely_user_event)
|
||||
vm.lua_pushboolean(true)
|
||||
else:
|
||||
vm.lua_pushboolean(false)
|
||||
return 1
|
||||
|
||||
static func _lua_audio_pause_handler(vm: LuauVM) -> int:
|
||||
var audio_node = _get_audio_node_from_table(vm)
|
||||
if audio_node:
|
||||
audio_node.call_deferred("pause")
|
||||
return 0
|
||||
|
||||
static func _lua_audio_stop_handler(vm: LuauVM) -> int:
|
||||
var audio_node = _get_audio_node_from_table(vm)
|
||||
if audio_node:
|
||||
audio_node.call_deferred("stop")
|
||||
return 0
|
||||
|
||||
# Property access handlers for programmatic audio
|
||||
static func _lua_audio_index_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
|
||||
var audio_node = _get_audio_node_from_table(vm)
|
||||
if not audio_node:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
match key:
|
||||
"volume":
|
||||
vm.lua_pushnumber(audio_node.volume)
|
||||
return 1
|
||||
"loop":
|
||||
vm.lua_pushboolean(audio_node.loop)
|
||||
return 1
|
||||
"currentTime":
|
||||
vm.lua_pushnumber(audio_node.get_current_time())
|
||||
return 1
|
||||
"duration":
|
||||
vm.lua_pushnumber(audio_node.get_duration())
|
||||
return 1
|
||||
_:
|
||||
# Look up other methods/properties in the table itself
|
||||
vm.lua_rawget(1)
|
||||
return 1
|
||||
|
||||
static func _lua_audio_newindex_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
var value = vm.lua_tovariant(3)
|
||||
|
||||
var audio_node = _get_audio_node_from_table(vm)
|
||||
if not audio_node:
|
||||
return 0
|
||||
|
||||
match key:
|
||||
"volume":
|
||||
audio_node.call_deferred("set", "volume", float(value))
|
||||
return 0
|
||||
"loop":
|
||||
audio_node.call_deferred("set", "loop", bool(value))
|
||||
return 0
|
||||
"currentTime":
|
||||
audio_node.call_deferred("set_current_time", float(value))
|
||||
return 0
|
||||
_:
|
||||
vm.lua_rawset(1)
|
||||
return 0
|
||||
|
||||
static func _dom_audio_play_handler(vm: LuauVM) -> int:
|
||||
var element_id: String = vm.luaL_checkstring(1)
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
mark_user_event()
|
||||
var audio_node = _get_dom_audio_node(element_id, lua_api)
|
||||
if audio_node:
|
||||
audio_node.call_deferred("_deferred_play_with_user_context", true)
|
||||
return 0
|
||||
|
||||
static func _dom_audio_pause_handler(vm: LuauVM) -> int:
|
||||
var element_id: String = vm.luaL_checkstring(1)
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
var audio_node = _get_dom_audio_node(element_id, lua_api)
|
||||
if audio_node:
|
||||
audio_node.call_deferred("pause")
|
||||
return 0
|
||||
|
||||
static func _get_dom_audio_node(element_id: String, lua_api) -> HTMLAudio:
|
||||
return lua_api.dom_parser.parse_result.dom_nodes.get(element_id, null) as HTMLAudio
|
||||
|
||||
static func handle_dom_audio_index(vm: LuauVM, element_id: String, key: String) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
var audio_node = _get_dom_audio_node(element_id, lua_api)
|
||||
if not audio_node:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
match key:
|
||||
"play":
|
||||
var play_code = "return function(self) _dom_audio_play('" + element_id + "') end"
|
||||
vm.load_string(play_code, "audio.play_closure")
|
||||
if vm.lua_pcall(0, 1, 0) == vm.LUA_OK:
|
||||
return 1
|
||||
else:
|
||||
vm.lua_pop(1)
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
"pause":
|
||||
var pause_code = "return function(self) _dom_audio_pause('" + element_id + "') end"
|
||||
vm.load_string(pause_code, "audio.pause_closure")
|
||||
if vm.lua_pcall(0, 1, 0) == vm.LUA_OK:
|
||||
return 1
|
||||
else:
|
||||
vm.lua_pop(1)
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
"volume":
|
||||
vm.lua_pushnumber(audio_node.volume)
|
||||
return 1
|
||||
"loop":
|
||||
vm.lua_pushboolean(audio_node.loop)
|
||||
return 1
|
||||
"currentTime":
|
||||
vm.lua_pushnumber(audio_node.get_current_time())
|
||||
return 1
|
||||
"duration":
|
||||
vm.lua_pushnumber(audio_node.get_duration())
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
static func handle_dom_audio_newindex(vm: LuauVM, element_id: String, key: String, value: Variant) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
var audio_node = _get_dom_audio_node(element_id, lua_api)
|
||||
if not audio_node:
|
||||
return 0
|
||||
|
||||
match key:
|
||||
"volume":
|
||||
audio_node.call_deferred("set", "volume", float(value))
|
||||
"loop":
|
||||
audio_node.call_deferred("set", "loop", bool(value))
|
||||
"currentTime":
|
||||
audio_node.call_deferred("set_current_time", float(value))
|
||||
|
||||
return 0
|
||||
1
flumi/Scripts/Utils/Lua/Audio.gd.uid
Normal file
1
flumi/Scripts/Utils/Lua/Audio.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://defnka61xoi8d
|
||||
@@ -477,6 +477,12 @@ static func create_element_wrapper(vm: LuauVM, element: HTMLParser.HTMLElement,
|
||||
static func add_element_methods(vm: LuauVM, lua_api: LuaAPI) -> void:
|
||||
vm.set_meta("lua_api", lua_api)
|
||||
|
||||
vm.lua_pushcallable(LuaAudioUtils._dom_audio_play_handler, "_dom_audio_play")
|
||||
vm.lua_setglobal("_dom_audio_play")
|
||||
|
||||
vm.lua_pushcallable(LuaAudioUtils._dom_audio_pause_handler, "_dom_audio_pause")
|
||||
vm.lua_setglobal("_dom_audio_pause")
|
||||
|
||||
vm.lua_pushcallable(LuaDOMUtils._element_on_wrapper, "element.on")
|
||||
vm.lua_setfield(-2, "on")
|
||||
|
||||
@@ -827,10 +833,20 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
|
||||
vm.lua_getfield(1, "_tag_name")
|
||||
var tag_name: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
if tag_name == "audio":
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
return LuaAudioUtils.handle_dom_audio_index(vm, element_id, key)
|
||||
|
||||
match key:
|
||||
"text":
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if lua_api:
|
||||
# Get element ID and find the element
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
@@ -846,8 +862,6 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
|
||||
vm.lua_pushstring("")
|
||||
return 1
|
||||
"children":
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if lua_api:
|
||||
# Get element ID and find the element
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
@@ -870,7 +884,6 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
|
||||
return 1
|
||||
_:
|
||||
# Check for DOM traversal properties first
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if lua_api:
|
||||
match key:
|
||||
"parent":
|
||||
@@ -991,6 +1004,16 @@ static func _element_newindex_wrapper(vm: LuauVM) -> int:
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
var value = vm.lua_tovariant(3)
|
||||
|
||||
vm.lua_getfield(1, "_tag_name")
|
||||
var tag_name: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
if tag_name == "audio":
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
return LuaAudioUtils.handle_dom_audio_newindex(vm, element_id, key, value)
|
||||
|
||||
match key:
|
||||
"text":
|
||||
var text: String = str(value) # Convert value to string
|
||||
|
||||
@@ -14,14 +14,22 @@ static func connect_element_event(signal_node: Node, event_name: String, subscri
|
||||
match event_name:
|
||||
"click":
|
||||
if signal_node.has_signal("pressed"):
|
||||
signal_node.pressed.connect(subscription.lua_api._on_event_triggered.bind(subscription))
|
||||
var wrapper = func():
|
||||
LuaAudioUtils.mark_user_event()
|
||||
subscription.lua_api._on_event_triggered(subscription)
|
||||
signal_node.pressed.connect(wrapper)
|
||||
subscription.connected_signal = "pressed"
|
||||
subscription.connected_node = signal_node if signal_node != subscription.lua_api.get_dom_node(signal_node.get_parent(), "signal") else null
|
||||
subscription.wrapper_func = wrapper
|
||||
return true
|
||||
elif signal_node is Control:
|
||||
signal_node.gui_input.connect(subscription.lua_api._on_gui_input_click.bind(subscription))
|
||||
var wrapper = func(event: InputEvent):
|
||||
LuaAudioUtils.mark_user_event()
|
||||
subscription.lua_api._on_gui_input_click(subscription, event)
|
||||
signal_node.gui_input.connect(wrapper)
|
||||
subscription.connected_signal = "gui_input"
|
||||
subscription.connected_node = signal_node
|
||||
subscription.wrapper_func = wrapper
|
||||
return true
|
||||
"mousedown", "mouseup":
|
||||
if signal_node is Control:
|
||||
@@ -212,10 +220,16 @@ static func disconnect_subscription(subscription, lua_api) -> void:
|
||||
match subscription.connected_signal:
|
||||
"pressed":
|
||||
if target_node.has_signal("pressed"):
|
||||
target_node.pressed.disconnect(lua_api._on_event_triggered.bind(subscription))
|
||||
if subscription.has("wrapper_func") and subscription.wrapper_func:
|
||||
target_node.pressed.disconnect(subscription.wrapper_func)
|
||||
else:
|
||||
target_node.pressed.disconnect(lua_api._on_event_triggered.bind(subscription))
|
||||
"gui_input":
|
||||
if target_node.has_signal("gui_input"):
|
||||
target_node.gui_input.disconnect(lua_api._on_gui_input_click.bind(subscription))
|
||||
if subscription.has("wrapper_func") and subscription.wrapper_func:
|
||||
target_node.gui_input.disconnect(subscription.wrapper_func)
|
||||
else:
|
||||
target_node.gui_input.disconnect(lua_api._on_gui_input_click.bind(subscription))
|
||||
"gui_input_mouse":
|
||||
if target_node.has_signal("gui_input"):
|
||||
target_node.gui_input.disconnect(lua_api._on_gui_input_mouse_universal.bind(target_node))
|
||||
|
||||
@@ -333,6 +333,7 @@ func _setup_additional_lua_apis():
|
||||
LuaNetworkUtils.setup_network_api(lua_vm)
|
||||
LuaJSONUtils.setup_json_api(lua_vm)
|
||||
LuaWebSocketUtils.setup_websocket_api(lua_vm)
|
||||
LuaAudioUtils.setup_audio_api(lua_vm)
|
||||
|
||||
func _threaded_table_tostring_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
Reference in New Issue
Block a user