Explorar o código

added stereo waveform preview and fixed weird background colour

Jonathan Higgins hai 7 meses
pai
achega
238e00fbe9

+ 59 - 66
addons/audio_preview/voice_preview_generator.gd

@@ -8,7 +8,7 @@ const MAX_FREQUENCY: float = 3000.0 # Maximum frequency captured
 const IMAGE_HEIGHT: int = 64
 
 var image_compression: float = 10.0 # How many samples in one pixel
-var background_color = Color(0.2, 0.2, 0.4, 0.5)
+var background_color = Color(0, 0, 0, 0)
 var foreground_color = Color.SILVER
 
 
@@ -30,11 +30,10 @@ func generate_preview(stream: AudioStreamWAV, image_max_width: int = 500):
 		return
 	
 	if stream.format == AudioStreamWAV.FORMAT_IMA_ADPCM:
-		return # not supported
+		return
 	
 	if image_max_width <= 0:
-		return # User wasn't remarkably brilliant
-		
+		return
 	
 	if is_working:
 		must_abort = true
@@ -48,13 +47,6 @@ func generate_preview(stream: AudioStreamWAV, image_max_width: int = 500):
 	var is_16bit = (stream.format == AudioStreamWAV.FORMAT_16_BITS)
 	var is_stereo = stream.stereo
 	
-	
-	
-	# For display reasons, lower frequencies than the sampling rate might suffice. 
-	# According to the gentlemen of noble steem known as Nyquist and Shannon, 
-	# we can sample at SAMPLING_RATE
-	
-	
 	var sample_interval = 1
 	if stream.mix_rate > SAMPLING_RATE:
 		sample_interval = int(round(stream.mix_rate / SAMPLING_RATE))
@@ -64,20 +56,13 @@ func generate_preview(stream: AudioStreamWAV, image_max_width: int = 500):
 		sample_interval *= 2
 	
 	var reduced_data = PackedByteArray()
-	# We use floor(), not round(), because extra elements in the end of data
-	# before next sampling interval are discarded
-	var reduced_data_size = int(floor( data_size / float(sample_interval) ))
+	var reduced_data_size = int(floor(data_size / float(sample_interval)))
 	reduced_data.resize(reduced_data_size)
 	
-	
-	# For drawing a preview, we use only one byte left channel per sample
-	# PCM16 is little endian, so MSB is index 1, not 0
-	# reduced_data will contain only that one byte per sample
 	var sample_in_i := 1 if is_16bit else 0
 	var sample_out_i := 0
 	while (sample_in_i < data_size) and (sample_out_i < reduced_data_size):
 		reduced_data[sample_out_i] = data[sample_in_i]
-		
 		sample_in_i += sample_interval
 		sample_out_i += 1
 		
@@ -86,62 +71,58 @@ func generate_preview(stream: AudioStreamWAV, image_max_width: int = 500):
 			must_abort = false
 			return
 	
-	
-	# From now on we work only with reduced_data 
-	
 	image_compression = ceil(reduced_data_size / float(image_max_width))
-	
-	var img_width = floor(reduced_data_size/image_compression) # Again floor as we discard remaining samples
+	var img_width = floor(reduced_data_size / image_compression)
 	var img = Image.create(img_width, IMAGE_HEIGHT, true, Image.FORMAT_RGBA8)
-	img.fill(Color.DARK_SLATE_GRAY)
+	img.fill(background_color)
 	
 	var sample_i = 0
 	var img_x = 0
-	var final_sample_i = (reduced_data_size - image_compression)
+	var final_sample_i = reduced_data_size - image_compression
+	
 	while sample_i < final_sample_i:
-		var min_val := 128
-		var max_val := 128
+		var min_val_left := 128
+		var max_val_left := 128
+		var min_val_right := 128
+		var max_val_right := 128
+		
 		for block_i in range(image_compression):
-			var sample_val = reduced_data[sample_i]
-			# Convert signed bytes to unsigned bytes
-			sample_val += 128
+			if sample_i >= reduced_data_size:
+				break
+			var sample_val = reduced_data[sample_i] + 128
 			if sample_val >= 256:
 				sample_val -= 256
-			
-			# Get minmax
-			if sample_val < min_val:
-				min_val = sample_val
-			if sample_val > max_val:
-				max_val = sample_val
-			
-			
+
+			if is_stereo:
+				if (sample_i % 2) == 0:
+					if sample_val < min_val_left:
+						min_val_left = sample_val
+					if sample_val > max_val_left:
+						max_val_left = sample_val
+				else:
+					if sample_val < min_val_right:
+						min_val_right = sample_val
+					if sample_val > max_val_right:
+						max_val_right = sample_val
+			else:
+				if sample_val < min_val_left:
+					min_val_left = sample_val
+				if sample_val > max_val_left:
+					max_val_left = sample_val
+
 			sample_i += 1
-		
-		
-		# Center pixel is always drawn
-		if (min_val == 128) and (max_val == 128):
-			img.set_pixel(img_x, IMAGE_CENTER_Y, foreground_color)
-		
-		else:
-			var min_height = int(clamp(
-				floor(IMAGE_HEIGHT - (min_val*IMAGE_HEIGHT_FACTOR)),
-				0, IMAGE_HEIGHT-1
-			))
-			var max_height = int(clamp(
-				floor(IMAGE_HEIGHT - (max_val*IMAGE_HEIGHT_FACTOR)),
-				0, IMAGE_HEIGHT-1
-			
-			))
-			
-			# min_height and max_height are in audio sample direction (positive up)
-			# while img_y is in image direction (positive down)
-			var img_y = max_height # top value is lower img_y
-			while img_y <= min_height: # bottom value is higher img_y
-				img.set_pixel(img_x, img_y, foreground_color)
-				img_y += 1
-		
+
+			if is_stereo:
+				# Draw top (left)
+				_draw_half_waveform(img, img_x, min_val_left, max_val_left, 0, IMAGE_HEIGHT / 2)
+				# Draw bottom (right)
+				_draw_half_waveform(img, img_x, min_val_right, max_val_right, IMAGE_HEIGHT / 2, IMAGE_HEIGHT / 2)
+			else:
+				# Mono: use full height
+				_draw_half_waveform(img, img_x, min_val_left, max_val_left, 0, IMAGE_HEIGHT)
+
 		img_x += 1
-		
+
 		if must_abort:
 			is_working = false
 			must_abort = false
@@ -151,11 +132,23 @@ func generate_preview(stream: AudioStreamWAV, image_max_width: int = 500):
 			var progress = sample_i / final_sample_i
 			emit_signal("generation_progress", progress)
 			await get_tree().process_frame
-	
+
 	is_working = false
-	
 	emit_signal("texture_ready", ImageTexture.create_from_image(img))
 	
+func _draw_half_waveform(img: Image, x: int, min_val: int, max_val: int, y_offset: int, draw_height: int):
+	var scale = draw_height / 256.0
+	var center_y = y_offset + int(draw_height / 2)
+
+	var min_y = int(center_y - (max_val - 128) * scale)
+	var max_y = int(center_y - (min_val - 128) * scale)
+
+	min_y = clamp(min_y, y_offset, y_offset + draw_height - 1)
+	max_y = clamp(max_y, y_offset, y_offset + draw_height - 1)
+
+	for y in range(min_y, max_y + 1):
+		img.set_pixel(x, y, foreground_color)
+		
 func _reset_to_blank():
 	var img = Image.create(1, IMAGE_HEIGHT, true, Image.FORMAT_RGBA8)
 	img.fill(Color.DARK_SLATE_GRAY)

+ 2 - 2
scenes/main/control.tscn

@@ -400,10 +400,10 @@ offset_right = 382.0
 offset_bottom = 100.0
 text = "Get the update"
 
-[node name="Settings" parent="." instance=ExtResource("8_16l5g")]
+[node name="Settings" parent="." groups=["popup_windows"] instance=ExtResource("8_16l5g")]
 visible = false
 
-[node name="ProgressWindow" type="Window" parent="."]
+[node name="ProgressWindow" type="Window" parent="." groups=["popup_windows"]]
 initial_position = 2
 size = Vector2i(600, 110)
 visible = false

+ 10 - 1
theme/main_theme.tres

@@ -1,4 +1,4 @@
-[gd_resource type="Theme" load_steps=66 format=3 uid="uid://cefwkdcoxihro"]
+[gd_resource type="Theme" load_steps=67 format=3 uid="uid://cefwkdcoxihro"]
 
 [ext_resource type="Texture2D" uid="uid://b4o8vm5o4uptk" path="res://theme/images/toggle_checked.png" id="1_cibxr"]
 [ext_resource type="Texture2D" uid="uid://d0dubcywvqtkw" path="res://theme/images/toggle_unchecked.png" id="2_adhqp"]
@@ -386,6 +386,14 @@ border_color = Color(0, 0, 0, 0.6)
 corner_detail = 5
 shadow_color = Color(0, 0, 0, 0)
 
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5mqb0"]
+content_margin_left = 0.0
+content_margin_top = 0.0
+content_margin_right = 0.0
+content_margin_bottom = 0.0
+bg_color = Color(0.1, 0.1, 0.1, 0.6)
+corner_detail = 5
+
 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cibxr"]
 content_margin_left = 4.0
 content_margin_top = 4.0
@@ -597,6 +605,7 @@ ItemList/styles/selected = SubResource("StyleBoxFlat_xnh8w")
 ItemList/styles/selected_focus = SubResource("StyleBoxFlat_2b3ki")
 LineEdit/styles/focus = SubResource("StyleBoxFlat_7hjjl")
 LineEdit/styles/normal = SubResource("StyleBoxFlat_wnmfn")
+Panel/styles/panel = SubResource("StyleBoxFlat_5mqb0")
 PopupMenu/constants/item_end_padding = 20
 PopupMenu/constants/v_separation = 8
 PopupMenu/styles/hover = SubResource("StyleBoxFlat_cibxr")