当前位置:AIGC资讯 > AIGC > 正文

webassembly002 whisper.wasm wasm_eval 与js代码交互 EMSCRIPTEN_BINDINGS,Module

# build using Emscripten
git clone https://github.com/ggerganov/whisper.cpp
cd whisper.cpp
mkdir build-em && cd build-em
emcmake cmake ..
make -j

# copy the produced page to your HTTP path
cp bin/whisper.wasm/*    /path/to/html/
cp bin/libmain.worker.js /path/to/html/
$ emcmake cmake ..
configure: cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/pdd/Downloads/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CROSSCOMPILING_EMULATOR=/home/pdd/Downloads/emsdk/node/16.20.0_64bit/bin/node
-- Found Git: /usr/bin/git (found version "2.34.1") 
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- CMAKE_SYSTEM_PROCESSOR: x86
-- x86 detected
-- Embedding WASM inside whisper.js
-- SDL2_INCLUDE_DIRS = /home/pdd/Downloads/emsdk/upstream/emscripten/cache/sysroot/include/SDL2
-- SDL2_LIBRARIES = -sUSE_SDL=2
-- Embedding WASM inside main.js
-- Embedding WASM inside stream.js
-- Embedding WASM inside command.js
-- Embedding WASM inside talk.js
-- Embedding WASM inside bench.js
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pdd/le/whisper.cpp-1.5.0/build-em
$ make -j
[ 20%] Building C object CMakeFiles/whisper.dir/ggml-alloc.c.o
[ 20%] Building C object CMakeFiles/whisper.dir/ggml-backend.c.o
[ 20%] Building C object CMakeFiles/whisper.dir/ggml.c.o
[ 20%] Building CXX object examples/CMakeFiles/common-sdl.dir/common-sdl.cpp.o
[ 20%] Building C object CMakeFiles/whisper.dir/ggml-quants.c.o
[ 24%] Building CXX object CMakeFiles/whisper.dir/whisper.cpp.o
In file included from /home/pdd/le/whisper.cpp-1.5.0/examples/common-sdl.cpp:1:
In file included from /home/pdd/le/whisper.cpp-1.5.0/examples/common-sdl.h:3:
/home/pdd/Downloads/emsdk/upstream/emscripten/cache/sysroot/include/fakesdl/SDL.h:1:2: error: "To use the emscripten port of SDL use -sUSE_SDL or -sUSE_SDL=2"
    1 | #error "To use the emscripten port of SDL use -sUSE_SDL or -sUSE_SDL=2"
以上错误的原因是我在文章“webassembly003 whisper.cpp的main项目-1https://blog.csdn.net/ResumeProject/article/details/135584313”中想使用SDL,所以设置了:
              set(WHISPER_SDL2 ON)#option(WHISPER_SDL2                   "whisper: support for libSDL2" OFF)
现在改回来option(WHISPER_SDL2  "whisper: support for libSDL2" OFF)或者直接set(WHISPER_SDL2 OFF)即可
$ make -j
-- CMAKE_SYSTEM_PROCESSOR: x86
-- x86 detected
-- Embedding WASM inside whisper.js
-- Embedding WASM inside main.js
-- Embedding WASM inside stream.js
-- Embedding WASM inside command.js
-- Embedding WASM inside talk.js
-- Embedding WASM inside bench.js
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pdd/le/whisper.cpp-1.5.0/build-em
Consolidate compiler generated dependencies of target whisper
[ 26%] Built target whisper
[ 43%] Building CXX object examples/stream.wasm/CMakeFiles/libstream.dir/emscripten.cpp.o
[ 43%] Building CXX object bindings/javascript/CMakeFiles/libwhisper.dir/emscripten.cpp.o
[ 43%] Building CXX object examples/bench.wasm/CMakeFiles/libbench.dir/emscripten.cpp.o
[ 43%] Building CXX object examples/CMakeFiles/common.dir/common.cpp.o
[ 47%] Building CXX object examples/whisper.wasm/CMakeFiles/libmain.dir/emscripten.cpp.o
[ 52%] Building CXX object examples/CMakeFiles/common.dir/grammar-parser.cpp.o
[ 56%] Building CXX object examples/CMakeFiles/common.dir/common-ggml.cpp.o
/home/pdd/le/whisper.cpp-1.5.0/examples/whisper.wasm/emscripten.cpp:105:52: warning: initialized lambda captures are a C++14 extension [-Wc++14-extensions]
  105 |             g_worker = std::thread([index, params, pcmf32 = std::move(pcmf32)]() {
      |                                                    ^
/home/pdd/le/whisper.cpp-1.5.0/examples/stream.wasm/emscripten.cpp:109:35: warning: unused variable 't0' [-Wunused-variable]
  109 |                     const int64_t t0 = whisper_full_get_segment_t0(ctx, i);
      |                                   ^~
/home/pdd/le/whisper.cpp-1.5.0/examples/stream.wasm/emscripten.cpp:110:35: warning: unused variable 't1' [-Wunused-variable]
  110 |                     const int64_t t1 = whisper_full_get_segment_t1(ctx, i);
      |                                   ^~
/home/pdd/le/whisper.cpp-1.5.0/examples/stream.wasm/emscripten.cpp:155:74: warning: unused parameter 'index' [-Wunused-parameter]
  155 |     emscripten::function("free", emscripten::optional_override([](size_t index) {
      |                                                                          ^
[ 60%] Linking CXX executable ../../bin/libbench.js
1 warning generated.
[ 65%] Linking CXX executable ../../bin/libmain.js
[ 69%] Linking CXX executable ../../bin/libwhisper.js
cache:INFO: generating system asset: symbol_lists/86ce62757fe763e6970094b2fb6256b6a9303462.json... (this will be cached in "/home/pdd/Downloads/emsdk/upstream/emscripten/cache/symbol_lists/86ce62757fe763e6970094b2fb6256b6a9303462.json" for subsequent builds)
3 warnings generated.
[ 73%] Linking CXX executable ../../bin/libstream.js
em++: warning: -pthread + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth]
cache:INFO:  - ok
cache:INFO: generating system asset: symbol_lists/48222c973266f0adfbab77d6354fb223bdb90ef2.json... (this will be cached in "/home/pdd/Downloads/emsdk/upstream/emscripten/cache/symbol_lists/48222c973266f0adfbab77d6354fb223bdb90ef2.json" for subsequent builds)
cache:INFO:  - ok
warning: deprecated item in EXPORTED_RUNTIME_METHODS: print use out instead.
warning: deprecated item in EXPORTED_RUNTIME_METHODS: printErr use err instead.
em++: warning: warnings in JS library compilation [-Wjs-compiler]
warning: deprecated item in EXPORTED_RUNTIME_METHODS: print use out instead.
warning: deprecated item in EXPORTED_RUNTIME_METHODS: printErr use err instead.
em++: warning: warnings in JS library compilation [-Wjs-compiler]
warning: deprecated item in EXPORTED_RUNTIME_METHODS: print use out instead.
warning: deprecated item in EXPORTED_RUNTIME_METHODS: printErr use err instead.
em++: warning: warnings in JS library compilation [-Wjs-compiler]
[ 78%] Linking CXX static library libcommon.a
[ 78%] Built target common
[ 82%] Building CXX object examples/command.wasm/CMakeFiles/libcommand.dir/emscripten.cpp.o
[ 86%] Building CXX object examples/talk.wasm/CMakeFiles/libtalk.dir/emscripten.cpp.o
[ 91%] Building CXX object examples/talk.wasm/CMakeFiles/libtalk.dir/gpt-2.cpp.o
/home/pdd/le/whisper.cpp-1.5.0/examples/talk.wasm/gpt-2.cpp:130:31: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
  130 |             fin.read((char *) word.data(), len);
      |                               ^
/home/pdd/le/whisper.cpp-1.5.0/examples/command.wasm/emscripten.cpp:266:74: warning: unused parameter 'index' [-Wunused-parameter]
  266 |     emscripten::function("free", emscripten::optional_override([](size_t index) {
      |                                                                          ^
/home/pdd/le/whisper.cpp-1.5.0/examples/talk.wasm/emscripten.cpp:294:74: warning: unused parameter 'index' [-Wunused-parameter]
  294 |     emscripten::function("free", emscripten::optional_override([](size_t index) {
      |                                                                          ^
/home/pdd/le/whisper.cpp-1.5.0/examples/talk.wasm/emscripten.cpp:327:81: warning: unused parameter 'index' [-Wunused-parameter]
  327 |     emscripten::function("force_speak", emscripten::optional_override([](size_t index) {
      |                                                                                 ^
1 warning generated.
[ 95%] Linking CXX executable ../../bin/libcommand.js
1 warning generated.
warning: deprecated item in EXPORTED_RUNTIME_METHODS: print use out instead.
warning: deprecated item in EXPORTED_RUNTIME_METHODS: printErr use err instead.
em++: warning: warnings in JS library compilation [-Wjs-compiler]
2 warnings generated.
[100%] Linking CXX executable ../../bin/libtalk.js
cache:INFO: generating system asset: symbol_lists/861182d6f58950bacb50ecea0d73a4f4991e3033.json... (this will be cached in "/home/pdd/Downloads/emsdk/upstream/emscripten/cache/symbol_lists/861182d6f58950bacb50ecea0d73a4f4991e3033.json" for subsequent builds)
cache:INFO:  - ok
[100%] Built target libbench
warning: deprecated item in EXPORTED_RUNTIME_METHODS: print use out instead.
warning: deprecated item in EXPORTED_RUNTIME_METHODS: printErr use err instead.
em++: warning: warnings in JS library compilation [-Wjs-compiler]
[100%] Built target libmain
[100%] Built target libstream
[100%] Built target libwhisper
[100%] Built target libcommand
[100%] Built target libtalk
$ tree ./bin
./bin
├── bench.wasm
│   ├── bench.js
│   ├── helpers.js
│   └── index.html
├── command.wasm
│   ├── command.js
│   ├── helpers.js
│   └── index.html
├── libbench.js
├── libbench.worker.js
├── libcommand.js
├── libcommand.worker.js
├── libmain.js
├── libmain.worker.js
├── libstream.js
├── libstream.worker.js
├── libtalk.js
├── libtalk.worker.js
├── libwhisper.js
├── libwhisper.worker.js
├── stream.wasm
│   ├── helpers.js
│   ├── index.html
│   └── stream.js
├── talk.wasm
│   ├── helpers.js
│   ├── index.html
│   └── talk.js
└── whisper.wasm
    ├── helpers.js
    ├── index.html
    └── main.js

5 directories, 27 files

CODE

基础知识

EMSCRIPTEN_BINDINGS

官方文档:https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html EMSCRIPTEN_BINDINGS可创建函数、类(抽象类,这些抽象类可以在JavaScript中被重写)、值类型、指针 (包括原始和智能指针)、枚举和常量的绑定。通过使用这个宏,你可以在 C++ 代码中声明哪些部分需要在 JavaScript 中可见和可调用。 类型 Emscripten 绑定关键字和宏 示例 函数 emscripten::function emscripten::function("function_name", &cpp_function)emscripten::class_ emscripten::class_<MyClass>("MyClass") 抽象类 emscripten::abstract_class emscripten::abstract_class<AbstractClass>("AbstractClass") 抽象类函数重写 EM_JSEM_JS(void, overrideFunction, (MyClass* obj), { /* JavaScript implementation */ }); 值类型 emscripten::value_object emscripten::value_object<MyStruct>("MyStruct") 指针 emscripten::register_pointer emscripten::register_pointer<T>("T*") 智能指针 emscripten::smart_ptr emscripten::smart_ptr<std::shared_ptr<MyClass>>("shared_ptr<MyClass>") 原始指针 emscripten::raw_pointer emscripten::raw_pointer("MyClass*") 枚举 emscripten::enum_ emscripten::enum_<MyEnum>("MyEnum") 常量 emscripten::constant emscripten::constant("MY_CONSTANT", MY_CONSTANT_VALUE) 嵌入式 JavaScript 代码块 EM_ASM宏 这个宏允许你在 C++ 代码中直接插入 JavaScript 代码,并在编译时将其嵌入到生成的 JavaScript 代码中。 以下是一个 MyClass 类使用示例:
#include <emscripten.h>
#include <emscripten/bind.h>

class MyClass {
public:
    MyClass(int value) : value(value) {}

    int getValue() const {
        return value;
    }

    void setValue(int newValue) {
        value = newValue;
    }

private:
    int value;
};

// 使用 EMSCRIPTEN_BINDINGS 宏定义 Emscripten 绑定
EMSCRIPTEN_BINDINGS(my_module) {
    // 绑定 MyClass 类,使其在 JavaScript 中可用
    emscripten::class_<MyClass>("MyClass")
        .constructor<int>()                 // 构造函数绑定
        .function("getValue", &MyClass::getValue)  // 成员函数绑定
        .function("setValue", &MyClass::setValue); // 成员函数绑定
}

// 示例函数,演示在 JavaScript 中如何使用绑定的 MyClass 类
int main() {
    // 在 JavaScript 中可以这样调用 MyClass 类
    // var myObject = new MyClass(42);
    // console.log(myObject.getValue());  // 输出: 42
    // myObject.setValue(100);
    // console.log(myObject.getValue());  // 输出: 100

    return 0;
}

Module

Module 是 Emscripten 的一个全局对象,用于在 JavaScript 环境中管理与编译后的 WebAssembly 模块交互的相关设置和功能。 方法/属性 描述 示例代码 print(value) 在控制台打印信息。通常用于在 C/C++ 代码中输出调试信息,然后在浏览器控制台查看。 Module.print("Hello from WebAssembly!"); printErr(value) 在错误控制台打印信息。与 print 类似,但用于打印错误信息。 Module.printErr("An error occurred!"); setStatus(value) 设置状态信息。通常用于在页面上更新状态信息。 Module.setStatus("WebAssembly module loaded!"); onRuntimeInitialized 一个回调函数,当 WebAssembly 模块初始化完成时被调用。你可以为这个属性设置一个函数,该函数将在初始化完成后执行。 javascript Module.onRuntimeInitialized = function() { console.log("WebAssembly module initialized!"); }; then(callback) 一个 Promise 对象,当 WebAssembly 模块加载和初始化完成后,将调用指定的回调函数。 javascript Module.then(function() { console.log("WebAssembly module loaded and initialized!"); }); cwrap(name, returnType, argTypes) 创建一个包装了 C 函数的 JavaScript 函数,使得可以从 JavaScript 直接调用 C 函数。 javascript var myFunction = Module.cwrap('my_function', 'number', ['number', 'number']); var result = myFunction(10, 20); ccall(func, returnType, argTypes, args)cwrap 类似,用于调用 C 函数,但是接受一个函数指针而不是函数名。 javascript var result = Module.ccall('my_function', 'number', ['number', 'number'], [10, 20]);

whisper.wasm代码

whisper.wasm主要绑定了"init"、"free"和"full_default"三个函数

声明和初始化全局变量

#include "whisper.h"

#include <emscripten.h>
#include <emscripten/bind.h>

#include <vector>
#include <thread>

// 全局变量
std::thread g_worker;
std::vector<struct whisper_context *> g_contexts(4, nullptr);

// 计算最接近的2的幂函数
static inline int mpow2(int n) {
    int p = 1;
    while (p <= n) p *= 2;
    return p/2;
}

// Whisper库的Emscripten绑定
EMSCRIPTEN_BINDINGS(whisper) {
    // 三个函数的部分
}

初始化函数 init

    emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
        // 在初始化前确保工作线程不在运行
        if (g_worker.joinable()) {
            g_worker.join();
        }

        // 遍历上下文并初始化第一个可用的上下文
        for (size_t i = 0; i < g_contexts.size(); ++i) {
            if (g_contexts[i] == nullptr) {
                g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
                if (g_contexts[i] != nullptr) {
                    return i + 1;  // 返回已初始化上下文的索引
                } else {
                    return (size_t) 0;  // 如果初始化失败,返回0
                }
            }
        }

        return (size_t) 0;  // 如果没有可用上下文,返回0
    }));

释放函数 free

    emscripten::function("free", emscripten::optional_override([](size_t index) {
        // 在释放上下文前确保工作线程不在运行
        if (g_worker.joinable()) {
            g_worker.join();
        }

        --index;  // 调整索引以匹配数组索引

        // 如果索引有效,则释放上下文
        if (index < g_contexts.size()) {
            whisper_free(g_contexts[index]);
            g_contexts[index] = nullptr;
        }
    }));

具有默认参数的完整处理函数 full_default

    emscripten::function("full_default", emscripten::optional_override([](size_t index, const emscripten::val & audio, const std::string & lang, int nthreads, bool translate) {
        // 在处理前确保工作线程不在运行
        if (g_worker.joinable()) {
            g_worker.join();
        }

        --index;  // 调整索引以匹配数组索引

        // 检查索引是否越界
        if (index >= g_contexts.size()) {
            return -1;  // 对于无效的上下文索引,返回-1
        }

        // 检查上下文是否未初始化
        if (g_contexts[index] == nullptr) {
            return -2;  // 对于未初始化的上下文,返回-2
        }

        // 设置处理参数
        struct whisper_full_params params = whisper_full_default_params(whisper_sampling_strategy::WHISPER_SAMPLING_GREEDY);
        params.print_realtime   = true;
        params.print_progress   = false;
        params.print_timestamps = true;
        params.print_special    = false;
        params.translate        = translate;
        params.language         = whisper_is_multilingual(g_contexts[index]) ? lang.c_str() : "en";
        params.n_threads        = std::min(nthreads, std::min(16, mpow2(std::thread::hardware_concurrency())));
        params.offset_ms        = 0;

        // 处理音频数据
        std::vector<float> pcmf32;
        const int n = audio["length"].as<int>();

        emscripten::val heap = emscripten::val::module_property("HEAPU8");
        emscripten::val memory = heap["buffer"];

        pcmf32.resize(n);

        emscripten::val memoryView = audio["constructor"].new_(memory, reinterpret_cast<uintptr_t>(pcmf32.data()), n);
        memoryView.call<void>("set", audio);

        // 打印系统信息
        {
            printf("system_info: n_threads = %d / %d | %s\n",
                    params.n_threads, std::thread::hardware_concurrency(), whisper_print_system_info());

            printf("%s: 处理 %d 个样本,%.1f 秒,%d 个线程,%d 个处理器,语言 = %s,任务 = %s ...\n",
                    __func__, int(pcmf32.size()), float(pcmf32.size())/WHISPER_SAMPLE_RATE,
                    params.n_threads, 1,
                    params.language,
                    params.translate ? "翻译" : "转录");

            printf("\n");
        }

        // 运行处理的工作线程
        {
            g_worker = std::thread([index, params, pcmf32 = std::move(pcmf32)]() {
                whisper_reset_timings(g_contexts[index]);
                whisper_full(g_contexts[index], params, pcmf32.data(), pcmf32.size());
                whisper_print_timings(g_contexts[index]);
            });
        }

        return 0;  // 返回0表示处理启动成功
    }));

html

head

<!doctype html>
<html lang="en-us">
    <head>
        <title>whisper.cpp : WASM example</title>

        <style>
            #output {
                width: 100%;
                height: 100%;
                margin: 0 auto;
                margin-top: 10px;
                border-left: 0px;
                border-right: 0px;
                padding-left: 0px;
                padding-right: 0px;
                display: block;
                background-color: black;
                color: white;
                font-size: 10px;
                font-family: 'Lucida Console', Monaco, monospace;
                outline: none;
                white-space: pre;
                overflow-wrap: normal;
                overflow-x: scroll;
            }
        </style>
    </head>

main-container

    <body>
        <div id="main-container">
            <b>Minimal <a href="https://github.com/ggerganov/whisper.cpp">whisper.cpp</a> example running fully in the browser</b>

            <br><br>

            Usage instructions:<br>
            <ul>
                <li>Load a ggml model file (you can obtain one from <a href="https://ggml.ggerganov.com/">here</a>, recommended: <b>tiny</b> or <b>base</b>)</li>
                <li>Select audio file to transcribe or record audio from the microphone (sample: <a href="https://whisper.ggerganov.com/jfk.wav">jfk.wav</a>)</li>
                <li>Click on the "Transcribe" button to start the transcription</li>
            </ul>

            Note that the computation is quite heavy and may take a few seconds to complete.<br>
            The transcription results will be displayed in the text area below.<br><br>
            <b>Important:</b>
                <ul>
                    <li>your browser must support WASM SIMD instructions for this to work</li>
                    <li>Firefox cannot load files larger than 256 MB - use Chrome instead</li>
                </ul>

            <b>More examples:</b>
                <a href="https://whisper.ggerganov.com/">main</a> |
                <a href="https://whisper.ggerganov.com/bench">bench</a> |
                <a href="https://whisper.ggerganov.com/stream">stream</a> |
                <a href="https://whisper.ggerganov.com/command">command</a> |
                <a href="https://whisper.ggerganov.com/talk">talk</a> |

            <hr>

            <div id="model">
                Whisper models: <span id="model-whisper-status"></span><br><br>
                <button    onclick="loadWhisper('tiny.en')">tiny.en (75 MB)</button>
                <button       onclick="loadWhisper('tiny')">tiny (75 MB)</button>
                <button    onclick="loadWhisper('base.en')">base.en (142 MB)</button>
                <button       onclick="loadWhisper('base')">base (142 MB)</button>
                <button   onclick="loadWhisper('small.en')">small.en (466 MB)</button>
                <button      onclick="loadWhisper('small')">small (466 MB)</button>
                <input type="file"   name="file" onchange="loadFile(event, 'whisper.bin')" />
                <br><br>
                Quantized models:<br><br>
                <button     onclick="loadWhisper('tiny-en-q5_1')">tiny.en (Q5_1, 31 MB)</button>
                <button        onclick="loadWhisper('tiny-q5_1')">tiny (Q5_1, 31 MB)</button>
                <button     onclick="loadWhisper('base-en-q5_1')">base.en (Q5_1, 57 MB)</button>
                <button        onclick="loadWhisper('base-q5_1')">base (Q5_1, 57 MB)</button>
                <button    onclick="loadWhisper('small-en-q5_1')">small.en (Q5_1, 182 MB)</button>
                <button       onclick="loadWhisper('small-q5_1')">small (Q5_1, 182 MB)</button><br>
                <button   onclick="loadWhisper('medium-en-q5_0')">medium.en (Q5_0, 515 MB)</button>
                <button      onclick="loadWhisper('medium-q5_0')">medium (Q5_0, 515 MB)</button>
                <button       onclick="loadWhisper('large-q5_0')">large (Q5_0, 1030 MB)</button>
                <span id="fetch-whisper-progress"></span>
            </div>

            <br>

            <!-- radio button to select between file upload or microphone -->
            <div id="input">
                Input:
                <input type="radio"   name="input" value="file" checked="checked" onchange="changeInput('file')" /> <label for="file">File</label>
                <input type="radio"   name="input" value="mic" onchange="changeInput('mic')" /> <label for="mic">Microphone</label>
            </div>

            <br>

            <div id="input_file">
                Audio file:
                <input type="file"   name="file" onchange="loadAudio(event)" />
            </div>

            <div   style="display: none;">
                Microphone:
                <button   onclick="startRecording()">Start</button>
                <button   onclick="stopRecording()" disabled>Stop</button>

                <!-- progress bar to show recording progress -->
                <br><br>
                <div   style="display: none;">
                    <div   style="width: 0%; height: 10px; background-color: #4CAF50;"></div>
                    <div id="progress-text">0%</div>
                </div>
            </div>

            <audio controls="controls"   loop hidden>
                Your browser does not support the &lt;audio&gt; tag.
                <source   src="https://blog.csdn.net/ResumeProject/article/details/136021304" type="audio/wav" />
            </audio>

            <hr><br>

            <table>
                <tr>
                    <td>
                        Language:
                        <select   name="language">
                            <option value="en">English</option>
                             ……
                            <option value="yi">Yiddish</option>
                        </select>
                    </td>
                    <!-- Slider to select number of threads between 1 and 16 -->
                    <td>
                        Threads:
                        <input type="range"   name="threads" min="1" max="16" value="8" onchange="changeThreads(this.value)" />
                        <span id="threads-value">8</span>
                    </td>
                    <td>
                        <button onclick="onProcess(false);">Transcribe</button>
                    </td>
                    <td>
                        <button onclick="onProcess(true);">Translate</button>
                    </td>
                </tr>
            </table>

            <br>

            <!-- textarea with height filling the rest of the page -->
            <textarea   rows="20"></textarea>

            <br><br>

            <div class="cell-version">
                <span>
                    |
                    Build time: <span class="nav-link">@GIT_DATE@</span> |
                    Commit hash: <a   href="https://github.com/ggerganov/whisper.cpp/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
                    Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
                    <a   href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/whisper.wasm">Source Code</a> |
                </span>
            </div>
        </div>

html的js

webassembly002 whisper.wasm wasm_eval 与js代码交互 js部分

CG

https://whisper.ggerganov.com/command/ https://whisper.ggerganov.com

早期版本的相关设置记录

https://github.com/ggerganov/whisper.cpp/tree/1.0.3

https://github.com/ggerganov/whisper.cpp/tree/1.0.3/bindings/javascript

ffmpeg -i samples/jfk.wav -f f32le -acodec pcm_f32le samples/jfk.pcmf32 这是一个使用FFmpeg工具的命令,用于将一个音频文件(samples/jfk.wav)转换为PCM(Pulse Code Modulation)格式的32位浮点数(f32le)音频文件(samples/jfk.pcmf32)。

// https://github1s.com/ggerganov/whisper.cpp/blob/1.0.3/CMakeLists.txt
cmake_minimum_required (VERSION 3.0)
project(whisper.cpp VERSION 1.0.3)

set(CMAKE_EXPORT_COMPILE_COMMANDS "on")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)// 判断CMAKE_SOURCE_DIR和CMAKE_CURRENT_SOURCE_DIR是否相等
    set(WHISPER_STANDALONE ON)
    //包含两个cmake文件
    include(cmake/GitVars.cmake)
    include(cmake/BuildTypes.cmake)

    # configure project version
    if (EXISTS "${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl")
        configure_file(${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl ${CMAKE_SOURCE_DIR}/bindings/ios/Makefile @ONLY)
    endif()
    configure_file(${CMAKE_SOURCE_DIR}/bindings/javascript/package-tmpl.json ${CMAKE_SOURCE_DIR}/bindings/javascript/package.json @ONLY)
else()
    set(WHISPER_STANDALONE OFF)
endif()

if (EMSCRIPTEN)# 如果使用 EMSCRIPTEN 编译器
    set(BUILD_SHARED_LIBS_DEFAULT OFF)

    option(WHISPER_WASM_SINGLE_FILE "whisper: embed WASM inside the generated whisper.js" ON) # 添加一个名为 WHISPER_WASM_SINGLE_FILE 的选项,默认为 ON
else()# 如果不使用 EMSCRIPTEN 编译器
    if (MINGW)# 如果使用 MINGW 编译器
        set(BUILD_SHARED_LIBS_DEFAULT OFF)
    else()
        set(BUILD_SHARED_LIBS_DEFAULT ON)
    endif()
endif()

# options

option(BUILD_SHARED_LIBS               "whisper: build shared libs" ${BUILD_SHARED_LIBS_DEFAULT})

option(WHISPER_ALL_WARNINGS            "whisper: enable all compiler warnings"                   ON)
option(WHISPER_ALL_WARNINGS_3RD_PARTY  "whisper: enable all compiler warnings in 3rd party libs" OFF)

option(WHISPER_SANITIZE_THREAD         "whisper: enable thread sanitizer"    OFF)
option(WHISPER_SANITIZE_ADDRESS        "whisper: enable address sanitizer"   OFF)
option(WHISPER_SANITIZE_UNDEFINED      "whisper: enable undefined sanitizer" OFF)

option(WHISPER_BUILD_TESTS             "whisper: build tests"    ${WHISPER_STANDALONE})
option(WHISPER_BUILD_EXAMPLES          "whisper: build examples" ${WHISPER_STANDALONE})

option(WHISPER_SUPPORT_SDL2            "whisper: support for libSDL2" OFF)

if (APPLE)
    option(WHISPER_NO_ACCELERATE       "whisper: disable Accelerate framework" OFF)
    option(WHISPER_NO_AVX              "whisper: disable AVX" OFF)
    option(WHISPER_NO_AVX2             "whisper: disable AVX2" OFF)
else()
    option(WHISPER_SUPPORT_OPENBLAS    "whisper: support for OpenBLAS" OFF)
endif()

option(WHISPER_PERF                    "whisper: enable perf timings" OFF)

# sanitizers

if (NOT MSVC)#是否使用的是 MSVC 编译器,如果不是,则继续执行下面的代码。
	# 如果 WHISPER_SANITIZE_THREAD 宏定义为真,则将 -fsanitize=thread 选项添加到 C 和 C++ 编译器的编译选项中,用于启用线程相关的内存错误检测。
    if (WHISPER_SANITIZE_THREAD)
        set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -fsanitize=thread")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
    endif()

    if (WHISPER_SANITIZE_ADDRESS)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fsanitize=address -fno-omit-frame-pointer")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
    endif()

    if (WHISPER_SANITIZE_UNDEFINED)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fsanitize=undefined")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
    endif()
endif()# 最后,如果以上条件都不满足,则不对编译选项进行任何修改。

#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffast-math")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")

# dependencies

set(CMAKE_C_STANDARD   11)
set(CMAKE_CXX_STANDARD 11)

find_package(Threads REQUIRED)

# on APPLE - include Accelerate framework
if (APPLE AND NOT WHISPER_NO_ACCELERATE)
    find_library(ACCELERATE_FRAMEWORK Accelerate)
    if (ACCELERATE_FRAMEWORK)
        message(STATUS "Accelerate framework found")

        set(WHISPER_EXTRA_LIBS  ${WHISPER_EXTRA_LIBS}  ${ACCELERATE_FRAMEWORK})
        set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_ACCELERATE)
    else()
        message(WARNING "Accelerate framework not found")
    endif()
endif()

if (WHISPER_SUPPORT_OPENBLAS)
    find_library(OPENBLAS_LIB
        NAMES openblas libopenblas
        )
    if (OPENBLAS_LIB)
        message(STATUS "OpenBLAS found")

        set(WHISPER_EXTRA_LIBS  ${WHISPER_EXTRA_LIBS}  ${OPENBLAS_LIB})
        set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_OPENBLAS)
    else()
        message(WARNING "OpenBLAS not found")# 如果没有找到`OPENBLAS_LIB`,则输出一条警告消息"OpenBLAS not found"。
    endif()
endif()

# compiler flags

if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo")
endif ()

if (WHISPER_ALL_WARNINGS)
    if (NOT MSVC)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \
            -Wall                           \
            -Wextra                         \
            -Wpedantic                      \
            -Wshadow                        \
            -Wcast-qual                     \
            -Wstrict-prototypes             \
            -Wpointer-arith                 \
        ")
    else()
        # todo : msvc
    endif()
endif()

if (NOT MSVC)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=vla")
    #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-math-errno -ffinite-math-only -funsafe-math-optimizations")
endif()

message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")

if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
    message(STATUS "ARM detected")
else()
    message(STATUS "x86 detected")
    if (MSVC)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
        set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX2")
    else()
        if (EMSCRIPTEN)
            set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -pthread")
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
        else()
            if(NOT WHISPER_NO_AVX)
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx")
            endif()
            if(NOT WHISPER_NO_AVX2)
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx2")
            endif()
            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma -mf16c")
        endif()
    endif()
endif()

if (WHISPER_PERF)
    set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_PERF)
endif()

#
# whisper - this is the main library of the project
#

set(TARGET whisper)

add_library(${TARGET}
    ggml.c
    whisper.cpp
    )

target_include_directories(${TARGET} PUBLIC
    .
    )

if (MSVC)
    target_link_libraries(${TARGET} PRIVATE ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT})

    set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -D_CRT_SECURE_NO_WARNINGS)
else()
    target_link_libraries(${TARGET} PRIVATE m ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT})
endif()

if (BUILD_SHARED_LIBS)
    target_link_libraries(${TARGET} PUBLIC
        ${CMAKE_DL_LIBS}
        )

    target_compile_definitions(${TARGET} PUBLIC
        WHISPER_SHARED
        )
endif()

if (EMSCRIPTEN)
    set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS "-msimd128")
endif()

target_compile_definitions(${TARGET} PUBLIC
    ${WHISPER_EXTRA_FLAGS}
    )

install(TARGETS ${TARGET}
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib/static
    )

#
# bindings
#

add_subdirectory(bindings)

#
# programs, examples and tests
#

if (WHISPER_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif ()

if (WHISPER_BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()
// https://github.com/ggerganov/whisper.cpp/blob/1.0.3/bindings/CMakeLists.txt
# 如果是在EMSCRIPTEN编译环境下
if (EMSCRIPTEN)
# 添加javascript子目录进入构建
add_subdirectory(javascript)

Plain Text
Copy code
# 添加自定义命令
add_custom_command(
    # 输出文件为CMAKE_CURRENT_SOURCE_DIR下的javascript/publish.log
    OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/javascript/publish.log
    # 依赖文件包括CMAKE_CURRENT_SOURCE_DIR下的javascript/whisper.js
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/whisper.js
    # 依赖文件包括CMAKE_CURRENT_SOURCE_DIR下的javascript/libwhisper.worker.js
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/libwhisper.worker.js
    # 依赖文件包括CMAKE_CURRENT_SOURCE_DIR下的javascript/package.json
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/package.json
    # 命令所在的工作目录为CMAKE_CURRENT_SOURCE_DIR下的javascript目录
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/javascript
    # 执行命令为npm publish
    COMMAND npm publish
    # 执行命令后使用touch命令生成publish.log文件
    COMMAND touch publish.log
    # 输出信息为"Publishing npm module v${PROJECT_VERSION}"
    COMMENT "Publishing npm module v${PROJECT_VERSION}"
    # 保留命令的所有良好的格式,不会执行任何shell转义
    VERBATIM
)

# 添加自定义目标publish-npm
add_custom_target(publish-npm
    # 依赖文件为javascript/publish.log
    DEPENDS javascript/publish.log
)
endif() 结束
// https://github1s.com/ggerganov/whisper.cpp/blob/1.0.3/bindings/javascript/CMakeLists.txt
set(TARGET libwhisper)// 用于构建一个名为libwhisper的目标。
add_executable(${TARGET} emscripten.cpp )// 包含了一个名为emscripten.cpp的源文件
target_link_libraries(${TARGET} PRIVATE whisper) // 链接了名为whisper的库。

unset(EXTRA_FLAGS)
// 如果定义了WHISPER_WASM_SINGLE_FILE变量,则会设置一个名为EXTRA_FLAGS的变量,值为"-s SINGLE_FILE=1"。
if (WHISPER_WASM_SINGLE_FILE)
    set(EXTRA_FLAGS "-s SINGLE_FILE=1")
    message(STATUS "Embedding WASM inside whisper.js")// 输出一条信息"Embedding WASM inside whisper.js"。

	// 然后,通过add_custom_command命令,在构建目标后执行两个自定义命令。
	// 第一个命令将构建生成的libwhisper.js文件复制到当前源代码目录下的whisper.js文件中。
    add_custom_command(
        TARGET ${TARGET} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
        ${CMAKE_BINARY_DIR}/bin/libwhisper.js
        ${CMAKE_CURRENT_SOURCE_DIR}/whisper.js
        )
	//第二个命令将构建生成的libwhisper.worker.js文件复制到当前源代码目录下的libwhisper.worker.js文件中。
    add_custom_command(
        TARGET ${TARGET} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
        ${CMAKE_BINARY_DIR}/bin/libwhisper.worker.js
        ${CMAKE_CURRENT_SOURCE_DIR}/libwhisper.worker.js
        )
endif()

//最后,使用set_target_properties命令设置目标属性,包括链接标志。
// 链接标志包括"--bind"、"-s MODULARIZE=1"、"-s EXPORT_NAME=\"'whisper_factory'\""、"-s FORCE_FILESYSTEM=1"、"-s USE_PTHREADS=1"、"-s PTHREAD_POOL_SIZE=8"、"-s ALLOW_MEMORY_GROWTH=1"和EXTRA_FLAGS变量的值。
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
    --bind \
    -s MODULARIZE=1 \
    -s EXPORT_NAME=\"'whisper_factory'\" \
    -s FORCE_FILESYSTEM=1 \
    -s USE_PTHREADS=1 \
    -s PTHREAD_POOL_SIZE=8 \
    -s ALLOW_MEMORY_GROWTH=1 \
    ${EXTRA_FLAGS} \
    ")
Build instructions of whisper.wasm
// https://github.com/ggerganov/whisper.cpp/tree/master/examples/whisper.wasm
# build using Emscripten
git clone https://github.com/ggerganov/whisper.cpp
cd whisper.cpp
mkdir build-em && cd build-em
emcmake cmake ..
make -j

# copy the produced page to your HTTP path
cp bin/whisper.wasm/*    /path/to/html/
cp bin/libmain.worker.js /path/to/html/

更新时间 2024-03-14