1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
extends Node
signal song_paused(paused : bool)
signal song_started(track_id : int)
## Only triggers when song naturally ends
signal song_finished(track_id : int)
signal song_scrubbed(percentage : float, seconds : float)
# REQUIREMENTS
# * SS-67
@export_dir var music_directory : String
@export var first_song_name : String
@export var _audio_stream_player : AudioStreamPlayer
var _selected_track := 0
var _available_songs : Array[SongInfo] = []
var _auto_play_next_song : bool = true
var playlist: Array[int] = []
var playlist_index:int = 0
var preferred_playlist_len: int = 7
var last_played: int = -1
## True if music player should be visible.
## Used to keep keep consistency between scene changes
var is_music_player_visible : bool = true
var _has_startup_happened : bool = false
func get_all_song_names() -> PackedStringArray:
var songNames : PackedStringArray = []
for si : SongInfo in _available_songs:
songNames.append(si.song_name)
return songNames
func get_all_song_paths() -> PackedStringArray:
var songPaths : PackedStringArray = []
for si : SongInfo in _available_songs:
songPaths.append(si.song_path)
return songPaths
func get_current_song_index() -> int:
return _selected_track
func get_current_song_name() -> String:
return _available_songs[_selected_track].song_name
func scrub_song_by_percentage(percentage: float) -> void:
var percentInSeconds : float = (percentage / 100.0) * _audio_stream_player.stream.get_length()
_audio_stream_player.play(percentInSeconds)
song_scrubbed.emit(percentage, percentInSeconds)
func get_current_song_progress_percentage() -> float:
return 100 * (_audio_stream_player.get_playback_position() / _audio_stream_player.stream.get_length())
func is_paused() -> bool:
return _audio_stream_player.stream_paused
func set_paused(paused : bool) -> void:
_audio_stream_player.stream_paused = paused
song_paused.emit(paused)
func toggle_play_pause() -> void:
set_paused(not is_paused())
func start_current_song() -> void:
_audio_stream_player.stream = _available_songs[_selected_track].song_stream
_audio_stream_player.play()
song_started.emit(_selected_track)
# REQUIREMENTS
# * SS-70
func start_song_by_index(id: int) -> void:
_selected_track = id
start_current_song()
# REQUIREMENTS
# * SS-69
func select_next_song() -> void:
#_selected_track = (_selected_track + 1) % len(_available_songs)
if playlist_index >= preferred_playlist_len or playlist_index >= len(playlist):
generate_playlist()
playlist_index = 0
_selected_track = playlist[playlist_index]
playlist_index += 1
last_played = playlist_index
start_current_song()
func select_previous_song() -> void:
_selected_track = (len(_available_songs) - 1) if (_selected_track == 0) else (_selected_track - 1)
start_current_song()
func setup_compat_song(file_name) -> void:
var song = SongInfo.new()
var stream = SoundSingleton.get_song(file_name)
if stream == null:
push_error("Audio Stream for compat song %s was null" % file_name)
return
var metadata = MusicMetadata.new()
metadata.set_from_stream(stream)
var title = metadata.title
if title == "":
#use the file name without the extension if there's no metadata
title = file_name.split(".")[0]
song.init_stream(file_name,title,stream)
_available_songs.append(song)
func add_compat_songs() -> void:
for file_name : String in SoundSingleton.song_list:
setup_compat_song(file_name)
func add_ootb_music() -> void:
var dir := DirAccess.open(music_directory)
for fname : String in dir.get_files():
if fname.ends_with(".import"):
fname = fname.get_basename()
if fname.get_basename() == first_song_name:
_selected_track = _available_songs.size()
var song = SongInfo.new()
song.init_file_path(music_directory, fname)
_available_songs.append(song)
func generate_playlist() -> void:
var song_names = MusicConductor.get_all_song_paths()
var possible_indices = range(len(song_names)-1)
var title_index = song_names.find(SoundSingleton.title_theme)
possible_indices.remove_at(title_index)
var actual_playlist_len = min(preferred_playlist_len,len(possible_indices))
#if the playlist size is too large or small, make it the same size as what we
#need to support
if len(playlist) != actual_playlist_len:
playlist.resize(actual_playlist_len)
playlist.fill(0)
#The song we just played can be in the playlist, just not the first one
if last_played != -1:
possible_indices.remove_at(last_played)
#essentially shuffle-bag randomness, picking from a list of song indices
for i in range(actual_playlist_len):
var ind = randi_range(0,len(possible_indices)-1)
#add back the last song we just played as an option
if i==2:
possible_indices.append(last_played)
playlist[i] = possible_indices[ind]
possible_indices.remove_at(ind)
# REQUIREMENTS
# * SND-2, SND-3
func _ready() -> void:
add_ootb_music()
#don't start the current song for compat mode, do that from
#GameStart so we can wait until the music is loaded
func set_startup_music(play : bool) -> void:
if not _has_startup_happened:
_has_startup_happened = true
set_paused(not play)
func _on_audio_stream_player_finished() -> void:
song_finished.emit(_selected_track)
if _auto_play_next_song:
select_next_song()
start_current_song()
|