diff --git a/Makefile b/Makefile index d3fa442bf..832f5e12a 100644 --- a/Makefile +++ b/Makefile @@ -56,11 +56,11 @@ install: pnpm install .PHONY: app -app: install whisper-wasm-cached +app: install whisper-wasm pnpm run build .PHONY: app-cached -app-cached: whisper-wasm-cached +app-cached: whisper-wasm if [ ! -f js/app/dist/index.html ] || [ ! -f js/web/dist/index.html ]; then $(MAKE) app; else echo "frontends already built, run make app to rebuild"; fi # Worker generation uses the workspace TypeScript compiler. app-cached can @@ -70,21 +70,6 @@ whisper-wasm: test -f node_modules/typescript/package.json || pnpm install bash hack/whisper-wasm/build.sh -# Releases use the checked-in emscripten build for this pinned revision, which -# needs no emsdk (e.g. on the iOS runner); the worker is still regenerated from -# source. `make whisper-wasm` rebuilds everything. -WHISPER_WASM_DIR = js/app/assets/whisper/4979e04f5dcaccb36057e059bbaed8a2f5288315 -.PHONY: whisper-wasm-cached -whisper-wasm-cached: - @if [ -s $(WHISPER_WASM_DIR)/caption-whisper.mjs ] && \ - [ -s $(WHISPER_WASM_DIR)/caption-whisper.wasm ]; then \ - echo "using pinned whisper WASM artifacts"; \ - test -f node_modules/typescript/package.json || pnpm install; \ - node hack/whisper-wasm/transpile-worker.mjs $(WHISPER_WASM_DIR); \ - else \ - $(MAKE) whisper-wasm; \ - fi - .PHONY: ci-ios ci-ios: version install app $(MAKE) ios diff --git a/docs/captions-whisper.md b/docs/captions-whisper.md index c6517e369..9b05ce50e 100644 --- a/docs/captions-whisper.md +++ b/docs/captions-whisper.md @@ -32,14 +32,12 @@ header supplies the standard power-throttling declarations missing from MinGW-w64 8's older SDK headers, without disabling ggml's API call. Cross-platform archive configuration does not run a target executable. -The frontend builds, including `make ci-ios`, reuse the committed emscripten -build (`caption-whisper.mjs` and `caption-whisper.wasm`) under -`js/app/assets/whisper/4979e04f5dcaccb36057e059bbaed8a2f5288315/` and only -regenerate `worker.js` from its TypeScript source, so they need no emsdk. They -rebuild everything only when the emscripten build is missing or empty. Run -`make whisper-wasm` explicitly after changing the WASM bridge; it builds with the -pinned emsdk on Linux or macOS. Source checksums use portable `sha256sum -c` -when available, otherwise macOS's `shasum -a 256 -c`. +The emscripten build of the WASM captioner is not committed: `make app` runs +`make whisper-wasm`, which builds it with the pinned emsdk on Linux or macOS +(including the iOS runner) and is incremental after the first run. The +whisper.cpp source tarball is checked by comparing its SHA-256 digest, since +macOS's `sha256sum` reads no check list from stdin and older macOS only has +`shasum`. ## Assets and licenses diff --git a/hack/whisper-wasm/build.sh b/hack/whisper-wasm/build.sh index 966b9719f..5e4068f36 100755 --- a/hack/whisper-wasm/build.sh +++ b/hack/whisper-wasm/build.sh @@ -20,12 +20,12 @@ source "$CACHE/emsdk/emsdk_env.sh" if [ ! -f "$CACHE/source.tar.gz" ]; then curl --fail --location --retry 3 "https://codeload.github.com/ggml-org/whisper.cpp/tar.gz/$REV" -o "$CACHE/source.tar.gz" fi -# Both GNU coreutils and BSD sha256sum accept -c; BSD rejects --check. -# macOS also ships shasum even when sha256sum is not installed. -if command -v sha256sum >/dev/null 2>&1; then - printf '%s %s\n' "$SOURCE_SHA256" "$CACHE/source.tar.gz" | sha256sum -c -else - printf '%s %s\n' "$SOURCE_SHA256" "$CACHE/source.tar.gz" | shasum -a 256 -c +# Compare the digest directly: macOS's sha256sum takes no check list on +# stdin, and older macOS only has shasum. +sum=$(shasum -a 256 "$CACHE/source.tar.gz" 2>/dev/null || sha256sum "$CACHE/source.tar.gz") +if [ "${sum%% *}" != "$SOURCE_SHA256" ]; then + echo "whisper.cpp source checksum mismatch: got ${sum%% *}, want $SOURCE_SHA256" >&2 + exit 1 fi if [ ! -f "$CACHE/source/CMakeLists.txt" ]; then mkdir -p "$CACHE/source" diff --git a/pkg/stt/bridge.c b/pkg/stt/bridge.c index b8e3dc414..51c33fe03 100644 --- a/pkg/stt/bridge.c +++ b/pkg/stt/bridge.c @@ -1,9 +1,19 @@ #include "bridge.h" #include +#if defined(__x86_64__) || defined(_M_X64) +#include +#endif int sp_whisper_supported_cpu(void) { #if defined(__x86_64__) || defined(_M_X64) - __builtin_cpu_init(); - return __builtin_cpu_supports("avx2") && __builtin_cpu_supports("fma") && __builtin_cpu_supports("f16c"); + // CPUID directly: __builtin_cpu_supports needs a runtime osxcross lacks, + // and GCC before 11 (the Windows cross compiler) can't test f16c with it. + unsigned a, b, c, d, xcr0; + if (!__get_cpuid(1, &a, &b, &c, &d) || !(c & bit_FMA) || !(c & bit_F16C) || !(c & bit_OSXSAVE)) { + return 0; + } + // AVX registers are usable only if the OS saves them (XCR0 bits 1 and 2). + __asm__("xgetbv" : "=a"(xcr0), "=d"(d) : "c"(0)); + return (xcr0 & 6) == 6 && __get_cpuid_count(7, 0, &a, &b, &c, &d) && (b & bit_AVX2); #else return 1; #endif diff --git a/subprojects/packagefiles/whisper/meson.build b/subprojects/packagefiles/whisper/meson.build index 65ad45d54..e8bee8af6 100644 --- a/subprojects/packagefiles/whisper/meson.build +++ b/subprojects/packagefiles/whisper/meson.build @@ -50,7 +50,12 @@ elif host_machine.system() == 'windows' '-static-libgcc', '-Wl,-Bstatic', '-lstdc++', '-lwinpthread', '-Wl,-Bdynamic', ]) elif get_option('default_library') == 'static' - stdlib = declare_dependency(link_args: '-static-libstdc++') + # -static-libstdc++ only reaches g++, which meson links release binaries + # with; cgo links `go test` binaries with gcc, which needs the explicit + # static library. + stdlib = declare_dependency(link_args: [ + '-static-libstdc++', '-Wl,-Bstatic', '-lstdc++', '-Wl,-Bdynamic', + ]) else stdlib = cpp.find_library('stdc++') endif