mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2026-01-06 07:50:22 +01:00
Use static typing in all demos (#1063)
This leads to code that is easier to understand and runs faster thanks to GDScript's typed instructions. The untyped declaration warning is now enabled on all projects where type hints were added. All projects currently run without any untyped declration warnings. Dodge the Creeps and Squash the Creeps demos intentionally don't use type hints to match the documentation, where type hints haven't been adopted yet (given its beginner focus).
This commit is contained in:
@@ -1,36 +1,29 @@
|
||||
extends Panel
|
||||
|
||||
const BPM = 116
|
||||
const BARS = 4
|
||||
|
||||
var playing = false
|
||||
const COMPENSATE_FRAMES = 2
|
||||
const COMPENSATE_HZ = 60.0
|
||||
|
||||
enum SyncSource {
|
||||
SYSTEM_CLOCK,
|
||||
SOUND_CLOCK,
|
||||
}
|
||||
|
||||
var sync_source = SyncSource.SYSTEM_CLOCK
|
||||
const BPM = 116
|
||||
const BARS = 4
|
||||
|
||||
const COMPENSATE_FRAMES = 2
|
||||
const COMPENSATE_HZ = 60.0
|
||||
|
||||
var playing := false
|
||||
var sync_source := SyncSource.SYSTEM_CLOCK
|
||||
|
||||
# Used by system clock.
|
||||
var time_begin
|
||||
var time_delay
|
||||
var time_begin: float
|
||||
var time_delay: float
|
||||
|
||||
|
||||
func strsec(secs):
|
||||
var s = str(secs)
|
||||
if (secs < 10):
|
||||
s = "0" + s
|
||||
return s
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
func _process(_delta: float) -> void:
|
||||
if not playing or not $Player.playing:
|
||||
return
|
||||
|
||||
var time = 0.0
|
||||
var time := 0.0
|
||||
if sync_source == SyncSource.SYSTEM_CLOCK:
|
||||
# Obtain from ticks.
|
||||
time = (Time.get_ticks_usec() - time_begin) / 1000000.0
|
||||
@@ -39,14 +32,14 @@ func _process(_delta):
|
||||
elif sync_source == SyncSource.SOUND_CLOCK:
|
||||
time = $Player.get_playback_position() + AudioServer.get_time_since_last_mix() - AudioServer.get_output_latency() + (1 / COMPENSATE_HZ) * COMPENSATE_FRAMES
|
||||
|
||||
var beat = int(time * BPM / 60.0)
|
||||
var seconds = int(time)
|
||||
var seconds_total = int($Player.stream.get_length())
|
||||
var beat := int(time * BPM / 60.0)
|
||||
var seconds := int(time)
|
||||
var seconds_total := int($Player.stream.get_length())
|
||||
@warning_ignore("integer_division")
|
||||
$Label.text = str("BEAT: ", beat % BARS + 1, "/", BARS, " TIME: ", seconds / 60, ":", strsec(seconds % 60), " / ", seconds_total / 60, ":", strsec(seconds_total % 60))
|
||||
$Label.text = str("BEAT: ", beat % BARS + 1, "/", BARS, " TIME: ", seconds / 60, ":", str(seconds % 60).pad_zeros(2), " / ", seconds_total / 60, ":", str(seconds_total % 60).pad_zeros(2))
|
||||
|
||||
|
||||
func _on_PlaySystem_pressed():
|
||||
func _on_PlaySystem_pressed() -> void:
|
||||
sync_source = SyncSource.SYSTEM_CLOCK
|
||||
time_begin = Time.get_ticks_usec()
|
||||
time_delay = AudioServer.get_time_to_next_mix() + AudioServer.get_output_latency()
|
||||
@@ -54,7 +47,7 @@ func _on_PlaySystem_pressed():
|
||||
$Player.play()
|
||||
|
||||
|
||||
func _on_PlaySound_pressed():
|
||||
func _on_PlaySound_pressed() -> void:
|
||||
sync_source = SyncSource.SOUND_CLOCK
|
||||
playing = true
|
||||
$Player.play()
|
||||
|
||||
@@ -19,32 +19,62 @@ size_flags_vertical = 4
|
||||
script = ExtResource("7")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 106.895
|
||||
offset_top = 427.158
|
||||
offset_right = 914.895
|
||||
offset_bottom = 488.158
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -404.0
|
||||
offset_top = 55.0
|
||||
offset_right = 404.0
|
||||
offset_bottom = 121.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(0.654902, 1, 0.67451, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/shadow_offset_x = 4
|
||||
theme_override_constants/shadow_offset_y = 4
|
||||
theme_override_fonts/font = ExtResource("2_wyi3x")
|
||||
theme_override_font_sizes/font_size = 48
|
||||
text = "Press one of the buttons."
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Player" type="AudioStreamPlayer" parent="."]
|
||||
stream = ExtResource("3")
|
||||
volume_db = -6.0
|
||||
|
||||
[node name="PlaySystem" type="TextureButton" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 214.737
|
||||
offset_top = 187.368
|
||||
offset_right = 342.737
|
||||
offset_bottom = 315.368
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -288.0
|
||||
offset_top = -136.0
|
||||
offset_right = -160.0
|
||||
offset_bottom = -7.99997
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture_normal = ExtResource("5")
|
||||
texture_pressed = ExtResource("5")
|
||||
texture_hover = ExtResource("1")
|
||||
|
||||
[node name="PlaySound" type="TextureButton" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 622.105
|
||||
offset_top = 183.158
|
||||
offset_right = 750.105
|
||||
offset_bottom = 311.158
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 160.0
|
||||
offset_top = -136.0
|
||||
offset_right = 288.0
|
||||
offset_bottom = -8.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture_normal = ExtResource("2")
|
||||
texture_pressed = ExtResource("2")
|
||||
texture_hover = ExtResource("4")
|
||||
|
||||
@@ -17,6 +17,10 @@ run/main_scene="res://bpm_sync.tscn"
|
||||
config/features=PackedStringArray("4.2")
|
||||
config/icon="res://icon.webp"
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[display]
|
||||
|
||||
window/stretch/mode="canvas_items"
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
extends Control
|
||||
|
||||
@onready var item_list = get_node(^"ItemList")
|
||||
@onready var item_list: ItemList = $ItemList
|
||||
|
||||
|
||||
func _ready():
|
||||
func _ready() -> void:
|
||||
for item in AudioServer.get_output_device_list():
|
||||
item_list.add_item(item)
|
||||
|
||||
var device = AudioServer.get_output_device()
|
||||
for i in range(item_list.get_item_count()):
|
||||
var device := AudioServer.get_output_device()
|
||||
for i in item_list.get_item_count():
|
||||
if device == item_list.get_item_text(i):
|
||||
item_list.select(i)
|
||||
break
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
var speaker_mode_text = "Stereo"
|
||||
var speaker_mode = AudioServer.get_speaker_mode()
|
||||
func _process(_delta: float) -> void:
|
||||
var speaker_mode_text := "Stereo"
|
||||
var speaker_mode := AudioServer.get_speaker_mode()
|
||||
|
||||
if speaker_mode == AudioServer.SPEAKER_SURROUND_31:
|
||||
speaker_mode_text = "Surround 3.1"
|
||||
@@ -29,13 +28,13 @@ func _process(_delta):
|
||||
$DeviceInfo.text += "Speaker Mode: " + speaker_mode_text
|
||||
|
||||
|
||||
func _on_Button_button_down():
|
||||
func _on_Button_button_down() -> void:
|
||||
for item in item_list.get_selected_items():
|
||||
var device = item_list.get_item_text(item)
|
||||
var device := item_list.get_item_text(item)
|
||||
AudioServer.set_output_device(device)
|
||||
|
||||
|
||||
func _on_Play_Audio_button_down():
|
||||
func _on_Play_Audio_button_down() -> void:
|
||||
if $AudioStreamPlayer.playing:
|
||||
$AudioStreamPlayer.stop()
|
||||
$PlayAudio.text = "Play Audio"
|
||||
|
||||
@@ -27,10 +27,11 @@ offset_bottom = 228.0
|
||||
|
||||
[node name="DeviceInfo" type="Label" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 321.0
|
||||
offset_left = -64.0
|
||||
offset_top = 248.0
|
||||
offset_right = 660.0
|
||||
offset_right = 1046.0
|
||||
offset_bottom = 284.0
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="SetDevice" type="Button" parent="."]
|
||||
layout_mode = 0
|
||||
|
||||
@@ -18,6 +18,10 @@ config/features=PackedStringArray("4.2")
|
||||
run/low_processor_mode=true
|
||||
config/icon="res://icon.webp"
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[display]
|
||||
|
||||
window/stretch/mode="canvas_items"
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
extends Node
|
||||
|
||||
var sample_hz = 22050.0 # Keep the number of samples to mix low, GDScript is not super fast.
|
||||
var pulse_hz = 440.0
|
||||
var phase = 0.0
|
||||
# Keep the number of samples per second to mix low, as GDScript is not super fast.
|
||||
var sample_hz := 22050.0
|
||||
var pulse_hz := 440.0
|
||||
var phase := 0.0
|
||||
|
||||
var playback: AudioStreamPlayback = null # Actual playback stream, assigned in _ready().
|
||||
# Actual playback stream, assigned in _ready().
|
||||
var playback: AudioStreamPlayback
|
||||
|
||||
func _fill_buffer():
|
||||
var increment = pulse_hz / sample_hz
|
||||
func _fill_buffer() -> void:
|
||||
var increment := pulse_hz / sample_hz
|
||||
|
||||
var to_fill = playback.get_frames_available()
|
||||
var to_fill: int = playback.get_frames_available()
|
||||
while to_fill > 0:
|
||||
playback.push_frame(Vector2.ONE * sin(phase * TAU)) # Audio frames are stereo.
|
||||
phase = fmod(phase + increment, 1.0)
|
||||
to_fill -= 1
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
func _process(_delta: float) -> void:
|
||||
_fill_buffer()
|
||||
|
||||
|
||||
func _ready():
|
||||
func _ready() -> void:
|
||||
# Setting mix rate is only possible before play().
|
||||
$Player.stream.mix_rate = sample_hz
|
||||
$Player.play()
|
||||
@@ -30,12 +32,12 @@ func _ready():
|
||||
_fill_buffer()
|
||||
|
||||
|
||||
func _on_frequency_h_slider_value_changed(value):
|
||||
func _on_frequency_h_slider_value_changed(value: float) -> void:
|
||||
%FrequencyLabel.text = "%d Hz" % value
|
||||
pulse_hz = value
|
||||
|
||||
|
||||
func _on_volume_h_slider_value_changed(value):
|
||||
func _on_volume_h_slider_value_changed(value: float) -> void:
|
||||
# Use `linear_to_db()` to get a volume slider that matches perceptual human hearing.
|
||||
%VolumeLabel.text = "%.2f dB" % linear_to_db(value)
|
||||
$Player.volume_db = linear_to_db(value)
|
||||
|
||||
@@ -20,6 +20,10 @@ config/features=PackedStringArray("4.2")
|
||||
run/low_processor_mode=true
|
||||
config/icon="res://icon.webp"
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[display]
|
||||
|
||||
window/stretch/mode="canvas_items"
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
extends Control
|
||||
|
||||
var effect # See AudioEffect in docs
|
||||
var recording # See AudioStreamSample in docs
|
||||
var effect: AudioEffect
|
||||
var recording: AudioStreamWAV
|
||||
|
||||
var stereo := true
|
||||
var mix_rate := 44100 # This is the default mix rate on recordings
|
||||
var format := 1 # This equals to the default format: 16 bits
|
||||
var mix_rate := 44100 # This is the default mix rate on recordings.
|
||||
var format := AudioStreamWAV.FORMAT_16_BITS # This is the default format on recordings.
|
||||
|
||||
|
||||
func _ready():
|
||||
var idx = AudioServer.get_bus_index("Record")
|
||||
func _ready() -> void:
|
||||
var idx := AudioServer.get_bus_index("Record")
|
||||
effect = AudioServer.get_bus_effect(idx, 0)
|
||||
|
||||
|
||||
func _on_RecordButton_pressed():
|
||||
func _on_record_button_pressed() -> void:
|
||||
if effect.is_recording_active():
|
||||
recording = effect.get_recording()
|
||||
$PlayButton.disabled = false
|
||||
@@ -32,18 +32,18 @@ func _on_RecordButton_pressed():
|
||||
$Status.text = "Status: Recording..."
|
||||
|
||||
|
||||
func _on_PlayButton_pressed():
|
||||
func _on_play_button_pressed() -> void:
|
||||
print_rich("\n[b]Playing recording:[/b] %s" % recording)
|
||||
print_rich("[b]Format:[/b] %s" % ("8-bit uncompressed" if recording.format == 0 else "16-bit uncompressed" if recording.format == 1 else "IMA ADPCM compressed"))
|
||||
print_rich("[b]Mix rate:[/b] %s Hz" % recording.mix_rate)
|
||||
print_rich("[b]Stereo:[/b] %s" % ("Yes" if recording.stereo else "No"))
|
||||
var data = recording.get_data()
|
||||
var data := recording.get_data()
|
||||
print_rich("[b]Size:[/b] %s bytes" % data.size())
|
||||
$AudioStreamPlayer.stream = recording
|
||||
$AudioStreamPlayer.play()
|
||||
|
||||
|
||||
func _on_Play_Music_pressed():
|
||||
func _on_play_music_pressed() -> void:
|
||||
if $AudioStreamPlayer2.playing:
|
||||
$AudioStreamPlayer2.stop()
|
||||
$PlayMusic.text = "Play Music"
|
||||
@@ -52,13 +52,13 @@ func _on_Play_Music_pressed():
|
||||
$PlayMusic.text = "Stop Music"
|
||||
|
||||
|
||||
func _on_SaveButton_pressed():
|
||||
var save_path = $SaveButton/Filename.text
|
||||
func _on_save_button_pressed() -> void:
|
||||
var save_path: String = $SaveButton/Filename.text
|
||||
recording.save_to_wav(save_path)
|
||||
$Status.text = "Status: Saved WAV file to: %s\n(%s)" % [save_path, ProjectSettings.globalize_path(save_path)]
|
||||
|
||||
|
||||
func _on_MixRateOptionButton_item_selected(index: int) -> void:
|
||||
func _on_mix_rate_option_button_item_selected(index: int) -> void:
|
||||
match index:
|
||||
0:
|
||||
mix_rate = 11025
|
||||
@@ -76,7 +76,7 @@ func _on_MixRateOptionButton_item_selected(index: int) -> void:
|
||||
recording.set_mix_rate(mix_rate)
|
||||
|
||||
|
||||
func _on_FormatOptionButton_item_selected(index: int) -> void:
|
||||
func _on_format_option_button_item_selected(index: int) -> void:
|
||||
match index:
|
||||
0:
|
||||
format = AudioStreamWAV.FORMAT_8_BITS
|
||||
@@ -88,11 +88,12 @@ func _on_FormatOptionButton_item_selected(index: int) -> void:
|
||||
recording.set_format(format)
|
||||
|
||||
|
||||
func _on_StereoCheckButton_toggled(button_pressed: bool) -> void:
|
||||
func _on_stereo_check_button_toggled(button_pressed: bool) -> void:
|
||||
stereo = button_pressed
|
||||
if recording != null:
|
||||
recording.set_stereo(stereo)
|
||||
|
||||
|
||||
func _on_open_user_folder_button_pressed():
|
||||
func _on_open_user_folder_button_pressed() -> void:
|
||||
OS.shell_open(ProjectSettings.globalize_path("user://"))
|
||||
|
||||
|
||||
@@ -159,11 +159,11 @@ offset_right = 372.0
|
||||
offset_bottom = 374.0
|
||||
text = "Open User Folder"
|
||||
|
||||
[connection signal="pressed" from="RecordButton" to="." method="_on_RecordButton_pressed"]
|
||||
[connection signal="pressed" from="PlayButton" to="." method="_on_PlayButton_pressed"]
|
||||
[connection signal="pressed" from="PlayMusic" to="." method="_on_Play_Music_pressed"]
|
||||
[connection signal="item_selected" from="FormatOptionButton" to="." method="_on_FormatOptionButton_item_selected"]
|
||||
[connection signal="item_selected" from="MixRateOptionButton" to="." method="_on_MixRateOptionButton_item_selected"]
|
||||
[connection signal="toggled" from="StereoCheckButton" to="." method="_on_StereoCheckButton_toggled"]
|
||||
[connection signal="pressed" from="SaveButton" to="." method="_on_SaveButton_pressed"]
|
||||
[connection signal="pressed" from="RecordButton" to="." method="_on_record_button_pressed"]
|
||||
[connection signal="pressed" from="PlayButton" to="." method="_on_play_button_pressed"]
|
||||
[connection signal="pressed" from="PlayMusic" to="." method="_on_play_music_pressed"]
|
||||
[connection signal="item_selected" from="FormatOptionButton" to="." method="_on_format_option_button_item_selected"]
|
||||
[connection signal="item_selected" from="MixRateOptionButton" to="." method="_on_mix_rate_option_button_item_selected"]
|
||||
[connection signal="toggled" from="StereoCheckButton" to="." method="_on_stereo_check_button_toggled"]
|
||||
[connection signal="pressed" from="SaveButton" to="." method="_on_save_button_pressed"]
|
||||
[connection signal="pressed" from="OpenUserFolderButton" to="." method="_on_open_user_folder_button_pressed"]
|
||||
|
||||
@@ -24,6 +24,10 @@ config/icon="res://icon.webp"
|
||||
driver/enable_input=true
|
||||
enable_audio_input=true
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=640
|
||||
|
||||
@@ -9,37 +9,38 @@ extends Control
|
||||
const START_KEY = 21
|
||||
const END_KEY = 108
|
||||
|
||||
const WhiteKeyScene = preload("res://piano_keys/white_piano_key.tscn")
|
||||
const BlackKeyScene = preload("res://piano_keys/black_piano_key.tscn")
|
||||
const WhiteKeyScene := preload("res://piano_keys/white_piano_key.tscn")
|
||||
const BlackKeyScene := preload("res://piano_keys/black_piano_key.tscn")
|
||||
|
||||
var piano_key_dict := Dictionary()
|
||||
|
||||
@onready var white_keys = $WhiteKeys
|
||||
@onready var black_keys = $BlackKeys
|
||||
@onready var white_keys: HBoxContainer = $WhiteKeys
|
||||
@onready var black_keys: HBoxContainer = $BlackKeys
|
||||
|
||||
func _ready():
|
||||
# Sanity checks.
|
||||
if _is_note_index_sharp(_pitch_index_to_note_index(START_KEY)):
|
||||
printerr("The start key can't be a sharp note (limitation of this piano-generating algorithm). Try 21.")
|
||||
return
|
||||
func _ready() -> void:
|
||||
assert(not _is_note_index_sharp(_pitch_index_to_note_index(START_KEY)), "The start key can't be a sharp note (limitation of this piano-generating algorithm). Try 21.")
|
||||
|
||||
for i in range(START_KEY, END_KEY + 1):
|
||||
piano_key_dict[i] = _create_piano_key(i)
|
||||
|
||||
if white_keys.get_child_count() != black_keys.get_child_count():
|
||||
_add_placeholder_key(black_keys)
|
||||
|
||||
OS.open_midi_inputs()
|
||||
if len(OS.get_connected_midi_inputs()) > 0:
|
||||
|
||||
if not OS.get_connected_midi_inputs().is_empty():
|
||||
print(OS.get_connected_midi_inputs())
|
||||
|
||||
|
||||
func _input(input_event):
|
||||
if not (input_event is InputEventMIDI):
|
||||
func _input(input_event: InputEvent) -> void:
|
||||
if not input_event is InputEventMIDI:
|
||||
return
|
||||
|
||||
var midi_event: InputEventMIDI = input_event
|
||||
if midi_event.pitch < START_KEY or midi_event.pitch > END_KEY:
|
||||
# The given pitch isn't on the on-screen keyboard, so return.
|
||||
return
|
||||
|
||||
_print_midi_info(midi_event)
|
||||
var key: PianoKey = piano_key_dict[midi_event.pitch]
|
||||
if midi_event.message == MIDI_MESSAGE_NOTE_ON:
|
||||
@@ -48,16 +49,16 @@ func _input(input_event):
|
||||
key.deactivate()
|
||||
|
||||
|
||||
func _add_placeholder_key(container):
|
||||
var placeholder = Control.new()
|
||||
func _add_placeholder_key(container: HBoxContainer) -> void:
|
||||
var placeholder := Control.new()
|
||||
placeholder.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
placeholder.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
placeholder.name = &"Placeholder"
|
||||
container.add_child(placeholder)
|
||||
|
||||
|
||||
func _create_piano_key(pitch_index):
|
||||
var note_index = _pitch_index_to_note_index(pitch_index)
|
||||
func _create_piano_key(pitch_index: int) -> PianoKey:
|
||||
var note_index := _pitch_index_to_note_index(pitch_index)
|
||||
var piano_key: PianoKey
|
||||
if _is_note_index_sharp(note_index):
|
||||
piano_key = BlackKeyScene.instantiate()
|
||||
@@ -71,22 +72,22 @@ func _create_piano_key(pitch_index):
|
||||
return piano_key
|
||||
|
||||
|
||||
func _is_note_index_lacking_sharp(note_index: int):
|
||||
func _is_note_index_lacking_sharp(note_index: int) -> bool:
|
||||
# B and E, because no B# or E#
|
||||
return note_index in [2, 7]
|
||||
|
||||
|
||||
func _is_note_index_sharp(note_index: int):
|
||||
func _is_note_index_sharp(note_index: int) -> bool:
|
||||
# A#, C#, D#, F#, and G#
|
||||
return note_index in [1, 4, 6, 9, 11]
|
||||
|
||||
|
||||
func _pitch_index_to_note_index(pitch: int):
|
||||
func _pitch_index_to_note_index(pitch: int) -> int:
|
||||
pitch += 3
|
||||
return pitch % 12
|
||||
|
||||
|
||||
func _print_midi_info(midi_event: InputEventMIDI):
|
||||
func _print_midi_info(midi_event: InputEventMIDI) -> void:
|
||||
print(midi_event)
|
||||
print("Channel: " + str(midi_event.channel))
|
||||
print("Message: " + str(midi_event.message))
|
||||
|
||||
@@ -7,14 +7,13 @@ var pitch_scale: float
|
||||
@onready var start_color: Color = key.color
|
||||
@onready var color_timer: Timer = $ColorTimer
|
||||
|
||||
|
||||
func setup(pitch_index: int):
|
||||
func setup(pitch_index: int) -> void:
|
||||
name = "PianoKey" + str(pitch_index)
|
||||
var exponent := (pitch_index - 69.0) / 12.0
|
||||
pitch_scale = pow(2, exponent)
|
||||
|
||||
|
||||
func activate():
|
||||
func activate() -> void:
|
||||
key.color = (Color.YELLOW + start_color) / 2
|
||||
var audio := AudioStreamPlayer.new()
|
||||
add_child(audio)
|
||||
@@ -26,5 +25,5 @@ func activate():
|
||||
audio.queue_free()
|
||||
|
||||
|
||||
func deactivate():
|
||||
func deactivate() -> void:
|
||||
key.color = start_color
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
extends ColorRect
|
||||
|
||||
@onready var parent = get_parent()
|
||||
@onready var parent: PianoKey = get_parent()
|
||||
|
||||
# Yes, this script exists just for this one method.
|
||||
func _gui_input(input_event):
|
||||
func _gui_input(input_event: InputEvent) -> void:
|
||||
if input_event is InputEventMouseButton and input_event.pressed:
|
||||
parent.activate()
|
||||
|
||||
@@ -16,6 +16,10 @@ run/main_scene="res://piano.tscn"
|
||||
config/features=PackedStringArray("4.2")
|
||||
config/icon="res://icon.webp"
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=1280
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends Node2D
|
||||
|
||||
|
||||
const VU_COUNT = 16
|
||||
const FREQ_MAX = 11050.0
|
||||
|
||||
@@ -10,14 +9,14 @@ const HEIGHT_SCALE = 8.0
|
||||
const MIN_DB = 60
|
||||
const ANIMATION_SPEED = 0.1
|
||||
|
||||
var spectrum
|
||||
var min_values = []
|
||||
var max_values = []
|
||||
var spectrum: AudioEffectSpectrumAnalyzerInstance
|
||||
var min_values: Array[float] = []
|
||||
var max_values: Array[float] = []
|
||||
|
||||
|
||||
func _draw():
|
||||
var w = WIDTH / VU_COUNT
|
||||
for i in range(VU_COUNT):
|
||||
func _draw() -> void:
|
||||
@warning_ignore("integer_division")
|
||||
var w := WIDTH / VU_COUNT
|
||||
for i in VU_COUNT:
|
||||
var min_height = min_values[i]
|
||||
var max_height = max_values[i]
|
||||
var height = lerp(min_height, max_height, ANIMATION_SPEED)
|
||||
@@ -48,32 +47,32 @@ func _draw():
|
||||
)
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
var data = []
|
||||
var prev_hz = 0
|
||||
func _process(_delta: float) -> void:
|
||||
var data: Array[float] = []
|
||||
var prev_hz := 0.0
|
||||
|
||||
for i in range(1, VU_COUNT + 1):
|
||||
var hz = i * FREQ_MAX / VU_COUNT
|
||||
var magnitude = spectrum.get_magnitude_for_frequency_range(prev_hz, hz).length()
|
||||
var energy = clampf((MIN_DB + linear_to_db(magnitude)) / MIN_DB, 0, 1)
|
||||
var height = energy * HEIGHT * HEIGHT_SCALE
|
||||
var hz := i * FREQ_MAX / VU_COUNT
|
||||
var magnitude := spectrum.get_magnitude_for_frequency_range(prev_hz, hz).length()
|
||||
var energy := clampf((MIN_DB + linear_to_db(magnitude)) / MIN_DB, 0, 1)
|
||||
var height := energy * HEIGHT * HEIGHT_SCALE
|
||||
data.append(height)
|
||||
prev_hz = hz
|
||||
|
||||
for i in range(VU_COUNT):
|
||||
for i in VU_COUNT:
|
||||
if data[i] > max_values[i]:
|
||||
max_values[i] = data[i]
|
||||
else:
|
||||
max_values[i] = lerp(max_values[i], data[i], ANIMATION_SPEED)
|
||||
max_values[i] = lerpf(max_values[i], data[i], ANIMATION_SPEED)
|
||||
|
||||
if data[i] <= 0.0:
|
||||
min_values[i] = lerp(min_values[i], 0.0, ANIMATION_SPEED)
|
||||
min_values[i] = lerpf(min_values[i], 0.0, ANIMATION_SPEED)
|
||||
|
||||
# Sound plays back continuously, so the graph needs to be updated every frame.
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _ready():
|
||||
func _ready() -> void:
|
||||
spectrum = AudioServer.get_bus_effect_instance(0, 0)
|
||||
min_values.resize(VU_COUNT)
|
||||
max_values.resize(VU_COUNT)
|
||||
|
||||
@@ -52,6 +52,7 @@ offset_top = 56.0
|
||||
offset_right = 704.0
|
||||
offset_bottom = 296.0
|
||||
columns = 2
|
||||
select_mode = 1
|
||||
|
||||
[node name="Utterance" type="TextEdit" parent="."]
|
||||
layout_mode = 0
|
||||
@@ -187,7 +188,6 @@ offset_bottom = 40.0
|
||||
layout_mode = 0
|
||||
offset_right = 128.0
|
||||
offset_bottom = 32.0
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "Speaking..."
|
||||
|
||||
[node name="Log" type="TextEdit" parent="."]
|
||||
@@ -232,13 +232,12 @@ text = "Demo"
|
||||
|
||||
[connection signal="text_changed" from="LineEditFilterLang" to="." method="_on_LineEditFilterName_text_changed"]
|
||||
[connection signal="text_changed" from="LineEditFilterName" to="." method="_on_LineEditFilterName_text_changed"]
|
||||
[connection signal="item_activated" from="Tree" to="." method="_on_ItemList_item_activated"]
|
||||
[connection signal="pressed" from="ButtonSpeak" to="." method="_on_ButtonSpeak_pressed"]
|
||||
[connection signal="pressed" from="ButtonIntSpeak" to="." method="_on_ButtonIntSpeak_pressed"]
|
||||
[connection signal="pressed" from="ButtonStop" to="." method="_on_ButtonStop_pressed"]
|
||||
[connection signal="pressed" from="ButtonPause" to="." method="_on_ButtonPause_pressed"]
|
||||
[connection signal="pressed" from="ButtonSpeak" to="." method="_on_button_speak_pressed"]
|
||||
[connection signal="pressed" from="ButtonIntSpeak" to="." method="_on_button_int_speak_pressed"]
|
||||
[connection signal="pressed" from="ButtonStop" to="." method="_on_button_stop_pressed"]
|
||||
[connection signal="pressed" from="ButtonPause" to="." method="_on_button_pause_pressed"]
|
||||
[connection signal="value_changed" from="HSliderRate" to="." method="_on_HSliderRate_value_changed"]
|
||||
[connection signal="value_changed" from="HSliderPitch" to="." method="_on_HSliderPitch_value_changed"]
|
||||
[connection signal="value_changed" from="HSliderVolume" to="." method="_on_HSliderVolume_value_changed"]
|
||||
[connection signal="pressed" from="Log/ButtonClearLog" to="." method="_on_ButtonClearLog_pressed"]
|
||||
[connection signal="pressed" from="Log/ButtonClearLog" to="." method="_on_button_clear_log_pressed"]
|
||||
[connection signal="pressed" from="ButtonDemo" to="." method="_on_Button_pressed"]
|
||||
|
||||
@@ -21,6 +21,10 @@ config/icon="res://icon.webp"
|
||||
|
||||
general/text_to_speech=true
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[display]
|
||||
|
||||
window/stretch/mode="canvas_items"
|
||||
|
||||
@@ -1,127 +1,150 @@
|
||||
extends Control
|
||||
|
||||
var id = 0 #utterance id
|
||||
var ut_map = {}
|
||||
var vs
|
||||
## The utterance ID to use for text to speech.
|
||||
var id := 0
|
||||
|
||||
func _ready():
|
||||
# get voice data
|
||||
var ut_map := {}
|
||||
var vs: Array[Dictionary]
|
||||
|
||||
func _ready() -> void:
|
||||
# Get voice data.
|
||||
vs = DisplayServer.tts_get_voices()
|
||||
var root = $Tree.create_item()
|
||||
var root: TreeItem = $Tree.create_item()
|
||||
$Tree.set_hide_root(true)
|
||||
$Tree.set_column_title(0, "Name")
|
||||
$Tree.set_column_title(1, "Language")
|
||||
$Tree.set_column_titles_visible(true)
|
||||
for v in vs:
|
||||
var child = $Tree.create_item(root)
|
||||
var child: TreeItem = $Tree.create_item(root)
|
||||
child.set_text(0, v["name"])
|
||||
child.set_metadata(0, v["id"])
|
||||
child.set_text(1, v["language"])
|
||||
$Log.text += "%d voices available\n" % [vs.size()]
|
||||
$Log.text += "%d voices available.\n" % [vs.size()]
|
||||
$Log.text += "=======\n"
|
||||
|
||||
# add callbacks
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_STARTED, Callable(self, "_on_utterance_start"))
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_ENDED, Callable(self, "_on_utterance_end"))
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_CANCELED, Callable(self, "_on_utterance_error"))
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_BOUNDARY, Callable(self, "_on_utterance_boundary"))
|
||||
set_process(true)
|
||||
# Ensure the first voice added to the list is preselected.
|
||||
$Tree.get_root().get_child(0).select(0)
|
||||
|
||||
func _process(_delta):
|
||||
# Add callbacks.
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_STARTED, _on_utterance_start)
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_ENDED, _on_utterance_end)
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_CANCELED, _on_utterance_error)
|
||||
DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_BOUNDARY, _on_utterance_boundary)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
$ButtonPause.button_pressed = DisplayServer.tts_is_paused()
|
||||
if DisplayServer.tts_is_speaking():
|
||||
$ColorRect.color = Color(1, 0, 0)
|
||||
$ColorRect.color = Color(0.9, 0.3, 0.1)
|
||||
else:
|
||||
$ColorRect.color = Color(1, 1, 1)
|
||||
|
||||
func _on_utterance_boundary(pos, ut_id):
|
||||
|
||||
func _on_utterance_boundary(pos: int, ut_id: int) -> void:
|
||||
$RichTextLabel.text = "[bgcolor=yellow][color=black]" + ut_map[ut_id].substr(0, pos) + "[/color][/bgcolor]" + ut_map[ut_id].substr(pos, -1)
|
||||
|
||||
func _on_utterance_start(ut_id):
|
||||
$Log.text += "utterance %d started\n" % [ut_id]
|
||||
|
||||
func _on_utterance_end(ut_id):
|
||||
func _on_utterance_start(ut_id: int) -> void:
|
||||
$Log.text += "Utterance %d started.\n" % [ut_id]
|
||||
|
||||
|
||||
func _on_utterance_end(ut_id: int) -> void:
|
||||
$RichTextLabel.text = "[bgcolor=yellow][color=black]" + ut_map[ut_id] + "[/color][/bgcolor]"
|
||||
$Log.text += "utterance %d ended\n" % [ut_id]
|
||||
$Log.text += "Utterance %d ended.\n" % [ut_id]
|
||||
ut_map.erase(ut_id)
|
||||
|
||||
func _on_utterance_error(ut_id):
|
||||
|
||||
func _on_utterance_error(ut_id: int) -> void:
|
||||
$RichTextLabel.text = ""
|
||||
$Log.text += "utterance %d canceled/failed\n" % [ut_id]
|
||||
$Log.text += "Utterance %d canceled/failed.\n" % [ut_id]
|
||||
ut_map.erase(ut_id)
|
||||
|
||||
func _on_ButtonStop_pressed():
|
||||
|
||||
func _on_button_stop_pressed() -> void:
|
||||
DisplayServer.tts_stop()
|
||||
|
||||
func _on_ButtonPause_pressed():
|
||||
|
||||
func _on_button_pause_pressed() -> void:
|
||||
if $ButtonPause.pressed:
|
||||
DisplayServer.tts_pause()
|
||||
else:
|
||||
DisplayServer.tts_resume()
|
||||
|
||||
func _on_ButtonSpeak_pressed():
|
||||
|
||||
func _on_button_speak_pressed() -> void:
|
||||
if $Tree.get_selected():
|
||||
$Log.text += "utterance %d queried\n" % [id]
|
||||
$Log.text += "Utterance %d queried.\n" % [id]
|
||||
ut_map[id] = $Utterance.text
|
||||
DisplayServer.tts_speak($Utterance.text, $Tree.get_selected().get_metadata(0), $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id, false)
|
||||
id += 1
|
||||
else:
|
||||
OS.alert("No voice selected.\nSelect a voice in the list, then try using Speak again.")
|
||||
|
||||
func _on_ButtonIntSpeak_pressed():
|
||||
|
||||
func _on_button_int_speak_pressed() -> void:
|
||||
if $Tree.get_selected():
|
||||
$Log.text += "utterance %d interrupt\n" % [id]
|
||||
$Log.text += "Utterance %d interrupted.\n" % [id]
|
||||
ut_map[id] = $Utterance.text
|
||||
DisplayServer.tts_speak($Utterance.text, $Tree.get_selected().get_metadata(0), $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id, true)
|
||||
id += 1
|
||||
else:
|
||||
OS.alert("No voice selected.\nSelect a voice in the list, then try using Interrupt again.")
|
||||
|
||||
func _on_ButtonClearLog_pressed():
|
||||
|
||||
func _on_button_clear_log_pressed() -> void:
|
||||
$Log.text = ""
|
||||
|
||||
func _on_HSliderRate_value_changed(value):
|
||||
$HSliderRate/Value.text = "%.2fx" % [value]
|
||||
|
||||
func _on_HSliderPitch_value_changed(value):
|
||||
$HSliderPitch/Value.text = "%.2fx" % [value]
|
||||
func _on_HSliderRate_value_changed(value: float) -> void:
|
||||
$HSliderRate/Value.text = "%.2fx" % value
|
||||
|
||||
func _on_HSliderVolume_value_changed(value):
|
||||
$HSliderVolume/Value.text = "%d%%" % [value]
|
||||
|
||||
func _on_Button_pressed():
|
||||
var vc
|
||||
func _on_HSliderPitch_value_changed(value: float) -> void:
|
||||
$HSliderPitch/Value.text = "%.2fx" % value
|
||||
|
||||
|
||||
func _on_HSliderVolume_value_changed(value: float) -> void:
|
||||
$HSliderVolume/Value.text = "%d%%" % value
|
||||
|
||||
|
||||
func _on_Button_pressed() -> void:
|
||||
var vc: PackedStringArray
|
||||
#demo - en
|
||||
vc = DisplayServer.tts_get_voices_for_language("en")
|
||||
if !vc.is_empty():
|
||||
if not vc.is_empty():
|
||||
ut_map[id] = "Beware the Jabberwock, my son!"
|
||||
ut_map[id + 1] = "The jaws that bite, the claws that catch!"
|
||||
DisplayServer.tts_speak("Beware the Jabberwock, my son!", vc[0], 50, 1, 1, id)
|
||||
DisplayServer.tts_speak("The jaws that bite, the claws that catch!", vc[0], 50, 1, 1, id + 1)
|
||||
DisplayServer.tts_speak("Beware the Jabberwock, my son!", vc[0], $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id)
|
||||
DisplayServer.tts_speak("The jaws that bite, the claws that catch!", vc[0], $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id + 1)
|
||||
id += 2
|
||||
#demo - es
|
||||
vc = DisplayServer.tts_get_voices_for_language("es")
|
||||
if !vc.is_empty():
|
||||
if not vc.is_empty():
|
||||
ut_map[id] = "¡Cuidado, hijo, con el Fablistanón!"
|
||||
ut_map[id + 1] = "¡Con sus dientes y garras, muerde, apresa!"
|
||||
DisplayServer.tts_speak("¡Cuidado, hijo, con el Fablistanón!", vc[0], 50, 1, 1, id)
|
||||
DisplayServer.tts_speak("¡Con sus dientes y garras, muerde, apresa!", vc[0], 50, 1, 1, id + 1)
|
||||
DisplayServer.tts_speak("¡Cuidado, hijo, con el Fablistanón!", vc[0], $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id)
|
||||
DisplayServer.tts_speak("¡Con sus dientes y garras, muerde, apresa!", vc[0], $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id + 1)
|
||||
id += 2
|
||||
#demo - ru
|
||||
vc = DisplayServer.tts_get_voices_for_language("ru")
|
||||
if !vc.is_empty():
|
||||
if not vc.is_empty():
|
||||
ut_map[id] = "О, бойся Бармаглота, сын!"
|
||||
ut_map[id + 1] = "Он так свирлеп и дик!"
|
||||
DisplayServer.tts_speak("О, бойся Бармаглота, сын!", vc[0], 50, 1, 1, id)
|
||||
DisplayServer.tts_speak("Он так свирлеп и дик!", vc[0], 50, 1, 1, id + 1)
|
||||
DisplayServer.tts_speak("О, бойся Бармаглота, сын!", vc[0], $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id)
|
||||
DisplayServer.tts_speak("Он так свирлеп и дик!", vc[0], $HSliderVolume.value, $HSliderPitch.value, $HSliderRate.value, id + 1)
|
||||
id += 2
|
||||
|
||||
func _on_LineEditFilterName_text_changed(_new_text):
|
||||
|
||||
func _on_LineEditFilterName_text_changed(_new_text: String) -> void:
|
||||
$Tree.clear()
|
||||
var root = $Tree.create_item()
|
||||
var root: TreeItem = $Tree.create_item()
|
||||
for v in vs:
|
||||
if ($LineEditFilterName.text.is_empty() || $LineEditFilterName.text.to_lower() in v["name"].to_lower()) && ($LineEditFilterLang.text.is_empty() || $LineEditFilterLang.text.to_lower() in v["language"].to_lower()):
|
||||
var child = $Tree.create_item(root)
|
||||
if (
|
||||
$LineEditFilterName.text.is_empty() or $LineEditFilterName.text.to_lower() in v["name"].to_lower()
|
||||
) and (
|
||||
$LineEditFilterLang.text.is_empty() or $LineEditFilterLang.text.to_lower() in v["language"].to_lower()
|
||||
):
|
||||
var child: TreeItem = $Tree.create_item(root)
|
||||
child.set_text(0, v["name"])
|
||||
child.set_metadata(0, v["id"])
|
||||
child.set_text(1, v["language"])
|
||||
|
||||
Reference in New Issue
Block a user