Repository: godotengine/godot (language: cpp)

Issue:
Playing many audio samples causes ever increasing cpu usage in web export
### Tested versions

v4.4.beta2.official [a013481b0]

### System information

Godot v4.4.beta2 - Windows 11 (build 26100) - Multi-window, 2 monitors - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 2070 SUPER (NVIDIA; 32.0.15.6636) - 13th Gen Intel(R) Core(TM) i7-13700KF (24 threads)

### Issue description

In web export of my game after playing for a while, the audio starts to crackle. I think I narrowed it down to playing lots of audio samples over time. Even thought the audio samples are short and finish quickly, the cpu usage goes up over time and does not come back down. 


### Steps to reproduce

I've attached a simple project that plays 100 audio samples each time the 'play' button is pressed.

Export MRP as a no-thread web app. Open in Chrome or Firefox and open the browser's task manager. 
Find WebAudioTest task and note CPU usage (starts low - a few % on my computer)
Click the play button a bunch of times. CPU starts increasing and doesn't drop again.
Eventually CPU usage becomes so high that audio starts to crackle as well.

MRP also includes a short python script to start a local server:
D:\...\webaudiotest>python server.py 

### Minimal reproduction project (MRP)

[webaudiotest.zip](https://github.com/user-attachments/files/18698995/webaudiotest.zip)


Discussion / hints:
Poked around a bit and I think the root cause it that GodotPositionReportingProcessor.process always returns true and is forced to remain active and keeps processing (no data) even after the sample has finished playing.

See comment here: https://github.com/godotengine/godot/pull/102163/files#r1945897924


Fix patch:
diff --git a/platform/web/js/libs/audio.position.worklet.js b/platform/web/js/libs/audio.position.worklet.js
index 7dbeb8a0adde..155d4e6e8765 100644
--- a/platform/web/js/libs/audio.position.worklet.js
+++ b/platform/web/js/libs/audio.position.worklet.js
@@ -35,16 +35,20 @@ class GodotPositionReportingProcessor extends AudioWorkletProcessor {
 		super(...args);
 		this.lastPostTime = currentTime;
 		this.position = 0;
+		this.ended = false;
 
 		this.port.onmessage = (event) => {
-			if (event?.data?.type === 'clear') {
-				this.lastPostTime = currentTime;
-				this.position = 0;
+			if (event?.data?.type === 'ended') {
+				this.ended = true;
 			}
 		};
 	}
 
 	process(inputs, _outputs, _parameters) {
+		if (this.ended) {
+			return false;
+		}
+
 		if (inputs.length > 0) {
 			const input = inputs[0];
 			if (input.length > 0) {
@@ -55,7 +59,7 @@ class GodotPositionReportingProcessor extends AudioWorkletProcessor {
 		// Posting messages is expensive. Let's limit the number of posts.
 		if (currentTime - this.lastPostTime > POST_THRESHOLD_S) {
 			this.lastPostTime = currentTime;
-			this.port.postMessage({ 'type': 'position', 'data': this.position });
+			this.port.postMessage({ type: 'position', data: this.position });
 		}
 
 		return true;
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index c5fa7d5f0fad..d04862795308 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -621,28 +621,24 @@ class SampleNode {
 		if (this.isCanceled) {
 			return;
 		}
-		this.getPositionWorklet();
-		this._source.connect(this._positionWorklet);
+		this._source.connect(this.getPositionWorklet());
 		if (start) {
 			this.start();
 		}
 	}
 
 	/**
-	 * Get a AudioWorkletProcessor from the pool, or create one if no processor is available.
+	 * Get a AudioWorkletProcessor
+	 * @returns {AudioWorkletNode}
 	 */
 	getPositionWorklet() {
 		if (this._positionWorklet != null) {
-			return;
-		}
-		if (GodotAudio.audioPositionWorkletPool.length == 0) {
-			this._positionWorklet = new AudioWorkletNode(
-				GodotAudio.ctx,
-				'godot-position-reporting-processor'
-			);
-		} else {
-			this._positionWorklet = GodotAudio.audioPositionWorkletPool.pop();
+			return this._positionWorklet;
 		}
+		this._positionWorklet = new AudioWorkletNode(
+			GodotAudio.ctx,
+			'godot-position-reporting-processor'
+		);
 		this._positionWorklet.port.onmessage = (event) => {
 			switch (event.data['type']) {
 			case 'position':
@@ -652,6 +648,7 @@ class SampleNode {
 				// Do nothing.
 			}
 		};
+		return this._positionWorklet;
 	}
 
 	/**
@@ -681,8 +678,7 @@ class SampleNode {
 		if (this._positionWorklet) {
 			this._positionWorklet.disconnect();
 			this._positionWorklet.port.onmessage = null;
-			this._positionWorklet.port.postMessage({ type: 'clear' });
-			GodotAudio.audioPositionWorkletPool.push(this._positionWorklet);
+			this._positionWorklet.port.postMessage({ type: 'ended' });
 			this._positionWorklet = null;
 		}
 
@@ -1199,7 +1195,6 @@ const _GodotAudio = {
 
 		/** @type {Promise} */
 		audioPositionWorkletPromise: null,
-		audioPositionWorkletPool: [],
 
 		/**
 		 * Converts linear volume to Db.


Test patch: