Răsfoiți Sursa

Merge pull request #192 from j-p-higgins/fft-window-size

Fft window size now adjustable
Jonathan Higgins 3 luni în urmă
părinte
comite
4a35943f5d

+ 0 - 1
scenes/Nodes/check_for_updates.gd

@@ -27,7 +27,6 @@ func _on_request_completed(result, response_code, headers, body):
 		return
 		return
 
 
 	var latest_version = response.get("tag_name", "")
 	var latest_version = response.get("tag_name", "")
-	print("Latest GitHub version: ", latest_version)
 	
 	
 	var update_notes = response.get("body", "")
 	var update_notes = response.get("body", "")
 
 

+ 46 - 0
scenes/main/control.tscn

@@ -573,6 +573,51 @@ offset_right = 592.0
 offset_bottom = 100.0
 offset_bottom = 100.0
 text = "Stop Running Thread"
 text = "Stop Running Thread"
 
 
+[node name="Label" type="Label" parent="."]
+layout_mode = 0
+offset_left = 348.0
+offset_top = 48.0
+offset_right = 420.0
+offset_bottom = 67.0
+text = "FFT Size:"
+
+[node name="FFTSize" type="OptionButton" parent="."]
+layout_mode = 0
+offset_left = 425.0
+offset_top = 45.0
+offset_right = 504.0
+offset_bottom = 72.0
+tooltip_text = "Adjusts the number of analysis points used by the frequency domain processes in this thread. More points give better frequency resolution but worse time resolution."
+item_count = 14
+popup/item_0/text = "2"
+popup/item_0/id = 0
+popup/item_1/text = "4"
+popup/item_1/id = 1
+popup/item_2/text = "8"
+popup/item_2/id = 2
+popup/item_3/text = "16"
+popup/item_3/id = 3
+popup/item_4/text = "32"
+popup/item_4/id = 4
+popup/item_5/text = "64"
+popup/item_5/id = 5
+popup/item_6/text = "128"
+popup/item_6/id = 6
+popup/item_7/text = "256"
+popup/item_7/id = 7
+popup/item_8/text = "512"
+popup/item_8/id = 8
+popup/item_9/text = "1024"
+popup/item_9/id = 9
+popup/item_10/text = "2048"
+popup/item_10/id = 10
+popup/item_11/text = "4096"
+popup/item_11/id = 11
+popup/item_12/text = "8192"
+popup/item_12/id = 12
+popup/item_13/text = "16380"
+popup/item_13/id = 13
+
 [connection signal="connection_request" from="GraphEdit" to="GraphEdit" method="_on_connection_request"]
 [connection signal="connection_request" from="GraphEdit" to="GraphEdit" method="_on_connection_request"]
 [connection signal="copy_nodes_request" from="GraphEdit" to="GraphEdit" method="_on_copy_nodes_request"]
 [connection signal="copy_nodes_request" from="GraphEdit" to="GraphEdit" method="_on_copy_nodes_request"]
 [connection signal="delete_nodes_request" from="GraphEdit" to="GraphEdit" method="_on_graph_edit_delete_nodes_request"]
 [connection signal="delete_nodes_request" from="GraphEdit" to="GraphEdit" method="_on_graph_edit_delete_nodes_request"]
@@ -616,3 +661,4 @@ text = "Stop Running Thread"
 [connection signal="close_requested" from="CheckForUpdates/UpdatePopup" to="CheckForUpdates" method="_on_update_popup_close_requested"]
 [connection signal="close_requested" from="CheckForUpdates/UpdatePopup" to="CheckForUpdates" method="_on_update_popup_close_requested"]
 [connection signal="button_down" from="CheckForUpdates/UpdatePopup/OpenAudioSettings" to="CheckForUpdates" method="_on_open_audio_settings_button_down"]
 [connection signal="button_down" from="CheckForUpdates/UpdatePopup/OpenAudioSettings" to="CheckForUpdates" method="_on_open_audio_settings_button_down"]
 [connection signal="button_down" from="ProgressWindow/KillProcess2" to="." method="_on_kill_process_button_down"]
 [connection signal="button_down" from="ProgressWindow/KillProcess2" to="." method="_on_kill_process_button_down"]
+[connection signal="item_selected" from="FFTSize" to="." method="_on_fft_size_item_selected"]

Fișier diff suprimat deoarece este prea mare
+ 273 - 91
scenes/main/process_help.json


+ 16 - 0
scenes/main/scripts/control.gd

@@ -53,6 +53,8 @@ func _ready() -> void:
 	$LoadDialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
 	$LoadDialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
 	$LoadDialog.filters = ["*.thd"]
 	$LoadDialog.filters = ["*.thd"]
 	
 	
+	
+	
 	get_tree().set_auto_accept_quit(false) #disable closing the app with the x and instead handle it internally
 	get_tree().set_auto_accept_quit(false) #disable closing the app with the x and instead handle it internally
 	
 	
 	
 	
@@ -170,10 +172,15 @@ func new_patch():
 	get_window().title = "SoundThread"
 	get_window().title = "SoundThread"
 	link_output()
 	link_output()
 	
 	
+	#set fft size to default
+	$FFTSize.select(9)
+	_on_fft_size_item_selected(9)
+	
 	
 	
 func 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
 	#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
 	for control in get_tree().get_nodes_in_group("outputnode"): #check all items in outputnode group
+		#if control.has_meta("outputfunciton"):
 		if control.get_meta("outputfunction") == "deleteintermediate": #link delete intermediate files toggle to script
 		if control.get_meta("outputfunction") == "deleteintermediate": #link delete intermediate files toggle to script
 			control.toggled.connect(_toggle_delete)
 			control.toggled.connect(_toggle_delete)
 			_toggle_delete(control.button_pressed)
 			_toggle_delete(control.button_pressed)
@@ -856,3 +863,12 @@ func on_files_dropped(files):
 				new_input_node.position_offset = position_plus_offset
 				new_input_node.position_offset = position_plus_offset
 				new_input_node.get_node("AudioPlayer")._on_file_selected(file)
 				new_input_node.get_node("AudioPlayer")._on_file_selected(file)
 				position_plus_offset.y = position_plus_offset.y + 250
 				position_plus_offset.y = position_plus_offset.y + 250
+
+
+func _on_fft_size_item_selected(index: int) -> void:
+	var fft_size
+	if index == 13:
+		fft_size = 16380
+	else:
+		fft_size = 1 << (index + 1)
+	run_thread.fft_size = fft_size

+ 4 - 0
scenes/main/scripts/graph_edit.gd

@@ -142,6 +142,8 @@ func _make_node(command: String, skip_undo_redo := false) -> GraphNode:
 						var min = param_data.get("min", false)
 						var min = param_data.get("min", false)
 						var max = param_data.get("max", false)
 						var max = param_data.get("max", false)
 						var flag = param_data.get("flag", "")
 						var flag = param_data.get("flag", "")
+						var fftwindowsize = param_data.get("fftwindowsize", false)
+						var fftwindowcount = param_data.get("fftwindowcount", false)
 						var minrange = param_data.get("minrange", 0)
 						var minrange = param_data.get("minrange", 0)
 						var maxrange = param_data.get("maxrange", 10)
 						var maxrange = param_data.get("maxrange", 10)
 						var step = param_data.get("step", 0.01)
 						var step = param_data.get("step", 0.01)
@@ -163,6 +165,8 @@ func _make_node(command: String, skip_undo_redo := false) -> GraphNode:
 						hslider.set_meta("max", max)
 						hslider.set_meta("max", max)
 						hslider.set_meta("flag", flag)
 						hslider.set_meta("flag", flag)
 						hslider.set_meta("default_value", value)
 						hslider.set_meta("default_value", value)
+						hslider.set_meta("fftwindowsize", fftwindowsize)
+						hslider.set_meta("fftwindowcount", fftwindowcount)
 						
 						
 						#set slider params
 						#set slider params
 						hslider.min_value = minrange
 						hslider.min_value = minrange

+ 116 - 4
scenes/main/scripts/run_thread.gd

@@ -12,6 +12,7 @@ var process_info = {} #tracks the data of the currently running process
 var process_running := false #tracks if a process is currently running
 var process_running := false #tracks if a process is currently running
 var process_cancelled = false #checks if the currently running process has been cancelled
 var process_cancelled = false #checks if the currently running process has been cancelled
 var final_output_dir
 var final_output_dir
+var fft_size = 1024 #tracks the fft size for the thread set in the main window
 
 
 # Called when the node enters the scene tree for the first time.
 # Called when the node enters the scene tree for the first time.
 func _ready() -> void:
 func _ready() -> void:
@@ -722,7 +723,6 @@ func get_soundfile_properties(file: String) -> Dictionary:
 		"samplerate": 0,
 		"samplerate": 0,
 		"bitdepth": 0,
 		"bitdepth": 0,
 		"duration": 0.0
 		"duration": 0.0
-	
 	}
 	}
 	
 	
 	#open the audio file
 	#open the audio file
@@ -798,6 +798,90 @@ func get_soundfile_properties(file: String) -> Dictionary:
 		
 		
 	return soundfile_properties
 	return soundfile_properties
 
 
+func get_analysis_file_properties(file: String) -> Dictionary:
+	var analysis_file_properties:= {
+		"windowsize": 0,
+		"windowcount": 0,
+		"decimationfactor": 0
+	}
+	
+	#open the audio file
+	var f = FileAccess.open(file, FileAccess.READ)
+	if f == null:
+		log_console("Could not find file: " + file, true)
+		return analysis_file_properties  # couldn't open
+	
+	#Skip the RIFF header (12 bytes: "RIFF", file size, "WAVE")
+	f.seek(12)
+	
+	var data_chunk_size = 0
+	
+	#read through file until end of file if needed
+	while f.get_position() + 8 <= f.get_length():
+		#read the 4 byte chunk id to identify what this chunk is
+		var chunk_id = f.get_buffer(4).get_string_from_ascii() 
+		#read how big this chunk is
+		var chunk_size = f.get_32()
+		
+		if chunk_id == "LIST":
+			f.seek(f.get_position() + 4) # skip first four bits of data - list type "adtl"
+			var list_end = f.get_position() + chunk_size
+			while f.get_position() <= list_end:
+				var sub_chunk_id = f.get_buffer(4).get_string_from_ascii() 
+				var sub_chunk_size = f.get_32()
+				
+				if sub_chunk_id == "note":
+					var note_bytes = f.get_buffer(sub_chunk_size)
+					var note_text = ""
+					for b in note_bytes:
+						note_text += char(b)
+					var pvoc_header_data = note_text.split("\n", false)
+					var i = 0
+					for entry in pvoc_header_data:
+						if entry == "analwinlen":
+							analysis_file_properties["windowsize"] = hex_string_to_int_le(pvoc_header_data[i+1])
+						elif entry == "decfactor":
+							analysis_file_properties["decimationfactor"] =  hex_string_to_int_le(pvoc_header_data[i+1])
+						i += 1
+					break
+			#check if we have already found the data chunk (not likely) and break the loop
+			if data_chunk_size > 0:
+				f.close()
+				break
+		elif chunk_id == "data":
+			#this is where the audio is stored
+			data_chunk_size = chunk_size
+			#check if we have already found the sfif chunk and break loop
+			if analysis_file_properties["windowsize"] > 0:
+				f.close()
+				break
+			#skip the rest of the chunk
+			f.seek(f.get_position() + chunk_size)
+		else:
+			#don't care about any other data in the file skip it
+			f.seek(f.get_position() + chunk_size)
+			
+	#close the file
+	f.close()
+	if analysis_file_properties["windowsize"] != 0 and data_chunk_size != 0:
+		var bytes_per_frame = (analysis_file_properties["windowsize"] + 2) * 4
+		analysis_file_properties["windowcount"] = int(data_chunk_size / bytes_per_frame)
+	else:
+		log_console("Error: Could not get information from analysis file", true)
+		
+	return analysis_file_properties
+	
+func hex_string_to_int_le(hex_string: String) -> int:
+	# Ensure the string is 8 characters (4 bytes)
+	if hex_string.length() != 8:
+		push_error("Invalid hex string length: " + hex_string)
+		return 0
+	var le_string = ""
+	for i in [6, 4, 2, 0]: #flip the order of the bytes as ana format uses little endian
+		le_string += hex_string.substr(i, 2)
+	
+	return le_string.hex_to_int()
+
 func merge_many_files(inlet_id: int, process_count: int, input_files: Array) -> Array:
 func merge_many_files(inlet_id: int, process_count: int, input_files: Array) -> Array:
 	var merge_output = "%s_merge_%d_%d.wav" % [Global.outfile.get_basename(), inlet_id, process_count]
 	var merge_output = "%s_merge_%d_%d.wav" % [Global.outfile.get_basename(), inlet_id, process_count]
 	var converted_files := []  # Track any mono->stereo converted files or upsampled files
 	var converted_files := []  # Track any mono->stereo converted files or upsampled files
@@ -982,6 +1066,9 @@ func match_pvoc_channels(dict: Dictionary) -> void:
 			
 			
 func _get_slider_values_ordered(node: Node) -> Array:
 func _get_slider_values_ordered(node: Node) -> Array:
 	var results := []
 	var results := []
+	if node.has_meta("command") and node.get_meta("command") == "pvoc_anal_1":
+		results.append(["slider", "-c", fft_size, false, [], 2, 32768, false, false])
+		return results
 	for child in node.get_children():
 	for child in node.get_children():
 		if child is Range:
 		if child is Range:
 			var flag = child.get_meta("flag") if child.has_meta("flag") else ""
 			var flag = child.get_meta("flag") if child.has_meta("flag") else ""
@@ -990,9 +1077,21 @@ func _get_slider_values_ordered(node: Node) -> Array:
 			var min_slider = child.min_value
 			var min_slider = child.min_value
 			var max_slider = child.max_value
 			var max_slider = child.max_value
 			var exp = child.exp_edit
 			var exp = child.exp_edit
+			var fftwindowsize = child.get_meta("fftwindowsize")
+			var fftwindowcount = child.get_meta("fftwindowcount")
+			var value = child.value
+			
 			if child.has_meta("brk_data"):
 			if child.has_meta("brk_data"):
 				brk_data = child.get_meta("brk_data")
 				brk_data = child.get_meta("brk_data")
-			results.append(["slider", flag, child.value, time, brk_data, min_slider, max_slider, exp])
+			#if this slider is a percentage of the fft size just calulate this here as fft size is a global value
+			if fftwindowsize == true:
+				if value == 100:
+					value = fft_size
+				else:
+					value = max(int(fft_size * (value/100)), 1)
+				min_slider = max(int(fft_size * (min_slider/100)), 1)
+				max_slider = int(fft_size * (max_slider/100))
+			results.append(["slider", flag, value, time, brk_data, min_slider, max_slider, exp, fftwindowcount])
 		elif child is CheckButton:
 		elif child is CheckButton:
 			var flag = child.get_meta("flag") if child.has_meta("flag") else ""
 			var flag = child.get_meta("flag") if child.has_meta("flag") else ""
 			results.append(["checkbutton", flag, child.button_pressed])
 			results.append(["checkbutton", flag, child.button_pressed])
@@ -1058,6 +1157,7 @@ func make_process(node: Node, process_count: int, current_infile: Array, slider_
 		command = "%s/%s" %[control_script.cdpprogs_location, "morph"]
 		command = "%s/%s" %[control_script.cdpprogs_location, "morph"]
 		args = ["glide", window1_outfile, window2_outfile, output_file, duration]
 		args = ["glide", window1_outfile, window2_outfile, output_file, duration]
 	else:
 	else:
+		# Normal node process as usual 
 		# Get the command name from metadata
 		# Get the command name from metadata
 		var command_name = str(node.get_meta("command"))
 		var command_name = str(node.get_meta("command"))
 		if command_name.find("_") != -1:
 		if command_name.find("_") != -1:
@@ -1072,7 +1172,7 @@ func make_process(node: Node, process_count: int, current_infile: Array, slider_
 			for file in current_infile:
 			for file in current_infile:
 				args.append(file)
 				args.append(file)
 		args.append(output_file)
 		args.append(output_file)
-
+		
 		
 		
 
 
 		# Append parameter values from the sliders, include flags if present
 		# Append parameter values from the sliders, include flags if present
@@ -1088,6 +1188,14 @@ func make_process(node: Node, process_count: int, current_infile: Array, slider_
 				var min_slider = entry[5]
 				var min_slider = entry[5]
 				var max_slider = entry[6]
 				var max_slider = entry[6]
 				var exp = entry[7]
 				var exp = entry[7]
+				var fftwindowcount = entry[8]
+				var window_count
+				if fftwindowcount == true:
+					var analysis_file_data = get_analysis_file_properties(current_infile[0])
+					window_count = analysis_file_data["windowcount"]
+					min_slider = int(max(window_count * (min_slider / 100), 1))
+					max_slider = int(window_count * (max_slider / 100))
+				
 				if brk_data.size() > 0: #if breakpoint data is present on slider
 				if brk_data.size() > 0: #if breakpoint data is present on slider
 					#Sort all points by time
 					#Sort all points by time
 					var sorted_brk_data = []
 					var sorted_brk_data = []
@@ -1156,7 +1264,11 @@ func make_process(node: Node, process_count: int, current_infile: Array, slider_
 							value = infile_length - 0.1
 							value = infile_length - 0.1
 						else:
 						else:
 							value = infile_length * (value / 100) #calculate percentage time of the input file
 							value = infile_length * (value / 100) #calculate percentage time of the input file
-					#line += ("%s%.2f " % [flag, value]) if flag.begins_with("-") else ("%.2f " % value)
+					if fftwindowcount == true:
+						if value == 100:
+							value = window_count
+						else:
+							value = int(window_count * (value / 100))
 					args.append(("%s%.2f " % [flag, value]) if flag.begins_with("-") else str(value))
 					args.append(("%s%.2f " % [flag, value]) if flag.begins_with("-") else str(value))
 					
 					
 			elif entry[0] == "checkbutton":
 			elif entry[0] == "checkbutton":

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff