Shannon's Hatchet

something, but almost nothing

Friday, February 24, 2023

TypeScript modules With Emscripten and CMake, part 1

When I set out to create an NPM package for SoundSwallower, I was unable to find much relevant information in the Emscripten documentation or elsewhere on the Web, so I have written this guide, which will hopefully be useful to anyone doing similar things.

This is the first of a series of posts, which will be collected in a single table of contents when complete.

Should you read this guide?

If you have, or wish to write, a library written in C, or which exposes a C API, and which uses CMake as its build system, and you wish to package it:

  • as a CommonJS or ES6 module
  • for the Web and for Node.js
  • with type definitions for TypeScript
  • on npmjs.com

Then yes, you should read this guide!

If you have a library written in Rust, then you don’t need this guide, since you have much better documentation already.

If you have a library written in C++ which exposes a C++ API, you should really reconsider your life choices.

Prerequisites

I presume, here, that you know what WebAssembly is and roughly how it works, and that you have already installed:

I have only tested this stuff on Ubuntu 22.04 with Chrome and Firefox, if there is anything missing to make it work on reasonable development environments elsewhere, let me know. It ought to work with Windows Subsystem for Linux or MSYS2, and probably can work on MacOS as well.

Overview

As I mentioned, this guide comes from my experience with SoundSwallower, and if you want, you can just go and see how I did things there. Because it’s a fairly complicated library and API, I won’t use it for this guide, but rather something simpler, which even has “simple” in its name, specifically Kiss FFT, a library which implements the Fast Fourier Transform (FFT from here on).

Although, predictably, a complete WebAssembly package for Kiss FFT is already available on npm, the goal here is not to compete with that package, but to demonstrate the workings of Emscripten and the process of integrating a WebAssembly build into a CMake build system. If you just want to do an FFT in your application, I strongly recommend using that existing package!

We will go through the process of building a library, then we will test this library in an excessively simple web application, which records from your microphone and displays a spectrogram.

Why would you ever want to do this? Well, the Web Audio API, in its infinite wisdom, provides an FFT implementation that is useless for anything other than making an animated VU meter, and though there is a good and fast FFT implementation inside WebRTC, there is no way to access it, or any of the other useful WebRTC functionality like voice activity detection from JavaScript. So, if we want to do something actually useful, we have no choice but to reimplement an FFT, either in JavaScript (this works just fine, and is what wavesurfer.js does) or in some other language, which we will compile to WebAssembly (or JavaScript). As you can guess, we will do the latter, since that’s the subject of this guide!

Since we are keeping this simple, we will create a module which wraps exactly one API, namely the real-valued FFT in kiss_fftr.h. Nonetheless, this is a very typical C API, so it should be applicable to various other libraries.

Initial build

First, let’s make sure that we can actually build Kiss FFT with Emscripten. To check out the source code and configure it to build in a subdirectory called jsbuild:

git clone https://github.com/mborgerding/kissfft.git
cd kissfft
# This is expected to fail
emcmake cmake -S . -B jsbuild -DCMAKE_BUILD_TYPE=Debug

Oops! That didn’t work at all, because it depends on some other libraries that Emscripten doesn’t know about. We are not going to port these libraries here - instead, we’ll look in the README, which helpfully tells us how to disable those parts of the build, and also to build it as a static library only:

emcmake cmake -S . -B jsbuild -DCMAKE_BUILD_TYPE=Debug \
    -DKISSFFT_TOOLS=OFF -DKISSFFT_STATIC=ON -DKISSFFT_TEST=OFF

Much better! We can build the library, giving us a file called libkissfft-float.a which we don’t exactly know what to do with:

cmake --build jsbuild

This is not a WebAssembly file, nor is it a JavaScript file, but some kind of intermediate representation that Emscripten will turn into one or both of them. To produce a JavaScript/WebAssembly library, we have to “link” it into a JavaScript module. Ultimately we’ll set up CMake to do this for us, but just to try it out, you can run:

emcc -o kissfft.js jsbuild/libkissfft-float.a -sMODULARIZE=1

This will produce the files kissfft.js and kissfft.wasm in jsbuild. You should even be able to import this module in the Node REPL:

> require("./kissfft.js");

Hooray! You have built a module … or have you?

On further inspection you’ll notice that kissfft.wasm is suspiciously small. Indeed, if you “disassemble” it (actually just convert it to text format) using this handy online tool, you will see some Emscripten runtime functions… and definitely nothing Kiss FFT related. If you peruse the copious boilerplate in kisfft.js, same thing. What the h*ck?

What’s next

In the next post, we will learn how to export functions from C code so they are accessible to JavaScript code, how to pass blocks of data from JavaScript TypedArray objects to C functions, and how to handle returned blocks of data.

Friday, February 10, 2023

Il n'y a rien là!

What am I working on lately?

Mostly ReadAlongs Web (or see the demo). Things I’ve learned along the way are:

  • Dependency management in the JavaScript world is, as they say, a real hell, and somehow even worse than in the Python world, which seems to have made great strides in cleaning up its act with regard to packaging.
  • Package lockfiles do not really help. Perhaps there will be a full-fledged “opinion piece” on this later.
  • Webpack, for all the complaining it attracts, is a mature and well-maintained piece of software that really tries hard to do the right thing, and we should be kind to it.
  • Too many so-called “frameworks” in this so-called “ecosystem”. Most JavaScript-related project webpages are 80% hype, 10% rows and rows of icons for other projects, and 10% buzzwords.
  • Angular, for all the complaining it attracts, is fairly good, if only because it does not use JSX/TSX templating, which is deeply distasteful, or any other weird magic, with the exception of sigh dependency injection, which is thoroughly unnecessary. Explicit is always better than implicit (says the Python programmer).
  • There are few problems solved by ReactiveX that can’t be solved async, await, and generators, and those ones are probably not worth solving.

This is all in preparation for our very exciting workshop which will be presented (virtually) at ICLDC!

Aside from that … stay tuned for various WFST-related things in your browser. More to come.