From ce325c16c0f5d82ed51abd8bd13928a7bc609ba5 Mon Sep 17 00:00:00 2001 From: ClarkeCode <33846391+ClarkeCode@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:14:19 -0500 Subject: Add Music Player (#49) * Adding MusicConductor * Added selectable songs and player visibility toggle * Refinements to the music system * SongInfo compatability with various audio formats * Moved UI reqs to editor description; flipped conditions to exclude music .import files * Made selection of the first music track extension-agnostic * Fixed visual bug with play/pause button when interacting with progress slider --- game/src/GameMenu.tscn | 9 ++++ game/src/MusicConductor/MusicConductor.gd | 68 ++++++++++++++++++++++++++ game/src/MusicConductor/MusicConductor.tscn | 13 +++++ game/src/MusicConductor/MusicUIController.gd | 66 +++++++++++++++++++++++++ game/src/MusicConductor/MusicUIController.tscn | 64 ++++++++++++++++++++++++ game/src/MusicConductor/SongInfo.gd | 11 +++++ 6 files changed, 231 insertions(+) create mode 100644 game/src/MusicConductor/MusicConductor.gd create mode 100644 game/src/MusicConductor/MusicConductor.tscn create mode 100644 game/src/MusicConductor/MusicUIController.gd create mode 100644 game/src/MusicConductor/MusicUIController.tscn create mode 100644 game/src/MusicConductor/SongInfo.gd (limited to 'game/src') diff --git a/game/src/GameMenu.tscn b/game/src/GameMenu.tscn index 9442d1a..2c223ec 100644 --- a/game/src/GameMenu.tscn +++ b/game/src/GameMenu.tscn @@ -6,6 +6,7 @@ [ext_resource type="PackedScene" uid="uid://b7oncobnacxmt" path="res://src/LocaleButton.tscn" id="4_jno35"] [ext_resource type="PackedScene" uid="uid://c8knthxkwj1uj" path="res://src/Credits/Credits.tscn" id="4_n0hoo"] [ext_resource type="PackedScene" uid="uid://crhkgngfnxf4y" path="res://src/LobbyMenu/LobbyMenu.tscn" id="4_nofk1"] +[ext_resource type="PackedScene" uid="uid://dmnqyvl3qfq2e" path="res://src/MusicConductor/MusicUIController.tscn" id="6_lts1m"] [node name="GameMenu" type="Control"] layout_mode = 3 @@ -20,6 +21,14 @@ script = ExtResource("1_cafwe") layout_mode = 1 metadata/_edit_vertical_guides_ = [251.0, 269.0, 504.0, 523.0, 15.0, 759.0, 777.0] +[node name="MusicUIController" parent="." instance=ExtResource("6_lts1m")] +layout_mode = 1 +anchor_left = 0.843 +anchor_right = 0.953 +offset_left = -0.0400391 +offset_right = 0.159912 +metadata/_edit_use_anchors_ = true + [node name="OptionsMenu" parent="." instance=ExtResource("3_111lv")] visible = false layout_mode = 1 diff --git a/game/src/MusicConductor/MusicConductor.gd b/game/src/MusicConductor/MusicConductor.gd new file mode 100644 index 0000000..a463d6d --- /dev/null +++ b/game/src/MusicConductor/MusicConductor.gd @@ -0,0 +1,68 @@ +extends Node + +# SS-67 +@export_dir var musicDir : String +@export_file var firstSongName : String + +var selectedTrack = 0 +var availableSongs : Array[SongInfo] = [] +var autoPlayNextSong : bool = true + +func getAllSongNames() -> Array[String]: + var songNames : Array[String] = [] + for si in availableSongs: + songNames.append(si.readableName) + return songNames + +func getCurrentSongIndex() -> int: + return selectedTrack + +func getCurrentSongName() -> String: + return availableSongs[selectedTrack].readableName + +func scrubSongByPercentage(percentage: float) -> void: + var percentInSeconds : float = (percentage / 100.0) * $AudioStreamPlayer.stream.get_length() + $AudioStreamPlayer.play(percentInSeconds) + +func getCurrentSongProgressPercentage() -> float: + return 100 * ($AudioStreamPlayer.get_playback_position() / $AudioStreamPlayer.stream.get_length()) + +func isPaused() -> bool: + return $AudioStreamPlayer.stream_paused + +func togglePlayPause() -> void: + $AudioStreamPlayer.stream_paused = !$AudioStreamPlayer.stream_paused + +func startCurrentSong() -> void: + $AudioStreamPlayer.stream = availableSongs[selectedTrack].loadedStream + $AudioStreamPlayer.play() + +# SS-70 +func startSongByIndex(id: int) -> void: + selectedTrack = id + startCurrentSong() + +# SS-69 +func nextSong() -> void: + selectedTrack = (selectedTrack + 1) % len(availableSongs) + startCurrentSong() + +func prevSong() -> void: + selectedTrack = (len(availableSongs) - 1) if (selectedTrack == 0) else (selectedTrack - 1) + startCurrentSong() + +# Called when the node enters the scene tree for the first time. +func _ready(): + var dir = DirAccess.open(musicDir) + for fname in dir.get_files(): + if !fname.ends_with(".import"): + if fname.get_basename() == firstSongName: + selectedTrack = availableSongs.size() + availableSongs.append(SongInfo.new(musicDir, fname)) + startCurrentSong() + + +func _on_audio_stream_player_finished(): + if autoPlayNextSong: + nextSong() + startCurrentSong() diff --git a/game/src/MusicConductor/MusicConductor.tscn b/game/src/MusicConductor/MusicConductor.tscn new file mode 100644 index 0000000..69529d8 --- /dev/null +++ b/game/src/MusicConductor/MusicConductor.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://b1h31mnn8n2nu"] + +[ext_resource type="Script" path="res://src/MusicConductor/MusicConductor.gd" id="1_56t1b"] + +[node name="MusicConductor" type="Node"] +script = ExtResource("1_56t1b") +musicDir = "res://audio/music/" +firstSongName = "The_Crown" + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +bus = &"Music" + +[connection signal="finished" from="AudioStreamPlayer" to="." method="_on_audio_stream_player_finished"] diff --git a/game/src/MusicConductor/MusicUIController.gd b/game/src/MusicConductor/MusicUIController.gd new file mode 100644 index 0000000..f2c9225 --- /dev/null +++ b/game/src/MusicConductor/MusicUIController.gd @@ -0,0 +1,66 @@ +extends Control + +@export var songSelectorButton : OptionButton +@export var progressSlider : HSlider +@export var prevSongButton : Button +@export var playPauseButton : Button +@export var nextSongButton : Button +@export var widgetVisibilityButton : Button + +var isMusicPlayerVisible : bool = true +var isUserDraggingProgressSlider : bool = false + +# Called when the node enters the scene tree for the first time. +func _ready(): + for songName in MusicConductor.getAllSongNames(): + songSelectorButton.add_item(songName, songSelectorButton.item_count) + updateSongNameVisual() + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(_delta): + if !isUserDraggingProgressSlider: + progressSlider.value = MusicConductor.getCurrentSongProgressPercentage() + +func updateSongNameVisual(): + songSelectorButton.selected = MusicConductor.getCurrentSongIndex() + +func updatePlayPauseButtonVisual(): + playPauseButton.text = "||" if MusicConductor.isPaused() else ">" + +func _on_play_pause_button_pressed(): + MusicConductor.togglePlayPause() + updatePlayPauseButtonVisual() + +func _on_next_song_button_pressed(): + MusicConductor.nextSong() + updateSongNameVisual() + updatePlayPauseButtonVisual() + +func _on_previous_song_button_pressed(): + MusicConductor.prevSong() + updateSongNameVisual() + updatePlayPauseButtonVisual() + +func _on_option_button_item_selected(index): + # UIFUN-92 + MusicConductor.startSongByIndex(index) + + +func _on_progress_slider_drag_started(): + isUserDraggingProgressSlider = true + + +func _on_progress_slider_drag_ended(_value_changed): + MusicConductor.scrubSongByPercentage(progressSlider.value) + isUserDraggingProgressSlider = false + updatePlayPauseButtonVisual() + +func _on_music_ui_visibility_button_pressed(): + isMusicPlayerVisible = !isMusicPlayerVisible + widgetVisibilityButton.text = "Hide Player" if isMusicPlayerVisible else "Show Player" + songSelectorButton.visible = isMusicPlayerVisible + progressSlider.visible = isMusicPlayerVisible + prevSongButton.visible = isMusicPlayerVisible + playPauseButton.visible = isMusicPlayerVisible + nextSongButton.visible = isMusicPlayerVisible diff --git a/game/src/MusicConductor/MusicUIController.tscn b/game/src/MusicConductor/MusicUIController.tscn new file mode 100644 index 0000000..49ea355 --- /dev/null +++ b/game/src/MusicConductor/MusicUIController.tscn @@ -0,0 +1,64 @@ +[gd_scene load_steps=2 format=3 uid="uid://dmnqyvl3qfq2e"] + +[ext_resource type="Script" path="res://src/MusicConductor/MusicUIController.gd" id="1_u4qbn"] + +[node name="MusicUIController" type="Control" node_paths=PackedStringArray("songSelectorButton", "progressSlider", "prevSongButton", "playPauseButton", "nextSongButton", "widgetVisibilityButton")] +editor_description = "UI-104" +layout_mode = 3 +anchor_right = 0.11 +anchor_bottom = 0.165 +offset_right = 0.199997 +offset_bottom = 0.199997 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_u4qbn") +songSelectorButton = NodePath("VBoxContainer/SongSelectorButton") +progressSlider = NodePath("VBoxContainer/ProgressSlider") +prevSongButton = NodePath("VBoxContainer/HBoxContainer/PreviousSongButton") +playPauseButton = NodePath("VBoxContainer/HBoxContainer/PlayPauseButton") +nextSongButton = NodePath("VBoxContainer/HBoxContainer/NextSongButton") +widgetVisibilityButton = NodePath("VBoxContainer/MusicUIVisibilityButton") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="SongSelectorButton" type="OptionButton" parent="VBoxContainer"] +editor_description = "UI-107" +layout_mode = 2 + +[node name="ProgressSlider" type="HSlider" parent="VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 + +[node name="PreviousSongButton" type="Button" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Prev" + +[node name="PlayPauseButton" type="Button" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 +text = ">" + +[node name="NextSongButton" type="Button" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Next" + +[node name="MusicUIVisibilityButton" type="Button" parent="VBoxContainer"] +editor_description = "UI-106" +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 3 +text = "Hide Player" + +[connection signal="item_selected" from="VBoxContainer/SongSelectorButton" to="." method="_on_option_button_item_selected"] +[connection signal="drag_ended" from="VBoxContainer/ProgressSlider" to="." method="_on_progress_slider_drag_ended"] +[connection signal="drag_started" from="VBoxContainer/ProgressSlider" to="." method="_on_progress_slider_drag_started"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/PreviousSongButton" to="." method="_on_previous_song_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/PlayPauseButton" to="." method="_on_play_pause_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/NextSongButton" to="." method="_on_next_song_button_pressed"] +[connection signal="pressed" from="VBoxContainer/MusicUIVisibilityButton" to="." method="_on_music_ui_visibility_button_pressed"] diff --git a/game/src/MusicConductor/SongInfo.gd b/game/src/MusicConductor/SongInfo.gd new file mode 100644 index 0000000..85ef96a --- /dev/null +++ b/game/src/MusicConductor/SongInfo.gd @@ -0,0 +1,11 @@ +extends Node +class_name SongInfo + +var fullyQualifiedPath : String = "" +var readableName : String = "" +var loadedStream : Resource + +func _init(dirname:String, fname:String): + fullyQualifiedPath = dirname + fname + readableName = fname.get_basename().replace("_", " ") + loadedStream = load(fullyQualifiedPath) -- cgit v1.2.3-56-ga3b1