save_load.gd 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. extends Node
  2. var control_script
  3. var graph_edit
  4. var open_help
  5. var register_movement
  6. var register_input
  7. var link_output
  8. # Called when the node enters the scene tree for the first time.
  9. func _ready() -> void:
  10. pass # Replace with function body.
  11. func init(main_node: Node, graphedit: GraphEdit, openhelp: Callable, registermovement: Callable, registerinput: Callable, linkoutput: Callable) -> void:
  12. control_script = main_node
  13. graph_edit = graphedit
  14. open_help = openhelp
  15. register_movement = registermovement
  16. register_input = registerinput
  17. link_output = linkoutput
  18. func save_graph_edit(path: String):
  19. var file = FileAccess.open(path, FileAccess.WRITE)
  20. if file == null:
  21. print("Failed to open file for saving")
  22. return
  23. var node_data_list = []
  24. var connection_data_list = []
  25. var node_id_map = {} # Map node name to numeric ID
  26. var node_id = 1
  27. # Assign each node a unique numeric ID and gather node data
  28. for node in graph_edit.get_children():
  29. if node is GraphNode:
  30. node_id_map[node.name] = node_id
  31. var offset = node.position_offset
  32. var node_data = {
  33. "id": node_id,
  34. "name": node.name,
  35. "command": node.get_meta("command"),
  36. "offset": { "x": offset.x, "y": offset.y },
  37. "slider_values": {},
  38. "addremoveinlets":{},
  39. "notes": {},
  40. "checkbutton_states": {},
  41. "optionbutton_values": {}
  42. }
  43. # Save slider values and metadata
  44. for child in node.find_children("*", "Slider", true, false):
  45. var relative_path = node.get_path_to(child)
  46. var path_str = str(relative_path)
  47. node_data["slider_values"][path_str] = {
  48. "value": child.value,
  49. "editable": child.editable,
  50. "meta": {}
  51. }
  52. for key in child.get_meta_list():
  53. node_data["slider_values"][path_str]["meta"][str(key)] = child.get_meta(key)
  54. #save add remove inlet meta data
  55. if node.has_node("addremoveinlets"):
  56. if node.get_node("addremoveinlets").has_meta("inlet_count"):
  57. node_data["addremoveinlets"]["inlet_count"] = node.get_node("addremoveinlets").get_meta("inlet_count")
  58. # Save notes from CodeEdit children
  59. for child in node.find_children("*", "CodeEdit", true, false):
  60. node_data["notes"][child.name] = child.text
  61. #save checkbutton states
  62. for child in node.find_children("*", "CheckButton", true, false):
  63. node_data["checkbutton_states"][child.name] = child.button_pressed
  64. #save optionbutton states
  65. for child in node.find_children("*", "OptionButton", true, false):
  66. node_data["optionbutton_values"][child.name] = child.selected
  67. node_data_list.append(node_data)
  68. node_id += 1
  69. # Save connections using node IDs instead of names
  70. for conn in graph_edit.get_connection_list():
  71. # Map from_node and to_node names to IDs
  72. var from_id = node_id_map.get(conn["from_node"], null)
  73. var to_id = node_id_map.get(conn["to_node"], null)
  74. if from_id != null and to_id != null:
  75. connection_data_list.append({
  76. "from_node_id": from_id,
  77. "from_port": conn["from_port"],
  78. "to_node_id": to_id,
  79. "to_port": conn["to_port"]
  80. })
  81. else:
  82. print("Warning: Connection references unknown node(s). Skipping connection.")
  83. var graph_data = {
  84. "nodes": node_data_list,
  85. "connections": connection_data_list,
  86. "fftsize": control_script.fft_size_option_button.selected,
  87. "fftoverlap": control_script.fft_overlap_option_button.selected
  88. }
  89. var json = JSON.new()
  90. var json_string = json.stringify(graph_data, "\t")
  91. file.store_string(json_string)
  92. file.close()
  93. print("Graph saved.")
  94. control_script.changesmade = false
  95. get_window().title = "SoundThread - " + path.get_file().trim_suffix(".thd")
  96. func load_graph_edit(path: String):
  97. var file = FileAccess.open(path, FileAccess.READ)
  98. if file == null:
  99. print("Failed to open file for loading")
  100. return
  101. var json_text = file.get_as_text()
  102. file.close()
  103. var json = JSON.new()
  104. if json.parse(json_text) != OK:
  105. print("Error parsing JSON")
  106. return
  107. var graph_data = json.get_data()
  108. graph_edit.clear_connections()
  109. # Remove all existing GraphNodes from graph_edit
  110. for node in graph_edit.get_children():
  111. if node is GraphNode:
  112. node.queue_free()
  113. await get_tree().process_frame # Ensure nodes are freed before adding new ones
  114. #set fft size and window overlap if available
  115. if graph_data.has("fftsize"):
  116. control_script.fft_size_option_button.select(graph_data["fftsize"])
  117. control_script._on_fft_size_item_selected(graph_data["fftsize"])
  118. if graph_data.has("fftoverlap"):
  119. control_script.fft_overlap_option_button.select(graph_data["fftoverlap"])
  120. control_script._on_fft_overlap_item_selected(graph_data["fftoverlap"])
  121. var id_to_node = {}
  122. # Create nodes
  123. for node_data in graph_data["nodes"]:
  124. var command_name = node_data.get("command", "")
  125. var new_node = graph_edit._make_node(command_name, true)
  126. if new_node == null:
  127. print("Failed to create node for command:", command_name)
  128. continue
  129. new_node.name = node_data["name"]
  130. new_node.position_offset = Vector2(node_data["offset"]["x"], node_data["offset"]["y"])
  131. id_to_node[node_data["id"]] = new_node
  132. # Restore sliders
  133. for slider_path_str in node_data["slider_values"]:
  134. var slider = new_node.get_node_or_null(slider_path_str)
  135. if slider and (slider is HSlider or slider is VSlider):
  136. var slider_info = node_data["slider_values"][slider_path_str]
  137. if typeof(slider_info) == TYPE_DICTIONARY:
  138. slider.value = slider_info.get("value", slider.value)
  139. if slider_info.has("editable"):
  140. slider.editable = slider_info["editable"]
  141. if slider_info.has("meta"):
  142. for key in slider_info["meta"]:
  143. var value = slider_info["meta"][key]
  144. if key == "brk_data" and typeof(value) == TYPE_ARRAY:
  145. var new_array: Array = []
  146. for item in value:
  147. if typeof(item) == TYPE_STRING:
  148. var numbers: PackedStringArray = item.strip_edges().trim_prefix("(").trim_suffix(")").split(",")
  149. if numbers.size() == 2:
  150. var x = float(numbers[0])
  151. var y = float(numbers[1])
  152. new_array.append(Vector2(x, y))
  153. value = new_array
  154. slider.set_meta(key, value)
  155. else:
  156. slider.value = slider_info
  157. #fill label for output file node on load
  158. if new_node.has_meta("command") and new_node.get_meta("command") == "outputfile":
  159. #set label for last output folder
  160. var interface_settings = ConfigHandler.load_interface_settings()
  161. control_script.output_folder_label = new_node.get_node("OutputFolderMargin/OutputFolderLabel")
  162. if control_script.output_folder_label != null and interface_settings.last_used_output_folder != "no_file":
  163. control_script.output_folder_label.text = interface_settings.last_used_output_folder
  164. control_script.output_folder_label.get_parent().tooltip_text = interface_settings.last_used_output_folder
  165. # Restore notes
  166. for codeedit_name in node_data["notes"]:
  167. var codeedit = new_node.find_child(codeedit_name, true, false)
  168. if codeedit and (codeedit is CodeEdit):
  169. codeedit.text = node_data["notes"][codeedit_name]
  170. # Restore check buttons if this exists in the file (if statement is to stop crashes when opening old save files)
  171. if node_data.has("checkbutton_states"):
  172. for checkbutton_name in node_data["checkbutton_states"]:
  173. var checkbutton = new_node.find_child(checkbutton_name, true, false)
  174. if checkbutton and (checkbutton is CheckButton):
  175. checkbutton.button_pressed = node_data["checkbutton_states"][checkbutton_name]
  176. # Restore option buttons if this exists in the file (if statement is to stop crashes when opening old save files)
  177. if node_data.has("optionbutton_values"):
  178. for optionbutton_name in node_data["optionbutton_values"]:
  179. var optionbutton = new_node.find_child(optionbutton_name, true, false)
  180. if optionbutton and (optionbutton is OptionButton):
  181. optionbutton.selected = node_data["optionbutton_values"][optionbutton_name]
  182. #restore dynamic inlets
  183. if node_data.has("addremoveinlets") and new_node.has_node("addremoveinlets"):
  184. print("restoring inlets")
  185. var addremoveinlets = new_node.get_node("addremoveinlets")
  186. addremoveinlets.set_meta("inlet_count", node_data["addremoveinlets"]["inlet_count"])
  187. await get_tree().process_frame
  188. addremoveinlets.restore_inlets()
  189. register_input.call(new_node)
  190. # Recreate connections
  191. for conn in graph_data["connections"]:
  192. var from_node = id_to_node.get(conn["from_node_id"], null)
  193. var to_node = id_to_node.get(conn["to_node_id"], null)
  194. if from_node != null and to_node != null:
  195. graph_edit.connect_node(
  196. from_node.name, conn["from_port"],
  197. to_node.name, conn["to_port"]
  198. )
  199. else:
  200. print("Warning: Connection references unknown node ID(s). Skipping connection.")
  201. link_output.call()
  202. print("Graph loaded.")
  203. get_window().title = "SoundThread - " + path.get_file().trim_suffix(".thd")
  204. control_script.changesmade = false