Browse Source

improved save and load to track changes, fixed localisation of input and output file nodes, added proper handling for flags in batch file generation

Jonathan Higgins 7 months ago
parent
commit
c0fb2d41ea

+ 1 - 1
.godot/editor/audioplayer.tscn-editstate-5c5be15cd7cbde82fbc3248f6958ab76.cfg

@@ -192,4 +192,4 @@ Game={
 "hide_selection": false,
 "select_mode": 0
 }
-selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/GraphEdit")])
+selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/MenuBar/FileButton")])

+ 1 - 1
.godot/editor/audioplayer.tscn-folding-5c5be15cd7cbde82fbc3248f6958ab76.cfg

@@ -1,5 +1,5 @@
 [folding]
 
-node_unfolds=[NodePath("."), PackedStringArray("metadata", "Layout"), NodePath("LoadButton"), PackedStringArray("metadata"), NodePath("PlayButton"), PackedStringArray("Layout", "Layout/Transform"), NodePath("WavError"), PackedStringArray("Flags"), NodePath("WaveformPreview"), PackedStringArray("Layout", "Layout/Transform"), NodePath("Playhead"), PackedStringArray("Transform")]
+node_unfolds=[NodePath("."), PackedStringArray("metadata", "Layout"), NodePath("LoadButton"), PackedStringArray("metadata"), NodePath("RecycleButton"), PackedStringArray("metadata"), NodePath("PlayButton"), PackedStringArray("Layout", "Layout/Transform"), NodePath("WavError"), PackedStringArray("Flags"), NodePath("WaveformPreview"), PackedStringArray("Layout", "Layout/Transform"), NodePath("Playhead"), PackedStringArray("Transform")]
 resource_unfolds=[]
 nodes_folded=[NodePath("WavError")]

+ 3 - 3
.godot/editor/control.tscn-editstate-475cf43e2d21753002d8a2b4ccf5105f.cfg

@@ -8,7 +8,7 @@ Anim={
 "grid_snap_active": true,
 "grid_step": Vector2(8, 8),
 "grid_visibility": 1,
-"ofs": Vector2(-473.8, -45),
+"ofs": Vector2(176.805, 207.598),
 "primary_grid_step": Vector2i(8, 8),
 "show_group_gizmos": true,
 "show_guides": true,
@@ -34,7 +34,7 @@ Anim={
 "snap_rotation_step": 0.261799,
 "snap_scale": false,
 "snap_scale_step": 0.1,
-"zoom": 0.909091
+"zoom": 2.14359
 }
 3D={
 "fov": 70.01,
@@ -192,4 +192,4 @@ Game={
 "hide_selection": false,
 "select_mode": 0
 }
-selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/GraphEdit")])
+selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/MenuBar/FileButton")])

+ 1 - 1
.godot/editor/control.tscn-folding-475cf43e2d21753002d8a2b4ccf5105f.cfg

@@ -2,4 +2,4 @@
 
 node_unfolds=[NodePath("."), PackedStringArray("Layout"), NodePath("GraphEdit"), PackedStringArray("Layout", "Layout/Transform"), NodePath("NoLocationPopup"), PackedStringArray("Flags"), NodePath("NoInputPopup"), PackedStringArray("Flags"), NodePath("Console"), PackedStringArray("Flags"), NodePath("Console/Panel"), PackedStringArray("Layout", "Layout/Transform"), NodePath("Console/ConsoleOutput"), PackedStringArray("Layout"), NodePath("MenuBar"), PackedStringArray("Layout"), NodePath("MenuBar/FileButton"), PackedStringArray("item_count_array"), NodePath("MenuBar/SettingsButton"), PackedStringArray("item_count_array"), NodePath("MenuBar/HelpButton"), PackedStringArray("item_count_array")]
 resource_unfolds=[]
-nodes_folded=[NodePath("NoLocationPopup"), NodePath("NoInputPopup"), NodePath("MultipleConnectionsPopup"), NodePath("Console")]
+nodes_folded=[NodePath("NoLocationPopup"), NodePath("NoInputPopup"), NodePath("MultipleConnectionsPopup"), NodePath("SaveChangesPopup")]

+ 6 - 6
.godot/editor/editor_layout.cfg

@@ -9,8 +9,8 @@ dock_filesystem_v_split_offset=0
 dock_filesystem_display_mode=0
 dock_filesystem_file_sort=0
 dock_filesystem_file_list_display_mode=1
-dock_filesystem_selected_paths=PackedStringArray("res://config_handler.gd")
-dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://scenes/main/", "res://addons/audio_preview/")
+dock_filesystem_selected_paths=PackedStringArray()
+dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://scenes/", "res://scenes/Nodes/", "res://scenes/main/", "res://addons/audio_preview/")
 dock_node_current_tab=0
 dock_history_include_scene=true
 dock_history_include_global=true
@@ -29,11 +29,11 @@ dock_5="Inspector,Node,History"
 [EditorNode]
 
 open_scenes=PackedStringArray("res://scenes/main/control.tscn", "res://scenes/Nodes/valueslider.tscn", "res://scenes/Nodes/nodes.tscn", "res://scenes/menu/menu.tscn", "res://scenes/Nodes/audioplayer.tscn")
-current_scene="res://scenes/main/control.tscn"
+current_scene="res://scenes/Nodes/nodes.tscn"
 center_split_offset=0
 selected_default_debugger_tab_idx=0
-selected_main_editor_idx=2
-selected_bottom_panel_item=1
+selected_main_editor_idx=0
+selected_bottom_panel_item=0
 
 [EditorWindow]
 
@@ -44,7 +44,7 @@ size=Vector2i(1152, 648)
 
 [ScriptEditor]
 
-open_scripts=["res://scenes/Nodes/audioplayer.gd", "res://addons/audio_preview/AudioStreamPreview.gd", "res://config_handler.gd", "res://scenes/main/control.gd", "res://export_presets.cfg", "res://scenes/Nodes/focus_accu_sliders.gd", "res://Global.gd", "res://scenes/main/graph_edit.gd", "res://scenes/Nodes/scatter_value.gd", "res://scenes/Nodes/valueslider.gd", "res://addons/audio_preview/voice_preview_generator.gd", "res://scenes/Nodes/waveform_preview.gd"]
+open_scripts=["res://scenes/Nodes/audioplayer.gd", "res://addons/audio_preview/AudioStreamPreview.gd", "res://config_handler.gd", "res://scenes/main/control.gd", "res://export_presets.cfg", "res://Global.gd", "res://scenes/main/graph_edit.gd", "res://scenes/Nodes/valueslider.gd", "res://addons/audio_preview/voice_preview_generator.gd", "res://scenes/Nodes/waveform_preview.gd"]
 selected_script="res://scenes/main/control.gd"
 open_help=[]
 script_split_offset=200

+ 14 - 12
.godot/editor/filesystem_cache10

@@ -1,9 +1,10 @@
 ea4bc82a6ad023ab7ee23ee620429895
-::res://::1746745335
-config_handler.gd::GDScript/GDScript::9123848664534566230::1746479238::0::1::::<>Node<><>0<>0<><>::
+::res://::1746788545
+config_handler.gd::GDScript::9123848664534566230::1746759327::0::1::::<>Node<><>0<>0<><>::
 export_presets.cfg::TextFile/TextFile::-1::1746584894::0::1::::<><><>0<>0<><>::
 Global.gd::GDScript/GDScript::7717406573998402474::1746534642::0::1::::<>Node<><>0<>0<><>::
 icon.svg::CompressedTexture2D/CompressedTexture2D::5168976688331411336::1745928495::1745928503::1::::<><><>0<>0<>5d9c9b5d6e3d90bedad9d000ed97534c<>res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex::
+NodeData.tres::Resource::7763297342208290105::1746762476::0::1::::<><><>0<>0<><>::
 README.md::TextFile/TextFile::-1::1746495214::0::1::::<><><>0<>0<><>::
 splash.png::CompressedTexture2D/CompressedTexture2D::1486976329801462502::1746631442::1746631447::1::::<><><>0<>0<>3eb9acb3a672643bd6ed5acadd0dc708<>res://.godot/imported/splash.png-929ed8a00b89ba36c51789452f874c77.ctex::
 ::res://addons/::1746462377
@@ -12,19 +13,20 @@ AudioStreamPreview.gd::GDScript/GDScript::2308550898442263711::1746444878::0::1:
 AudioStreamPreview.tscn::PackedScene/PackedScene::3762817095482496943::1746452502::0::1::::<><><>0<>0<><>::uid://75jq4nle8md8::::res://addons/audio_preview/AudioStreamPreview.gd<>uid://c2fo50owdhh06::::res://addons/audio_preview/voice_preview_generator.tscn
 voice_preview_generator.gd::GDScript/GDScript::6244997812245505292::1746444274::0::1::::<>Node<><>0<>1<><>::
 voice_preview_generator.tscn::PackedScene/PackedScene::6679166981814140597::1746444565::0::1::::<><><>0<>0<><>::uid://cu8eg4agw08xs::::res://addons/audio_preview/voice_preview_generator.gd
+::res://examples/::1746770939
 ::res://scenes/::1746186301
-::res://scenes/main/::1746718071
-control.gd::GDScript::2620037524409541442::1746718070::0::1::::<>Control<><>0<>0<><>::
-control.tscn::PackedScene::2566019287410494992::1746718071::0::1::::<><><>0<>0<><>::uid://bdlfvuljckmu1::::res://scenes/main/control.gd<>uid://l2yejnjysupr::::res://scenes/main/graph_edit.gd<>uid://b0wdj8v6o0wq0::::res://scenes/menu/menu.tscn
-graph_edit.gd::GDScript/GDScript::829280323614315599::1746582824::0::1::::<>GraphEdit<><>0<>0<><>::
-::res://scenes/menu/::1746718072
-menu.tscn::PackedScene::4186758075496332121::1746718072::0::1::::<><><>0<>0<><>::
-::res://scenes/Nodes/::1746718072
+::res://scenes/main/::1746771537
+control.gd::GDScript::2620037524409541442::1746771534::0::1::::<>Control<><>0<>0<><>::
+control.tscn::PackedScene::2566019287410494992::1746771537::0::1::::<><><>0<>0<><>::uid://bdlfvuljckmu1::::res://scenes/main/control.gd<>uid://l2yejnjysupr::::res://scenes/main/graph_edit.gd<>uid://b0wdj8v6o0wq0::::res://scenes/menu/menu.tscn
+graph_edit.gd::GDScript::829280323614315599::1746754372::0::1::::<>GraphEdit<><>0<>0<><>::
+::res://scenes/menu/::1746771537
+menu.tscn::PackedScene::4186758075496332121::1746771537::0::1::::<><><>0<>0<><>::
+::res://scenes/Nodes/::1746771537
 audioplayer.gd::GDScript/GDScript::5570864814132306429::1746710375::0::1::::<>Control<><>0<>0<><>::
-audioplayer.tscn::PackedScene::6037166449976350293::1746718072::0::1::::<><><>0<>0<><>::uid://clmtlg8via3qn::::res://scenes/Nodes/audioplayer.gd
+audioplayer.tscn::PackedScene::6037166449976350293::1746771537::0::1::::<><><>0<>0<><>::uid://clmtlg8via3qn::::res://scenes/Nodes/audioplayer.gd
 focus_accu_sliders.gd::GDScript/GDScript::8821949764991756997::1746492997::0::1::::<>GraphNode<><>0<>0<><>::
-nodes.tscn::PackedScene::8614413456730569426::1746718072::0::1::::<><><>0<>0<><>::uid://csapiqka522fh::::res://scenes/Nodes/audioplayer.tscn<>uid://dya5kxx132fgp::::res://scenes/Nodes/valueslider.tscn<>uid://dyf0qutxeqio3::::res://scenes/Nodes/scatter_value.gd<>uid://dxxohuvlw5e3n::::res://scenes/Nodes/focus_accu_sliders.gd
+nodes.tscn::PackedScene::8614413456730569426::1746771537::0::1::::<><><>0<>0<><>::uid://csapiqka522fh::::res://scenes/Nodes/audioplayer.tscn<>uid://dya5kxx132fgp::::res://scenes/Nodes/valueslider.tscn<>uid://dyf0qutxeqio3::::res://scenes/Nodes/scatter_value.gd<>uid://dxxohuvlw5e3n::::res://scenes/Nodes/focus_accu_sliders.gd
 scatter_value.gd::GDScript/GDScript::8855663765553304696::1746710375::0::1::::<>VBoxContainer<><>0<>0<><>::
 valueslider.gd::GDScript/GDScript::2557655848205010713::1746187131::0::1::::<>VBoxContainer<><>0<>0<><>::
-valueslider.tscn::PackedScene::8845634910901483783::1746718072::0::1::::<><><>0<>0<><>::uid://bco7hof3wqck4::::res://scenes/Nodes/valueslider.gd
+valueslider.tscn::PackedScene::8845634910901483783::1746771537::0::1::::<><><>0<>0<><>::uid://bco7hof3wqck4::::res://scenes/Nodes/valueslider.gd
 waveform_preview.gd::GDScript/GDScript::5688062150584079786::1746458041::0::1::::<>Control<><>0<>0<><>::

+ 4 - 5
.godot/editor/filesystem_update4

@@ -1,8 +1,7 @@
-res://scenes/main/control.tscn
-res://scenes/Nodes/valueslider.tscn
 res://scenes/Nodes/nodes.tscn
-res://scenes/menu/menu.tscn
 res://scenes/Nodes/audioplayer.tscn
+res://scenes/main/control.tscn
 res://scenes/main/control.gd
-res://scenes/main/graph_edit.gd
-res://config_handler.gd
+res://scenes/Nodes/valueslider.tscn
+res://scenes/menu/menu.tscn
+res://scenes/Nodes/audioplayer.gd

+ 1 - 1
.godot/editor/menu.tscn-editstate-523ba9f1be4474a87fc09942b9fbb098.cfg

@@ -192,4 +192,4 @@ Game={
 "hide_selection": false,
 "select_mode": 0
 }
-selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/GraphEdit")])
+selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/MenuBar/FileButton")])

+ 2 - 2
.godot/editor/nodes.tscn-editstate-d18a50cdbd65798e64eea9469be45949.cfg

@@ -8,7 +8,7 @@ Anim={
 "grid_snap_active": false,
 "grid_step": Vector2(8, 8),
 "grid_visibility": 1,
-"ofs": Vector2(-253.998, -151.976),
+"ofs": Vector2(-234.672, 271.588),
 "primary_grid_step": Vector2i(8, 8),
 "show_group_gizmos": true,
 "show_guides": true,
@@ -192,4 +192,4 @@ Game={
 "hide_selection": false,
 "select_mode": 0
 }
-selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/GraphEdit")])
+selected_nodes=Array[NodePath]([])

File diff suppressed because it is too large
+ 0 - 0
.godot/editor/nodes.tscn-folding-d18a50cdbd65798e64eea9469be45949.cfg


+ 1 - 1
.godot/editor/quick_open_dialog_cache.cfg

@@ -1,3 +1,3 @@
 [selected_history]
 
-PackedScene=PackedStringArray("uid://csapiqka522fh", "uid://dya5kxx132fgp", "uid://b0wdj8v6o0wq0")
+PackedScene=PackedStringArray("uid://dya5kxx132fgp", "uid://csapiqka522fh", "uid://b0wdj8v6o0wq0")

+ 8 - 40
.godot/editor/script_editor_cache.cfg

@@ -3,11 +3,11 @@
 state={
 "bookmarks": PackedInt32Array(),
 "breakpoints": PackedInt32Array(),
-"column": 64,
-"folded_lines": Array[int]([77, 82, 86, 91, 95, 111, 142, 167, 170, 173, 176, 214, 217, 271, 341, 347, 592, 602, 665, 741, 746, 750, 902]),
+"column": 0,
+"folded_lines": Array[int]([72, 90, 96, 106, 111, 115, 120, 124, 140, 176, 202, 212, 257, 264, 312, 385, 401, 408, 411, 414, 417, 692, 755, 785, 833, 838, 842, 895, 944, 1046]),
 "h_scroll_position": 0,
-"row": 807,
-"scroll_position": 516.0,
+"row": 680,
+"scroll_position": 406.0,
 "selection": false,
 "syntax_highlighter": "GDScript"
 }
@@ -49,16 +49,12 @@ state={
 state={
 "bookmarks": PackedInt32Array(),
 "breakpoints": PackedInt32Array(),
-"column": 0,
+"column": 1,
 "folded_lines": Array[int]([]),
 "h_scroll_position": 0,
-"row": 15,
-"scroll_position": 36.0,
-"selection": true,
-"selection_from_column": 0,
-"selection_from_line": 15,
-"selection_to_column": 50,
-"selection_to_line": 17,
+"row": 6,
+"scroll_position": 27.0,
+"selection": false,
 "syntax_highlighter": "GDScript"
 }
 
@@ -140,34 +136,6 @@ state={
 "syntax_highlighter": "GDScript"
 }
 
-[res://scenes/Nodes/focus_accu_sliders.gd]
-
-state={
-"bookmarks": PackedInt32Array(),
-"breakpoints": PackedInt32Array(),
-"column": 0,
-"folded_lines": Array[int]([]),
-"h_scroll_position": 0,
-"row": 15,
-"scroll_position": 0.0,
-"selection": false,
-"syntax_highlighter": "GDScript"
-}
-
-[res://scenes/Nodes/scatter_value.gd]
-
-state={
-"bookmarks": PackedInt32Array(),
-"breakpoints": PackedInt32Array(),
-"column": 0,
-"folded_lines": Array[int]([]),
-"h_scroll_position": 0,
-"row": 11,
-"scroll_position": 0.0,
-"selection": false,
-"syntax_highlighter": "GDScript"
-}
-
 [res://export_presets.cfg]
 
 state={

+ 1 - 1
.godot/editor/valueslider.tscn-editstate-d535ab38e866eae88d73c3fd55232f95.cfg

@@ -192,4 +192,4 @@ Game={
 "hide_selection": false,
 "select_mode": 0
 }
-selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/GraphEdit")])
+selected_nodes=Array[NodePath]([NodePath("/root/@EditorNode@21272/@Panel@14/@VBoxContainer@15/DockHSplitLeftL/DockHSplitLeftR/DockHSplitMain/@VBoxContainer@26/DockVSplitCenter/@VSplitContainer@54/@VBoxContainer@55/@EditorMainScreen@102/MainScreen/@CanvasItemEditor@11482/@VSplitContainer@11134/@HSplitContainer@11136/@HSplitContainer@11138/@Control@11139/@SubViewportContainer@11140/@SubViewport@11141/Control/MenuBar/FileButton")])

+ 8 - 0
.godot/scene_groups_cache.cfg

@@ -1,3 +1,11 @@
 [res://scenes/menu/menu.tscn]
 
 groups=[&"make_node_buttons"]
+
+[res://scenes/Nodes/audioplayer.tscn]
+
+groups=[&"outputnode"]
+
+[res://scenes/Nodes/nodes.tscn]
+
+groups=[&"inputnode", &"outputnode"]

BIN
.godot/uid_cache.bin


+ 3 - 3
examples/frequency_domain.thd

@@ -133,14 +133,14 @@
 				"y": 77.7777786254883
 			},
 			"slider_values": {
-				"AudioPlayer/FileDialog/@VBoxContainer@1365/@MarginContainer@1405/Tree/@Popup@1391/@VBoxContainer@1392/@HSlider@1400": 0.0
+				"AudioPlayer/FileDialog/@VBoxContainer@1113/@MarginContainer@1153/Tree/@Popup@1139/@VBoxContainer@1140/@HSlider@1148": 0.0
 			}
 		},
 		{
 			"command": "notes",
 			"name": "notes",
 			"notes": {
-				"CodeEdit": "The \"Frequency Domain\" processes allow you to perform unique transformations to a sound"
+				"CodeEdit": "The \"Frequency Domain\" processes allow you to perform unique transformations to a sound. Load a file into Input File to test it out."
 			},
 			"offset": {
 				"x": 64.6666793823242,
@@ -217,7 +217,7 @@
 				"y": 78.8888854980469
 			},
 			"slider_values": {
-				"AudioPlayer/FileDialog/@VBoxContainer@1513/@MarginContainer@1553/Tree/@Popup@1539/@VBoxContainer@1540/@HSlider@1548": 0.0
+				"AudioPlayer/FileDialog/@VBoxContainer@1261/@MarginContainer@1301/Tree/@Popup@1287/@VBoxContainer@1288/@HSlider@1296": 0.0
 			}
 		},
 		{

+ 5 - 0
project.godot

@@ -32,6 +32,11 @@ window/size/mode=2
 window/subwindows/embed_subwindows=false
 window/stretch/aspect="ignore"
 
+[global_group]
+
+outputnode="Controls from the output node"
+inputnode="input file controls"
+
 [input]
 
 open_menu={

+ 4 - 4
scenes/Nodes/audioplayer.gd

@@ -4,7 +4,7 @@ extends Control
 @onready var file_dialog = $FileDialog
 @onready var waveform_display = $WaveformPreview
 var outfile_path = "not_loaded"
-signal recycle_outfile_trigger
+#signal recycle_outfile_trigger
 
 #Used for waveform preview
 var voice_preview_generator : Node = null
@@ -105,7 +105,7 @@ func _process(delta: float) -> void:
 		
 		
 
-func _on_recycle_button_button_down() -> void:
-	if outfile_path != "not_loaded":
-		recycle_outfile_trigger.emit(outfile_path)
+#func _on_recycle_button_button_down() -> void:
+	#if outfile_path != "not_loaded":
+		#recycle_outfile_trigger.emit(outfile_path)
 	

+ 2 - 1
scenes/Nodes/audioplayer.tscn

@@ -39,12 +39,13 @@ offset_right = 128.0
 offset_bottom = 147.0
 text = "Load File"
 
-[node name="RecycleButton" type="Button" parent="."]
+[node name="RecycleButton" type="Button" parent="." groups=["outputnode"]]
 layout_mode = 0
 offset_top = 104.0
 offset_right = 128.0
 offset_bottom = 147.0
 text = "Recycle File"
+metadata/outputfunction = "recycle"
 
 [node name="PlayButton" type="Button" parent="."]
 layout_mode = 0

+ 0 - 15
scenes/Nodes/focus_accu_sliders.gd

@@ -1,15 +0,0 @@
-extends GraphNode
-
-
-# Called when the node enters the scene tree for the first time.
-func _ready() -> void:
-	$VBoxContainer3/HSplitContainer/ValueLabel.text = str($"VBoxContainer3/HSplitContainer/-d".value) # initial value
-	$VBoxContainer4/HSplitContainer/ValueLabel.text = str($"VBoxContainer4/HSplitContainer/-g".value) # initial value
-
-
-func _on_g_value_changed(value: float) -> void:
-	$VBoxContainer4/HSplitContainer/ValueLabel.text = str(value)
-
-
-func _on_d_value_changed(value: float) -> void:
-	$VBoxContainer3/HSplitContainer/ValueLabel.text = str(value)

+ 0 - 1
scenes/Nodes/focus_accu_sliders.gd.uid

@@ -1 +0,0 @@
-uid://dxxohuvlw5e3n

+ 32 - 75
scenes/Nodes/nodes.tscn

@@ -1,9 +1,7 @@
-[gd_scene load_steps=5 format=3 uid="uid://duy5epq25pj8u"]
+[gd_scene load_steps=3 format=3 uid="uid://duy5epq25pj8u"]
 
 [ext_resource type="PackedScene" uid="uid://csapiqka522fh" path="res://scenes/Nodes/audioplayer.tscn" id="2_b6nw4"]
 [ext_resource type="PackedScene" uid="uid://dya5kxx132fgp" path="res://scenes/Nodes/valueslider.tscn" id="3_b6nw4"]
-[ext_resource type="Script" uid="uid://dyf0qutxeqio3" path="res://scenes/Nodes/scatter_value.gd" id="3_uv17x"]
-[ext_resource type="Script" uid="uid://dxxohuvlw5e3n" path="res://scenes/Nodes/focus_accu_sliders.gd" id="4_uv17x"]
 
 [node name="Control" type="Control"]
 layout_mode = 3
@@ -43,9 +41,10 @@ metadata/command = "inputfile"
 [node name="Control" type="Control" parent="inputfile"]
 layout_mode = 2
 
-[node name="AudioPlayer" parent="inputfile" instance=ExtResource("2_b6nw4")]
+[node name="AudioPlayer" parent="inputfile" groups=["inputnode"] instance=ExtResource("2_b6nw4")]
 layout_mode = 2
 metadata/loadenable = true
+metadata/inputfunction = "audioplayer"
 
 [node name="outputfile" type="GraphNode" parent="."]
 layout_mode = 0
@@ -104,12 +103,13 @@ metadata/command = "outputfile"
 [node name="Control" type="Control" parent="outputfile"]
 layout_mode = 2
 
-[node name="RunProcess" type="Button" parent="outputfile"]
+[node name="RunProcess" type="Button" parent="outputfile" groups=["outputnode"]]
 custom_minimum_size = Vector2(0, 43)
 layout_mode = 2
 text = "Run Process"
+metadata/outputfunction = "runprocess"
 
-[node name="DeleteIntermediateFilesToggle" type="CheckButton" parent="outputfile"]
+[node name="DeleteIntermediateFilesToggle" type="CheckButton" parent="outputfile" groups=["outputnode"]]
 custom_minimum_size = Vector2(0, 43)
 layout_mode = 2
 text = "Delete Intermediate files"
@@ -119,8 +119,9 @@ expand_icon = true
 layout_mode = 2
 text = "Rendered Output File:"
 
-[node name="AudioPlayer" parent="outputfile" instance=ExtResource("2_b6nw4")]
+[node name="AudioPlayer" parent="outputfile" groups=["outputnode"] instance=ExtResource("2_b6nw4")]
 layout_mode = 2
+metadata/outputfunction = "audioplayer"
 
 [node name="distort_average" type="GraphNode" parent="."]
 layout_mode = 0
@@ -1202,7 +1203,7 @@ exp_edit = true
 
 [node name="VBoxContainer4" parent="filter_bank_1" instance=ExtResource("3_b6nw4")]
 layout_mode = 2
-tooltip_text = "The highest frequency filter, must be higer than low filer"
+tooltip_text = "The highest frequency filter, must be higer than low filter"
 
 [node name="Label" parent="filter_bank_1/VBoxContainer4" index="0"]
 text = "Highest Band"
@@ -1214,32 +1215,18 @@ step = 0.01
 value = 12000.0
 exp_edit = true
 
-[node name="VBoxContainer5" type="VBoxContainer" parent="filter_bank_1"]
-custom_minimum_size = Vector2(270, 0)
+[node name="VBoxContainer6" parent="filter_bank_1" instance=ExtResource("3_b6nw4")]
 layout_mode = 2
-tooltip_text = "Amound of random spread applied to filer frequencies"
-script = ExtResource("3_uv17x")
+tooltip_text = "Amound of random spread applied to filter frequencies"
 
-[node name="Label" type="Label" parent="filter_bank_1/VBoxContainer5"]
-layout_mode = 2
+[node name="Label" parent="filter_bank_1/VBoxContainer6" index="0"]
 text = "Scatter"
 
-[node name="HSplitContainer" type="HSplitContainer" parent="filter_bank_1/VBoxContainer5"]
-layout_mode = 2
-split_offset = 210
-dragging_enabled = false
-
-[node name="-s" type="HSlider" parent="filter_bank_1/VBoxContainer5/HSplitContainer"]
-custom_minimum_size = Vector2(0, 23)
-layout_mode = 2
+[node name="HSlider" parent="filter_bank_1/VBoxContainer6/HSplitContainer" index="0"]
 max_value = 1.0
 step = 0.01
-
-[node name="ValueLabel" type="Label" parent="filter_bank_1/VBoxContainer5/HSplitContainer"]
-layout_mode = 2
-text = "0.00"
-horizontal_alignment = 2
-vertical_alignment = 1
+value = 0.0
+metadata/flag = "-s"
 
 [node name="pvoc_anal_1" type="GraphNode" parent="."]
 layout_mode = 0
@@ -1346,74 +1333,45 @@ slot/2/right_type = 0
 slot/2/right_color = Color(1, 1, 1, 1)
 slot/2/right_icon = null
 slot/2/draw_stylebox = true
-script = ExtResource("4_uv17x")
 metadata/command = "focus_accu"
 
 [node name="Control" type="Control" parent="focus_accu"]
 layout_mode = 2
 
-[node name="VBoxContainer3" type="VBoxContainer" parent="focus_accu"]
-custom_minimum_size = Vector2(270, 0)
+[node name="VBoxContainer2" parent="focus_accu" instance=ExtResource("3_b6nw4")]
 layout_mode = 2
-tooltip_text = "Sustained channel data decays by a factor of decay each second"
+tooltip_text = "Gain factor for accumulated sound"
 
-[node name="Label" type="Label" parent="focus_accu/VBoxContainer3"]
-layout_mode = 2
+[node name="Label" parent="focus_accu/VBoxContainer2" index="0"]
 text = "Decay"
 
-[node name="HSplitContainer" type="HSplitContainer" parent="focus_accu/VBoxContainer3"]
-layout_mode = 2
-split_offset = 210
-dragging_enabled = false
-
-[node name="-d" type="HSlider" parent="focus_accu/VBoxContainer3/HSplitContainer"]
-custom_minimum_size = Vector2(0, 23)
-layout_mode = 2
+[node name="HSlider" parent="focus_accu/VBoxContainer2/HSplitContainer" index="0"]
 min_value = 0.001
 max_value = 1.0
 step = 0.001
 value = 0.5
+metadata/flag = "-d"
 
-[node name="ValueLabel" type="Label" parent="focus_accu/VBoxContainer3/HSplitContainer"]
-layout_mode = 2
-text = "0.00"
-horizontal_alignment = 2
-vertical_alignment = 1
-
-[node name="VBoxContainer4" type="VBoxContainer" parent="focus_accu"]
-custom_minimum_size = Vector2(270, 0)
+[node name="VBoxContainer" parent="focus_accu" instance=ExtResource("3_b6nw4")]
 layout_mode = 2
 tooltip_text = "Sustained channel data glissandos at glis octaves per second"
 
-[node name="Label" type="Label" parent="focus_accu/VBoxContainer4"]
-layout_mode = 2
+[node name="Label" parent="focus_accu/VBoxContainer" index="0"]
 text = "Glissandos"
 
-[node name="HSplitContainer" type="HSplitContainer" parent="focus_accu/VBoxContainer4"]
-layout_mode = 2
-split_offset = 210
-dragging_enabled = false
-
-[node name="-g" type="HSlider" parent="focus_accu/VBoxContainer4/HSplitContainer"]
-custom_minimum_size = Vector2(0, 23)
-layout_mode = 2
+[node name="HSlider" parent="focus_accu/VBoxContainer/HSplitContainer" index="0"]
 min_value = -5.0
 max_value = 10.0
-step = 0.01
+step = 0.1
 value = -0.5
-
-[node name="ValueLabel" type="Label" parent="focus_accu/VBoxContainer4/HSplitContainer"]
-layout_mode = 2
-text = "0.00"
-horizontal_alignment = 2
-vertical_alignment = 1
+metadata/flag = "-g"
 
 [node name="blur_blur" type="GraphNode" parent="."]
 layout_mode = 0
-offset_left = 2224.0
-offset_top = 527.0
-offset_right = 2530.0
-offset_bottom = 647.0
+offset_left = 2227.0
+offset_top = 514.0
+offset_right = 2533.0
+offset_bottom = 634.0
 tooltip_text = "Time-averages the spectrum bluring detail in the time dimension"
 title = "PVOC: Blur"
 slot/0/left_enabled = true
@@ -1837,10 +1795,6 @@ custom_minimum_size = Vector2(0, 150)
 layout_mode = 2
 wrap_mode = 1
 
-[connection signal="value_changed" from="filter_bank_1/VBoxContainer5/HSplitContainer/-s" to="filter_bank_1/VBoxContainer5" method="_on_s_value_changed"]
-[connection signal="value_changed" from="focus_accu/VBoxContainer3/HSplitContainer/-d" to="focus_accu" method="_on_d_value_changed"]
-[connection signal="value_changed" from="focus_accu/VBoxContainer4/HSplitContainer/-g" to="focus_accu" method="_on_g_value_changed"]
-
 [editable path="distort_average/VBoxContainer"]
 [editable path="clip_clip_2/VBoxContainer"]
 [editable path="distort_divide/VBoxContainer"]
@@ -1873,6 +1827,9 @@ wrap_mode = 1
 [editable path="filter_bank_1/VBoxContainer2"]
 [editable path="filter_bank_1/VBoxContainer3"]
 [editable path="filter_bank_1/VBoxContainer4"]
+[editable path="filter_bank_1/VBoxContainer6"]
+[editable path="focus_accu/VBoxContainer2"]
+[editable path="focus_accu/VBoxContainer"]
 [editable path="blur_blur/VBoxContainer"]
 [editable path="blur_chorus_5/VBoxContainer"]
 [editable path="blur_chorus_5/VBoxContainer2"]

+ 0 - 11
scenes/Nodes/scatter_value.gd

@@ -1,11 +0,0 @@
-extends VBoxContainer
-
-
-# Called when the node enters the scene tree for the first time.
-func _ready() -> void:
-	$HSplitContainer/ValueLabel.text = str($"HSplitContainer/-s".value) # initial value
-	pass
-
-
-func _on_s_value_changed(value: float) -> void:
-	$HSplitContainer/ValueLabel.text = str(value)

+ 0 - 1
scenes/Nodes/scatter_value.gd.uid

@@ -1 +0,0 @@
-uid://dyf0qutxeqio3

+ 200 - 35
scenes/main/control.gd

@@ -4,13 +4,20 @@ var mainmenu_visible : bool = false #used to test if mainmenu is open
 var effect_position = Vector2(40,40) #tracks mouse position for node placement offset
 @onready var graph_edit = $GraphEdit
 var selected_nodes = {} #used to track which nodes in the GraphEdit are selected
-var cdpprogs_location
-var delete_intermediate_outputs
+var cdpprogs_location #stores the cdp programs location from user prefs for easy access
+var delete_intermediate_outputs # tracks state of delete intermediate outputs toggle
 @onready var console_output: RichTextLabel = $Console/ConsoleOutput
 var final_output_dir
-var copied_nodes_data = []
-var copied_connections = []
-var undo_redo := UndoRedo.new()
+var copied_nodes_data = [] #stores node data on ctrl+c
+var copied_connections = [] #stores all connections on ctrl+c
+var undo_redo := UndoRedo.new() 
+var output_audio_player #tracks the node that is the current output player for linking
+var input_audio_player #tracks node that is the current input player for linking
+var outfile = "no file" #tracks dir of output file from cdp process
+var currentfile = "none" #tracks dir of currently loaded file for saving
+var changesmade = false #tracks if user has made changes to the currently loaded save file
+var savestate # tracks what the user is trying to do when savechangespopup is called
+var helpfile #tracks which help file the user was trying to load when savechangespopup is called
 
 # Called when the node enters the scene tree for the first time.
 func _ready() -> void:
@@ -35,8 +42,23 @@ func _ready() -> void:
 		if child is Button:
 			child.pressed.connect(_on_button_pressed.bind(child))
 	
-	DisplayServer.screen_get_size().x
-	#Generate input and output nodes
+	check_cdp_location_set()
+	check_user_preferences()
+	new_patch()
+	
+	#link output file to input file to enable audio output file loopback
+	#$GraphEdit/outputfile/AudioPlayer.recycle_outfile_trigger.connect($GraphEdit/inputfile/AudioPlayer.recycle_outfile)
+	
+	
+func new_patch():
+	#clear old patch
+	graph_edit.clear_connections()
+
+	for node in graph_edit.get_children():
+		if node is GraphNode:
+			node.queue_free()
+	
+		#Generate input and output nodes
 	var effect: GraphNode = Nodes.get_node(NodePath("inputfile")).duplicate()
 	get_node("GraphEdit").add_child(effect, true)
 	effect.position_offset = Vector2(20,80)
@@ -44,21 +66,28 @@ func _ready() -> void:
 	effect = Nodes.get_node(NodePath("outputfile")).duplicate()
 	get_node("GraphEdit").add_child(effect, true)
 	effect.position_offset = Vector2((DisplayServer.screen_get_size().x - 480) ,80)
+	_register_node_movement() #link nodes for tracking position changes for changes tracking
 	
-	check_cdp_location_set()
-	check_user_preferences()
-	
-	
-	#link output file to input file to enable audio output file loopback
-	$GraphEdit/outputfile/AudioPlayer.recycle_outfile_trigger.connect($GraphEdit/inputfile/AudioPlayer.recycle_outfile)
-	
-	#link run process button to the batch generation script
-	$GraphEdit/outputfile/RunProcess.button_down.connect(_run_process)
-	
-	#link and set delete intermediat files toggle from outputfile
-	$GraphEdit/outputfile/DeleteIntermediateFilesToggle.toggled.connect(_toggle_delete)
-	$GraphEdit/outputfile/DeleteIntermediateFilesToggle.button_pressed = true
-	
+	link_output()
+
+func link_output():
+	#links various buttons and function in the input nodes - this is called after they are created so that it still works on new and loading files
+	for control in get_tree().get_nodes_in_group("outputnode"): #check all items in outputnode group
+		if control is CheckButton: #link delete intermediate files toggle to script
+			control.toggled.connect(_toggle_delete)
+			control.button_pressed = true
+		elif control.get_meta("outputfunction") == "runprocess": #link runprocess button
+			control.button_down.connect(_run_process)
+		elif control.get_meta("outputfunction") == "recycle": #link recycle button
+			control.button_down.connect(_recycle_outfile)
+		elif control.get_meta("outputfunction") == "audioplayer": #link output audio player
+			output_audio_player = control
+
+	for control in get_tree().get_nodes_in_group("inputnode"):
+		if control.get_meta("inputfunction") == "audioplayer": #link input for recycle function
+			print("input player found")
+			input_audio_player = control
+
 func check_user_preferences():
 	var interface_settings = ConfigHandler.load_interface_settings()
 	$MenuBar/SettingsButton.set_item_checked(1, interface_settings.disable_pvoc_warning)
@@ -132,12 +161,17 @@ func _on_button_pressed(button: Button):
 	var effect: GraphNode = Nodes.get_node(NodePath(button.name)).duplicate()
 	get_node("GraphEdit").add_child(effect, true)
 	effect.position_offset = effect_position
+	_register_inputs_in_node(effect) #link sliders for changes tracking
+	_register_node_movement() #link nodes for tracking position changes for changes tracking
+
+	changesmade = true
 
 
 	# Remove node with UndoRedo
 	undo_redo.create_action("Add Node")
 	undo_redo.add_undo_method(Callable(graph_edit, "remove_child").bind(effect))
 	undo_redo.add_undo_method(Callable(effect, "queue_free"))
+	undo_redo.add_undo_method(Callable(self, "_track_changes"))
 	undo_redo.commit_action()
 
 func _on_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
@@ -164,9 +198,11 @@ func _on_graph_edit_connection_request(from_node: StringName, from_port: int, to
 
 	# If no conflict, allow the connection
 	graph_edit.connect_node(from_node, from_port, to_node, to_port)
+	changesmade = true
 
 func _on_graph_edit_disconnection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
 	get_node("GraphEdit").disconnect_node(from_node, from_port, to_node, to_port)
+	changesmade = true
 
 func _on_graph_edit_node_selected(node: Node) -> void:
 	selected_nodes[node] = true
@@ -196,6 +232,7 @@ func _on_graph_edit_delete_nodes_request(nodes: Array[StringName]) -> void:
 				# Delete
 				remove_connections_to_node(node)
 				node.queue_free()
+				changesmade = true
 
 				# Register undo restore
 				undo_redo.add_undo_method(Callable(graph_edit, "add_child").bind(node_data, true))
@@ -206,6 +243,9 @@ func _on_graph_edit_delete_nodes_request(nodes: Array[StringName]) -> void:
 						con["to_node"], con["to_port"]
 					))
 				undo_redo.add_undo_method(Callable(self, "set_node_selected").bind(node_data, true))
+				undo_redo.add_undo_method(Callable(self, "_track_changes"))
+				undo_redo.add_undo_method(Callable(self, "_register_inputs_in_node").bind(node_data)) #link sliders for changes tracking
+				undo_redo.add_undo_method(Callable(self, "_register_node_movement")) # link nodes for changes tracking
 
 	# Clear selection
 	selected_nodes = {}
@@ -219,6 +259,7 @@ func remove_connections_to_node(node):
 	for con in get_node("GraphEdit").get_connection_list():
 		if con["to_node"] == node.name or con["from_node"] == node.name:
 			get_node("GraphEdit").disconnect_node(con["from_node"], con["from_port"], con["to_node"], con["to_port"])
+			changesmade = true
 			
 #copy and paste nodes with vertical offset on paste
 func copy_selected_nodes():
@@ -302,6 +343,7 @@ func paste_copied_nodes():
 			node_data["offset"].x,
 			base_y_offset + relative_y
 		)
+		
 
 		# Restore sliders
 		for child in new_node.get_children():
@@ -309,6 +351,8 @@ func paste_copied_nodes():
 				child.value = node_data["slider_values"][child.name]
 
 		graph_edit.add_child(new_node, true)
+		_register_inputs_in_node(new_node) #link sliders for changes tracking
+		_register_node_movement() # link nodes for changes tracking
 		name_map[node_data["name"]] = new_node.name
 		pasted_nodes.append(new_node)
 
@@ -325,17 +369,58 @@ func paste_copied_nodes():
 	for pasted_node in pasted_nodes:
 		graph_edit.set_selected(pasted_node)
 		selected_nodes[pasted_node] = true
-
+	
+	changesmade = true
+	
 	# Remove node with UndoRedo
 	undo_redo.create_action("Paste Nodes")
 	for pasted_node in pasted_nodes:
 		undo_redo.add_undo_method(Callable(graph_edit, "remove_child").bind(pasted_node))
 		undo_redo.add_undo_method(Callable(pasted_node, "queue_free"))
 		undo_redo.add_undo_method(Callable(self, "remove_connections_to_node").bind(pasted_node))
+		undo_redo.add_undo_method(Callable(self, "_track_changes"))
 	undo_redo.commit_action()
+	
 
+#functions for tracking changes for save state detection
+func _register_inputs_in_node(node: Node):
+	#tracks input to nodes sliders and codeedit to track if patch is saved
+	# Track Sliders
+	for slider in node.find_children("*", "HSlider", true, false):
+		# Create a Callable to the correct method
+		var callable = Callable(self, "_on_any_slider_changed")
+		# Check if it's already connected, and connect if not
+		if not slider.is_connected("value_changed", callable):
+			slider.connect("value_changed", callable)
+		
+	# Track CodeEdits (if necessary)
+	for editor in node.find_children("*", "CodeEdit", true, false):
+		var callable = Callable(self, "_on_any_input_changed")
+		if not editor.is_connected("text_changed", callable):
+			editor.connect("text_changed", callable)
+			
+func _register_node_movement():
+	for graphnode in graph_edit.get_children():
+		if graphnode is GraphNode:
+			var callable = Callable(self, "_on_graphnode_moved")
+			if not graphnode.is_connected("position_offset_changed", callable):
+				graphnode.connect("position_offset_changed", callable)
+
+func _on_graphnode_moved():
+	changesmade = true
+	
+func _on_any_slider_changed(value: float) -> void:
+	changesmade = true
+	
+func _on_any_input_changed():
+	changesmade = true
+
+func _track_changes():
+	changesmade = true
+	
 ######## Here be dragons #########
 ##################################
+####### Don't let them out #######
 
 #Scans through all nodes and generates a batch file based on their order
 
@@ -457,8 +542,11 @@ func generate_batch_file_with_branches():
 				var command_name = str(node.get_meta("command")) if node.has_meta("command") else node_name
 				command_name = command_name.replace("_", " ")
 				var line = "%s/%s \"%s\" \"%s\" " % [cdpprogs_location, command_name, current_infile, output_file]
+				#checks if slider has a flag meta value and appends the flag before the parameter
 				for entry in slider_data:
-					line += ("%s%.2f " % [entry[0], entry[1]]) if entry[0].begins_with("-") else ("%.2f " % entry[1])
+					var flag = entry[0]
+					var value = entry[1]
+					line += ("%s%.2f " % [flag, value]) if flag.begins_with("-") else ("%.2f " % value)
 				batch_lines.append(line.strip_edges())
 				output_files[node_name] = output_file
 				if delete_intermediate_outputs:
@@ -538,8 +626,11 @@ func generate_batch_file_with_branches():
 			var command_name = str(node.get_meta("command")) if node.has_meta("command") else node_name
 			command_name = command_name.replace("_", " ")
 			var line = "%s/%s \"%s\" \"%s\" " % [cdpprogs_location, command_name, current_infile, output_file]
+			#checks if slider has a flag meta value and appends the flag before the parameter
 			for entry in slider_data:
-				line += ("%s%.2f " % [entry[0], entry[1]]) if entry[0].begins_with("-") else ("%.2f " % entry[1])
+				var flag = entry[0]
+				var value = entry[1]
+				line += ("%s%.2f " % [flag, value]) if flag.begins_with("-") else ("%.2f " % value)
 			batch_lines.append(line.strip_edges())
 			output_files[node_name] = output_file
 			if delete_intermediate_outputs:
@@ -588,13 +679,12 @@ func generate_batch_file_with_branches():
 	await get_tree().process_frame
 	run_batch_file()
 
-
-# Ordered slider collection
 func _get_slider_values_ordered(node: Node) -> Array:
 	var results := []
 	for child in node.get_children():
 		if child is Range:
-			results.append([child.name, child.value])
+			var flag = child.get_meta("flag") if child.has_meta("flag") else ""
+			results.append([flag, child.value])
 		elif child.get_child_count() > 0:
 			var nested := _get_slider_values_ordered(child)
 			results.append_array(nested)
@@ -714,7 +804,8 @@ func run_batch_file():
 		console_output.scroll_to_line(console_output.get_line_count() - 1)
 		console_output.append_text(output_str + "/n")
 		if final_output_dir.ends_with(".wav"):
-			$GraphEdit/outputfile/AudioPlayer.play_outfile(final_output_dir)
+			output_audio_player.play_outfile(final_output_dir)
+			outfile = final_output_dir
 		var interface_settings = ConfigHandler.load_interface_settings()
 		if interface_settings.auto_close_console == true:
 			$Console.hide()
@@ -728,6 +819,7 @@ func run_batch_file():
 
 func _toggle_delete(toggled_on: bool):
 	delete_intermediate_outputs = toggled_on
+	print(toggled_on)
 
 func _on_console_close_requested() -> void:
 	$Console.hide()
@@ -778,9 +870,27 @@ func _on_settings_button_index_pressed(index: int) -> void:
 func _on_file_button_index_pressed(index: int) -> void:
 	match index:
 		0:
-			$SaveDialog.popup_centered()
+			if changesmade == true:
+				savestate = "newfile"
+				$SaveChangesPopup.show()
+			else:
+				new_patch()
+				currentfile = "none" #reset current file to none for save tracking
 		1:
-			$LoadDialog.popup_centered()
+			if currentfile == "none":
+				savestate = "saveas"
+				$SaveDialog.popup_centered()
+			else:
+				save_graph_edit(currentfile)
+		2:
+			savestate = "saveas"
+			$SaveDialog.popup_centered()
+		3:
+			if changesmade == true:
+				savestate = "load"
+				$SaveChangesPopup.show()
+			else:
+				$LoadDialog.popup_centered()
 
 
 func save_graph_edit(path: String):
@@ -887,19 +997,28 @@ func load_graph_edit(path: String):
 			conn["from_node"], conn["from_port"],
 			conn["to_node"], conn["to_port"]
 		)
-
+	link_output()
 	print("Graph loaded.")
 
 
 func _on_save_dialog_file_selected(path: String) -> void:
-	save_graph_edit(path)
-
+	save_graph_edit(path) #save file
+	#check what the user was trying to do before save and do that action
+	if savestate == "newfile":
+		new_patch()
+		currentfile = "none" #reset current file to none for save tracking
+	elif savestate == "load":
+		$LoadDialog.popup_centered()
+	elif savestate == "helpfile":
+		currentfile = "none" #reset current file to none for save tracking so user cant save over help file
+		load_graph_edit(helpfile)
+	savestate = "none" #reset save state, not really needed but feels good
 
 
 func _on_load_dialog_file_selected(path: String) -> void:
+	currentfile = path #tracking path here only means "save" only saves patches the user has loaded rather than overwriting help files
 	load_graph_edit(path)
 
-
 func _on_help_button_index_pressed(index: int) -> void:
 	match index:
 		0:
@@ -907,8 +1026,54 @@ func _on_help_button_index_pressed(index: int) -> void:
 		1:
 			pass
 		2:
-			load_graph_edit("res://examples/frequency_domain.thd")
+			if changesmade == true:
+				savestate = "helpfile"
+				helpfile = "res://examples/frequency_domain.thd"
+				$SaveChangesPopup.show()
+			else:
+				currentfile = "none" #reset current file to none for save tracking so user cant save over help file
+				load_graph_edit("res://examples/frequency_domain.thd")
 		3:
 			pass
 		4:
 			OS.shell_open("https://www.composersdesktop.com/docs/html/cdphome.htm")
+
+func _recycle_outfile():
+	if outfile != "no file":
+		input_audio_player.recycle_outfile(outfile)
+
+
+
+func _on_save_changes_button_down() -> void:
+	$SaveChangesPopup.hide()
+	if currentfile == "none":
+		$SaveDialog.show()
+	else:
+		save_graph_edit(currentfile)
+		if savestate == "newfile":
+			new_patch()
+			currentfile = "none" #reset current file to none for save tracking
+			savestate = "none"
+		elif savestate == "load":
+			savestate == "none"
+			$LoadDialog.popup_centered()
+		elif savestate == "helpfile":
+			savestate = "none"
+			currentfile = "none" #reset current file to none for save tracking so user cant save over help file
+			load_graph_edit(helpfile)
+
+
+func _on_dont_save_changes_button_down() -> void:
+	$SaveChangesPopup.hide()
+	if savestate == "newfile":
+		new_patch()
+		currentfile = "none" #reset current file to none for save tracking
+		savestate = "none"
+	elif savestate == "load":
+		savestate == "none"
+		$LoadDialog.popup_centered()
+	elif savestate == "helpfile":
+		savestate = "none"
+		currentfile = "none" #reset current file to none for save tracking so user cant save over help file
+		load_graph_edit(helpfile)
+	

+ 47 - 5
scenes/main/control.tscn

@@ -180,11 +180,15 @@ flat = true
 
 [node name="FileButton" type="PopupMenu" parent="MenuBar"]
 title = "File"
-item_count = 2
-item_0/text = "Save Process Chain"
-item_0/id = 0
-item_1/text = "Load Process Chain"
-item_1/id = 1
+item_count = 4
+item_0/text = "New Thread"
+item_0/id = 2
+item_1/text = "Save Thread"
+item_1/id = 3
+item_2/text = "Save Thread As"
+item_2/id = 0
+item_3/text = "Load Thread"
+item_3/id = 1
 
 [node name="SettingsButton" type="PopupMenu" parent="MenuBar"]
 title = "Settings"
@@ -222,6 +226,42 @@ use_native_dialog = true
 auto_translate_mode = 1
 use_native_dialog = true
 
+[node name="SaveChangesPopup" type="Window" parent="."]
+auto_translate_mode = 1
+title = "No Input Selected"
+initial_position = 2
+size = Vector2i(350, 100)
+visible = false
+exclusive = true
+unresizable = true
+borderless = true
+always_on_top = true
+popup_window = true
+
+[node name="Label" type="Label" parent="SaveChangesPopup"]
+offset_left = -1.0
+offset_top = 18.0
+offset_right = 348.0
+offset_bottom = 49.0
+text = "Save changes to this thread?"
+horizontal_alignment = 1
+vertical_alignment = 1
+autowrap_mode = 2
+
+[node name="SaveChanges" type="Button" parent="SaveChangesPopup"]
+offset_left = 55.0
+offset_top = 58.0
+offset_right = 167.0
+offset_bottom = 89.0
+text = "Save"
+
+[node name="DontSaveChanges" type="Button" parent="SaveChangesPopup"]
+offset_left = 183.0
+offset_top = 58.0
+offset_right = 295.0
+offset_bottom = 89.0
+text = "Don't Save"
+
 [connection signal="connection_request" from="GraphEdit" to="." method="_on_graph_edit_connection_request"]
 [connection signal="delete_nodes_request" from="GraphEdit" to="." method="_on_graph_edit_delete_nodes_request"]
 [connection signal="disconnection_request" from="GraphEdit" to="." method="_on_graph_edit_disconnection_request"]
@@ -240,3 +280,5 @@ use_native_dialog = true
 [connection signal="index_pressed" from="MenuBar/HelpButton" to="." method="_on_help_button_index_pressed"]
 [connection signal="file_selected" from="SaveDialog" to="." method="_on_save_dialog_file_selected"]
 [connection signal="file_selected" from="LoadDialog" to="." method="_on_load_dialog_file_selected"]
+[connection signal="button_down" from="SaveChangesPopup/SaveChanges" to="." method="_on_save_changes_button_down"]
+[connection signal="button_down" from="SaveChangesPopup/DontSaveChanges" to="." method="_on_dont_save_changes_button_down"]

Some files were not shown because too many files changed in this diff