Load an audio file from outside res://

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By mymokol

Hello!

I’ve been trying to create a web app for myself to be able to play and change the volume of tracks live in chrome, so that i can share the tab audio in jitsi.

It’s been going well up until this point.

i tried to make it so that the user can choose a custom file from their own computer, so i made a FileDialog and some code that grabbed the selected file and put it in the AudioStreamPlayer’s “stream” property using load(path). when i went into the FileDialog and chose a file that was inside the res:// directory, it worked just fine, and my debug tool told me that the AudioStreamPlayer’s “stream” property looked something like [AudioStreamSample:2442] but when i went outside the res:// directory and chose a file from my documents, the debug tool said [Object:Null].

So my question is: Is there a way to make Godot load a file that isn’t contained in the res:// directory? If so, what do i need to do?

Thanks!

EDIT: I tried to use the File class, so now the path of the chosen file gets opened by a new File in godot, it gets passed to the AudioStreamPlayer and then, instead of $AudioStreamPlayer.stream = load(current.file), it’s just $AudioStreamPlayer.stream = current.file. It still shows the “stream” property as [Object:Null] though.

On web you’re naturally blocked from accessing user’s local filesystem, with the exception of user uploading files. I haven’t found a way to do this directly from godot, but you might be able to have a javascript program which gets files via <input type="file"> and feeds it to godot using Javascript singleton.

aXu_AP | 2021-10-31 16:20

user uploading files is exactly what i’m after…

mymokol | 2021-10-31 20:38

I’m having the exact same issue as mymokol. Audio file loading works fine from res:// but doesn’t seem to load from user://
though video files load fine from res:// as well as user://

Harish | 2022-03-05 01:54

:bust_in_silhouette: Reply From: r.bailey

The res:// directory is the directory that is built into the project you can’t add anything to it that isn’t there when you build the application, since it is read-only by design.(at least how it is currently, the engine is open-source of course and you can change that if you like). What you are looking for is the user:// directory, which is generally a write-able directory on all operating system. I’ll link this data-paths Godot information.

Data-Paths – Godot Engine(stable)

Yes, I know that, what i’m asking is how to load the files that aren’t stored in the res:// directory, but instead are stored locally in the user’s drive, or alternatively how to get the files chosen by the user into the user:// directory, and whether (audio) files can be loaded from there as they would from the res:// directory.

EDIT: I managed to make the program successfully copy the file into the user:// directory, but it still doesn’t play. It doesn’t throw me any error or anything, it just doesn’t play whatsoever, even though with files from the res:// dir it played just fine. When i tell it to open the file through OS.shell_open, it does so, and everything works beautifully, but the audiostreamplayer in the program stays silent. Can load() not be used on files in the user:// directory? How would one go about loading files from there?

mymokol | 2021-11-01 19:39

Load should work everywhere, as long you are attempting to open a path that exists and you have read permissions on your system where you are trying to load from. I am no expert of the Audiostreamplayer classes, but it might just be how you are using that class.

You need to load the sounds into the stream then play them once they are loaded. I believe that you can only have one file loaded per AudioStreamPlayer instance, if you change the (music/sound)stream you would have to load another file into the stream. All you have to do once the file is loaded is call the play function on the Audiostreamplayer instance.

You can just created a global class called sounds. Then just added all the sounds as there own AudioStreamPlayer instances to just load them once and add them to sound scene. And make a function to stop and start sounds that exist based on the name. I will post a generalized example of what I mean.

 global class sounds.gd 
 
 var SoundFileNames: Array 

func _init():

func AddSound(var filename)
   SoundFileNames.append(filename)
   var sound = AudioStreamPlayer.new()
   sound.stream = load("user://" + path + filename);
   sound.name = filename
   add_child(sound)

func SelectSound(sound, state):
   var size = SoundFileNames.size()
	for n in range(size):
		if sound == SoundFileNames[n]:
                    var soundNode = get_node(sound))
			if state == 1:
				soundNode.play()
			else:
				soundNode.stop()


func PlaySound(sound):
	SelectSound(sound, 1)

func StopSound(sound):
	SelectSound(sound, 0)

From outside of this class you can just call sounds.add(), and .play or .stop for playing or stopping. .

Disclaimers:
After looking at this as well, keep in mind that this doesn’t check for duplicate names for filenames, so if that would be an issue you would have to handle the adding and playing of files to handle that.

Also I am not certain if this is the correct way to do this with creating new instances for the audiostreamplayer classes for each sound… this might get bad if you are doing ten-of-thousands of sounds/songs, as I have not tested doing this with beyond like 100 sounds. Hope this leads you to a solution.

r.bailey | 2021-11-03 16:35