html的js
<script type="text/javascript" src="https://blog.csdn.net/ResumeProject/article/details/helpers.js"></script> <script type='text/javascript'>
# # libmain # set(TARGET libmain) add_executable(${TARGET} emscripten.cpp)# 添加可执行目标 libmain,并包含 emscripten.cpp 文件 include(DefaultTargetOptions) # 引入默认的目标选项,https://blog.csdn.net/ResumeProject/article/details/135571952 target_link_libraries(${TARGET} PRIVATE whisper) # 链接whisper库到 libmain 目标 unset(EXTRA_FLAGS)# 清除 EXTRA_FLAGS 变量 # 如果启用了 WHISPER_WASM_SINGLE_FILE 选项,设置 EXTRA_FLAGS,并进行一些额外的操作 if (WHISPER_WASM_SINGLE_FILE) set(EXTRA_FLAGS "-s SINGLE_FILE=1") message(STATUS "Embedding WASM inside main.js") # 在构建后,将 libmain.js 复制到 whisper.wasm 目录下的 main.js add_custom_command( TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bin/libmain.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/whisper.wasm/main.js ) endif() # 设置目标属性,包括链接标志、编译标志等 set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \ --bind \ -s USE_PTHREADS=1 \ -s PTHREAD_POOL_SIZE_STRICT=0 \ -s INITIAL_MEMORY=2000MB \ -s TOTAL_MEMORY=2000MB \ -s FORCE_FILESYSTEM=1 \ -s EXPORTED_RUNTIME_METHODS=\"['print', 'printErr', 'ccall', 'cwrap']\" \ ${EXTRA_FLAGS} \ ") # whisper.wasm 目标 set(TARGET whisper.wasm) # 配置 index-tmpl.html 和 helpers.js,将其拷贝到指定目录 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../helpers.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/helpers.js @ONLY)
convert audio buffer to WAV # TODO
// TODO: convert audio buffer to WAV function setAudio(audio) { //if (audio) { // // convert to 16-bit PCM // var blob = new Blob([audio], { type: 'audio/wav' }); // var url = URL.createObjectURL(blob); // document.getElementById('source').src = url; // document.getElementById('audio').hidden = false; // document.getElementById('audio').loop = false; // document.getElementById('audio').load(); //} else { // document.getElementById('audio').hidden = true; //} }
changeInput函数
这个函数的作用是根据传入的参数 input 的不同值,控制页面上特定元素的显示和隐藏状态,从而根据用户的选择切换不同的输入方式 function changeInput(input) { if (input == 'file') { document.getElementById('input_file').style.display = 'block'; document.getElementById('input_mic' ).style.display = 'none'; document.getElementById('progress' ).style.display = 'none'; } else { document.getElementById('input_file').style.display = 'none'; document.getElementById('input_mic' ).style.display = 'block'; document.getElementById('progress' ).style.display = 'block'; } }
Module
var Module = { print: printTextarea,// 将 JavaScript 的 print 属性设置为 printTextarea 函数。这意味着当 C/C++ 代码中调用 Module.print 时,实际上是调用了 printTextarea 函数 printErr: printTextarea, setStatus: function(text) { printTextarea('js: ' + text); }, monitorRunDependencies: function(left) { } }; // web audio context var context = null; // audio data var audio = null; // the whisper instance var instance = null; var model_whisper = ''; // helper function function convertTypedArray(src, type) { var buffer = new ArrayBuffer(src.byteLength); var baseView = new src.constructor(buffer).set(src); return new type(buffer); }
load model 加载模型文件
// // load model // let dbVersion = 1 let dbName = 'whisper.ggerganov.com'; let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB
loadF函数
使用FileReader()对象加载本地文件 function storeFS(fname, buf) { // write to WASM file using FS_createDataFile // if the file exists, delete it try { Module.FS_unlink(fname); } catch (e) { // ignore } Module.FS_createDataFile("/", fname, buf, true, true); //model_whisper = fname; document.getElementById('model-whisper-status').innerHTML = 'loaded "' + model_whisper + '"!'; printTextarea('storeFS: stored model: ' + fname + ' size: ' + buf.length); document.getElementById('model').innerHTML = 'Model fetched: ' + model_whisper; } // <input type="file" name="file" onchange="loadFile(event, 'whisper.bin')"> function loadFile(event, fname) { var file = event.target.files[0] || null; if (file == null) { return; } printTextarea("loadFile: loading model: " + file.name + ", size: " + file.size + " bytes"); printTextarea('loadFile: please wait ...'); var reader = new FileReader();// 创建一个新的 FileReader 对象 // 设置 FileReader 对象的 onload 回调函数,在文件读取完成时触发 reader.onload = function(event) { // 从 FileReader 中获取读取到的文件内容,存储在 Uint8Array 中 var buf = new Uint8Array(reader.result); // 调用 storeFS 函数,将文件名 fname 和读取到的二进制数据 buf 传递给该函数 storeFS(fname, buf); } reader.readAsArrayBuffer(file);// 调用 readAsArrayBuffer 方法,以 ArrayBuffer 格式异步读取指定的文件 file // 所有按钮不可见 document.getElementById('fetch-whisper-tiny-en' ).style.display = 'none'; document.getElementById('fetch-whisper-base-en' ).style.display = 'none'; document.getElementById('fetch-whisper-small-en').style.display = 'none'; document.getElementById('fetch-whisper-tiny' ).style.display = 'none'; document.getElementById('fetch-whisper-base' ).style.display = 'none'; document.getElementById('fetch-whisper-small' ).style.display = 'none'; document.getElementById('fetch-whisper-tiny-en-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-tiny-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-base-en-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-base-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-small-en-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-small-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-medium-en-q5_0').style.display = 'none'; document.getElementById('fetch-whisper-medium-q5_0' ).style.display = 'none'; document.getElementById('fetch-whisper-large-q5_0' ).style.display = 'none'; document.getElementById('whisper-file' ).style.display = 'none'; // 设置一个显示标签 document.getElementById('model-whisper-status' ).innerHTML = 'loaded model: ' + file.name; }
loadWhisper函数
会调用loadRemote(定义在helper.js)函数
loadRemote: storage quota: 77309408870 bytes loadRemote: storage usage: 77707010 bytes loadRemote: "https://whisper.ggerganov.com/ggml-model-whisper-tiny.en.bin" is already in the IndexedDB js: loading audio: 1424079488-1-16.aac, size: 7373610 bytes
加载的模型会存储到IndexDB中 //<button onclick="loadWhisper('tiny.en')">tiny.en (75 MB)</button> function loadWhisper(model) { let urls = { 'tiny.en': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en.bin', 'tiny': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.bin', 'base.en': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en.bin', 'base': 'https://whisper.ggerganov.com/ggml-model-whisper-base.bin', 'small.en': 'https://whisper.ggerganov.com/ggml-model-whisper-small.en.bin', 'small': 'https://whisper.ggerganov.com/ggml-model-whisper-small.bin', 'tiny-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en-q5_1.bin', 'tiny-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny-q5_1.bin', 'base-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en-q5_1.bin', 'base-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-base-q5_1.bin', 'small-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-small.en-q5_1.bin', 'small-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-small-q5_1.bin', 'medium-en-q5_0':'https://whisper.ggerganov.com/ggml-model-whisper-medium.en-q5_0.bin', 'medium-q5_0': 'https://whisper.ggerganov.com/ggml-model-whisper-medium-q5_0.bin', 'large-q5_0': 'https://whisper.ggerganov.com/ggml-model-whisper-large-q5_0.bin', }; let sizes = { 'tiny.en': 75, 'tiny': 75, 'base.en': 142, 'base': 142, 'small.en': 466, 'small': 466, 'tiny-en-q5_1': 31, 'tiny-q5_1': 31, 'base-en-q5_1': 57, 'base-q5_1': 57, 'small-en-q5_1': 182, 'small-q5_1': 182, 'medium-en-q5_0': 515, 'medium-q5_0': 515, 'large-q5_0': 1030, }; let url = urls[model]; let dst = 'whisper.bin'; let size_mb = sizes[model]; model_whisper = model; // 前端显示设置 document.getElementById('fetch-whisper-tiny-en' ).style.display = 'none'; document.getElementById('fetch-whisper-base-en' ).style.display = 'none'; document.getElementById('fetch-whisper-small-en').style.display = 'none'; document.getElementById('fetch-whisper-tiny' ).style.display = 'none'; document.getElementById('fetch-whisper-base' ).style.display = 'none'; document.getElementById('fetch-whisper-small' ).style.display = 'none'; document.getElementById('fetch-whisper-tiny-en-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-tiny-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-base-en-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-base-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-small-en-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-small-q5_1' ).style.display = 'none'; document.getElementById('fetch-whisper-medium-en-q5_0').style.display = 'none'; document.getElementById('fetch-whisper-medium-q5_0' ).style.display = 'none'; document.getElementById('fetch-whisper-large-q5_0' ).style.display = 'none'; document.getElementById('whisper-file' ).style.display = 'none'; document.getElementById('model-whisper-status').innerHTML = 'loading model: ' + model; cbProgress = function(p) { let el = document.getElementById('fetch-whisper-progress'); el.innerHTML = Math.round(100*p) + '%'; }; cbCancel = function() { var el; el = document.getElementById('fetch-whisper-tiny-en' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-base-en' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-small-en'); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-tiny' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-base' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-small' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-tiny-en-q5_1' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-tiny-q5_1' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-base-en-q5_1' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-base-q5_1' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-small-en-q5_1' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-small-q5_1' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-medium-en-q5_0'); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-medium-q5_0' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('fetch-whisper-large-q5_0' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('whisper-file' ); if (el) el.style.display = 'inline-block'; el = document.getElementById('model-whisper-status'); if (el) el.innerHTML = ''; }; loadRemote(url, dst, size_mb, cbProgress, storeFS, cbCancel, printTextarea); }
audio file 加载音频文件
AudioContext // // audio file // // 定义常量,表示最大的音频时长为 30 分钟,最大录音时长为 2 分钟,采样率为 16000 Hz const kMaxAudio_s = 30 * 60; const kMaxRecording_s = 2 * 60; const kSampleRate = 16000; // 兼容性处理:获取浏览器支持的 AudioContext 和 OfflineAudioContext window.AudioContext = window.AudioContext || window.webkitAudioContext; window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext; // <input type="file" name="file" onchange="loadAudio(event)"> // 当选择文件后触发的函数,用于加载音频文件 function loadAudio(event) { // 如果上下文对象不存在,创建一个 AudioContext 对象 if (!context) { context = new AudioContext({ sampleRate: kSampleRate, channelCount: 1, echoCancellation: false, autoGainControl: true, noiseSuppression: true, }); } // 获取选择的文件 var file = event.target.files[0] || null; if (file == null) { return; } // 输出加载音频文件的信息 printTextarea('js: loading audio: ' + file.name + ', size: ' + file.size + ' bytes'); printTextarea('js: please wait ...'); // 创建 FileReader 对象,用于读取文件内容 var reader = new FileReader(); reader.onload = function(event) { // 读取到的文件内容存储在 Uint8Array 中 var buf = new Uint8Array(reader.result); // 解码音频数据 context.decodeAudioData(buf.buffer, function(audioBuffer) { // 创建 OfflineAudioContext 对象,用于渲染音频 var offlineContext = new OfflineAudioContext(audioBuffer.numberOfChannels, audioBuffer.length, audioBuffer.sampleRate); var source = offlineContext.createBufferSource(); source.buffer = audioBuffer; source.connect(offlineContext.destination); source.start(0); // 开始渲染音频,处理渲染后的数据 offlineContext.startRendering().then(function(renderedBuffer) { // 获取渲染后的音频数据 audio = renderedBuffer.getChannelData(0); // 输出加载音频成功的信息 printTextarea('js: audio loaded, size: ' + audio.length); // 如果音频长度超过 30 分钟,截取前 30 分钟 if (audio.length > kMaxAudio_s * kSampleRate) { audio = audio.slice(0, kMaxAudio_s * kSampleRate); printTextarea('js: truncated audio to first ' + kMaxAudio_s + ' seconds'); } // 设置音频数据 setAudio(audio); }); }, function(e) { // 解码错误时输出错误信息 printTextarea('js: error decoding audio: ' + e); audio = null; setAudio(audio); }); } // 以 ArrayBuffer 格式读取文件 reader.readAsArrayBuffer(file); }
microphone
// // microphone // var mediaRecorder = null; var doRecording = false; var startTime = 0; function stopRecording() { doRecording = false; } // record up to kMaxRecording_s seconds of audio from the microphone // check if doRecording is false every 1000 ms and stop recording if so // update progress information function startRecording() { if (!context) { context = new AudioContext({ sampleRate: kSampleRate, channelCount: 1, echoCancellation: false, autoGainControl: true, noiseSuppression: true, }); } document.getElementById('start').disabled = true; document.getElementById('stop').disabled = false; document.getElementById('progress-bar').style.width = '0%'; document.getElementById('progress-text').innerHTML = '0%'; doRecording = true; startTime = Date.now(); var chunks = []; var stream = null; navigator.mediaDevices.getUserMedia({audio: true, video: false}) .then(function(s) { stream = s; mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = function(e) { chunks.push(e.data); }; mediaRecorder.onstop = function(e) { var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' }); chunks = []; document.getElementById('start').disabled = false; document.getElementById('stop').disabled = true; var reader = new FileReader(); reader.onload = function(event) { var buf = new Uint8Array(reader.result); context.decodeAudioData(buf.buffer, function(audioBuffer) { var offlineContext = new OfflineAudioContext(audioBuffer.numberOfChannels, audioBuffer.length, audioBuffer.sampleRate); var source = offlineContext.createBufferSource(); source.buffer = audioBuffer; source.connect(offlineContext.destination); source.start(0); offlineContext.startRendering().then(function(renderedBuffer) { audio = renderedBuffer.getChannelData(0); printTextarea('js: audio recorded, size: ' + audio.length); // truncate to first 30 seconds if (audio.length > kMaxRecording_s*kSampleRate) { audio = audio.slice(0, kMaxRecording_s*kSampleRate); printTextarea('js: truncated audio to first ' + kMaxRecording_s + ' seconds'); } setAudio(audio); }); }, function(e) { printTextarea('js: error decoding audio: ' + e); audio = null; setAudio(audio); }); } reader.readAsArrayBuffer(blob); }; mediaRecorder.start(); }) .catch(function(err) { printTextarea('js: error getting audio stream: ' + err); }); var interval = setInterval(function() { if (!doRecording) { clearInterval(interval); mediaRecorder.stop(); stream.getTracks().forEach(function(track) { track.stop(); }); } document.getElementById('progress-bar').style.width = (100*(Date.now() - startTime)/1000/kMaxRecording_s) + '%'; document.getElementById('progress-text').innerHTML = (100*(Date.now() - startTime)/1000/kMaxRecording_s).toFixed(0) + '%'; }, 1000); printTextarea('js: recording ...'); setTimeout(function() { if (doRecording) { printTextarea('js: recording stopped after ' + kMaxRecording_s + ' seconds'); stopRecording(); } }, kMaxRecording_s*1000); }
transcribe
主要调用Module.full_default(instance, audio, document.getElementById('language').value, nthreads, translate);
函数。 setTimeout在当前执行堆栈清空后100 毫秒执行,以确保当前正在执行的函数及其相关的代码已经执行完毕,使得代码在后台运行而不会阻塞主线程的执行。 // // transcribe // var nthreads = 8; function changeThreads(value) { nthreads = value; document.getElementById('threads-value').innerHTML = nthreads; } //<button οnclick="onProcess(false);">Transcribe</button> <button οnclick="onProcess(true);">Translate</button> function onProcess(translate) { if (!instance) { instance = Module.init('whisper.bin'); if (instance) { printTextarea("js: whisper initialized, instance: " + instance); document.getElementById('model').innerHTML = 'Model loaded: ' + model_whisper; } } if (!instance) { printTextarea("js: failed to initialize whisper"); return; } if (!audio) { printTextarea("js: no audio data"); return; } if (instance) { printTextarea(''); printTextarea('js: processing - this might take a while ...'); printTextarea(''); setTimeout(function() { var ret = Module.full_default(instance, audio, document.getElementById('language').value, nthreads, translate); console.log('js: full_default returned: ' + ret); if (ret) { printTextarea("js: whisper returned: " + ret); } }, 100); } } </script> <script type="text/javascript" src="https://blog.csdn.net/ResumeProject/article/details/main.js"></script> </body> </html>
helpers.js
helpers.js并非是生成的,在项目的https://github.com/ggerganov/whisper.cpp/blob/master/examples/helpers.js// Common Javascript functions used by the examples function convertTypedArray(src, type) { var buffer = new ArrayBuffer(src.byteLength); var baseView = new src.constructor(buffer).set(src); return new type(buffer); } var printTextarea = (function() { var element = document.getElementById('output'); if (element) element.value = ''; // clear browser cache return function(text) { if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); console.log(text); if (element) { element.value += text + "\n"; element.scrollTop = element.scrollHeight; // focus on bottom } }; })(); async function clearCache() { if (confirm('Are you sure you want to clear the cache?\nAll the models will be downloaded again.')) { indexedDB.deleteDatabase(dbName); location.reload(); } } // fetch a remote file from remote URL using the Fetch API async function fetchRemote(url, cbProgress, cbPrint) { cbPrint('fetchRemote: downloading with fetch()...'); const response = await fetch( url, { method: 'GET', headers: { 'Content-Type': 'application/octet-stream', }, } ); if (!response.ok) { cbPrint('fetchRemote: failed to fetch ' + url); return; } const contentLength = response.headers.get('content-length'); const total = parseInt(contentLength, 10); const reader = response.body.getReader(); var chunks = []; var receivedLength = 0; var progressLast = -1; while (true) { const { done, value } = await reader.read(); if (done) { break; } chunks.push(value); receivedLength += value.length; if (contentLength) { cbProgress(receivedLength/total); var progressCur = Math.round((receivedLength / total) * 10); if (progressCur != progressLast) { cbPrint('fetchRemote: fetching ' + 10*progressCur + '% ...'); progressLast = progressCur; } } } var position = 0; var chunksAll = new Uint8Array(receivedLength); for (var chunk of chunks) { chunksAll.set(chunk, position); position += chunk.length; } return chunksAll; } // load remote data // - check if the data is already in the IndexedDB // - if not, fetch it from the remote URL and store it in the IndexedDB function loadRemote(url, dst, size_mb, cbProgress, cbReady, cbCancel, cbPrint) { if (!navigator.storage || !navigator.storage.estimate) { cbPrint('loadRemote: navigator.storage.estimate() is not supported'); } else { // query the storage quota and print it navigator.storage.estimate().then(function (estimate) { cbPrint('loadRemote: storage quota: ' + estimate.quota + ' bytes'); cbPrint('loadRemote: storage usage: ' + estimate.usage + ' bytes'); }); } // check if the data is already in the IndexedDB var rq = indexedDB.open(dbName, dbVersion); rq.onupgradeneeded = function (event) { var db = event.target.result; if (db.version == 1) { var os = db.createObjectStore('models', { autoIncrement: false }); cbPrint('loadRemote: created IndexedDB ' + db.name + ' version ' + db.version); } else { // clear the database var os = event.currentTarget.transaction.objectStore('models'); os.clear(); cbPrint('loadRemote: cleared IndexedDB ' + db.name + ' version ' + db.version); } }; rq.onsuccess = function (event) { var db = event.target.result; var tx = db.transaction(['models'], 'readonly'); var os = tx.objectStore('models'); var rq = os.get(url); rq.onsuccess = function (event) { if (rq.result) { cbPrint('loadRemote: "' + url + '" is already in the IndexedDB'); cbReady(dst, rq.result); } else { // data is not in the IndexedDB cbPrint('loadRemote: "' + url + '" is not in the IndexedDB'); // alert and ask the user to confirm if (!confirm( 'You are about to download ' + size_mb + ' MB of data.\n' + 'The model data will be cached in the browser for future use.\n\n' + 'Press OK to continue.')) { cbCancel(); return; } fetchRemote(url, cbProgress, cbPrint).then(function (data) { if (data) { // store the data in the IndexedDB var rq = indexedDB.open(dbName, dbVersion); rq.onsuccess = function (event) { var db = event.target.result; var tx = db.transaction(['models'], 'readwrite'); var os = tx.objectStore('models'); var rq = null; try { var rq = os.put(data, url); } catch (e) { cbPrint('loadRemote: failed to store "' + url + '" in the IndexedDB: \n' + e); cbCancel(); return; } rq.onsuccess = function (event) { cbPrint('loadRemote: "' + url + '" stored in the IndexedDB'); cbReady(dst, data); }; rq.onerror = function (event) { cbPrint('loadRemote: failed to store "' + url + '" in the IndexedDB'); cbCancel(); }; }; } }); } }; rq.onerror = function (event) { cbPrint('loadRemote: failed to get data from the IndexedDB'); cbCancel(); }; }; rq.onerror = function (event) { cbPrint('loadRemote: failed to open IndexedDB'); cbCancel(); }; rq.onblocked = function (event) { cbPrint('loadRemote: failed to open IndexedDB: blocked'); cbCancel(); }; rq.onabort = function (event) { cbPrint('loadRemote: failed to open IndexedDB: abort'); cbCancel(); }; }
CG
在cmake中开启 ASSERTIONSset(TARGET_NAME my_target) add_executable(${TARGET_NAME} main.cpp) # 添加 Emscripten 编译选项 set(EMSCRIPTEN_FLAGS "-sASSERTIONS=1") # 设置目标属性 set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".html" # 输出文件的后缀为 .html COMPILE_FLAGS "${EMSCRIPTEN_FLAGS}" # 添加 Emscripten 编译选项 )
在谷歌浏览器中,尝试扩大内存限制:
"C:Program Files (x86)GoogleChromeApplicationchrome.exe" --max_old_space_size=4096
chrome.exe --max_old_space_size=4096 # 这里的 `4096` 是以兆字节为单位的内存大小。
chrome.exe --js-flags="--max-old-space-size=4096"
chrome.exe --disable-gpu # 在一些情况下,禁用 GPU 加速可能会释放一些系统内存
探讨 Node 中的内存堆分配,并将本地硬件推向极限
whisperdocurlganscripthtmlstoremediumragthreadscreatechromectojavascriptjavawebtpucodecliaction