Skip to content

Commit eb7af9a

Browse files
committed
Add an ability to preload a sound file without decoding
PR #2006 effectively reverted the behavior of preloaded sounds to state from before #431, bringing back old memory consumption issues. For games that want to download all their assets during the loading screen, but don't want to actually decode all sounds right away because of memory consumption concerns, being able to dynamically load/unload Howler objects via events is not enough, as the application may already go out-of-memory during loading, and not doing that will cause the game to download additional assets during gameplay. This change adds a new property, "preloadAsFile", which makes GDJS request the sound asset via XHR. This puts the file into browser cache, so it can be reasonably expected to be decoded later without issuing network requests.
1 parent 70226f4 commit eb7af9a

File tree

4 files changed

+45
-15
lines changed

4 files changed

+45
-15
lines changed

Core/GDCore/Project/ResourcesManager.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ std::map<gd::String, gd::PropertyDescriptor> AudioResource::GetProperties()
189189
properties[_("Preload as music")]
190190
.SetValue(preloadAsMusic ? "true" : "false")
191191
.SetType("Boolean");
192+
properties[_("Preload as file")]
193+
.SetValue(preloadAsFile ? "true" : "false")
194+
.SetType("Boolean");
192195

193196
return properties;
194197
}
@@ -199,6 +202,8 @@ bool AudioResource::UpdateProperty(const gd::String& name,
199202
preloadAsSound = value == "1";
200203
else if (name == _("Preload as music"))
201204
preloadAsMusic = value == "1";
205+
else if (name == _("Preload as file"))
206+
preloadAsFile = value == "1";
202207

203208
return true;
204209
}

Core/GDCore/Project/ResourcesManager.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class GD_CORE_API ImageResource : public Resource {
223223
*/
224224
class GD_CORE_API AudioResource : public Resource {
225225
public:
226-
AudioResource() : Resource(), preloadAsMusic(false), preloadAsSound(false) {
226+
AudioResource() : Resource(), preloadAsMusic(false), preloadAsSound(false), preloadAsFile(false) {
227227
SetKind("audio");
228228
};
229229
virtual ~AudioResource(){};
@@ -263,10 +263,21 @@ class GD_CORE_API AudioResource : public Resource {
263263
*/
264264
void SetPreloadAsSound(bool enable = true) { preloadAsSound = enable; }
265265

266+
/**
267+
* \brief Return true if the audio resource should be preloaded as a file (without decoding).
268+
*/
269+
bool PreloadAsFile() const { return preloadAsFile; }
270+
271+
/**
272+
* \brief Set if the audio resource should be preloaded as a file (without decoding).
273+
*/
274+
void SetPreloadAsFile(bool enable = true) { preloadAsFile = enable; }
275+
266276
private:
267277
gd::String file;
268278
bool preloadAsSound;
269279
bool preloadAsMusic;
280+
bool preloadAsFile;
270281
};
271282

272283
/**

GDJS/Runtime/howler-sound-manager/howler-sound-manager.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -802,24 +802,37 @@ namespace gdjs {
802802
if (!filesToLoad.length) return;
803803
const file = filesToLoad.shift()!;
804804
const fileData = files[file][0];
805-
if (!fileData.preloadAsSound && !fileData.preloadAsMusic) {
806-
onLoad();
807-
} else if (fileData.preloadAsSound && fileData.preloadAsMusic) {
808-
let loadedOnce = false;
809-
const callback = (_, error) => {
810-
if (!loadedOnce) {
811-
loadedOnce = true;
812-
return;
813-
}
805+
806+
let loadCounter = 0;
807+
const callback = (_?: any, error?: string) => {
808+
loadCounter--;
809+
if (!loadCounter) {
814810
onLoad(_, error);
815-
};
811+
}
812+
};
816813

814+
if (fileData.preloadAsMusic) {
815+
loadCounter++;
817816
preloadAudioFile(file, callback, /* isMusic= */ true);
817+
}
818+
819+
if (fileData.preloadAsSound) {
820+
loadCounter++;
818821
preloadAudioFile(file, callback, /* isMusic= */ false);
819-
} else if (fileData.preloadAsSound) {
820-
preloadAudioFile(file, onLoad, /* isMusic= */ false);
821-
} else if (fileData.preloadAsMusic)
822-
preloadAudioFile(file, onLoad, /* isMusic= */ true);
822+
} else if (fileData.preloadAsFile) {
823+
// preloading as sound already does a XHR request, hence "else if"
824+
loadCounter++;
825+
const sound = new XMLHttpRequest();
826+
sound.addEventListener('load', callback);
827+
sound.addEventListener('error', (_) => callback(_, "XHR error: " + file));
828+
sound.addEventListener('abort', (_) => callback(_, "XHR abort: " + file));
829+
sound.open('GET', file);
830+
sound.send();
831+
}
832+
833+
if (!loadCounter) {
834+
onLoad();
835+
}
823836
};
824837
loadNextFile();
825838
}

GDJS/Runtime/types/project-data.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ declare interface ResourceData {
220220
disablePreload?: boolean;
221221
preloadAsSound?: boolean;
222222
preloadAsMusic?: boolean;
223+
preloadAsFile?: boolean;
223224
}
224225

225226
declare type ResourceKind =

0 commit comments

Comments
 (0)