Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .github/workflows/plugins_wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,33 @@ concurrency:
jobs:
package:
runs-on: ubuntu-latest
timeout-minutes: 60
timeout-minutes: 90
env:
CSOUND_WASM_PACKAGE: ${{ github.workspace }}/.csound/platform/wasm-wasi
steps:
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@v7.1.0
with:
node-version: 22
node-version: 24
cache: npm
- uses: cachix/install-nix-action@v31.11.1
- uses: cachix/cachix-action@v17
with:
name: csound
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
skipPush: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
# Depends on csound/csound#3556. Replace this checkout with published
# package pins once the matching browser and wasm-bin releases exist.
- uses: actions/checkout@v7.0.1
with:
repository: hlolli/csound
ref: 623c6e8fa1f76ed02fc74dfe178061e7b6ef7aec
path: .csound
persist-credentials: false
- name: Build the upstream Preview 2 engines and SDKs
run: bash wasm/scripts/prepare-csound.sh
- name: Install JS dependencies
run: npm ci
- name: Build WASM plugins with CMake and Nix
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,16 @@ platform facilities the browser does not provide: fluidOpcodes (FluidSynth),
hdf5, faust (libfaust), websockets, py (Python), wiimote, p5glove, CUDA and
OpenCL.

The `@csound/plugins` package exposes one asset URL per WASM plugin:
The default entry and `@csound/plugins/wasm32` expose wasm32 float plugins.
Use `@csound/plugins/wasm64` with the opt-in `@csound/browser/wasm64.js` engine.
Each entry exports one `plugins` object with these URLs:

| Export | Plugin | File |
| Export | wasm32 file | wasm64 file |
| --- | --- | --- |
| `plugins.chua` | chua | `chua.wasm` |
| `plugins.image` | image | `image.wasm` |
| `plugins.linearAlgebra` | linear_algebra | `linear_algebra.wasm` |
| `plugins.stk` | STK | `stkops.wasm` |
| `plugins.chua` | `chua.wasm` | `chua-wasm64.wasm` |
| `plugins.image` | `image.wasm` | `image-wasm64.wasm` |
| `plugins.linearAlgebra` | `linear_algebra.wasm` | `linear_algebra-wasm64.wasm` |
| `plugins.stk` | `stkops.wasm` | `stkops-wasm64.wasm` |

The URLs are passed to the runtime's `withPlugins` option, e.g.:

Expand All @@ -97,7 +99,7 @@ const csound = await Csound({ withPlugins: [plugins.chua, plugins.image] });
```

Unlike the native build, the WASM STK plugin embeds its raw-wave samples in
`stkops.wasm` (`STK_EMBED_RAWWAVES` defaults to ON for WASM): at initialization
each target's `stkops` binary (`STK_EMBED_RAWWAVES` defaults to ON for WASM): at initialization
it writes them to Csound's virtual filesystem and sets the wave path, so no
separate download or `RAWWAVE_PATH` setup is needed. An explicit `RAWWAVE_PATH`
still overrides the embedded samples.
Expand Down
23 changes: 13 additions & 10 deletions THIRD_PARTY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ The package also contains code from these libraries:

| Library | Used by | License notices |
| --- | --- | --- |
| Eigen | Chua | `dist/licenses/eigen/` |
| GMM++ (GetFEM) | Linear algebra | `dist/licenses/gmm.txt`; LGPL-3.0-or-later with the exception in the headers |
| libpng | Image | `dist/licenses/libpng.txt` |
| zlib | Image | `dist/licenses/zlib.txt` |
| STK and its raw-wave samples | STK instruments | `dist/licenses/stk.txt` |
| wasi-libc, musl, cloudlibc, musl-fts | C runtime | `dist/licenses/wasi-libc/` |
| LLVM libc++, libc++abi, libunwind, compiler-rt | C++ runtime and compiler support | `dist/licenses/libcxx.txt`, `libcxxabi.txt`, `libunwind.txt`, `compiler-rt.txt` |
| Eigen | Chua | `dist/licenses/TARGET/eigen/` |
| GMM++ (GetFEM) | Linear algebra | `dist/licenses/TARGET/gmm.txt`; LGPL-3.0-or-later with the exception in the headers |
| libpng | Image | `dist/licenses/TARGET/libpng.txt` |
| zlib | Image | `dist/licenses/TARGET/zlib.txt` |
| STK and its raw-wave samples | STK instruments | `dist/licenses/TARGET/stk.txt` |
| wasi-libc, musl, cloudlibc, musl-fts | C runtime | `dist/licenses/TARGET/wasi-libc/` |
| LLVM libc++, libc++abi, libunwind, compiler-rt | C++ runtime and compiler support | `dist/licenses/TARGET/libcxx.txt`, `libcxxabi.txt`, `libunwind.txt`, `compiler-rt.txt` |

The Nix build copies these notices from the source used to build each library.
`TARGET` is `wasm32` or `wasm64`.
[`wasm/nixpkgs.nix`](https://github.com/csound/plugins/blob/develop/wasm/nixpkgs.nix)
pins their source versions. The GMM++ headers and STK sources and samples live
in this repository. The build uses upstream CMake and make options for static
PIC libraries, plus a small CMake target for LLVM's WASM unwinder; it adds no
source patches. `wasm/default.nix` records those options.
in this repository. The build uses static PIC libraries and a small CMake target
for LLVM's WASM unwinder. It patches wasi-libc's CMake file to omit allocator
objects and map paths into each plugin's own descriptor table. It also uses the
upstream Csound toolchain's wasm64 libc and compiler-rt
patches. `wasm/default.nix` and the selected Csound toolchain record those changes.
14 changes: 3 additions & 11 deletions cmake/WasmPlugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(USE_COMPILER_OPTIMIZATIONS OFF CACHE BOOL "" FORCE)
set(REQUIRE_PTHREADS OFF CACHE BOOL "" FORCE)
set(USE_DOUBLE ON CACHE BOOL "" FORCE)
if(NOT WASM_MAX_MEMORY)
message(FATAL_ERROR "WASM_MAX_MEMORY must match the shared-memory Csound engine")
endif()
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-libgcc-file-name
OUTPUT_VARIABLE WASM_BUILTINS OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
Expand All @@ -14,9 +10,10 @@ function(configure_wasm_plugin target)
set_target_properties(${target} PROPERTIES SUFFIX ".wasm")
target_compile_features(${target} PRIVATE c_std_11)
target_compile_definitions(${target} PRIVATE
USE_DOUBLE=1 _WASI_EMULATED_SIGNAL _WASI_EMULATED_MMAN)
_WASI_EMULATED_SIGNAL _WASI_EMULATED_MMAN)
# The matching SDK float-version.h defines sample precision.
target_compile_options(${target} PRIVATE
-fPIC -pthread "SHELL:-mllvm -wasm-enable-sjlj"
-fPIC "SHELL:-mllvm -wasm-enable-sjlj"
"SHELL:-mllvm -wasm-use-legacy-eh=false"
"$<$<COMPILE_LANGUAGE:C>:-std=gnu11>"
"$<$<COMPILE_LANGUAGE:CXX>:-nostdinc++>"
Expand All @@ -34,12 +31,7 @@ function(configure_wasm_plugin target)
"LINKER:--no-entry"
"LINKER:--import-table"
"LINKER:--import-memory"
"LINKER:--shared-memory"
"LINKER:--max-memory=${WASM_MAX_MEMORY}"
"LINKER:--allow-undefined"
"LINKER:--export=__wasm_init_tls"
"LINKER:--export=__tls_size"
"LINKER:--export=__tls_align"
"LINKER:--export=__wasm_call_ctors"
"LINKER:--export-if-defined=__wasm_apply_data_relocs"
"LINKER:--export-if-defined=csound_opcode_init"
Expand Down
9 changes: 2 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
/** Asset URLs for Csound 7's `withPlugins` option. */
export const plugins = Object.freeze({
chua: new URL("./dist/chua.wasm", import.meta.url).href,
image: new URL("./dist/image.wasm", import.meta.url).href,
linearAlgebra: new URL("./dist/linear_algebra.wasm", import.meta.url).href,
stk: new URL("./dist/stkops.wasm", import.meta.url).href,
});
/** The default browser engine uses wasm32 with float samples. */
export { plugins } from "./wasm32.js";
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,37 @@
"access": "public"
},
"engines": {
"node": ">=22.13.0"
"node": ">=24"
},
"files": [
"dist",
"index.js",
"index.d.ts",
"LICENSE",
"THIRD_PARTY.md",
"wasm/README.md"
"wasm/README.md",
"wasm32.js",
"wasm64.js"
],
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
},
"./dist/*": "./dist/*",
"./package.json": "./package.json"
"./package.json": "./package.json",
"./wasm32": {
"types": "./index.d.ts",
"import": "./wasm32.js"
},
"./wasm64": {
"types": "./index.d.ts",
"import": "./wasm64.js"
}
},
"scripts": {
"build": "bash wasm/scripts/build.sh",
"test": "node --test wasm/tests/*.test.js",
"test": "node --experimental-wasm-exnref --test wasm/tests/*.test.js",
"prepack": "npm test"
},
"devDependencies": {
Expand Down
20 changes: 10 additions & 10 deletions src/linear_algebra/linear_algebra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,13 +373,13 @@ extern "C" {

using namespace csound;

static std::ostream &operator<<(std::ostream &o, const std::vector<double> &m) {
static std::ostream &operator<<(std::ostream &o, const std::vector<cs_float> &m) {
gmm::write(o, m);
return o;
}

static std::ostream &operator<<(std::ostream &o,
const std::vector<std::complex<double>> &m) {
const std::vector<std::complex<cs_float>> &m) {
gmm::write(o, m);
return o;
}
Expand Down Expand Up @@ -3783,7 +3783,7 @@ class la_i_qr_eigen_mr_t : public OpcodeBase<la_i_qr_eigen_mr_t> {
toa(lhs_eigenvalues_, lhs_eigenvalues);
toa(rhs_A_, rhs_A);
gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr,
double(*itolerance));
*itolerance);
return OK;
}
};
Expand All @@ -3802,7 +3802,7 @@ class la_k_qr_eigen_mr_t : public OpcodeBase<la_k_qr_eigen_mr_t> {
}
int32_t kontrol(CSOUND *) {
gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr,
double(*ktolerance));
*ktolerance);
return OK;
}
};
Expand All @@ -3818,7 +3818,7 @@ class la_i_qr_eigen_mc_t : public OpcodeBase<la_i_qr_eigen_mc_t> {
toa(lhs_eigenvalues_, lhs_eigenvalues);
toa(rhs_A_, rhs_A);
gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc,
double(*itolerance));
*itolerance);
return OK;
}
};
Expand All @@ -3837,7 +3837,7 @@ class la_k_qr_eigen_mc_t : public OpcodeBase<la_k_qr_eigen_mc_t> {
}
int32_t kontrol(CSOUND *) {
gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc,
double(*ktolerance));
*ktolerance);
return OK;
}
};
Expand All @@ -3856,7 +3856,7 @@ class la_i_qr_sym_eigen_mr_t : public OpcodeBase<la_i_qr_sym_eigen_mr_t> {
toa(lhs_eigenvectors_, lhs_eigenvectors);
toa(rhs_A_, rhs_A);
gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr,
lhs_eigenvectors->mr, double(*itolerance));
lhs_eigenvectors->mr, *itolerance);
return OK;
}
};
Expand All @@ -3878,7 +3878,7 @@ class la_k_qr_sym_eigen_mr_t : public OpcodeBase<la_k_qr_sym_eigen_mr_t> {
}
int32_t kontrol(CSOUND *) {
gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr,
lhs_eigenvectors->mr, double(*ktolerance));
lhs_eigenvectors->mr, *ktolerance);
return OK;
}
};
Expand All @@ -3897,7 +3897,7 @@ class la_i_qr_sym_eigen_mc_t : public OpcodeBase<la_i_qr_sym_eigen_mc_t> {
toa(lhs_eigenvectors_, lhs_eigenvectors);
toa(rhs_A_, rhs_A);
gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc,
lhs_eigenvectors->mc, double(*itolerance));
lhs_eigenvectors->mc, *itolerance);
return OK;
}
};
Expand All @@ -3919,7 +3919,7 @@ class la_k_qr_sym_eigen_mc_t : public OpcodeBase<la_k_qr_sym_eigen_mc_t> {
}
int32_t kontrol(CSOUND *) {
gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc,
lhs_eigenvectors->mc, double(*ktolerance));
lhs_eigenvectors->mc, *ktolerance);
return OK;
}
};
Expand Down
Loading
Loading