Browse Source

added user prefs for last used audio devices, implemented methods for switching outputs if audio cards are hot swapped

Jonathan Higgins 7 months ago
parent
commit
2bbaeceb9b
5 changed files with 159 additions and 47 deletions
  1. 31 6
      config_handler.gd
  2. 20 11
      scenes/main/audio_settings.tscn
  3. 48 25
      scenes/main/audiosettings.gd
  4. 29 4
      scenes/main/control.gd
  5. 31 1
      scenes/main/control.tscn

+ 31 - 6
config_handler.gd

@@ -4,13 +4,28 @@ var config = ConfigFile.new()
 const SETTINGS_FILE_PATH = "user://settings.ini"
 
 func _ready():
-	if !FileAccess.file_exists(SETTINGS_FILE_PATH):
-		config.set_value("cdpprogs", "location", "no_location")
-		config.set_value("interface_settings", "disable_pvoc_warning", false)
-		config.set_value("interface_settings", "auto_close_console", false)
-		config.save(SETTINGS_FILE_PATH)
-	else:
+	var file_exists = FileAccess.file_exists(SETTINGS_FILE_PATH)
+
+	if file_exists:
 		config.load(SETTINGS_FILE_PATH)
+
+	# Set defaults only if not present
+	ensure_setting("cdpprogs", "location", "no_location")
+	ensure_setting("interface_settings", "disable_pvoc_warning", false)
+	ensure_setting("interface_settings", "auto_close_console", false)
+	ensure_setting("audio_settings", "device", "Default")
+
+	# Only save if we added anything new
+	if !file_exists or config_changed:
+		config.save(SETTINGS_FILE_PATH)
+
+# Internal tracker
+var config_changed := false
+
+func ensure_setting(section: String, key: String, default_value):
+	if !config.has_section_key(section, key):
+		config.set_value(section, key, default_value)
+		config_changed = true
 	
 
 func save_cdpprogs_settings(location: String):
@@ -32,3 +47,13 @@ func load_interface_settings():
 	for key in config.get_section_keys("interface_settings"):
 		interface_settings[key] = config.get_value("interface_settings", key)
 	return interface_settings
+	
+func save_audio_settings(key: String, device: String):
+	config.set_value("audio_settings", key, device)
+	config.save(SETTINGS_FILE_PATH)
+
+func load_audio_settings():
+	var audio_settings = {}
+	for key in config.get_section_keys("audio_settings"):
+		audio_settings[key] = config.get_value("audio_settings", key)
+	return audio_settings

+ 20 - 11
scenes/main/audio_settings.tscn

@@ -2,20 +2,25 @@
 
 [ext_resource type="Script" uid="uid://c7krcoq5poxdn" path="res://scenes/main/audiosettings.gd" id="2_7qbns"]
 
-[node name="AudioSettingsControl" type="Control"]
-layout_mode = 3
-anchors_preset = 0
-offset_left = 12.0
-offset_top = 58.0
-offset_right = 588.0
-offset_bottom = 58.0
+[node name="AudioSettings" type="Window"]
+title = "Audio Settings"
+initial_position = 2
+size = Vector2i(600, 200)
+transient = true
+unresizable = true
+always_on_top = true
 script = ExtResource("2_7qbns")
 
+[node name="ColorRect" type="ColorRect" parent="."]
+offset_right = 604.0
+offset_bottom = 204.0
+color = Color(0.101961, 0.101961, 0.101961, 0.6)
+
 [node name="VBoxContainer" type="VBoxContainer" parent="."]
-layout_mode = 0
-offset_top = -52.0
-offset_right = 576.0
-offset_bottom = 47.0
+offset_left = 12.0
+offset_top = 6.0
+offset_right = 588.0
+offset_bottom = 192.0
 
 [node name="WindowTitle" type="Label" parent="VBoxContainer"]
 layout_mode = 2
@@ -29,6 +34,7 @@ text = "Available audio devices:"
 [node name="ItemList" type="ItemList" parent="VBoxContainer"]
 custom_minimum_size = Vector2(0, 100)
 layout_mode = 2
+allow_reselect = true
 
 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"]
 custom_minimum_size = Vector2(0, 1)
@@ -40,4 +46,7 @@ text = "Current device:"
 
 [node name="DevicePollTimer" type="Timer" parent="."]
 
+[connection signal="about_to_popup" from="." to="." method="_on_about_to_popup"]
+[connection signal="close_requested" from="." to="." method="_on_close_requested"]
 [connection signal="item_selected" from="VBoxContainer/ItemList" to="." method="_on_item_list_item_selected"]
+[connection signal="timeout" from="DevicePollTimer" to="." method="_on_device_poll_timer_timeout"]

+ 48 - 25
scenes/main/audiosettings.gd

@@ -1,37 +1,60 @@
-extends Control
+extends Window
 
 @onready var item_list = $VBoxContainer/ItemList
 @onready var device_timer = $DevicePollTimer
+var last_known_devices = []
 
-func _ready():
-	for item in AudioServer.get_output_device_list():
-		item_list.add_item(item)
 
+func _on_item_list_item_selected(index: int) -> void:
+	var device = item_list.get_item_text(index)
+	AudioServer.set_output_device(device)
+	ConfigHandler.save_audio_settings("device", device)
+
+
+func _on_about_to_popup() -> void:
+	$VBoxContainer/DeviceInfo.text = "Current Device: " + AudioServer.get_output_device()
+	
+	update_device_list()  # Initial fetch
+	device_timer.start()
+	
+
+
+func _on_close_requested() -> void:
+	device_timer.stop()
+
+
+func update_device_list():
+	var audio_settings = ConfigHandler.load_audio_settings()
+	var last_selected_device = audio_settings.device
+	var devices = AudioServer.get_output_device_list()
 	var device = AudioServer.get_output_device()
+	
+	item_list.clear()
+	last_known_devices = devices
+	for item in AudioServer.get_output_device_list():
+		item_list.add_item(item)
+	
+	#check if the users last selected device has now become available if not set to default to reset audio server to default so it reports properly
+	if device != last_selected_device and devices.has(last_selected_device):
+		AudioServer.set_output_device("Default") #hacky fix because the audio server doesn't work properly when hot swapping outputs
+		await get_tree().create_timer(0.1).timeout  # Wait 100 ms
+		AudioServer.set_output_device(last_selected_device)
+	elif !devices.has(last_selected_device):
+		AudioServer.set_output_device("Default")
+		
+	await get_tree().create_timer(0.1).timeout  # Wait 100 ms
+	device = AudioServer.get_output_device()
+	#highlight the currently selected device
 	for i in range(item_list.get_item_count()):
 		if device == item_list.get_item_text(i):
 			item_list.select(i)
 			break
-	
-	$VBoxContainer/DeviceInfo.text = "Current Device: " + AudioServer.get_output_device()
 
 
-func _process(_delta):
-	#var speaker_mode_text = "Stereo"
-	#var speaker_mode = AudioServer.get_speaker_mode()
-#
-	#if speaker_mode == AudioServer.SPEAKER_SURROUND_31:
-		#speaker_mode_text = "Surround 3.1"
-	#elif speaker_mode == AudioServer.SPEAKER_SURROUND_51:
-		#speaker_mode_text = "Surround 5.1"
-	#elif speaker_mode == AudioServer.SPEAKER_SURROUND_71:
-		#speaker_mode_text = "Surround 7.1"
-	#$VBoxContainer/DeviceInfo.text += "Speaker Mode: " + speaker_mode_text
-	#$VBoxContainer/DeviceInfo.text = "Current Device: " + AudioServer.get_output_device()
-	pass
-
-
-func _on_item_list_item_selected(index: int) -> void:
-	var device = item_list.get_item_text(index)
-	AudioServer.set_output_device(device)
-	$VBoxContainer/DeviceInfo.text = "Current Device: " + device
+func _on_device_poll_timer_timeout() -> void:
+	var current_devices = AudioServer.get_output_device_list()
+	if current_devices != last_known_devices:
+		last_known_devices = current_devices
+		update_device_list()
+		
+	$VBoxContainer/DeviceInfo.text = "Current Device: " + AudioServer.get_output_device()

+ 29 - 4
scenes/main/control.gd

@@ -24,6 +24,7 @@ var lastoutputfolder = "none" #tracks last output folder, this can in future be
 var process_successful #tracks if the last run process was successful
 var help_data := {} #stores help data for each node to display in help popup
 var HelpWindowScene = preload("res://scenes/main/help_window.tscn")
+var uiscale = 1.0 #tracks scaling for retina screens
 
 # Called when the node enters the scene tree for the first time.
 func _ready() -> void:
@@ -36,6 +37,8 @@ func _ready() -> void:
 	$Console.hide()
 	$NoInputPopup.hide()
 	$MultipleConnectionsPopup.hide()
+	$AudioSettings.hide()
+	$AudioDevicePopup.hide()
 	
 	$SaveDialog.access = FileDialog.ACCESS_FILESYSTEM
 	$SaveDialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
@@ -63,13 +66,14 @@ func _ready() -> void:
 	export_config.load("res://export_presets.cfg")
 	$MenuBar/About.set_item_text(0, "SoundThread v" + export_config.get_value("preset.0.options", "application/product_version", "version unknown") + "-alpha") 
 	
-	#checks if display is hidpi and scales ui accordingly
+	#checks if display is hidpi and scales ui accordingly hidpi - 144
 	if DisplayServer.screen_get_dpi(0) >= 144:
-		get_window().content_scale_factor = 2.0
+		uiscale = 2.0
+		get_window().content_scale_factor = uiscale
 		#goes through popup_windows group and scales all popups and resizes them
 		for window in get_tree().get_nodes_in_group("popup_windows"):
-			window.size = window.size * 2
-			window.content_scale_factor = 2
+			window.size = window.size * uiscale
+			window.content_scale_factor = uiscale
 
 	#checks if user has opened a file from the system file menu and loads it
 	var args = OS.get_cmdline_args()
@@ -136,8 +140,14 @@ func link_output():
 
 func check_user_preferences():
 	var interface_settings = ConfigHandler.load_interface_settings()
+	var audio_settings = ConfigHandler.load_audio_settings()
+	var audio_devices = AudioServer.get_output_device_list()
 	$MenuBar/SettingsButton.set_item_checked(1, interface_settings.disable_pvoc_warning)
 	$MenuBar/SettingsButton.set_item_checked(2, interface_settings.auto_close_console)
+	if audio_devices.has(audio_settings.device):
+		AudioServer.set_output_device(audio_settings.device)
+	else:
+		$AudioDevicePopup.popup()
 
 	
 func check_cdp_location_set():
@@ -266,6 +276,9 @@ func show_help_for_node(node_name: String, node_title: String):
 		
 		# Add to the current scene tree to show it
 		get_tree().current_scene.add_child(help_window)
+		if help_window.content_scale_factor < uiscale:
+			help_window.size = help_window.size * uiscale
+			help_window.content_scale_factor = uiscale
 		
 		help_window.popup() 
 		
@@ -1666,3 +1679,15 @@ func _on_graph_edit_popup_request(at_position: Vector2) -> void:
 	else:
 		$mainmenu.hide()
 		mainmenu_visible = false
+
+func _on_audio_settings_close_requested() -> void:
+	$AudioSettings.hide()
+
+
+func _on_open_audio_settings_button_down() -> void:
+	$AudioDevicePopup.hide()
+	$AudioSettings.popup()
+
+
+func _on_audio_device_popup_close_requested() -> void:
+	$AudioDevicePopup.hide()

+ 31 - 1
scenes/main/control.tscn

@@ -123,6 +123,34 @@ offset_right = 390.0
 offset_bottom = 141.0
 text = "Ok"
 
+[node name="AudioDevicePopup" type="Window" parent="." groups=["popup_windows"]]
+auto_translate_mode = 1
+title = "Audio Device Not Available"
+initial_position = 2
+size = Vector2i(380, 140)
+visible = false
+transient = true
+exclusive = true
+unresizable = true
+popup_window = true
+
+[node name="Label" type="Label" parent="AudioDevicePopup"]
+offset_left = 14.0
+offset_top = 7.0
+offset_right = 363.0
+offset_bottom = 100.0
+text = "The last selected audio output device is not available. SoundThread is using the system default."
+horizontal_alignment = 1
+vertical_alignment = 1
+autowrap_mode = 2
+
+[node name="OpenAudioSettings" type="Button" parent="AudioDevicePopup"]
+offset_left = -2.0
+offset_top = 110.0
+offset_right = 382.0
+offset_bottom = 141.0
+text = "Open Audio Settings"
+
 [node name="MultipleConnectionsPopup" type="Window" parent="." groups=["popup_windows"]]
 auto_translate_mode = 1
 title = "No Input Selected"
@@ -317,7 +345,7 @@ offset_right = 351.0
 offset_bottom = 101.0
 text = "Don't Save"
 
-[node name="AudioSettings" parent="." instance=ExtResource("5_dtf4o")]
+[node name="AudioSettings" parent="." groups=["popup_windows"] instance=ExtResource("5_dtf4o")]
 visible = false
 
 [connection signal="connection_request" from="GraphEdit" to="." method="_on_graph_edit_connection_request"]
@@ -330,6 +358,8 @@ visible = false
 [connection signal="meta_clicked" from="NoLocationPopup/RichTextLabel" to="." method="_on_rich_text_label_meta_clicked"]
 [connection signal="button_down" from="NoLocationPopup/OkButton" to="." method="_on_ok_button_button_down"]
 [connection signal="button_down" from="NoInputPopup/OkButton2" to="." method="_on_ok_button_2_button_down"]
+[connection signal="close_requested" from="AudioDevicePopup" to="." method="_on_audio_device_popup_close_requested"]
+[connection signal="button_down" from="AudioDevicePopup/OpenAudioSettings" to="." method="_on_open_audio_settings_button_down"]
 [connection signal="button_down" from="MultipleConnectionsPopup/OkButton3" to="." method="_on_ok_button_3_button_down"]
 [connection signal="canceled" from="CdpLocationDialog" to="." method="_on_cdp_location_dialog_canceled"]
 [connection signal="dir_selected" from="CdpLocationDialog" to="." method="_on_cdp_location_dialog_dir_selected"]