diff --git a/UI.gd b/UI.gd deleted file mode 100644 index 2b0d484..0000000 --- a/UI.gd +++ /dev/null @@ -1,35 +0,0 @@ -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) - - osc_server.register_callback("/string", "s", recv_osc) - osc_server.register_callback("/stringint", "si", recv_osc) - osc_server.register_callback("/blob", "b", recv_osc) - osc_server.register_callback("/blobint", "bi", recv_osc) - - # start listening for osc messages - osc_server.start_server() - -func recv_osc(msg_info, values): - print("Sender IP: %s" % msg_info["ip"]) - print("Sender Port: %d" % msg_info["port"]) - print("Address: %s" % msg_info["address"]) - print("Types: %s" % msg_info["types"]) - print("Values:", values) diff --git a/UI.tscn b/UI.tscn index fe50916..ab42bf3 100644 --- a/UI.tscn +++ b/UI.tscn @@ -1,8 +1,8 @@ [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"] +[ext_resource type="Script" path="res://ui.gd" id="1_28t32"] +[ext_resource type="Script" path="res://slider.gd" id="2_7hpi4"] +[ext_resource type="Script" path="res://toggle_button.gd" id="3_ch7hh"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y0b18"] bg_color = Color(1, 0, 0, 1) @@ -23,7 +23,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -script = ExtResource("1_ft657") +script = ExtResource("1_28t32") [node name="MarginContainer" type="MarginContainer" parent="."] layout_mode = 1 @@ -52,7 +52,7 @@ max_value = 1.0 step = 0.01 tick_count = 11 ticks_on_borders = true -script = ExtResource("2_310mb") +script = ExtResource("2_7hpi4") [node name="VSlider2" type="VSlider" parent="MarginContainer/GridContainer"] custom_minimum_size = Vector2(50, 300) @@ -63,7 +63,7 @@ max_value = 1.0 step = 0.01 tick_count = 11 ticks_on_borders = true -script = ExtResource("2_310mb") +script = ExtResource("2_7hpi4") [node name="VSlider3" type="VSlider" parent="MarginContainer/GridContainer"] custom_minimum_size = Vector2(50, 300) @@ -74,7 +74,7 @@ max_value = 1.0 step = 0.01 tick_count = 11 ticks_on_borders = true -script = ExtResource("2_310mb") +script = ExtResource("2_7hpi4") [node name="VSlider4" type="VSlider" parent="MarginContainer/GridContainer"] custom_minimum_size = Vector2(50, 300) @@ -85,32 +85,32 @@ max_value = 1.0 step = 0.01 tick_count = 11 ticks_on_borders = true -script = ExtResource("2_310mb") +script = ExtResource("2_7hpi4") [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") +script = ExtResource("3_ch7hh") [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") +script = ExtResource("3_ch7hh") [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") +script = ExtResource("3_ch7hh") [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") +script = ExtResource("3_ch7hh") diff --git a/OSCReceiver.gd b/osc_receiver.gd similarity index 96% rename from OSCReceiver.gd rename to osc_receiver.gd index ffea95c..566f318 100644 --- a/OSCReceiver.gd +++ b/osc_receiver.gd @@ -1,5 +1,7 @@ extends Node +class_name OSCReceiver + @export var default_address:String = "0.0.0.0" @export var default_port: int = 9001 @@ -51,6 +53,11 @@ func stop_server(): _server.stop() +func _debug(msg): + if debug: + print(msg) + + func _poll(): if not _server.is_listening(): return @@ -91,7 +98,7 @@ func _poll(): for callback in _observers[[address, types]]: callback.call(msg_info, values) -func _parse_osc_addr_and_types(packet): +func _parse_osc_addr_and_types(packet: PackedByteArray): var asep = packet.find(0) var address = packet.slice(0, asep).get_string_from_ascii() @@ -103,7 +110,7 @@ func _parse_osc_addr_and_types(packet): return [address, types, tsep + (4 - tsep % 4)] -func _parse_osc_values(packet, types): +func _parse_osc_values(packet: PackedByteArray, types: String): var result var values = [] var stream = StreamPeerBuffer.new() @@ -173,8 +180,3 @@ func _parse_osc_values(packet, types): return return values - - -func _debug(msg): - if debug: - print(msg) diff --git a/osc_receiver.tscn b/osc_receiver.tscn deleted file mode 100644 index 17a2e85..0000000 --- a/osc_receiver.tscn +++ /dev/null @@ -1,7 +0,0 @@ -[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/osc_sender.gd b/osc_sender.gd new file mode 100644 index 0000000..9dcb009 --- /dev/null +++ b/osc_sender.gd @@ -0,0 +1,108 @@ +extends Node + +class_name OSCSender + + +@export var default_address:String = "0.0.0.0" +@export var default_port: int = 9001 +@export var debug: bool = false +var _address: String +var _port: int +var socket = PacketPeerUDP.new() + + +func _init(): + _address = default_address + _port = default_port + + +func _debug(msg): + if debug: + print(msg) + + +func set_destination(dest): + if dest is int: + socket.set_dest_address(_address, dest) + _port = dest + elif dest is String: + socket.set_dest_address(dest, _port) + _address = dest + elif dest is Array: + socket.set_dest_address(dest[0], dest[1]) + _address = dest[0] + _port = dest[1] + + +func send_osc(dest, oscaddress: String, argtypes: String, values: Array): + assert(oscaddress.begins_with("/")) + var buf = StreamPeerBuffer.new() + buf.set_big_endian(true) + _pack_string(buf, oscaddress) + _pack_string(buf, "," + argtypes) + var vidx: int = 0 + + for i in argtypes.length(): + var typetag = argtypes[i] + var inc_vidx = true + + match typetag: + "i": + buf.put_32(values[vidx]) + "h": + buf.put_64(values[vidx]) + "f": + buf.put_float(values[vidx]) + "d": + buf.put_double(values[vidx]) + "c": + buf.put_32(values[vidx]) + "s", "S": + _pack_string(buf, values[vidx]) + "b": + _pack_blob(buf, values[vidx]) + "t": + assert(values[i].size() == 8) + buf.put_data(values[vidx]) + "m", "r": + assert(values[vidx].size() == 4) + buf.put_data(PackedByteArray(values[vidx])) + "TFI": + inc_vidx = false # no argument value sent + _: + _debug("Argument type '%s' not supported." % typetag) + return FAILED + + if inc_vidx: + vidx += 1 + + if dest != null: + set_destination(dest) + + socket.put_packet(buf.get_data_array()) + return OK + + +func _pack_string(buf: StreamPeerBuffer, s: String): + ## Pack a string into a binary OSC buffer + buf.put_data(s.to_ascii_buffer()) + + # pad to next 32-bit offset + while buf.get_position() % 4: + buf.put_u8(0) + + +func _pack_blob(buf, blob): + ## Pack a PackedByteArray, Array or String into a binary OSC buffer + if blob is String: + blob = blob.to_utf8_buffer() + elif blob is Array: + blob = PackedByteArray(blob) + + assert(blob.size() < 2 ** 31) # blob size per spec is _signed_ 32-bit int + buf.put_32(blob.size()) + buf.put_data(blob) + + # pad to next 32-bit offset + while buf.get_position() % 4: + buf.put_u8(0) diff --git a/project.godot b/project.godot index ba231f4..7e1dae7 100644 --- a/project.godot +++ b/project.godot @@ -10,7 +10,7 @@ config_version=5 [application] -config/name="OSCReceiver Demo" +config/name="OSC Demo" run/main_scene="res://UI.tscn" config/features=PackedStringArray("4.1") diff --git a/Slider.gd b/slider.gd similarity index 81% rename from Slider.gd rename to slider.gd index 0153765..8f78e9a 100644 --- a/Slider.gd +++ b/slider.gd @@ -6,4 +6,4 @@ func recv_osc(msg_info, values): print("Sender Port: %d" % msg_info["port"]) print("Address: %s" % msg_info["address"]) print("Types: %s" % msg_info["types"]) - set_value(clampf(values[0], 0.0, 1.0)) + set_value_no_signal(clampf(values[0], 0.0, 1.0)) diff --git a/testosc.sh b/testosc.sh new file mode 100755 index 0000000..6dcaba4 --- /dev/null +++ b/testosc.sh @@ -0,0 +1,13 @@ +HOST=localhost +PORT=9001 + +for ctrl in 0 1 2 3; do + for value in `LC_ALL=C seq 0.1 0.1 1.0`; do + oscsend $HOST $PORT /slider/$ctrl/set f $value + sleep 0.1 + done + + oscsend $HOST $PORT /button/$ctrl/set i 1 + sleep 1 + oscsend $HOST $PORT /button/$ctrl/set i 0 +done diff --git a/ToggleButton.gd b/toggle_button.gd similarity index 100% rename from ToggleButton.gd rename to toggle_button.gd diff --git a/ui.gd b/ui.gd new file mode 100644 index 0000000..99aa293 --- /dev/null +++ b/ui.gd @@ -0,0 +1,49 @@ +extends Control + +const OSCReceiver = preload("res://osc_receiver.gd") +var osc_server: OSCReceiver +const OSCSender = preload("res://osc_sender.gd") +var osc_client: OSCSender + +func _ready(): + osc_client = OSCSender.new() + osc_server = OSCReceiver.new() + add_child(osc_server) + var button_callback = Callable(self, "_button_pressed") + var slider_callback = Callable(self, "_slider_moved") + + # 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) + slider_node.value_changed.connect(slider_callback.bind(i)) + + 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) + button_node.pressed.connect(button_callback.bind(button_node, i)) + + # start listening for osc messages + osc_server.start_server() + + +func _button_pressed(btn: Button, idx: int): + print("Button %s pressed: state = %s" % [idx, btn.button_pressed]) + osc_client.send_osc(9000, "/button/%d" % idx, "i", [1 if btn.button_pressed else 0]) + + +func _slider_moved(value: float, idx: int): + print("Slider %d moved: value = %f" % [idx, value]) + osc_client.send_osc(9000, "/slider/%d" % idx, "f", [value]) + + +func recv_osc(msg_info, values): + print("Sender IP: %s" % msg_info["ip"]) + print("Sender Port: %d" % msg_info["port"]) + print("Address: %s" % msg_info["address"]) + print("Types: %s" % msg_info["types"]) + print("Values:", values)