How to run Pyxel with the latest Emscripten (3.1.46+)
kitao opened this issue · comments
Hi Pyxel users,
The current web version of Pyxel is built using Emscripten 3.1.45. The reason is that if you compile and run Pyxel with 3.1.46 or later, you will get the following error and audio will not play.
Uncaught TypeError: Cannot read properties of null (reading 'apply')
at dynCall (pyodide.asm.js:9:145551)
at SDL2.audio.scriptProcessorNode.onaudioprocess (eval at addEmAsm (pyodide.asm.js:9:148345), <anonymous>:1:323)
Does anyone have any information on how to solve this problem? I will also provide build instructions if you would like to actually try it. (You will need to modify some parts of build.rs to build Pyxel with the latest version of Emscripten)
The code in the area of this error looks like this:
var dynCall = (sig,ptr,args)=>{
var rtn = getWasmTableEntry(ptr).apply(null, args);
return rtn
}
It seems that in SDL2.audio.scriptProcessorNode.onaudioprocess, when retrieving a function from the function table, it is not found and an error is occurring.
Perhaps this change in Emscripten 3.1.46 is the reason, but I'm not sure:
The wasmTable global is now a JS library function that will only be included as needed.
Code that references wasmTable will no need to declare a dependency on it.
It can also be explictly included using -sEXPORTED_RUNTIME_METHODS=wasmTable.
I've already tried -sEXPORTED_RUNTIME_METHODS=wasmTable
option and nothing changed.
I am thinking that perhaps the callback function for audio defined on the Rust side is not registered in the wasmTable.
This code is a part of audio.rs in the pyxel-platform crate.
pub trait AudioCallback {
fn update(&mut self, out: &mut [i16]);
}
extern "C" fn c_audio_callback(userdata: *mut c_void, stream: *mut u8, len: c_int) {
let audio_callback = unsafe { &*userdata.cast::<Arc<Mutex<dyn AudioCallback>>>() };
let stream: &mut [i16] =
unsafe { slice::from_raw_parts_mut(stream.cast::<i16>(), len as usize / 2) };
audio_callback.lock().update(stream);
}
pub fn start_audio(
sample_rate: u32,
num_channels: u8,
num_samples: u16,
audio_callback: Arc<Mutex<dyn AudioCallback>>,
) {
let userdata = Box::into_raw(Box::new(audio_callback)).cast();
let desired = SDL_AudioSpec {
freq: sample_rate as i32,
format: AUDIO_S16 as u16,
channels: num_channels,
silence: 0,
samples: num_samples,
padding: 0,
size: 0,
callback: Some(c_audio_callback),
userdata,
};
let mut obtained = MaybeUninit::uninit();
platform().audio_device_id =
unsafe { SDL_OpenAudioDevice(null_mut(), 0, &desired, obtained.as_mut_ptr(), 0) };
if platform().audio_device_id == 0 {
println!("PyxelWarning: Failed to initialize audio device");
}
set_audio_enabled(true);
}
c_audio_callback
is registered for SDL2 by start_audio
and should be called from JavaScript code of SDL2.audio.scriptProcessorNode.onaudioprocess, but it failed to find c_audio_callback
from Emscripten function table.
With the latest Pyodide, this issue was addressed. Thank you.