| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*  library_godot_audio.js                                               */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							|  |  |  | /*                      https://godotengine.org                          */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2021-01-01 20:13:46 +01:00
										 |  |  | /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							|  |  |  | /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */ | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* Permission is hereby granted, free of charge, to any person obtaining */ | 
					
						
							|  |  |  | /* a copy of this software and associated documentation files (the       */ | 
					
						
							|  |  |  | /* "Software"), to deal in the Software without restriction, including   */ | 
					
						
							|  |  |  | /* without limitation the rights to use, copy, modify, merge, publish,   */ | 
					
						
							|  |  |  | /* distribute, sublicense, and/or sell copies of the Software, and to    */ | 
					
						
							|  |  |  | /* permit persons to whom the Software is furnished to do so, subject to */ | 
					
						
							|  |  |  | /* the following conditions:                                             */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* The above copyright notice and this permission notice shall be        */ | 
					
						
							|  |  |  | /* included in all copies or substantial portions of the Software.       */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | 
					
						
							|  |  |  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | 
					
						
							|  |  |  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | 
					
						
							|  |  |  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | 
					
						
							|  |  |  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | 
					
						
							|  |  |  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | 
					
						
							|  |  |  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | const GodotAudio = { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 	$GodotAudio__deps: ['$GodotRuntime', '$GodotOS'], | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 	$GodotAudio: { | 
					
						
							|  |  |  | 		ctx: null, | 
					
						
							|  |  |  | 		input: null, | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		driver: null, | 
					
						
							|  |  |  | 		interval: 0, | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		init: function (mix_rate, latency, onstatechange, onlatencyupdate) { | 
					
						
							| 
									
										
										
										
											2021-09-13 03:06:34 +02:00
										 |  |  | 			const opts = {}; | 
					
						
							|  |  |  | 			// If mix_rate is 0, let the browser choose.
 | 
					
						
							|  |  |  | 			if (mix_rate) { | 
					
						
							|  |  |  | 				opts['sampleRate'] = mix_rate; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Do not specify, leave 'interactive' for good performance.
 | 
					
						
							|  |  |  | 			// opts['latencyHint'] = latency / 1000;
 | 
					
						
							|  |  |  | 			const ctx = new (window.AudioContext || window.webkitAudioContext)(opts); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			GodotAudio.ctx = ctx; | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			ctx.onstatechange = function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				let state = 0; | 
					
						
							|  |  |  | 				switch (ctx.state) { | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 				case 'suspended': | 
					
						
							|  |  |  | 					state = 0; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 'running': | 
					
						
							|  |  |  | 					state = 1; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 'closed': | 
					
						
							|  |  |  | 					state = 2; | 
					
						
							|  |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// no default
 | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				onstatechange(state); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2021-05-20 12:07:26 +02:00
										 |  |  | 			ctx.onstatechange(); // Immediately notify state.
 | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			// Update computed latency
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			GodotAudio.interval = setInterval(function () { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 				let computed_latency = 0; | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				if (ctx.baseLatency) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 					computed_latency += GodotAudio.ctx.baseLatency; | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if (ctx.outputLatency) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 					computed_latency += GodotAudio.ctx.outputLatency; | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 				onlatencyupdate(computed_latency); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			}, 1000); | 
					
						
							|  |  |  | 			GodotOS.atexit(GodotAudio.close_async); | 
					
						
							|  |  |  | 			return ctx.destination.channelCount; | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		create_input: function (callback) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			if (GodotAudio.input) { | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 				return 0; // Already started.
 | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			function gotMediaInput(stream) { | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 				try { | 
					
						
							|  |  |  | 					GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream); | 
					
						
							|  |  |  | 					callback(GodotAudio.input); | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					GodotRuntime.error('Failed creaating input.', e); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 			if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				navigator.mediaDevices.getUserMedia({ | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 					'audio': true, | 
					
						
							|  |  |  | 				}).then(gotMediaInput, function (e) { | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 					GodotRuntime.error('Error getting user media.', e); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				if (!navigator.getUserMedia) { | 
					
						
							|  |  |  | 					navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 				if (!navigator.getUserMedia) { | 
					
						
							|  |  |  | 					GodotRuntime.error('getUserMedia not available.'); | 
					
						
							|  |  |  | 					return 1; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				navigator.getUserMedia({ | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 					'audio': true, | 
					
						
							|  |  |  | 				}, gotMediaInput, function (e) { | 
					
						
							|  |  |  | 					GodotRuntime.print(e); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 			return 0; | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		close_async: function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			const ctx = GodotAudio.ctx; | 
					
						
							|  |  |  | 			GodotAudio.ctx = null; | 
					
						
							|  |  |  | 			// Audio was not initialized.
 | 
					
						
							|  |  |  | 			if (!ctx) { | 
					
						
							|  |  |  | 				resolve(); | 
					
						
							| 
									
										
										
										
											2020-10-23 18:33:20 +02:00
										 |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			// Remove latency callback
 | 
					
						
							|  |  |  | 			if (GodotAudio.interval) { | 
					
						
							|  |  |  | 				clearInterval(GodotAudio.interval); | 
					
						
							|  |  |  | 				GodotAudio.interval = 0; | 
					
						
							| 
									
										
										
										
											2020-10-23 18:33:20 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			// Disconnect input, if it was started.
 | 
					
						
							| 
									
										
										
										
											2020-10-23 18:33:20 +02:00
										 |  |  | 			if (GodotAudio.input) { | 
					
						
							|  |  |  | 				GodotAudio.input.disconnect(); | 
					
						
							|  |  |  | 				GodotAudio.input = null; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			// Disconnect output
 | 
					
						
							|  |  |  | 			let closed = Promise.resolve(); | 
					
						
							|  |  |  | 			if (GodotAudio.driver) { | 
					
						
							|  |  |  | 				closed = GodotAudio.driver.close(); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			closed.then(function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				return ctx.close(); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			}).then(function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				ctx.onstatechange = null; | 
					
						
							|  |  |  | 				resolve(); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			}).catch(function (e) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				ctx.onstatechange = null; | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 				GodotRuntime.error('Error closing AudioContext', e); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				resolve(); | 
					
						
							| 
									
										
										
										
											2020-10-23 18:33:20 +02:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_is_available__sig: 'i', | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 	godot_audio_is_available__proxy: 'sync', | 
					
						
							|  |  |  | 	godot_audio_is_available: function () { | 
					
						
							|  |  |  | 		if (!(window.AudioContext || window.webkitAudioContext)) { | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 1; | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 	godot_audio_has_worklet__sig: 'i', | 
					
						
							|  |  |  | 	godot_audio_has_worklet: function () { | 
					
						
							|  |  |  | 		return (GodotAudio.ctx && GodotAudio.ctx.audioWorklet) ? 1 : 0; | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	godot_audio_has_script_processor__sig: 'i', | 
					
						
							|  |  |  | 	godot_audio_has_script_processor: function () { | 
					
						
							|  |  |  | 		return (GodotAudio.ctx && GodotAudio.ctx.createScriptProcessor) ? 1 : 0; | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_init__sig: 'iiiii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_init: function (p_mix_rate, p_latency, p_state_change, p_latency_update) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 		const statechange = GodotRuntime.get_func(p_state_change); | 
					
						
							|  |  |  | 		const latencyupdate = GodotRuntime.get_func(p_latency_update); | 
					
						
							| 
									
										
										
										
											2021-09-13 03:06:34 +02:00
										 |  |  | 		const mix_rate = GodotRuntime.getHeapValue(p_mix_rate, 'i32'); | 
					
						
							|  |  |  | 		const channels = GodotAudio.init(mix_rate, p_latency, statechange, latencyupdate); | 
					
						
							|  |  |  | 		GodotRuntime.setHeapValue(p_mix_rate, GodotAudio.ctx.sampleRate, 'i32'); | 
					
						
							|  |  |  | 		return channels; | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_resume__sig: 'v', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_resume: function () { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 		if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') { | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 			GodotAudio.ctx.resume(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	godot_audio_capture_start__proxy: 'sync', | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 	godot_audio_capture_start__sig: 'i', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_capture_start: function () { | 
					
						
							| 
									
										
										
										
											2020-12-08 11:18:03 +01:00
										 |  |  | 		return GodotAudio.create_input(function (input) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			input.connect(GodotAudio.driver.get_node()); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	godot_audio_capture_stop__proxy: 'sync', | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_capture_stop__sig: 'v', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_capture_stop: function () { | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 		if (GodotAudio.input) { | 
					
						
							| 
									
										
										
										
											2020-10-23 18:33:20 +02:00
										 |  |  | 			const tracks = GodotAudio.input['mediaStream']['getTracks'](); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			for (let i = 0; i < tracks.length; i++) { | 
					
						
							| 
									
										
										
										
											2020-10-23 18:33:20 +02:00
										 |  |  | 				tracks[i]['stop'](); | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			GodotAudio.input.disconnect(); | 
					
						
							|  |  |  | 			GodotAudio.input = null; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | autoAddDeps(GodotAudio, '$GodotAudio'); | 
					
						
							| 
									
										
										
										
											2020-10-02 13:49:00 +02:00
										 |  |  | mergeInto(LibraryManager.library, GodotAudio); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * The AudioWorklet API driver, used when threads are available. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const GodotAudioWorklet = { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 	$GodotAudioWorklet__deps: ['$GodotAudio', '$GodotConfig'], | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 	$GodotAudioWorklet: { | 
					
						
							|  |  |  | 		promise: null, | 
					
						
							|  |  |  | 		worklet: null, | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 		ring_buffer: null, | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		create: function (channels) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 			const path = GodotConfig.locate_file('godot.audio.worklet.js'); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 			GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet.addModule(path).then(function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				GodotAudioWorklet.worklet = new AudioWorkletNode( | 
					
						
							|  |  |  | 					GodotAudio.ctx, | 
					
						
							|  |  |  | 					'godot-processor', | 
					
						
							|  |  |  | 					{ | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 						'outputChannelCount': [channels], | 
					
						
							| 
									
										
										
										
											2021-09-22 09:40:54 +02:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				); | 
					
						
							|  |  |  | 				return Promise.resolve(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			GodotAudio.driver = GodotAudioWorklet; | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		start: function (in_buf, out_buf, state) { | 
					
						
							|  |  |  | 			GodotAudioWorklet.promise.then(function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				const node = GodotAudioWorklet.worklet; | 
					
						
							|  |  |  | 				node.connect(GodotAudio.ctx.destination); | 
					
						
							|  |  |  | 				node.port.postMessage({ | 
					
						
							|  |  |  | 					'cmd': 'start', | 
					
						
							|  |  |  | 					'data': [state, in_buf, out_buf], | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 				node.port.onmessage = function (event) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 					GodotRuntime.error(event.data); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				}; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 		start_no_threads: function (p_out_buf, p_out_size, out_callback, p_in_buf, p_in_size, in_callback) { | 
					
						
							|  |  |  | 			function RingBuffer() { | 
					
						
							|  |  |  | 				let wpos = 0; | 
					
						
							|  |  |  | 				let rpos = 0; | 
					
						
							|  |  |  | 				let pending_samples = 0; | 
					
						
							|  |  |  | 				const wbuf = new Float32Array(p_out_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				function send(port) { | 
					
						
							|  |  |  | 					if (pending_samples === 0) { | 
					
						
							|  |  |  | 						return; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); | 
					
						
							|  |  |  | 					const size = buffer.length; | 
					
						
							|  |  |  | 					const tot_sent = pending_samples; | 
					
						
							|  |  |  | 					out_callback(wpos, pending_samples); | 
					
						
							|  |  |  | 					if (wpos + pending_samples >= size) { | 
					
						
							|  |  |  | 						const high = size - wpos; | 
					
						
							|  |  |  | 						wbuf.set(buffer.subarray(wpos, size)); | 
					
						
							|  |  |  | 						pending_samples -= high; | 
					
						
							|  |  |  | 						wpos = 0; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if (pending_samples > 0) { | 
					
						
							|  |  |  | 						wbuf.set(buffer.subarray(wpos, wpos + pending_samples), tot_sent - pending_samples); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					port.postMessage({ 'cmd': 'chunk', 'data': wbuf.subarray(0, tot_sent) }); | 
					
						
							|  |  |  | 					wpos += pending_samples; | 
					
						
							|  |  |  | 					pending_samples = 0; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				this.receive = function (recv_buf) { | 
					
						
							|  |  |  | 					const buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); | 
					
						
							|  |  |  | 					const from = rpos; | 
					
						
							|  |  |  | 					let to_write = recv_buf.length; | 
					
						
							|  |  |  | 					let high = 0; | 
					
						
							|  |  |  | 					if (rpos + to_write >= p_in_size) { | 
					
						
							|  |  |  | 						high = p_in_size - rpos; | 
					
						
							|  |  |  | 						buffer.set(recv_buf.subarray(0, high), rpos); | 
					
						
							|  |  |  | 						to_write -= high; | 
					
						
							|  |  |  | 						rpos = 0; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if (to_write) { | 
					
						
							|  |  |  | 						buffer.set(recv_buf.subarray(high, to_write), rpos); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					in_callback(from, recv_buf.length); | 
					
						
							|  |  |  | 					rpos += to_write; | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				this.consumed = function (size, port) { | 
					
						
							|  |  |  | 					pending_samples += size; | 
					
						
							|  |  |  | 					send(port); | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			GodotAudioWorklet.ring_buffer = new RingBuffer(); | 
					
						
							|  |  |  | 			GodotAudioWorklet.promise.then(function () { | 
					
						
							|  |  |  | 				const node = GodotAudioWorklet.worklet; | 
					
						
							|  |  |  | 				const buffer = GodotRuntime.heapSlice(HEAPF32, p_out_buf, p_out_size); | 
					
						
							|  |  |  | 				node.connect(GodotAudio.ctx.destination); | 
					
						
							|  |  |  | 				node.port.postMessage({ | 
					
						
							|  |  |  | 					'cmd': 'start_nothreads', | 
					
						
							|  |  |  | 					'data': [buffer, p_in_size], | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 				node.port.onmessage = function (event) { | 
					
						
							|  |  |  | 					if (!GodotAudioWorklet.worklet) { | 
					
						
							|  |  |  | 						return; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if (event.data['cmd'] === 'read') { | 
					
						
							|  |  |  | 						const read = event.data['data']; | 
					
						
							|  |  |  | 						GodotAudioWorklet.ring_buffer.consumed(read, GodotAudioWorklet.worklet.port); | 
					
						
							|  |  |  | 					} else if (event.data['cmd'] === 'input') { | 
					
						
							|  |  |  | 						const buf = event.data['data']; | 
					
						
							|  |  |  | 						if (buf.length > p_in_size) { | 
					
						
							|  |  |  | 							GodotRuntime.error('Input chunk is too big'); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						GodotAudioWorklet.ring_buffer.receive(buf); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						GodotRuntime.error(event.data); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		get_node: function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			return GodotAudioWorklet.worklet; | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		close: function () { | 
					
						
							|  |  |  | 			return new Promise(function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2021-03-08 14:07:19 +01:00
										 |  |  | 				if (GodotAudioWorklet.promise === null) { | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 				GodotAudioWorklet.promise.then(function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 					GodotAudioWorklet.worklet.port.postMessage({ | 
					
						
							|  |  |  | 						'cmd': 'stop', | 
					
						
							|  |  |  | 						'data': null, | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					GodotAudioWorklet.worklet.disconnect(); | 
					
						
							|  |  |  | 					GodotAudioWorklet.worklet = null; | 
					
						
							|  |  |  | 					GodotAudioWorklet.promise = null; | 
					
						
							|  |  |  | 					resolve(); | 
					
						
							| 
									
										
										
										
											2021-03-08 14:07:19 +01:00
										 |  |  | 				}).catch(function (err) { /* aborted? */ }); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 	godot_audio_worklet_create__sig: 'ii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_worklet_create: function (channels) { | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 		try { | 
					
						
							|  |  |  | 			GodotAudioWorklet.create(channels); | 
					
						
							|  |  |  | 		} catch (e) { | 
					
						
							|  |  |  | 			GodotRuntime.error('Error starting AudioDriverWorklet', e); | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_worklet_start__sig: 'viiiii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_worklet_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 		const out_buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); | 
					
						
							|  |  |  | 		const in_buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); | 
					
						
							|  |  |  | 		const state = GodotRuntime.heapSub(HEAP32, p_state, 4); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		GodotAudioWorklet.start(in_buffer, out_buffer, state); | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 	godot_audio_worklet_start_no_threads__sig: 'viiiiii', | 
					
						
							|  |  |  | 	godot_audio_worklet_start_no_threads: function (p_out_buf, p_out_size, p_out_callback, p_in_buf, p_in_size, p_in_callback) { | 
					
						
							|  |  |  | 		const out_callback = GodotRuntime.get_func(p_out_callback); | 
					
						
							|  |  |  | 		const in_callback = GodotRuntime.get_func(p_in_callback); | 
					
						
							|  |  |  | 		GodotAudioWorklet.start_no_threads(p_out_buf, p_out_size, out_callback, p_in_buf, p_in_size, in_callback); | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_worklet_state_wait__sig: 'iiii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_worklet_state_wait: function (p_state, p_idx, p_expected, p_timeout) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		Atomics.wait(HEAP32, (p_state >> 2) + p_idx, p_expected, p_timeout); | 
					
						
							|  |  |  | 		return Atomics.load(HEAP32, (p_state >> 2) + p_idx); | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_worklet_state_add__sig: 'iiii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_worklet_state_add: function (p_state, p_idx, p_value) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		return Atomics.add(HEAP32, (p_state >> 2) + p_idx, p_value); | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_worklet_state_get__sig: 'iii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_worklet_state_get: function (p_state, p_idx) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		return Atomics.load(HEAP32, (p_state >> 2) + p_idx); | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | autoAddDeps(GodotAudioWorklet, '$GodotAudioWorklet'); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | mergeInto(LibraryManager.library, GodotAudioWorklet); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |  * The deprecated ScriptProcessorNode API, used when threads are disabled. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const GodotAudioScript = { | 
					
						
							|  |  |  | 	$GodotAudioScript__deps: ['$GodotAudio'], | 
					
						
							|  |  |  | 	$GodotAudioScript: { | 
					
						
							|  |  |  | 		script: null, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		create: function (buffer_length, channel_count) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			GodotAudioScript.script = GodotAudio.ctx.createScriptProcessor(buffer_length, 2, channel_count); | 
					
						
							|  |  |  | 			GodotAudio.driver = GodotAudioScript; | 
					
						
							|  |  |  | 			return GodotAudioScript.script.bufferSize; | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) { | 
					
						
							|  |  |  | 			GodotAudioScript.script.onaudioprocess = function (event) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				// Read input
 | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 				const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				const input = event.inputBuffer; | 
					
						
							|  |  |  | 				if (GodotAudio.input) { | 
					
						
							|  |  |  | 					const inlen = input.getChannelData(0).length; | 
					
						
							|  |  |  | 					for (let ch = 0; ch < 2; ch++) { | 
					
						
							|  |  |  | 						const data = input.getChannelData(ch); | 
					
						
							|  |  |  | 						for (let s = 0; s < inlen; s++) { | 
					
						
							|  |  |  | 							inb[s * 2 + ch] = data[s]; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Let Godot process the input/output.
 | 
					
						
							|  |  |  | 				onprocess(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Write the output.
 | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 				const outb = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				const output = event.outputBuffer; | 
					
						
							|  |  |  | 				const channels = output.numberOfChannels; | 
					
						
							|  |  |  | 				for (let ch = 0; ch < channels; ch++) { | 
					
						
							|  |  |  | 					const data = output.getChannelData(ch); | 
					
						
							|  |  |  | 					// Loop through samples and assign computed values.
 | 
					
						
							|  |  |  | 					for (let sample = 0; sample < data.length; sample++) { | 
					
						
							|  |  |  | 						data[sample] = outb[sample * channels + ch]; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			GodotAudioScript.script.connect(GodotAudio.ctx.destination); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		get_node: function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 			return GodotAudioScript.script; | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 		close: function () { | 
					
						
							|  |  |  | 			return new Promise(function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 				GodotAudioScript.script.disconnect(); | 
					
						
							|  |  |  | 				GodotAudioScript.script.onaudioprocess = null; | 
					
						
							|  |  |  | 				GodotAudioScript.script = null; | 
					
						
							|  |  |  | 				resolve(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_script_create__sig: 'iii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_script_create: function (buffer_length, channel_count) { | 
					
						
							| 
									
										
										
										
											2021-09-12 19:23:30 +02:00
										 |  |  | 		const buf_len = GodotRuntime.getHeapValue(buffer_length, 'i32'); | 
					
						
							|  |  |  | 		try { | 
					
						
							|  |  |  | 			const out_len = GodotAudioScript.create(buf_len, channel_count); | 
					
						
							|  |  |  | 			GodotRuntime.setHeapValue(buffer_length, out_len, 'i32'); | 
					
						
							|  |  |  | 		} catch (e) { | 
					
						
							|  |  |  | 			GodotRuntime.error('Error starting AudioDriverScriptProcessor', e); | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 12:51:17 +01:00
										 |  |  | 	godot_audio_script_start__sig: 'viiiii', | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | 	godot_audio_script_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) { | 
					
						
							| 
									
										
										
										
											2020-11-19 16:54:07 +01:00
										 |  |  | 		const onprocess = GodotRuntime.get_func(p_cb); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | 		GodotAudioScript.start(p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess); | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 12:13:52 +01:00
										 |  |  | autoAddDeps(GodotAudioScript, '$GodotAudioScript'); | 
					
						
							| 
									
										
										
										
											2020-11-10 11:05:22 +01:00
										 |  |  | mergeInto(LibraryManager.library, GodotAudioScript); |