diff options
Diffstat (limited to 'platform/javascript/js/libs/library_godot_audio.js')
-rw-r--r-- | platform/javascript/js/libs/library_godot_audio.js | 484 |
1 files changed, 0 insertions, 484 deletions
diff --git a/platform/javascript/js/libs/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js deleted file mode 100644 index 756c1ac595..0000000000 --- a/platform/javascript/js/libs/library_godot_audio.js +++ /dev/null @@ -1,484 +0,0 @@ -/*************************************************************************/ -/* library_godot_audio.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -const GodotAudio = { - $GodotAudio__deps: ['$GodotRuntime', '$GodotOS'], - $GodotAudio: { - ctx: null, - input: null, - driver: null, - interval: 0, - - init: function (mix_rate, latency, onstatechange, onlatencyupdate) { - 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); - GodotAudio.ctx = ctx; - ctx.onstatechange = function () { - let state = 0; - switch (ctx.state) { - case 'suspended': - state = 0; - break; - case 'running': - state = 1; - break; - case 'closed': - state = 2; - break; - - // no default - } - onstatechange(state); - }; - ctx.onstatechange(); // Immediately notify state. - // Update computed latency - GodotAudio.interval = setInterval(function () { - let computed_latency = 0; - if (ctx.baseLatency) { - computed_latency += GodotAudio.ctx.baseLatency; - } - if (ctx.outputLatency) { - computed_latency += GodotAudio.ctx.outputLatency; - } - onlatencyupdate(computed_latency); - }, 1000); - GodotOS.atexit(GodotAudio.close_async); - return ctx.destination.channelCount; - }, - - create_input: function (callback) { - if (GodotAudio.input) { - return 0; // Already started. - } - function gotMediaInput(stream) { - try { - GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream); - callback(GodotAudio.input); - } catch (e) { - GodotRuntime.error('Failed creaating input.', e); - } - } - if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - navigator.mediaDevices.getUserMedia({ - 'audio': true, - }).then(gotMediaInput, function (e) { - GodotRuntime.error('Error getting user media.', e); - }); - } else { - if (!navigator.getUserMedia) { - navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; - } - if (!navigator.getUserMedia) { - GodotRuntime.error('getUserMedia not available.'); - return 1; - } - navigator.getUserMedia({ - 'audio': true, - }, gotMediaInput, function (e) { - GodotRuntime.print(e); - }); - } - return 0; - }, - - close_async: function (resolve, reject) { - const ctx = GodotAudio.ctx; - GodotAudio.ctx = null; - // Audio was not initialized. - if (!ctx) { - resolve(); - return; - } - // Remove latency callback - if (GodotAudio.interval) { - clearInterval(GodotAudio.interval); - GodotAudio.interval = 0; - } - // Disconnect input, if it was started. - if (GodotAudio.input) { - GodotAudio.input.disconnect(); - GodotAudio.input = null; - } - // Disconnect output - let closed = Promise.resolve(); - if (GodotAudio.driver) { - closed = GodotAudio.driver.close(); - } - closed.then(function () { - return ctx.close(); - }).then(function () { - ctx.onstatechange = null; - resolve(); - }).catch(function (e) { - ctx.onstatechange = null; - GodotRuntime.error('Error closing AudioContext', e); - resolve(); - }); - }, - }, - - godot_audio_is_available__sig: 'i', - godot_audio_is_available__proxy: 'sync', - godot_audio_is_available: function () { - if (!(window.AudioContext || window.webkitAudioContext)) { - return 0; - } - return 1; - }, - - 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; - }, - - godot_audio_init__sig: 'iiiii', - godot_audio_init: function (p_mix_rate, p_latency, p_state_change, p_latency_update) { - const statechange = GodotRuntime.get_func(p_state_change); - const latencyupdate = GodotRuntime.get_func(p_latency_update); - 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; - }, - - godot_audio_resume__sig: 'v', - godot_audio_resume: function () { - if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') { - GodotAudio.ctx.resume(); - } - }, - - godot_audio_capture_start__proxy: 'sync', - godot_audio_capture_start__sig: 'i', - godot_audio_capture_start: function () { - return GodotAudio.create_input(function (input) { - input.connect(GodotAudio.driver.get_node()); - }); - }, - - godot_audio_capture_stop__proxy: 'sync', - godot_audio_capture_stop__sig: 'v', - godot_audio_capture_stop: function () { - if (GodotAudio.input) { - const tracks = GodotAudio.input['mediaStream']['getTracks'](); - for (let i = 0; i < tracks.length; i++) { - tracks[i]['stop'](); - } - GodotAudio.input.disconnect(); - GodotAudio.input = null; - } - }, -}; - -autoAddDeps(GodotAudio, '$GodotAudio'); -mergeInto(LibraryManager.library, GodotAudio); - -/** - * The AudioWorklet API driver, used when threads are available. - */ -const GodotAudioWorklet = { - $GodotAudioWorklet__deps: ['$GodotAudio', '$GodotConfig'], - $GodotAudioWorklet: { - promise: null, - worklet: null, - ring_buffer: null, - - create: function (channels) { - const path = GodotConfig.locate_file('godot.audio.worklet.js'); - GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet.addModule(path).then(function () { - GodotAudioWorklet.worklet = new AudioWorkletNode( - GodotAudio.ctx, - 'godot-processor', - { - 'outputChannelCount': [channels], - } - ); - return Promise.resolve(); - }); - GodotAudio.driver = GodotAudioWorklet; - }, - - start: function (in_buf, out_buf, state) { - GodotAudioWorklet.promise.then(function () { - const node = GodotAudioWorklet.worklet; - node.connect(GodotAudio.ctx.destination); - node.port.postMessage({ - 'cmd': 'start', - 'data': [state, in_buf, out_buf], - }); - node.port.onmessage = function (event) { - GodotRuntime.error(event.data); - }; - }); - }, - - 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); - } - }; - }); - }, - - get_node: function () { - return GodotAudioWorklet.worklet; - }, - - close: function () { - return new Promise(function (resolve, reject) { - if (GodotAudioWorklet.promise === null) { - return; - } - GodotAudioWorklet.promise.then(function () { - GodotAudioWorklet.worklet.port.postMessage({ - 'cmd': 'stop', - 'data': null, - }); - GodotAudioWorklet.worklet.disconnect(); - GodotAudioWorklet.worklet = null; - GodotAudioWorklet.promise = null; - resolve(); - }).catch(function (err) { /* aborted? */ }); - }); - }, - }, - - godot_audio_worklet_create__sig: 'ii', - godot_audio_worklet_create: function (channels) { - try { - GodotAudioWorklet.create(channels); - } catch (e) { - GodotRuntime.error('Error starting AudioDriverWorklet', e); - return 1; - } - return 0; - }, - - godot_audio_worklet_start__sig: 'viiiii', - godot_audio_worklet_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) { - 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); - GodotAudioWorklet.start(in_buffer, out_buffer, state); - }, - - 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); - }, - - godot_audio_worklet_state_wait__sig: 'iiii', - godot_audio_worklet_state_wait: function (p_state, p_idx, p_expected, p_timeout) { - Atomics.wait(HEAP32, (p_state >> 2) + p_idx, p_expected, p_timeout); - return Atomics.load(HEAP32, (p_state >> 2) + p_idx); - }, - - godot_audio_worklet_state_add__sig: 'iiii', - godot_audio_worklet_state_add: function (p_state, p_idx, p_value) { - return Atomics.add(HEAP32, (p_state >> 2) + p_idx, p_value); - }, - - godot_audio_worklet_state_get__sig: 'iii', - godot_audio_worklet_state_get: function (p_state, p_idx) { - return Atomics.load(HEAP32, (p_state >> 2) + p_idx); - }, -}; - -autoAddDeps(GodotAudioWorklet, '$GodotAudioWorklet'); -mergeInto(LibraryManager.library, GodotAudioWorklet); - -/* - * The deprecated ScriptProcessorNode API, used when threads are disabled. - */ -const GodotAudioScript = { - $GodotAudioScript__deps: ['$GodotAudio'], - $GodotAudioScript: { - script: null, - - create: function (buffer_length, channel_count) { - GodotAudioScript.script = GodotAudio.ctx.createScriptProcessor(buffer_length, 2, channel_count); - GodotAudio.driver = GodotAudioScript; - return GodotAudioScript.script.bufferSize; - }, - - start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) { - GodotAudioScript.script.onaudioprocess = function (event) { - // Read input - const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); - 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. - const outb = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); - 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); - }, - - get_node: function () { - return GodotAudioScript.script; - }, - - close: function () { - return new Promise(function (resolve, reject) { - GodotAudioScript.script.disconnect(); - GodotAudioScript.script.onaudioprocess = null; - GodotAudioScript.script = null; - resolve(); - }); - }, - }, - - godot_audio_script_create__sig: 'iii', - godot_audio_script_create: function (buffer_length, channel_count) { - 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; - }, - - godot_audio_script_start__sig: 'viiiii', - godot_audio_script_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) { - const onprocess = GodotRuntime.get_func(p_cb); - GodotAudioScript.start(p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess); - }, -}; - -autoAddDeps(GodotAudioScript, '$GodotAudioScript'); -mergeInto(LibraryManager.library, GodotAudioScript); |