112 lines
2.8 KiB
GDScript
112 lines
2.8 KiB
GDScript
@tool
|
|
class_name MultiMorseBanner
|
|
extends Control
|
|
|
|
static var sample_hz := 22050.0
|
|
static var vol_on := -30
|
|
var phase := 0.0
|
|
|
|
var num: int
|
|
var tone_hz: int
|
|
var color_on := Color(0, 128, 0)
|
|
var color_off := Color(0, 0, 0)
|
|
var last_delta := 0.0
|
|
@export_range(0.1, 120.0) var display_sec := 5.0
|
|
@export var stretch_display := false
|
|
var playback;
|
|
|
|
func _ready() -> void:
|
|
$Player.stream.mix_rate = sample_hz
|
|
$Player.volume_db = -100
|
|
$Player.play()
|
|
playback = $Player.get_stream_playback()
|
|
fill_buffer()
|
|
|
|
var morse_step_perc := 0.45
|
|
const MMB_SCENE := preload("res://scenes/MultiMorseBanner.tscn")
|
|
|
|
class LocalMorseState:
|
|
var states: Array[int] = []
|
|
var curr_state := false
|
|
var last_change: int = 0
|
|
var start_time := 0
|
|
|
|
func reset() -> void:
|
|
last_change = Time.get_ticks_msec()
|
|
start_time = last_change
|
|
states = []
|
|
|
|
func set_state(state: bool) -> void:
|
|
if state == curr_state:
|
|
return
|
|
curr_state = state
|
|
|
|
var now := Time.get_ticks_msec()
|
|
states.push_back(now - last_change)
|
|
last_change = now
|
|
|
|
var morse_state := LocalMorseState.new()
|
|
|
|
static func new_banner(num: int, color: Color, tone: int):
|
|
var mmb = MMB_SCENE.instantiate()
|
|
mmb.num = num
|
|
mmb.color_on = color
|
|
mmb.tone_hz = tone
|
|
|
|
return mmb
|
|
|
|
func _draw_morse_rect(x: float, width: float, state: bool):
|
|
var rect := Rect2(max(x, 0.0), morse_step_perc * size.y, width, (0.5 - morse_step_perc) * size.y)
|
|
draw_rect(rect, color_on if state else color_off, true, -1.0, true)
|
|
|
|
func _draw():
|
|
# black background
|
|
draw_rect(Rect2(0.0, 0.0, size.x, size.y), Color.BLACK)
|
|
|
|
# in editor we only want a black rectangle
|
|
if Engine.is_editor_hint():
|
|
return
|
|
|
|
var morse_on := morse_state.curr_state
|
|
var first_time := Time.get_ticks_msec() - morse_state.last_change
|
|
var curr_x := float(size.x)
|
|
|
|
var px_per_s := 0.0
|
|
if not stretch_display:
|
|
px_per_s = size.x / display_sec
|
|
else:
|
|
px_per_s = size.x / (Time.get_ticks_msec() - morse_state.start_time) * 1000.0
|
|
|
|
for n in [-1] + range(morse_state.states.size() - 1, -1, -1):
|
|
var duration := first_time if n == -1 else morse_state.states[n]
|
|
var rect_width: float = min(duration / 1000.0 * px_per_s, curr_x)
|
|
curr_x -= rect_width
|
|
if morse_on:
|
|
# at the moment we only draw the morse rects
|
|
_draw_morse_rect(curr_x, rect_width, morse_on)
|
|
morse_on = not morse_on
|
|
if curr_x <= 0.0:
|
|
break
|
|
|
|
func _process(_delta):
|
|
last_delta += _delta
|
|
queue_redraw()
|
|
fill_buffer()
|
|
|
|
func set_morse_state(state: bool) -> void:
|
|
morse_state.set_state(state)
|
|
# morse_state = state
|
|
if state:
|
|
$Player.volume_db = vol_on
|
|
else:
|
|
$Player.volume_db = -100
|
|
|
|
func fill_buffer():
|
|
var increment = tone_hz / sample_hz
|
|
var frames_available = playback.get_frames_available()
|
|
|
|
for i in range(frames_available):
|
|
playback.push_frame(Vector2.ONE * sin(phase * TAU))
|
|
phase = fmod(phase + increment, 1.0)
|
|
|