commit 91ce62f680e6969fe58f3941f0c9159c8457e259 Author: Christopher Arndt Date: Thu Nov 2 05:22:31 2023 +0100 Initial commit Signed-off-by: Christopher Arndt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2baf1a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +export/ +.godot/ +.godot/ +*.translation diff --git a/OSCReceiver.gd b/OSCReceiver.gd new file mode 100644 index 0000000..37e64f5 --- /dev/null +++ b/OSCReceiver.gd @@ -0,0 +1,167 @@ +extends Node + + +@export var default_address:String = "0.0.0.0" +@export var default_port: int = 9001 +@export var poll_interval: float = 0.05 +@export var debug: bool = false +var _server: UDPServer +var _observers: Dictionary +var _timer: Timer + + +func _init(): + _observers = {} + + _timer = Timer.new() + _timer.autostart = false + _timer.one_shot = false + _timer.timeout.connect(self._poll) + add_child(_timer) + + +func _exit_tree(): + stop_server() + + +func register_callback(oscaddress, argtypes, callback): + if not _observers.has([oscaddress, argtypes]): + _observers[[oscaddress, argtypes]] = [] + + _observers[[oscaddress, argtypes]].append(callback) + + +func start_server(address:String = default_address, port:int = default_port): + _server = UDPServer.new() + + if _server.listen(port, address) != OK: + _debug("OSCReceiver could not bind to port: %s" % port) + else: + _debug("OSCReceiver listening on port: %s" % port) + + _timer.start(poll_interval) + + +func stop_server(): + _timer.stop() + remove_child(_timer) + _timer.free() + + if _server: + _server.stop() + + +func _poll(): + if not _server.is_listening(): + return + + _server.poll() + + if _server.is_connection_available(): + var peer: PacketPeerUDP = _server.take_connection() + var packet = peer.get_packet() + var sender_ip = peer.get_packet_ip() + var sender_port = peer.get_packet_port() + + _debug("Accepted peer: %s:%s" % [sender_ip, sender_port]) + + var result = _parse_osc_addr_and_types(packet) + var address = result[0] + var types = result[1] + var offset = result[2] + + _debug("OSC address: %s" % address) + _debug("OSC arg types: %s" % types) + + if _observers.has([address, types]): + var values = _parse_osc_values(packet, types, offset) + + if values == null: + _debug("Invalid/Unsupported OSC message.") + elif values.size() != types.length(): + _debug("Mismatch between expected / received number of OSC arguments.") + else: + values.append({ + "ip": sender_ip, + "port": sender_port, + "address": address, + "types": types + }) + + for callback in _observers[[address, types]]: + callback.callv(values) + +func _parse_osc_addr_and_types(packet): + var asep = packet.find(0) + var address = packet.slice(0, asep).get_string_from_ascii() + + var toffset = asep + (4 - asep % 4) + assert(char(packet.decode_u8(toffset)) == ",") + var tsep = packet.find(0, toffset) + var types = packet.slice(toffset + 1, tsep).get_string_from_ascii() + + return [address, types, tsep + (4 - tsep % 4)] + + +func _parse_osc_values(packet, types, offset): + var values = [] + var stream = StreamPeerBuffer.new() + stream.set_data_array(packet.slice(offset)) + stream.set_big_endian(true) + + for type_id in types: + match type_id: + "i": + values.append(stream.get_32()) + "h": + values.append(stream.get_62()) + "f": + values.append(stream.get_float()) + "d": + values.append(stream.get_double()) + "c": + values.append(char(stream.get_32())) + "s", "S": + var value = PackedStringArray() + var null_found = false + + while not null_found: + for _i in range(4): + var ch = stream.get_u8() + if not null_found and ch != 0: + value.append(char(ch)) + else: + null_found = true + + values.append("".join(value)) + "b": + var count = stream.get_u32() + values.append(stream.get_data(count)) + + if count % 4: + stream.seek(stream.get_position() + (count % 4)) + "t": + values.append(stream.get_data(8)) + "m", "r": + values.append([ + stream.get_u8(), + stream.get_u8(), + stream.get_u8(), + stream.get_u8(), + ]) + "T": + values.append(true) + "F": + values.append(false) + "I", "N": + values.append(null) + _: + _debug("Argument type '%s' not yet supported." % type_id) + return + + return values + + +func _debug(msg): + if debug: + print(msg) diff --git a/Slider.gd b/Slider.gd new file mode 100644 index 0000000..c931e69 --- /dev/null +++ b/Slider.gd @@ -0,0 +1,9 @@ +extends VSlider + + +func recv_osc(val, msg_info): + print("Sender IP: %s" % msg_info["ip"]) + print("Sender Port: %d" % msg_info["port"]) + print("Adress: %s" % msg_info["address"]) + print("Types: %s" % msg_info["types"]) + set_value(clampf(val, 0.0, 1.0)) diff --git a/ToggleButton.gd b/ToggleButton.gd new file mode 100644 index 0000000..cb72837 --- /dev/null +++ b/ToggleButton.gd @@ -0,0 +1,9 @@ +extends Button + + +func recv_osc(val, msg_info): + print("Sender IP: %s" % msg_info["ip"]) + print("Sender Port: %d" % msg_info["port"]) + print("Adress: %s" % msg_info["address"]) + print("Types: %s" % msg_info["types"]) + set_pressed_no_signal(bool(val)) diff --git a/UI.gd b/UI.gd new file mode 100644 index 0000000..59e4e7f --- /dev/null +++ b/UI.gd @@ -0,0 +1,23 @@ +extends Control + + +func _ready(): + var scene = preload("res://osc_receiver.tscn") + var osc_server = scene.instantiate() + add_child(osc_server) + + # configure all sensors + for i in range(4): + var nodename = "VSlider%d" % (i + 1) + var osc_addr = "/slider/%d/set" % i + var slider_node = find_child(nodename) + osc_server.register_callback(osc_addr, "f", slider_node.recv_osc) + + for i in range(4): + var nodename = "Button%d" % (i + 1) + var osc_addr = "/button/%d/set" % i + var button_node = find_child(nodename) + osc_server.register_callback(osc_addr, "i", button_node.recv_osc) + + # start listening for osc messages + osc_server.start_server() diff --git a/UI.tscn b/UI.tscn new file mode 100644 index 0000000..fe50916 --- /dev/null +++ b/UI.tscn @@ -0,0 +1,116 @@ +[gd_scene load_steps=8 format=3 uid="uid://8xyprd3yldfg"] + +[ext_resource type="Script" path="res://UI.gd" id="1_ft657"] +[ext_resource type="Script" path="res://Slider.gd" id="2_310mb"] +[ext_resource type="Script" path="res://ToggleButton.gd" id="3_ct66k"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y0b18"] +bg_color = Color(1, 0, 0, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_iu8u7"] +bg_color = Color(0, 1, 0, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7e33t"] +bg_color = Color(0, 0, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eeife"] +bg_color = Color(1, 1, 0.0235294, 1) + +[node name="UI" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ft657") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="GridContainer" type="GridContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 10 +theme_override_constants/v_separation = 10 +columns = 4 + +[node name="VSlider1" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="VSlider2" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="VSlider3" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="VSlider4" type="VSlider" parent="MarginContainer/GridContainer"] +custom_minimum_size = Vector2(50, 300) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.01 +tick_count = 11 +ticks_on_borders = true +script = ExtResource("2_310mb") + +[node name="Button1" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_y0b18") +toggle_mode = true +text = "1" +script = ExtResource("3_ct66k") + +[node name="Button2" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_iu8u7") +toggle_mode = true +text = "2" +script = ExtResource("3_ct66k") + +[node name="Button3" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_7e33t") +toggle_mode = true +text = "3" +script = ExtResource("3_ct66k") + +[node name="Button4" type="Button" parent="MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_styles/pressed = SubResource("StyleBoxFlat_eeife") +toggle_mode = true +text = "4 " +script = ExtResource("3_ct66k") diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..ed81c1c --- /dev/null +++ b/default_env.tres @@ -0,0 +1,4 @@ +[gd_resource type="Environment" format=2] + +[resource] +background_mode = 2 diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..1a2dc01 --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,39 @@ +[preset.0] + +name="Linux/X11" +platform="Linux/X11" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="export/osc_receiver_demo.x86_64" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" diff --git a/osc_receiver.tscn b/osc_receiver.tscn new file mode 100644 index 0000000..17a2e85 --- /dev/null +++ b/osc_receiver.tscn @@ -0,0 +1,7 @@ +[gd_scene load_steps=2 format=3 uid="uid://bqci5bw1h0lh7"] + +[ext_resource type="Script" path="res://OSCReceiver.gd" id="1_ujyu5"] + +[node name="OSCReceiver" type="Node"] +script = ExtResource("1_ujyu5") +debug = true diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..ba231f4 --- /dev/null +++ b/project.godot @@ -0,0 +1,28 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="OSCReceiver Demo" +run/main_scene="res://UI.tscn" +config/features=PackedStringArray("4.1") + +[debug] + +settings/fps/force_fps=30 + +[display] + +window/size/viewport_width=300 +window/size/viewport_height=400 + +[rendering] + +environment/default_environment="res://default_env.tres"