# 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/