@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)