# 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_JS
宏 EM_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 <audio> 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/
whisperscriptjavajavascriptidegitcliganthreadsctohtmlstemgithubmmosharejsonsemweb上下文sdk