Help fixing this code dealing with playing custom songs from a folder?

Godot Version

4.2.2

Question

Hey guys! I’m working on a Jukebox system where people can add their songs into the game so they can listen to their favorite tracks dynamically. I’ve run into the problem where when I try to load a track it returns <Object#null>.
Any ways I could make this work? Here’s the code (It’s an autoload).

extends AudioStreamPlayer

#Music being loaded on the titlescreen

const level_music = preload("res://Music/TheBouncerIsGone.ogg")
var currentLevelMusic
var songsInPlaylist 

func _play_music(music: AudioStream, volume = 0.0):
	if stream == music:
		return
		
	stream = music
	
	play()
	
#Music being loaded in the gameplay
func _get_songs():

	songsInPlaylist = DirAccess.get_files_at("user://Jukebox")
	print(songsInPlaylist)
	
#Playing the title screen song
func play_music_level():
	_play_music(level_music)

#Playing the songs during gameplay
func play_music_gameplay():
	var transformPackedIntoArray = Array(songsInPlaylist)
	currentLevelMusic = "user://Jukebox/" + str(transformPackedIntoArray.pick_random())
	print(load(currentLevelMusic))
	_play_music(load(currentLevelMusic))


func _on_finished():
	play_music_gameplay()

My guess is the fault is somewhere here, i am not sure why you are converting Packed into Array instead of just using an Array.

image

After testing a bit, i think the fault is in the file path, where did u save these songs?

image

Also in my project to open the user folder i used globalize_path, not sure why this is required but u should try it


This is where is saved the game’s music files, I have to convert the PackedStringArray into an Array to make the .pick_random() function work!

I suggest that you try globalize_path(), because that’s what worked for me

How so? I’m sorry, I’m confused here where I should put that into the mix.

Like the above image

So instead of “load(currentLevelMusic)”
You would use “load(ProjectSettings.globalize_path(currentLevelMusic))”


or maybe better to do it when declaring the variable

still returns <Object#null>.

Yeah im getting the same thing.

It might be that you cannot load resources from paths other than “res://…”

Could you show what you’re storing in the folder

Which folder?

The “Jukebox” folder
I’m guessing it’s the .mp3/.wav files for the music

image
it used to be there only .oggs but then i added mp3s to see if it worked, no dice.

I have tried loading a folder, a .mp3, and my settings.init file, all have returned null


image
The null is returned when trying to access a file in the user folder
The AudioStream resource is when trying to access a file in the res:// folder

– I think you should store your audio files in the res:// folder

can players acess the res:// folder in any way?

I believe each player has their own copy of the game world and game files
So yes each player has access to their own copy of the res:// folder

In my project i have a script called Preloads.gd

class_name Preloads

const ICONS = [
	preload("res://icon.svg"), # Block
	preload("res://Assets/DEADDROPS.png"), # Beer
]

const ITEMS = [
	preload("res://Scenes/block.tscn"), # Block
	preload("res://Scenes/beer.tscn"), # Beer
]
...

And when i need to instantiate something i put a preload reference to it in this script and instantiate on that reference


So to make a jukebox system i suggest having an array of preloaded audio files

const SONGS = [
	preload("res://Assets/Songs/song1.mp3"), # Replace these with actual references
	preload("res://Assets/Songs/song2.ogg"),
]

You could store this directly in the jukebox script, im not sure what your system is like right now.
And then to play the songs you would do something like this:

@onready var JukeboxAudioPlayer : AudioStreamPlayer = $[path_here]
...
var song = SONGS.pick_random()
JukeboxAudioPlayer.stream = song
JukeboxAudioPlayer.play()

However since your game is multiplayer, you can’t use a MultiplayerSynchronizer node to sync a .stream of an AudioStreamPlayer node
So the best thing to do would be an rpc function that calls the .play() method on all clients/players

In my project i have a function like this:

@rpc("any_peer", "call_local")
func play_sound(sound : NodePath):
	var sound_node = get_node_or_null(sound)
	if sound_node: sound_node.play()

And do this to call it:

GlobalRPC.play_sound.rpc(footstep_sound.get_path()) # GlobalRPC is an autoloaded script where i hold all my rpc functions

my game isnt multiplayer lol, but thanks for the consideration anyways!

Oh well lmao, might help someone else in the future. So your real issue was putting files in a folder other than res://, but yeah the res folder is where you want to keep all your files and such, as that is the whole point of it

You can load ogg files with AudioStreamOggVorbis.load_from_file(). Try this

func play_music_gameplay():
	var transformPackedIntoArray = Array(songsInPlaylist)
	currentLevelMusic = "user://Jukebox/" + str(transformPackedIntoArray.pick_random())
	var ogg := AudioStreamOggVorbis.load_from_file(currentLevelMusic)
	_play_music(ogg)

load is only for resources, the fact that it works with “res://” paths is because they are imported as resources.

1 Like


This actually works, also

I just realised why you wanted to put your files in the user:// folder, to let players put their own audio files, oops i may have misinformed you quite a bit, sorry