[HTML5] Refactor JS, threads support, closures.

- Refactored the Engine code, splitted across files.
- Use MODULARIZE option to build emscripten code into it's own closure.
- Optional closure compiler run for JS and generated code.
- Enable lto support (saves ~2MiB in release).
- Can now build with tools=yes (not much to see yet).
- Dropped some deprecated code for older toolchains.
- Add onExit, and onExecute JS function.
- Add files drag and drop support.
- Add support for low precessor usage mode (via offscreen render, swap).
This commit is contained in:
Fabio Alessandrelli 2020-01-19 11:44:19 +01:00
parent 93e20a4cd4
commit 21c9f37757
18 changed files with 1097 additions and 627 deletions

View file

@ -37,22 +37,18 @@
AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
const char *AudioDriverJavaScript::get_name() const {
return "JavaScript";
}
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() {
AudioDriverJavaScript::singleton->mix_to_js();
}
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_capture(float sample) {
AudioDriverJavaScript::singleton->process_capture(sample);
}
void AudioDriverJavaScript::mix_to_js() {
int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
int sample_count = memarr_len(internal_buffer) / channel_count;
int32_t *stream_buffer = reinterpret_cast<int32_t *>(internal_buffer);
@ -63,24 +59,24 @@ void AudioDriverJavaScript::mix_to_js() {
}
void AudioDriverJavaScript::process_capture(float sample) {
int32_t sample32 = int32_t(sample * 32768.f) * (1U << 16);
input_buffer_write(sample32);
}
Error AudioDriverJavaScript::init() {
int mix_rate = GLOBAL_GET("audio/mix_rate");
int latency = GLOBAL_GET("audio/output_latency");
/* clang-format off */
EM_ASM({
_driver_id = EM_ASM_INT({
const MIX_RATE = $0;
const LATENCY = $1 / 1000;
_audioDriver_audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY});
_audioDriver_audioInput = null;
_audioDriver_inputStream = null;
_audioDriver_scriptNode = null;
return Module.IDHandler.add({
'context': new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY}),
'input': null,
'stream': null,
'script': null
});
}, mix_rate, latency);
/* clang-format on */
@ -88,14 +84,16 @@ Error AudioDriverJavaScript::init() {
buffer_length = closest_power_of_2((latency * mix_rate / 1000) * channel_count);
/* clang-format off */
buffer_length = EM_ASM_INT({
const BUFFER_LENGTH = $0;
const CHANNEL_COUNT = $1;
var ref = Module.IDHandler.get($0);
const ctx = ref['context'];
const BUFFER_LENGTH = $1;
const CHANNEL_COUNT = $2;
_audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(BUFFER_LENGTH, 2, CHANNEL_COUNT);
_audioDriver_scriptNode.connect(_audioDriver_audioContext.destination);
return _audioDriver_scriptNode.bufferSize;
}, buffer_length, channel_count);
var script = ctx.createScriptProcessor(BUFFER_LENGTH, 2, CHANNEL_COUNT);
script.connect(ctx.destination);
ref['script'] = script;
return script.bufferSize;
}, _driver_id, buffer_length, channel_count);
/* clang-format on */
if (!buffer_length) {
return FAILED;
@ -111,14 +109,14 @@ Error AudioDriverJavaScript::init() {
}
void AudioDriverJavaScript::start() {
/* clang-format off */
EM_ASM({
var INTERNAL_BUFFER_PTR = $0;
const ref = Module.IDHandler.get($0);
var INTERNAL_BUFFER_PTR = $1;
var audioDriverMixFunction = cwrap('audio_driver_js_mix');
var audioDriverProcessCapture = cwrap('audio_driver_process_capture', null, ['number']);
_audioDriver_scriptNode.onaudioprocess = function(audioProcessingEvent) {
ref['script'].onaudioprocess = function(audioProcessingEvent) {
audioDriverMixFunction();
var input = audioProcessingEvent.inputBuffer;
@ -135,7 +133,7 @@ void AudioDriverJavaScript::start() {
}
}
if (_audioDriver_audioInput) {
if (ref['input']) {
var inputDataL = input.getChannelData(0);
var inputDataR = input.getChannelData(1);
for (var i = 0; i < inputDataL.length; i++) {
@ -144,51 +142,54 @@ void AudioDriverJavaScript::start() {
}
}
};
}, internal_buffer);
}, _driver_id, internal_buffer);
/* clang-format on */
}
void AudioDriverJavaScript::resume() {
/* clang-format off */
EM_ASM({
if (_audioDriver_audioContext.resume)
_audioDriver_audioContext.resume();
});
const ref = Module.IDHandler.get($0);
if (ref && ref['context'] && ref['context'].resume)
ref['context'].resume();
}, _driver_id);
/* clang-format on */
}
float AudioDriverJavaScript::get_latency() {
/* clang-format off */
return EM_ASM_DOUBLE({
const ref = Module.IDHandler.get($0);
var latency = 0;
if (_audioDriver_audioContext) {
if (_audioDriver_audioContext.baseLatency) {
latency += _audioDriver_audioContext.baseLatency;
if (ref && ref['context']) {
const ctx = ref['context'];
if (ctx.baseLatency) {
latency += ctx.baseLatency;
}
if (_audioDriver_audioContext.outputLatency) {
latency += _audioDriver_audioContext.outputLatency;
if (ctx.outputLatency) {
latency += ctx.outputLatency;
}
}
return latency;
});
}, _driver_id);
/* clang-format on */
}
int AudioDriverJavaScript::get_mix_rate() const {
/* clang-format off */
return EM_ASM_INT_V({
return _audioDriver_audioContext.sampleRate;
});
return EM_ASM_INT({
const ref = Module.IDHandler.get($0);
return ref && ref['context'] ? ref['context'].sampleRate : 0;
}, _driver_id);
/* clang-format on */
}
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
/* clang-format off */
return get_speaker_mode_by_total_channels(EM_ASM_INT_V({
return _audioDriver_audioContext.destination.channelCount;
}));
return get_speaker_mode_by_total_channels(EM_ASM_INT({
const ref = Module.IDHandler.get($0);
return ref && ref['context'] ? ref['context'].destination.channelCount : 0;
}, _driver_id));
/* clang-format on */
}
@ -199,16 +200,38 @@ void AudioDriverJavaScript::lock() {
void AudioDriverJavaScript::unlock() {
}
void AudioDriverJavaScript::finish() {
void AudioDriverJavaScript::finish_async() {
// Close the context, add the operation to the async_finish list in module.
int id = _driver_id;
_driver_id = 0;
/* clang-format off */
EM_ASM({
_audioDriver_audioContext = null;
_audioDriver_audioInput = null;
_audioDriver_scriptNode = null;
});
var ref = Module.IDHandler.get($0);
Module.async_finish.push(new Promise(function(accept, reject) {
if (!ref) {
console.log("Ref not found!", $0, Module.IDHandler);
setTimeout(accept, 0);
} else {
const context = ref['context'];
// Disconnect script and input.
ref['script'].disconnect();
if (ref['input'])
ref['input'].disconnect();
ref = null;
context.close().then(function() {
accept();
}).catch(function(e) {
accept();
});
}
}));
Module.IDHandler.remove($0);
}, id);
/* clang-format on */
}
void AudioDriverJavaScript::finish() {
if (internal_buffer) {
memdelete_arr(internal_buffer);
internal_buffer = NULL;
@ -216,15 +239,15 @@ void AudioDriverJavaScript::finish() {
}
Error AudioDriverJavaScript::capture_start() {
input_buffer_init(buffer_length);
/* clang-format off */
EM_ASM({
function gotMediaInput(stream) {
_audioDriver_inputStream = stream;
_audioDriver_audioInput = _audioDriver_audioContext.createMediaStreamSource(stream);
_audioDriver_audioInput.connect(_audioDriver_scriptNode);
var ref = Module.IDHandler.get($0);
ref['stream'] = stream;
ref['input'] = ref['context'].createMediaStreamSource(stream);
ref['input'].connect(ref['script']);
}
function gotMediaInputError(e) {
@ -238,30 +261,30 @@ Error AudioDriverJavaScript::capture_start() {
navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
navigator.getUserMedia({"audio": true}, gotMediaInput, gotMediaInputError);
}
});
}, _driver_id);
/* clang-format on */
return OK;
}
Error AudioDriverJavaScript::capture_stop() {
/* clang-format off */
EM_ASM({
if (_audioDriver_inputStream) {
const tracks = _audioDriver_inputStream.getTracks();
var ref = Module.IDHandler.get($0);
if (ref['stream']) {
const tracks = ref['stream'].getTracks();
for (var i = 0; i < tracks.length; i++) {
tracks[i].stop();
}
_audioDriver_inputStream = null;
ref['stream'] = null;
}
if (_audioDriver_audioInput) {
_audioDriver_audioInput.disconnect();
_audioDriver_audioInput = null;
if (ref['input']) {
ref['input'].disconnect();
ref['input'] = null;
}
});
}, _driver_id);
/* clang-format on */
input_buffer.clear();
@ -270,8 +293,9 @@ Error AudioDriverJavaScript::capture_stop() {
}
AudioDriverJavaScript::AudioDriverJavaScript() {
_driver_id = 0;
internal_buffer = NULL;
buffer_length = 0;
singleton = this;
}