Short songs (#30): a quiz settings switch and a Seconds per song row (30 by default, 10-120 in steps of 5), saved with the quiz settings. Only part of each quiz song plays, from a random point 10-60% into it, picked once per song so restart and repeat play the same part. The progress bar, time and seeking stay within the part, a hidden beginning counts inside it, and its end counts as the song ending (repeat, stop or go to the next song as set). Random songs (#51): the low-played switch in Settings is now a Normal / Low played / Classic choice, stored as Settings.random_mode and not saved. fetch_random_song(mode) picks /music/rand, /music/rand/low or /music/rand/classic. Closes #30 Closes #51 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0fc3be79e1
commit
a735c515bf
20 files changed
+1325
-327
No files matched your search
+81
-36
@@ -5,6 +5,11 @@ extends Control
|
||||
##
|
||||
## The buttons grow a little under the mouse and the play button pops when it switches
|
||||
## between play and pause, see [ButtonBounce].
|
||||
##
|
||||
## With Short songs on ([member Settings.short_songs]) only a part of each quiz song plays:
|
||||
## [member Settings.short_song_seconds] from a random point between
|
||||
## [constant SHORT_START_MIN] and [constant SHORT_START_MAX] of the song. The progress bar,
|
||||
## the time and seeking all stay within that part, and its end counts as the song ending.
|
||||
|
||||
signal update_song_list
|
||||
signal play_next_song
|
||||
@@ -16,6 +21,15 @@ var playback_position: float
|
||||
var stream: AudioStream
|
||||
var song_finished: bool = false
|
||||
|
||||
## Where in the song the part played with Short songs may start, as a share of its length.
|
||||
## Many songs have a quiet intro, so the start is never right at the beginning.
|
||||
const SHORT_START_MIN: float = 0.1
|
||||
const SHORT_START_MAX: float = 0.6
|
||||
|
||||
# The part of the song that plays: its start in seconds, and its length (0: to the end)
|
||||
var _clip_start: float = 0.0
|
||||
var _clip_length: float = 0.0
|
||||
|
||||
@onready var play_button: Button = %PlayButton
|
||||
@onready var restart_button: Button = %RestartButton
|
||||
@onready var audio_player: AudioStreamPlayer = $AudioStreamPlayer
|
||||
@@ -38,14 +52,46 @@ func _ready() -> void:
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if audio_player.has_stream_playback() && !is_changing && !audio_player.stream_paused:
|
||||
if !Settings.hide_beginning \
|
||||
|| audio_player.get_playback_position() > Settings.hide_for_seconds:
|
||||
progress_slider.value = audio_player.get_playback_position()
|
||||
if stream != null:
|
||||
music_time_label.text = format_text(progress_slider.value, stream.get_length())
|
||||
else:
|
||||
music_time_label.text = format_text(progress_slider.value, 0.0)
|
||||
if audio_player.has_stream_playback() && !is_changing && !audio_player.stream_paused \
|
||||
&& audio_player.playing:
|
||||
var position: float = position_in_part()
|
||||
if _clip_length > 0.0 && position >= _clip_length:
|
||||
# The short part has played, which counts as the song ending
|
||||
audio_player.stop()
|
||||
progress_slider.value = _clip_length
|
||||
_on_finished()
|
||||
return
|
||||
if !Settings.hide_beginning || position > Settings.hide_for_seconds:
|
||||
progress_slider.value = position
|
||||
music_time_label.text = format_text(progress_slider.value, part_length())
|
||||
|
||||
|
||||
## How long the part of the song that plays is: the short part, or the whole song.
|
||||
func part_length() -> float:
|
||||
if _clip_length > 0.0:
|
||||
return _clip_length
|
||||
return stream.get_length() if stream != null else 0.0
|
||||
|
||||
|
||||
## How far into the part that plays the song is, in seconds.
|
||||
func position_in_part() -> float:
|
||||
return audio_player.get_playback_position() - _clip_start
|
||||
|
||||
|
||||
## Plays only part of [param song_object]'s song when Short songs is on and the song is
|
||||
## longer than the part. The part's start is picked once per song.
|
||||
func choose_part(song_object: SongObject, song: AudioStream) -> void:
|
||||
_clip_start = 0.0
|
||||
_clip_length = 0.0
|
||||
var length: float = song.get_length()
|
||||
if !Settings.short_songs || song_object == null || length <= Settings.short_song_seconds:
|
||||
return
|
||||
if song_object.short_start < 0.0:
|
||||
var latest: float = minf(length * SHORT_START_MAX, length - Settings.short_song_seconds)
|
||||
var earliest: float = minf(length * SHORT_START_MIN, latest)
|
||||
song_object.short_start = randf_range(earliest, latest)
|
||||
_clip_start = minf(song_object.short_start, length - Settings.short_song_seconds)
|
||||
_clip_length = Settings.short_song_seconds
|
||||
|
||||
|
||||
## Whether the play button shows pause, because a song is playing.
|
||||
@@ -61,6 +107,14 @@ func _show_playing(playing: bool) -> void:
|
||||
ButtonBounce.pop(play_button)
|
||||
|
||||
|
||||
# The progress bar and time show the length of the part that plays
|
||||
func _show_length() -> void:
|
||||
var length: float = part_length()
|
||||
progress_slider.max_value = round(length)
|
||||
progress_slider.tick_count = 0 if Settings.hide_ticks else round(length / 60)
|
||||
music_time_label.text = format_text(progress_slider.value, length)
|
||||
|
||||
|
||||
func format_time(time: float) -> String:
|
||||
var total_seconds: int = roundi(time)
|
||||
@warning_ignore("integer_division")
|
||||
@@ -87,11 +141,7 @@ func play_or_pause() -> void:
|
||||
song_finished = false
|
||||
audio_player.seek(playback_position)
|
||||
GameLog.debug("continue")
|
||||
progress_slider.max_value = round(stream.get_length())
|
||||
if Settings.hide_ticks:
|
||||
progress_slider.tick_count = 0
|
||||
else:
|
||||
progress_slider.tick_count = round(stream.get_length() / 60)
|
||||
_show_length()
|
||||
else:
|
||||
pause()
|
||||
|
||||
@@ -102,15 +152,15 @@ func pause() -> void:
|
||||
_show_playing(false)
|
||||
|
||||
|
||||
## Plays the song, or its short part, from the beginning.
|
||||
func restart() -> void:
|
||||
audio_player.stop()
|
||||
audio_player.stream_paused = false
|
||||
song_finished = false
|
||||
progress_slider.value = 0
|
||||
playback_position = audio_player.get_playback_position()
|
||||
audio_player.seek(playback_position)
|
||||
playback_position = _clip_start
|
||||
_show_playing(true)
|
||||
audio_player.play()
|
||||
audio_player.play(_clip_start)
|
||||
|
||||
|
||||
func _on_finished() -> void:
|
||||
@@ -127,16 +177,18 @@ func _on_drag_started() -> void:
|
||||
|
||||
|
||||
func _on_drag_ended(_changed: bool) -> void:
|
||||
audio_player.seek(progress_slider.value)
|
||||
playback_position = progress_slider.value
|
||||
audio_player.seek(_clip_start + progress_slider.value)
|
||||
playback_position = _clip_start + progress_slider.value
|
||||
is_changing = false
|
||||
|
||||
|
||||
## Jumps [param new_position] seconds forward, or back when negative, staying within the
|
||||
## part that plays.
|
||||
func seek(new_position: float) -> void:
|
||||
progress_slider.value += new_position
|
||||
is_changing = true
|
||||
audio_player.seek(progress_slider.value)
|
||||
playback_position = progress_slider.value
|
||||
audio_player.seek(_clip_start + progress_slider.value)
|
||||
playback_position = _clip_start + progress_slider.value
|
||||
is_changing = false
|
||||
|
||||
|
||||
@@ -173,6 +225,9 @@ func play_sound(sound_name: AudioStream) -> void:
|
||||
|
||||
|
||||
func play_song(song: AudioStream) -> void:
|
||||
# Sounds such as the winning song always play whole
|
||||
_clip_start = 0.0
|
||||
_clip_length = 0.0
|
||||
audio_player.stream = song
|
||||
audio_player.play()
|
||||
sound_player.stop()
|
||||
@@ -180,12 +235,7 @@ func play_song(song: AudioStream) -> void:
|
||||
_show_playing(true)
|
||||
stream = audio_player.stream
|
||||
progress_slider.value = 0
|
||||
progress_slider.max_value = round(stream.get_length())
|
||||
if Settings.hide_ticks:
|
||||
progress_slider.tick_count = 0
|
||||
else:
|
||||
progress_slider.tick_count = round(stream.get_length() / 60)
|
||||
music_time_label.text = format_text(progress_slider.value, stream.get_length())
|
||||
_show_length()
|
||||
|
||||
|
||||
## Puts song [param song_object_no] in the player without playing it, e.g. the song that
|
||||
@@ -196,12 +246,11 @@ func load_song_object(song_object_no: int) -> void:
|
||||
return
|
||||
audio_player.stream = song
|
||||
stream = song
|
||||
choose_part(Playlist.song_object_array[song_object_no], song)
|
||||
song_finished = true
|
||||
_show_playing(false)
|
||||
progress_slider.value = 0
|
||||
progress_slider.max_value = round(stream.get_length())
|
||||
progress_slider.tick_count = 0 if Settings.hide_ticks else round(stream.get_length() / 60)
|
||||
music_time_label.text = format_text(progress_slider.value, stream.get_length())
|
||||
_show_length()
|
||||
|
||||
|
||||
func play_song_object(song_object_no: int) -> void:
|
||||
@@ -214,20 +263,16 @@ func play_song_object(song_object_no: int) -> void:
|
||||
audio_player.stop()
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
audio_player.stream = song
|
||||
choose_part(Playlist.song_object_array[song_object_no], song)
|
||||
sound_player.stop()
|
||||
audio_player.play()
|
||||
audio_player.play(_clip_start)
|
||||
song_finished = false
|
||||
_show_playing(true)
|
||||
# A new song makes the play button pop, also when it was already playing
|
||||
ButtonBounce.pop(play_button)
|
||||
stream = audio_player.stream
|
||||
progress_slider.value = 0
|
||||
progress_slider.max_value = round(stream.get_length())
|
||||
if Settings.hide_ticks:
|
||||
progress_slider.tick_count = 0
|
||||
else:
|
||||
progress_slider.tick_count = round(stream.get_length() / 60)
|
||||
music_time_label.text = format_text(progress_slider.value, stream.get_length())
|
||||
_show_length()
|
||||
Playlist.set_currently_playing_song(song_object_no)
|
||||
Playlist.unset_is_playing()
|
||||
if !Settings.hide_next_track:
|
||||
|
||||
Reference in new issue
Block a user