Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught Error: Converting base64 string to bytes failed. #20349

Closed
wxfred opened this issue Sep 28, 2023 · 37 comments
Closed

Uncaught Error: Converting base64 string to bytes failed. #20349

wxfred opened this issue Sep 28, 2023 · 37 comments

Comments

@wxfred
Copy link

wxfred commented Sep 28, 2023

Please include the following in your bug report:

Version of emscripten/emsdk:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.46 (1960782)
clang version 18.0.0 (https://github.com/llvm/llvm-project 75501f53624de92aafce2f1da698b249a7293dc7)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: C:\emsdk1\upstream\bin

I downloaded the entire emscripten project from github on 2023/9/28, the compiled xxx.wasmmodule.js by this new version came out a problem after being loaded: Uncaught Error: Converting base64 string to bytes failed.

I locate the problem and compare it with the emscripten I downloaded on 2022, in xxx.wasmmodule.js, the 2023 version is

// include: base64Utils.js
// Converts a string of base64 into a byte array.
// Throws error on invalid input.
function intArrayFromBase64(s) {

  try {
    var decoded = atob(s);
    var bytes = new Uint8Array(decoded.length);
    for (var i = 0 ; i < decoded.length ; ++i) {
      bytes[i] = decoded.charCodeAt(i);
    }
    return bytes;
  } catch (_) {
    throw new Error('Converting base64 string to bytes failed.');
  }
}

the 2022 version (which is no error) is

// Converts a string of base64 into a byte array.
// Throws error on invalid input.
function intArrayFromBase64(s) {

  try {
    var decoded = decodeBase64(s);
    var bytes = new Uint8Array(decoded.length);
    for (var i = 0 ; i < decoded.length ; ++i) {
      bytes[i] = decoded.charCodeAt(i);
    }
    return bytes;
  } catch (_) {
    throw new Error('Converting base64 string to bytes failed.');
  }
}

and emcc -v of the 2022 version is

emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.7 (48a16209b1a0de5efd8112ce6430415730008d18)
clang version 15.0.0 (https://github.com/llvm/llvm-project fbce4a78035c32792b0a13cf1f169048b822c06b)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: C:\emsdk\upstream\bin

the compile command is like

emcc -lembind `
    source/xxx  `
    -o dist/xxx.wasmmodule.js `
    -I xxx `
    -O1 `
    -s TOTAL_MEMORY=128MB `
    -s SINGLE_FILE=1 `
    -s BINARYEN_ASYNC_COMPILATION=0 `
    -s ENVIRONMENT=web `
    -s EXPORT_NAME=xxx `
    -Wno-c++11-narrowing -Wno-implicit-function-declaration `
    --post-js thirdparty/em-es6-module.js
@rbdannenberg
Copy link

I encountered the same problem with the same emcc 3.1.46. As a reality check, I tried the example here: https://github.com/GoogleChromeLabs/web-audio-samples/tree/gh-pages/audio-worklet/design-pattern/wasm-supersaw. One way to do this is:

  • download the whole repo.
  • cd to the gh-pages directory.
  • Serve that tree with python3 -m http.server 8000, and
  • visit http://localhost:8000/audio-worklet/design-pattern/wasm-supersaw

This example works, but if you use the corresponding Makefile in https://github.com/GoogleChromeLabs/web-audio-samples/tree/main/src/audio-worklet/design-pattern/wasm-supersaw to make a new synth.wasm.js and replace the existing one, the example no longer runs, and the error is the same "Converting base64 string to bytes failed" error.

To explore further, since there is a working synth.wasm.js in the repo, I took out the existing code to define atob() and in intArrayFrameBase64(s), I called decodeBase64() instead of atob(). I also added var decodeBase64 = typeof atob == 'function' ? atob : function (input) { ... } at the top of the file using the implementation from the repo version of synth.wasm.js. This seems to solve the immediate problem of decoding base 64.

However, execution proceeds and I now get the error:

Uncaught TypeError: this._module._malloc is not a function
    at HeapAudioBuffer._allocateHeap (wasm-audio-helper.js:67:34)
    at new HeapAudioBuffer (wasm-audio-helper.js:54:10)
    at new SynthProcessor (synth-processor.js:19:28)

and I see that _malloc is defined differently in the 2023 emscripten-generated synth.wasm.js than in the 2022 version, and many definitions follow the same pattern.

Is there any example with source code that builds a working audio worklet using wasm? I guess I can revert to emcc 3.1.11 which worked for me in May 2022.

@sbc100
Copy link
Collaborator

sbc100 commented Oct 2, 2023

It looks like your issue with HeapAudioBuffer._allocateHeap is that malloc is not being exported. It looks like you need to add _malloc to the -sEXPORTED_FUNCTIONS=... list.

@sbc100
Copy link
Collaborator

sbc100 commented Oct 2, 2023

@wxfred, can you set a breakpoint on the error to see what the root cause was? Is it that atob() is not available? If so, what platform are you running on?

sbc100 added a commit to sbc100/emscripten that referenced this issue Oct 2, 2023
It seems like the error is strictly worse than just letting the
exception propagate out.  For example, we have a report of someone
hitting this error but I can't tell what the root cause was:
emscripten-core#20349

This try/catch and this error message was part of the original
function back emscripten-core#5296 but I don't see any reason for them.
@rbdannenberg
Copy link

I'm using (recent) Google Chrome Version 117.0.5938.132 (Official Build) (arm64). Not sure what ECMAScript this implies.
Still working with wasm-supersaw example described above and emcc 3.1.46, the compiled synth.wasm.js tries to call atob(s) in intArrayFromBase(s) and throws an error because atob(s) is undefined at the point that intArrayFromBase(s) is called.

I found that atob is conditionally defined in synth.wasm.js just above intArrayFromBase in synth.wasm.js, but since the definition (or I guess more properly the binding of symbol atob) takes place in a top-level statement, hoisting does not ensure that things are defined properly.

When I manually moved the atob code near the top of synth.wasm.js (just after the "var Module = ..." declaration, but probably doesn't matter), the wasm-supersaw example runs.

Why is atob not defined? Near the end of the generated synth.wasm.js, it says:

// include: postamble.js
// === Auto-generated postamble setup entry stuff ===

followed by a conditionally binding of atob, which in simplified form looks like:

if (typeof atob == 'undefined') {
  if (typeof global != 'undefined' && typeof globalThis == 'undefined') {
    globalThis = global;
  }
  globalThis.atob = function(input) { ... }
}

Since moving this to the top of the file makes things work, it seems clear that it is defined too late.

In fact, looking at the call stack when the error occurs, the computation seems to be initiated by the declaration:

var wasmExports = createWasm();

which occurs before the definition of atob(). This explains why atob() is undefined.

Reverting to an earlier emscripten seems to require an earlier version of Python3 and I gave up on making it work.

The bottom line is I see no way to use emcc 3.1.46 for audio worklets. Can this be?

@sbc100
Copy link
Collaborator

sbc100 commented Oct 3, 2023

Ah, you are trying build and audio worklet. That makes sense why atob would be missing then. I was looking for -sAUDIO_WORKLET in your settings but I didn't see it.

I guess we need fix the https://github.com/GoogleChromeLabs/web-audio-samples to use that option since you are not the first person to try to build without -sAUDIO_WORKLET. See #19845 (comment).

@rbdannenberg
Copy link

When I added -sAUDIO_WORKLET emcc said to remove -sSINGLE_FILE and add -sWASM_WORKERS (I modified -sWASM to `-sWASM_WORKERS). So now there are 3 files produced: synth.wasm.js, synth.wasm.aw.js and synth.wasm.ww.js. The old code doesn't run, I assume because didn't do anything with the synth.wasm.aw.js and synth.wasm.ww.js. I'll point GoogleChromeLabs to this conversation. There's so much choreography involved in loading, initializing and communicating with an audio worklet, it would really help to have a working example. When the AUDIO_WORKLET option was created, was there any simple test, and is it available and working now? Thanks for all the pointers to far.

@sbc100
Copy link
Collaborator

sbc100 commented Oct 3, 2023

sbc100 added a commit to sbc100/emscripten that referenced this issue Oct 3, 2023
It seems like the error is strictly worse than just letting the
exception propagate out.  For example, we have a report of someone
hitting this error but I can't tell what the root cause was:
emscripten-core#20349

This try/catch and this error message was part of the original
function back emscripten-core#5296 but I don't see any reason for them.
sbc100 added a commit that referenced this issue Oct 3, 2023
It seems like the error is strictly worse than just letting the
exception propagate out.  For example, we have a report of someone
hitting this error but I can't tell what the root cause was:
#20349

This try/catch and this error message was part of the original
function back #5296 but I don't see any reason for them.
@rbdannenberg
Copy link

I was off for a week but spent today trying to make a wasm audio worklet. I looked at test/webaudio but I couldn't find any information about it or how to run it. I was able to use instructions in #20410 to compile it, and it generates a test.htm file, but it doesn't work because

Uncaught ReferenceError: SharedArrayBuffer is not defined
    <anonymous> http://localhost:8080/test.js:382

Eventually I found "test/runner interactive" to run the test and it actually runs, but things are so wrapped up inside a test framework, that it's very hard to understand. Even the source code is huge and automatically generated. I'm still looking for any example of a web audio wasm program.

@ad8e
Copy link
Contributor

ad8e commented Oct 14, 2023

SharedArrayBuffer is not defined

Make sure you're setting the CORS headers in your local web server. python3 -m http.server 8000 won't cut it.

@rbdannenberg
Copy link

I think I'm setting CORS headers correctly. At the point where the error occurs, windows.isSecureContext is true (see Feature Detection in https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts, referenced by https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet), although I'm not certain that's a correct or sufficient test.

@ad8e
Copy link
Contributor

ad8e commented Oct 14, 2023

I think I'm setting CORS headers correctly. At the point where the error occurs, windows.isSecureContext is true (see Feature Detection in https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts, referenced by https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet), although I'm not certain that's a correct or sufficient test.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements

@rbdannenberg
Copy link

rbdannenberg commented Oct 14, 2023

Good call. Even though I was using some python to set CORS headers, and I even copied the server code from someone working with audio worklets, crossOriginIsolated was false. It would be great for the emscripten-generated code to simply test or assert conditions for SharedArrayBuffer to exist before referencing it -- even trying to set headers properly and then test for proper conditions can both fail if my experience is any indication, and if the error message directly said CORS headers are not set, it would be much simpler to deal with. OK, back to work, and thanks again.

@sbc100
Copy link
Collaborator

sbc100 commented Oct 16, 2023

@rbdannenberg, is the reference to SharedArrayBuffer coming some emscripten code? Can you see what function it is part of? (I'm trying to add the assertion you mention but I can't see where that is coming from).

@rbdannenberg
Copy link

@sbc100, in emsdk/upstream/emscripten/test/webaudio, I was able to compile with

em++ audioworklet.c -s ENVIRONMENT='web','worker' -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -o web/test.html

Then, in web/test.js, there's a reference to SharedArrayBuffer.

@rbdannenberg
Copy link

Now that I can make and run a minimal wasm web audio module, I'm trying to figure out how to invoke a compiled C++ function in the scope of the audio node so that I can get information in and out. The AudioParam feature does not solve the problem because it provides only float-valued input signals and cannot, for example, represent a message such as [pitch, amp, mode, dur] either as input or output. Last year, I was using EMSCRIPTEN_BINDINGS to add a function interface to web audio modules, but the new code generated by -sAUDIO_WORKLET=1 is completely different, and I'm lost figuring out how to call it.

For more context, what worked before, based on Google's examples, created an Audio Worklet in Javascript that took audio input as JavaScript arrays, converted to float arrays, invoked a C++ function compiled to WASM to do DSP, converted results back to JavaScript float arrays, and finally returned to the Web Audio caller. It seemed inefficient to have a layer of JavaScript as a wrapper around a WASM function, but it was easy to export more WASM functions, and the JavaScript wrapper could be extended to post messages back and forth with the main JavaScript thread. It looks like the current -sAUDIO_WORKLET=1 scheme hands off the main WASM DSP function directly to Web Audio, so we do not have the intervening layer of JavaScript as an Audio Worklet that we can extend to call other WASM functions.

@ad8e
Copy link
Contributor

ad8e commented Oct 16, 2023

I'm trying to figure out how to invoke a compiled C++ function in the scope of the audio node so that I can get information in and out.

With emscripten, the audio thread behaves like a normal desktop audio thread, and you'll use a message passing system, like usual. For example, here is the audio thread for this webpage:

event_queue<vector<vector_note>, 8> emscripten_audio_incoming; //a single-reader, single-writer event queue of notes


EM_BOOL ProcessAudio(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* userData) {
	//the output is not initialized to 0. we manually zero it
	for (int o = 0; o < numOutputs; ++o)
		for (int ch = 0; ch < outputs[o].numberOfChannels; ++ch)
			for (int i = 0; i < 128; ++i) {
				outputs[o].data[ch * 128 + i] = 0;
			}

	if (emscripten_audio_incoming.elements_still()) {
		//get a message from the queue
		auto message = std::move(emscripten_audio_incoming.retrieve());
		for (auto& m : message) {
			//new message received in the audio thread, from javascript or elsewhere. process it
		}
	}

	current_tick += 128;

	// We generated audio and want to keep this processor going. Return EM_FALSE here to shut down.
	return EM_TRUE;
}

Note that ProcessAudio is in audioworklet.c: it's the same function.

If you need a SPSC queue, then pipewire has a correct one, cubeb has a correct one, and I have one, but many of the others online have subtle synchronization bugs. So you should copy from one of these three sources.

@rbdannenberg
Copy link

@ad8e, thanks for the pointer. I was able to see in the referenced javascript that it's as simple as calling Module.fnname -- I thought there was more isolation going on and never even guessed you could directly call into audio worker functions like that. Examples I've seen all posted messages to an audio worklet thread in JavaScript, but maybe that was just to deal with synchronization. I was able to complete the picture with EMSCRIPTEN_BINDINGS and -lembind. I'm writing a guide for anyone seeing this since there are apparently no working source code examples for web audio nodes using C++.

@ad8e
Copy link
Contributor

ad8e commented Oct 16, 2023

@ad8e, thanks for the pointer. I was able to see in the referenced javascript that it's as simple as calling Module.fnname -- I thought there was more isolation going on and never even guessed you could directly call into audio worker functions like that

You can't (or at least, I didn't). Module._fnname in that page is a normal C++ function, which creates a message and passes it to the audio worker; fnname doesn't run in the audio worker thread.

If you continue down this road, here was my compile command:

em++ emscripten.cpp -o "keyboard.html" --shell-file="keyfrag_shell.html" -std=c++2b -fno-rtti -fno-exceptions -s ENVIRONMENT='web','worker' -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$stringToNewUTF8' -sEXPORTED_FUNCTIONS='_malloc','_main','stringToNewUTF8' -sMALLOC=emmalloc -O3 -sFILESYSTEM=0 -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -s INCOMING_MODULE_JS_API='wasmMemory','instantiateWasm'

Some of those flags do necessary or helpful things.

@ad8e
Copy link
Contributor

ad8e commented Oct 16, 2023

And watch out that it's Module._fnname, not Module.fnname. You can see an outline of the cpp side here: example.cpp.txt

@rbdannenberg
Copy link

You can't (or at least, I didn't).

What I meant was you can directly call a function in the Module from outside the worker -- I didn't even think you could access the function or variables inside the module and was imagining relocatable code or base registers that had to be set to cross into the worker space. Online docs I've found sort of leave everything to your imagination and I was imagining much worse.

But I totally understand that these functions are not running on the audio thread, and I understand the fifo message passing scheme.

And watch out that it's Module._fnname, not Module.fnname.

That's interesting because I just created some tests and used this:

function call_soft() {
    Module.set_gain(0.1); 
}
function call_loud() {
    Module.set_gain(1.0); 
}

to call into a simple WASM audio worklet. It uses

float example3(int a, float b) {
    return a + b;
};

void set_gain(float g) {
    gain = g;
}

EMSCRIPTEN_BINDINGS(my_module) {
    emscripten::function("example3", &example3);
    emscripten::function("set_gain", &set_gain);
}

and -lembind in the compile command. I imagine it matters how the functions are exported.
Thanks!

@ad8e
Copy link
Contributor

ad8e commented Oct 16, 2023

Right; you are using embind and I'm exporting them raw. So you don't need the Module._ syntax.

@rbdannenberg
Copy link

rbdannenberg commented Oct 17, 2023

I have a context in JavaScript -- how do I pass it to emscripten_create_wasm_audio_worklet_processor_async or any C++ function? Alternatively, how can I create a wasm audio worklet processor from JavaScript? Does anyone know if emscriptenRegisterAudioObject() exists or is documented? Emscripten docs say "you would instead register that context to be visible to WebAssembly by calling the function emscriptenRegisterAudioObject()" but doesn't say how.

Update: For anyone reading, I found related discussion at #18853 (comment) and posted a similar question there.

@rbdannenberg
Copy link

For anyone reading, here are some working examples you might find useful: webaudiowasm-blog19oct2023.

@ad8e
Copy link
Contributor

ad8e commented Oct 19, 2023

MODULARIZE can create multiple modules, I think. https://emscripten.org/docs/tools_reference/emcc.html?highlight=modularize

No idea about sharing AudioContexts.

@nwsw
Copy link

nwsw commented Oct 31, 2023

emcc 3.1.47 
clang version 18.0.0 

Reiterating what was mentioned by rbdannenberg #20349 (comment):

Using build options:

-s ENVIRONMENT=shell \
-s SINGLE_FILE=1 \
-s WASM=1 \
-s WASM_ASYNC_COMPILATION=0 \

We use the resulting wasm js inside of a javascript AudioWorkletProcessor. The missing atob causes failure.

Function createWasm() is defined in src/preamble.js, uses instantiateSync(), which in our build, requires atob.
Function createWasm() is inserted into the wasm directly by emscripten.py:

module.append("var wasmExports = createWasm();\n")

The problem is that the atob polyfill has not yet been included when createWasm() is invoked. That doesn't happen until src/postamble.js ("base64Utils.js" -> "polyfill/atob.js")

It seems like the atob polyfill should really be instantiated earlier in the wasm js, since createWasm() relies on it. This would certainly solve the problem for us.

sbc100 added a commit to sbc100/emscripten that referenced this issue Nov 1, 2023
In this configuration the `atob` polfill is required but was not being
installed until after `createWasm` which (in the case of SINGLE_FILE)
depends on `atob`.

See emscripten-core#20349
@sbc100
Copy link
Collaborator

sbc100 commented Nov 1, 2023

Thanks @nwsw, that great summary of the issue allowed me to create a simple PR: #20587

sbc100 added a commit to sbc100/emscripten that referenced this issue Nov 1, 2023
In this configuration the `atob` polfill is required but was not being
installed until after `createWasm` which (in the case of SINGLE_FILE)
depends on `atob`.

See emscripten-core#20349
sbc100 added a commit that referenced this issue Nov 1, 2023
In this configuration the `atob` polfill is required but was not being
installed until after `createWasm` which (in the case of SINGLE_FILE)
depends on `atob`.

See #20349
@nwsw
Copy link

nwsw commented Nov 1, 2023

emcc 3.1.48-git (4de99429ecb854e346b32edc04cfa261c37b05d9)
clang version 18.0.0

I can confirm that #20587 solves our problem.

@wxfred
Copy link
Author

wxfred commented Nov 7, 2023

@sbc100 I updated to 3.1.48, the new problem appeared Uncaught ReferenceError: atob is not defined (running on Chrome).

// include: base64Utils.js
// Converts a string of base64 into a byte array (Uint8Array).
function intArrayFromBase64(s) {

    var decoded = atob(s);
    var bytes = new Uint8Array(decoded.length);
    for (var i = 0; i < decoded.length; ++i) {
        bytes[i] = decoded.charCodeAt(i);
    }
    return bytes;
}

and the difference between @nwsw's and my is ENVIRONMENT, my project is using ENVIRONMENT=web.

I compiled my project with these two settings, the result of ENVIRONMENT=web doesn't have the definition of atob while ENVIRONMENT=shell has.

@wxfred
Copy link
Author

wxfred commented Nov 7, 2023

@sbc100 The compiled result of ENVIRONMENT=shell has

// include: base64Utils.js
// include: polyfill/atob.js
// Copied from https://github.com/strophe/strophejs/blob/e06d027/src/polyfills.js#L149

// This code was written by Tyler Akins and has been placed in the
// public domain.  It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com

if (typeof atob == 'undefined') {
  if (typeof global != 'undefined' && typeof globalThis == 'undefined') {
    globalThis = global;
  }

  /**
   * Decodes a base64 string.
   * @param {string} input The string to decode.
   */
  globalThis.atob = function(input) {
    var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

    var output = '';
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
    // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
    do {
      enc1 = keyStr.indexOf(input.charAt(i++));
      enc2 = keyStr.indexOf(input.charAt(i++));
      enc3 = keyStr.indexOf(input.charAt(i++));
      enc4 = keyStr.indexOf(input.charAt(i++));

      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;

      output = output + String.fromCharCode(chr1);

      if (enc3 !== 64) {
        output = output + String.fromCharCode(chr2);
      }
      if (enc4 !== 64) {
        output = output + String.fromCharCode(chr3);
      }
    } while (i < input.length);
    return output;
  };
}

ENVIRONMENT=web doesn't have the codes above.

@sbc100
Copy link
Collaborator

sbc100 commented Nov 7, 2023

@wxfred if you are trying run the generated code in an audio worklet you currently either need to build -sENVIRONMENT=shell (or -sENVIRONMENT=shell,web I guess), or you need to build with -sAUDIO_WORKLET explicitly.

See https://github.com/emscripten-core/emscripten/issues/20581for more info. @juj is working on adding a new -sAUDIO_WORKLET=2 to address that issue.

@wxfred
Copy link
Author

wxfred commented Nov 8, 2023

@sbc100 Thanks for reply, yes, i'm using audio worklet. I followed your recommands and tried

  1. -sENVIRONMENT=shell,web causes another error Uncaught TypeError: this._module._malloc is not a function
    after adding -s EXPORTED_FUNCTIONS=_malloc , finally all problems solved!

  2. build with -s AUDIO_WORKLET=1 , and after solving some compilation errors below

#error "-sAUDIO_WORKLET does not support -sSINGLE_FILE"
#error "Building with -sAUDIO_WORKLET also requires enabling -sWASM_WORKERS!"
a "-sENVIRONMENT=" directive is specified, it must include "worker" as a target! (Try e.g. -sENVIRONMENT=web,worker)

I got hvraudio.wasmmodule.js:260 Uncaught ReferenceError: SharedArrayBuffer is not defined
This way is blocked

Although the first way is the solution, i'm curious about, why not just add atob definition when use -sENVIRONMENT=web ?

@sbc100
Copy link
Collaborator

sbc100 commented Nov 8, 2023

Although the first way is the solution, i'm curious about, why not just add atob definition when use -sENVIRONMENT=web ?

Because web browsers all have atob builtin.. the polyfill is only needed when you target very old browsers or environments like the d8 shell that don't have that builtin.

It also happens to be missing from the audio worklet environment but we don't (yet) have any way for your to tell emscripten that you are planning on running you code in an audio worklet (other than -sAUDIO_WORKLET but that apparently doesn't cover your use case).

@wxfred
Copy link
Author

wxfred commented Nov 8, 2023

@sbc100 Thanks, learned a lot.

@wxfred wxfred closed this as completed Nov 8, 2023
@Kutalia
Copy link

Kutalia commented Jan 18, 2024

I have a context in JavaScript -- how do I pass it to emscripten_create_wasm_audio_worklet_processor_async or any C++ function? Alternatively, how can I create a wasm audio worklet processor from JavaScript? Does anyone know if emscriptenRegisterAudioObject() exists or is documented? Emscripten docs say "you would instead register that context to be visible to WebAssembly by calling the function emscriptenRegisterAudioObject()" but doesn't say how.

Update: For anyone reading, I found related discussion at #18853 (comment) and posted a similar question there.

I was searching for the same answer. What I figured out is you can export similar functions from your C++ code like this:
EMSCRIPTEN_WEBAUDIO_T registerAudioContext() { return emscripten_create_audio_context(0); }
The function above will return an integer pointer. You can later reuse it when calling C++ functions.

@nick-thompson
Copy link

Hey @sbc100, I'm hitting the same atob is not defined issue as @wxfred mentioned in #20349 (comment). Setting -s ENVIRONMENT=shell or shell,web or shell,worker does not resolve the issue for me.

I'm in a similar situation, compiling via emscripten to produce code that will run in an audio worklet, but I don't want to use -s AUDIO_WORKLET=1 because I'm writing my audio worklet "glue" myself. Is there another way to resolve the issue around atob?

@sbc100
Copy link
Collaborator

sbc100 commented Feb 16, 2024

@nick-thompson can you share the full link command line you are using and also the generated JS code? Any time atob is part of the output and ENVIRONMENT includes shell we should be including the atob pollyfill code so I'm not sure what is going on here.

Another option is that you could simply include the atob polyfill yourself, either using --pre-js or some other mechanism.

@nick-thompson
Copy link

nick-thompson commented Feb 17, 2024

-lembind --closure 1 -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s MODULARIZE=1 -s ASSERTIONS=1 -s ENVIRONMENT=shell -s SINGLE_FILE=1 -s ALLOW_MEMORY_GROWTH=1

It looks like I was falling for the same thing as in #20587. I've updated to the latest Emscripten version and I get past the missing atob error.

I am hitting a separate issue now that I don't think is related but I'll write here in case it is; I'm now trying to polyfill a crypto variable to shim use of C++11 std::random_device, using --pre-js like you suggested there, and getting an error that it has already been defined: "Variable crypto declared more than once. First occurrence: externs.zip//w3c_webcrypto.js:833:4." When I don't try to polyfill, I get that runtime assertion I just linked. I'm happy to open a new issue for this if need be!

(Edit: I got around it by overwriting globalThis.crypto = in my polyfill. I'm not sure this is a proper solution but it seems to unblock me for now)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants