voice_preview_generator.gd 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. @tool
  2. extends Node
  3. signal texture_ready(texture)
  4. signal generation_progress(normalized_progress)
  5. const MAX_FREQUENCY: float = 3000.0 # Maximum frequency captured
  6. const IMAGE_HEIGHT: int = 64
  7. var image_compression: float = 10.0 # How many samples in one pixel
  8. var background_color = Color(0, 0, 0, 0)
  9. var foreground_color = Color.SILVER
  10. # =============================================================================
  11. const SAMPLING_RATE = 2.0*MAX_FREQUENCY
  12. const IMAGE_HEIGHT_FACTOR: float = float(IMAGE_HEIGHT) / 256.0 # Converts sample raw height to pixel
  13. const IMAGE_CENTER_Y = int(round(IMAGE_HEIGHT / 2.0))
  14. var is_working := false
  15. var must_abort := false
  16. func generate_preview(stream: AudioStreamWAV, image_max_width: int = 500):
  17. if not stream:
  18. return
  19. if stream.format == AudioStreamWAV.FORMAT_IMA_ADPCM:
  20. return
  21. if image_max_width <= 0:
  22. return
  23. if is_working:
  24. must_abort = true
  25. while is_working:
  26. await get_tree().process_frame
  27. is_working = true
  28. var data = stream.data
  29. var data_size = data.size()
  30. var is_16bit = (stream.format == AudioStreamWAV.FORMAT_16_BITS)
  31. var is_stereo = stream.stereo
  32. var sample_interval = 1
  33. if stream.mix_rate > SAMPLING_RATE:
  34. sample_interval = int(round(stream.mix_rate / SAMPLING_RATE))
  35. if is_16bit:
  36. sample_interval *= 2
  37. if is_stereo:
  38. sample_interval *= 2
  39. var reduced_data = PackedByteArray()
  40. var reduced_data_size = int(floor(data_size / float(sample_interval)))
  41. reduced_data.resize(reduced_data_size)
  42. var sample_in_i := 1 if is_16bit else 0
  43. var sample_out_i := 0
  44. while (sample_in_i < data_size) and (sample_out_i < reduced_data_size):
  45. reduced_data[sample_out_i] = data[sample_in_i]
  46. sample_in_i += sample_interval
  47. sample_out_i += 1
  48. if must_abort:
  49. is_working = false
  50. must_abort = false
  51. return
  52. image_compression = ceil(reduced_data_size / float(image_max_width))
  53. var img_width = floor(reduced_data_size / image_compression)
  54. var img = Image.create(img_width, IMAGE_HEIGHT, true, Image.FORMAT_RGBA8)
  55. img.fill(background_color)
  56. var sample_i = 0
  57. var img_x = 0
  58. var final_sample_i = reduced_data_size - image_compression
  59. while sample_i < final_sample_i:
  60. var min_val_left := 128
  61. var max_val_left := 128
  62. var min_val_right := 128
  63. var max_val_right := 128
  64. for block_i in range(image_compression):
  65. if sample_i >= reduced_data_size:
  66. break
  67. var sample_val = reduced_data[sample_i] + 128
  68. if sample_val >= 256:
  69. sample_val -= 256
  70. if is_stereo:
  71. if (sample_i % 2) == 0:
  72. if sample_val < min_val_left:
  73. min_val_left = sample_val
  74. if sample_val > max_val_left:
  75. max_val_left = sample_val
  76. else:
  77. if sample_val < min_val_right:
  78. min_val_right = sample_val
  79. if sample_val > max_val_right:
  80. max_val_right = sample_val
  81. else:
  82. if sample_val < min_val_left:
  83. min_val_left = sample_val
  84. if sample_val > max_val_left:
  85. max_val_left = sample_val
  86. sample_i += 1
  87. if is_stereo:
  88. # Draw top (left)
  89. _draw_half_waveform(img, img_x, min_val_left, max_val_left, 0, IMAGE_HEIGHT / 2)
  90. # Draw bottom (right)
  91. _draw_half_waveform(img, img_x, min_val_right, max_val_right, IMAGE_HEIGHT / 2, IMAGE_HEIGHT / 2)
  92. else:
  93. # Mono: use full height
  94. _draw_half_waveform(img, img_x, min_val_left, max_val_left, 0, IMAGE_HEIGHT)
  95. img_x += 1
  96. if must_abort:
  97. is_working = false
  98. must_abort = false
  99. return
  100. if (sample_i % 100) == 0:
  101. var progress = sample_i / final_sample_i
  102. emit_signal("generation_progress", progress)
  103. await get_tree().process_frame
  104. is_working = false
  105. emit_signal("texture_ready", ImageTexture.create_from_image(img))
  106. func _draw_half_waveform(img: Image, x: int, min_val: int, max_val: int, y_offset: int, draw_height: int):
  107. var scale = draw_height / 256.0
  108. var center_y = y_offset + int(draw_height / 2)
  109. var min_y = int(center_y - (max_val - 128) * scale)
  110. var max_y = int(center_y - (min_val - 128) * scale)
  111. min_y = clamp(min_y, y_offset, y_offset + draw_height - 1)
  112. max_y = clamp(max_y, y_offset, y_offset + draw_height - 1)
  113. for y in range(min_y, max_y + 1):
  114. img.set_pixel(x, y, foreground_color)
  115. func _reset_to_blank():
  116. var img = Image.create(1, IMAGE_HEIGHT, true, Image.FORMAT_RGBA8)
  117. img.fill(Color.DARK_SLATE_GRAY)
  118. emit_signal("texture_ready", ImageTexture.create_from_image(img))