Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions packages/loader/src/AudioLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,32 @@ class AudioLoader extends Loader<AudioClip> {
type: "arraybuffer"
};

// @ts-ignore
resourceManager._request<ArrayBuffer>(url, requestConfig).then((arrayBuffer) => {
const audioClip = new AudioClip(resourceManager.engine);
resourceManager
// @ts-ignore
AudioManager.getContext()
.decodeAudioData(arrayBuffer)
.then((result: AudioBuffer) => {
// @ts-ignore
audioClip._setAudioSource(result);
._request<ArrayBuffer>(url, requestConfig)
.then((arrayBuffer) => {
const audioClip = new AudioClip(resourceManager.engine);
// @ts-ignore
AudioManager.getContext()
.decodeAudioData(arrayBuffer)
.then((result: AudioBuffer) => {
// @ts-ignore
audioClip._setAudioSource(result);

if (url.indexOf("data:") !== 0) {
const index = url.lastIndexOf("/");
audioClip.name = url.substring(index + 1);
}
if (url.indexOf("data:") !== 0) {
const index = url.lastIndexOf("/");
audioClip.name = url.substring(index + 1);
}

resolve(audioClip);
})
.catch((e) => {
reject(e);
});
});
resolve(audioClip);
})
.catch((e) => {
reject(e);
});
Comment on lines +25 to +43
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find AudioClip class definition
fd AudioClip.ts

Repository: galacean/engine

Length of output: 95


🏁 Script executed:

#!/bin/bash
# Find ReferResource class definition
fd -t f ReferResource.ts -o -name "*ReferResource*"

Repository: galacean/engine

Length of output: 289


🏁 Script executed:

#!/bin/bash
# Search for AudioClip class and its inheritance
rg -t ts "class AudioClip" -A 5

Repository: galacean/engine

Length of output: 419


🏁 Script executed:

#!/bin/bash
# Find and examine ReferResource class
fd ReferResource.ts | head -1 | xargs cat

Repository: galacean/engine

Length of output: 2666


🏁 Script executed:

#!/bin/bash
# Check AudioClip class for destroy method and full implementation
cat packages/core/src/audio/AudioClip.ts

Repository: galacean/engine

Length of output: 1109


🏁 Script executed:

#!/bin/bash
# Search for decodeAudioData usage patterns in the codebase
rg "decodeAudioData" -B 3 -A 5

Repository: galacean/engine

Length of output: 3487


🏁 Script executed:

#!/bin/bash
# Check if there are other AudioLoader error handling patterns or similar resource creation patterns
rg "new AudioClip|new.*extends ReferResource" -B 2 -A 8 packages/loader/

Repository: galacean/engine

Length of output: 867


🏁 Script executed:

#!/bin/bash
# Search for destroy() calls in error handlers in the codebase to see the pattern
rg "\.destroy\(\)" -B 3 -A 1 | grep -A 4 -B 4 "catch\|error"

Repository: galacean/engine

Length of output: 1590


Add cleanup in the decode error handler to prevent resource leak.

The AudioClip instance created on line 26 is registered with the engine's resource manager via ReferResource constructor, but is not destroyed if decodeAudioData fails. This leaves the resource registered without being properly cleaned up, causing a memory leak.

The suggested fix should be applied. This pattern is already used elsewhere in the codebase (e.g., GLTFLoader.ts):

🔧 Fix
          .catch((e) => {
+           audioClip.destroy();
            reject(e);
          });
🤖 Prompt for AI Agents
In `@packages/loader/src/AudioLoader.ts` around lines 25 - 43, The AudioClip
created in AudioLoader.ts (new AudioClip(resourceManager.engine) assigned to
audioClip) is left registered if AudioManager.getContext().decodeAudioData
fails; update the decodeAudioData.catch handler to perform the same cleanup used
elsewhere (call the AudioClip's destroy/cleanup method to unregister the
resource) before rejecting the promise so the ReferResource registration is
removed; locate the catch inside AudioManager.getContext().decodeAudioData(...)
and add audioClip destruction (e.g., audioClip.destroy() or the project's
equivalent) then reject(e).

})
.catch((e) => {
reject(e);
});
});
}
}
16 changes: 16 additions & 0 deletions tests/src/core/audio/AudioSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe("AudioSource", () => {
audioSource.stop();
audioSource.play();

// @ts-ignore
if (AudioManager.isAudioContextRunning()) {
expect(audioSource.isPlaying).to.be.true;
} else {
Expand All @@ -70,4 +71,19 @@ describe("AudioSource", () => {
expect(cloneAudioSource.loop).to.be.equal(audioSource.loop);
expect(cloneAudioSource.mute).to.be.equal(audioSource.mute);
});

it("load error - network request fails with 404", async () => {
let errorCaught = false;
try {
await engine.resourceManager.load<AudioClip>({
url: "invalid_url",
type: AssetType.Audio
});
expect.fail("Should throw error for 404");
} catch (error) {
errorCaught = true;
expect(error).to.exist;
}
expect(errorCaught).to.be.true;
});
});
Loading