var printCharBuffers = [null, [], []] var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined var HEAP8 = null // Int8Array var HEAPU8 = null // Uint8Array var HEAP16 = null // Int16Array var HEAPU16 = null // Uint16Array var HEAP32 = null // Int32Array var HEAPU32 = null // Uint32Array var HEAPF32 = null // Float32Array var HEAPF64 = null // Float64Array //注入函数,打印字符串,C代码中打印字符串需要以\n结束 function printChar(stream, curr) { var buffer = printCharBuffers[stream]; //assert(buffer); if (curr === 0 || curr === 10) { //(stream === 1 ? out : err)(this.UTF8ArrayToString(buffer, 0)); //buffer.length = 0; let str = UTF8ArrayToString(buffer, 0) console.log(str)//打印字符串 buffer.length = 0; } else { buffer.push(curr); } } //注入函数,将字节数组转成字符数组 function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead; var endPtr = idx; // TextDecoder needs to know the byte length in advance, it doesn't stop on // null terminator by itself. Also, use the length info to avoid running tiny // strings through TextDecoder, since .subarray() allocates garbage. // (As a tiny code save trick, compare endPtr against endIdx using a negation, // so that undefined means Infinity) while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); } var str = ''; // If building with TextDecoder, we have already computed the string length // above, so test loop end condition against that while (idx < endPtr) { // For UTF8 byte structure, see: // http://en.wikipedia.org/wiki/UTF-8#Description // https://www.ietf.org/rfc/rfc2279.txt // https://tools.ietf.org/html/rfc3629 var u0 = heapOrArray[idx++]; if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } var u1 = heapOrArray[idx++] & 63; if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } var u2 = heapOrArray[idx++] & 63; if ((u0 & 0xF0) == 0xE0) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; } else { //if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); } if (u0 < 0x10000) { str += String.fromCharCode(u0); } else { var ch = u0 - 0x10000; str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); } } return str; } const wasmapi = { wasmInstance: null,//wasm实例 wasmFuncs: null,//wasm导出函数 wasmDataPtr: {//wasm模块的数据指针 installBtsDataPtr: 0,//安装基站数据指针 bleBtsDataPtr: 0,//蓝牙基站数据指针 audioDataPtr: 0,//音频数据指针 currentLocationCoordPtr: 0,//定位坐标数据指针 accDataPtr: 0,//加速度指针 }, data: { audioDataList: [],//音频数据队列 oneFrameLen: 12000,//一帧音频数据长度 bleBtsMonitorSuccessFlag: false,//蓝牙基站监听成功标识符 bleBtsDataList: [], //蓝牙基站队列 installBtsInjectFlag: false,//安装基站已注入标识符 accDataList: [],//加速度数据队列 }, ////wasm注入函数,将数据从源地址src拷贝长度为num的数据到目的地址dest _emscripten_memcpy_js(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) }, _emscripten_resize_heap(requestedSize) { let oldSize = HEAPU8.length // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. requestedSize >>>= 0 this.abortOnCannotGrowMemory(requestedSize) }, abortOnCannotGrowMemory(requestedSize) { abort(`Cannot enlarge memory arrays to size ${requestedSize} bytes (OOM). Either (1) compile with -sINITIAL_MEMORY=X with X higher than the current value ${HEAP8.length}, (2) compile with -sALLOW_MEMORY_GROWTH which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -sABORTING_MALLOC=0`); }, _fd_write(fd, iov, iovcnt, pnum) { let num = 0 for (let i = 0; i < iovcnt; i++) { let ptr = HEAP32[((iov) >> 2)] let len = HEAP32[(((iov) + (4)) >> 2)] iov += 8 for (let j = 0; j < len; j++) { printChar(fd, HEAPU8[ptr + j]) } num += len } HEAP32[((pnum) >> 2)] = num return 0 }, _fd_close(fd) { abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM'); return 0 }, _fd_seek(fd, offset_low, offset_high, whence, newOffset) { abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM') return 0; }, _emscripten_date_now() { return new Date().getTime() }, // __emscripten_get_now_is_monotonic() { // return 1 // }, // _emscripten_get_now() { // return new Date().getTime() // }, //加载微信wasm模块 loadWxWasm() { let self = this let configParam = {} configParam.emscripten_memcpy_js = this._emscripten_memcpy_js configParam.emscripten_resize_heap = this._emscripten_resize_heap configParam.fd_write = this._fd_write configParam.fd_close = this._fd_close configParam.fd_seek = this._fd_seek configParam.emscripten_date_now = this._emscripten_date_now // configParam._emscripten_get_now_is_monotonic = this.__emscripten_get_now_is_monotonic // configParam.emscripten_get_now = this._emscripten_get_now let importObject = {} importObject.env = configParam importObject.wasi_snapshot_preview1 = configParam //返回值res:{instance{}, Module{}}:包含instance和Module对象,instance中也包含Module。 //js使用instance或者Module中Exports对象中的函数(包括用户自定义的函数和系统函数)完成既定功能 //instantiate的第一个参数为绝对路径 WXWebAssembly.instantiate("wasm/aplm8000sdk.wasm", importObject).then((res) => { self.wasmInstance = res.instance self.wasmFuncs = this.wasmInstance.exports if (self.wasmInstance.exports['memory']) {//初始化内存 let buf = self.wasmInstance.exports['memory'].buffer HEAP8 = new Int8Array(buf) HEAPU8 = new Uint8Array(buf) HEAP16 = new Int16Array(buf) HEAPU16 = new Uint16Array(buf) HEAP32 = new Int32Array(buf) HEAPU32 = new Uint32Array(buf) HEAPF32 = new Float32Array(buf) HEAPF64 = new Float64Array(buf) } //wasm模块初始化 self.wasmFuncs.main() //获得wasm模块路由基站数据指针 self.wasmDataPtr.bleBtsDataPtr = self.wasmFuncs.exchange_js_get_route_bts_save_address() //获得wasm模块安装基站的数据指针 self.wasmDataPtr.installBtsDataPtr = self.wasmFuncs.exchange_js_get_install_bts_save_address() //获得wasm模块音频数据指针 self.wasmDataPtr.audioDataPtr = self.wasmFuncs.exchange_js_get_audio_data_save_address() //获得wasm模块定位坐标数据指针 self.wasmDataPtr.currentLocationCoordPtr = self.wasmFuncs.exchange_js_get_current_location_coord_save_address() //获得wasm模块加速度数据指针 self.wasmDataPtr.accDataPtr = self.wasmFuncs.exchange_js_get_acc_save_address() //通知主控模块开始监听蓝牙基站 worker.postMessage({ message: 'WORKER_MAIN_START_MONITOR_BLE_BTS', data: '' }) // //通知主控模块开始录音 // worker.postMessage({ // message: 'WORKER_MAIN_START_REC', // data: '' // }) // //通知主控模块开始读取本地文件 // worker.postMessage({ // message: 'WORKER_MAIN_NOTICE_DOWNLOAD_AUDIO_FILE', // data: '' // }) }).catch((err) => { console.log("加载wasm对象出错", err) }) }, //保存蓝牙基站数据至本地 saveBleBtsDataToLocal(dataList) { if (!dataList || dataList.length < 4) { return } //蓝牙基站监听成功,保存数据 this.data.bleBtsMonitorSuccessFlag = true//蓝牙基站监听成功 this.data.bleBtsDataList.splice(0, this.data.bleBtsDataList.length) this.data.bleBtsDataList = dataList //通知主控模块注入安装基站数据(只注入一次) if (!this.data.installBtsInjectFlag) { worker.postMessage({ message: 'WORKER_MAIN_ORGANIZE_INSTALL_BTS_DATA',//通知主线程组织安装基站数据 data: '' }) } }, //清除蓝牙基站设备(没有监听到本系统的蓝牙基站) clearBleBtsData() { this.data.bleBtsMonitorSuccessFlag = false//蓝牙基站监听失败 this.data.bleBtsDataList.splice(0, this.data.bleBtsDataList.length) }, //发送蓝牙基站数据至wasm模块 sendBleBtsDataToWasm() { let dataList = [] //监听蓝牙基站失败 if (!this.data.bleBtsMonitorSuccessFlag) { return } //将数据保存至wasm模块 dataList = this.data.bleBtsDataList let dataLen = dataList.length if (this.wasmDataPtr.bleBtsDataPtr > 0) { let ptr = this.wasmDataPtr.bleBtsDataPtr / 4//蓝牙基站数据在wasm模块按4字节存储 for (let i = 0; i < dataLen; i++) { HEAP32[ptr + i] = dataList[i]//写入数据 } } else { console.log("蓝牙指针尚未初始化") } //清除本地蓝牙基站数据 this.data.bleBtsDataList.splice(0, this.data.bleBtsDataList.length) this.data.bleBtsMonitorSuccessFlag = false // // //TODO,打印一下看看效果,正式版本要去掉 // if (this.wasmFuncs != null) { // this.wasmFuncs.exchange_js_notice_wasm_show_route_bts(); // } }, //发送安装基站数据至wasm模块 sendInstallBtsDataToWasm(dataList) { //将数据保存至wasm模块 if (this.wasmDataPtr.installBtsDataPtr > 0) { this.data.installBtsInjectFlag = true//注入标识符置1 //向wasm模块注入数据 let ptr = this.wasmDataPtr.installBtsDataPtr / 4//蓝牙基站数据在wasm模块按4字节存储 for (let i = 0; i < dataList.length; i++) { HEAP32[ptr + i] = dataList[i] } ////TODO 打印看看效果 //this.wasmFuncs.exchange_js_notice_wasm_show_install_bts() //向主线程发消息开始录音 worker.postMessage({ message: 'WORKER_MAIN_START_REC', data: '' }) } // // TODO,打印一下看看效果,正式版本要去掉 // if (this.wasmFuncs != null) { // this.wasmFuncs.exchange_js_notice_wasm_show_install_bts() // } }, //处理音频数据,实施定位操作 //buffer:录音数据,为二进制单字节(byte)数据数组 processAudioData(buffer) { //注入蓝牙基站数据 this.sendBleBtsDataToWasm() ////创建数据视图。 //录音数据是arraryBuffer类型,该类型数据为二进制单字节(byte)数据数组,不能直接读写,需要在buffer上创建数据视图后方可操作数据 const dataView = new DataView(buffer);//创建一个数据视图 //遍历数据视图 let sample = 0 let dataLen = Math.floor(dataView.byteLength / 2) //一个采样点为两个字节 if (dataLen > 0) { //把数据插入全局队列,以便实施成帧处理 for (let i = 0; i < dataLen; i++) { sample = dataView.getInt16(2 * i, true)//数据大小端模式,true:小端模式,false:大端模式 this.data.audioDataList.push(sample) } //计算全局队列中数据的帧数 let frameCounter = Math.floor(this.data.audioDataList.length / this.data.oneFrameLen) if (frameCounter) {//至少有一帧数据 //截取音频数据帧(可能是1帧,也可能是多帧。若是多帧,有可能导致处理超时) const dataList = this.data.audioDataList.splice(0, frameCounter * this.data.oneFrameLen) //把音频数据逐帧注入wasm模块 let ptr = this.wasmDataPtr.audioDataPtr / 2//音频数据在wasm模块中按2字节存储 for (let i = 0; i < frameCounter; i++) { //let beginTime = new Date().getTime()//开始计时 //1、把数据注入wasm模块 let baseIndex = i * this.data.oneFrameLen for (let j = 0; j < this.data.oneFrameLen; j++) { let item = dataList[baseIndex + j] HEAP16[ptr + j] = item//把音频数据注入wasm模块 } //2、注入加速度数据 let oneFrameAccDataList = [] oneFrameAccDataList = this.organizeOneFrameAccData()//组织一秒加速度数据 this.injectAccDataToWasm(oneFrameAccDataList)//注入数据 //3、通知wasm模块处理音频数据 let result = this.wasmFuncs.exchange_js_notice_wasm_start_work() //let stopTime = new Date().getTime() - beginTime//结束计时 //console.log("耗时:", stopTime) //4、获取wasm模块处理音频数据的结果 if (result != 1) { console.log("音频数据处理错误") } else { let item = [] //定位坐标数据在wasm模块中按4字节存储 let locationCoordPtr = this.wasmDataPtr.currentLocationCoordPtr / 4 //a、从wasm模块读取音频数据处理结果 for (let i = 0; i < 18; i++) { item.push(HEAP32[locationCoordPtr + i]) } //TODO,b、使用定位结果这部分,需要二次开放商根据业务需求,做定制开发 //使用定位结果 if (item[0] === 1 && item[1] === 1) { let coord = {} coord.x = item[2] coord.y = item[3] //imu移动标识符 const imuMoveFlag = item[17]//0:惯导计算出错,1:惯导计算成功,但手机未移动,50:惯导计算能成功,手机有移动 if (imuMoveFlag == 50) {//手机有移动 //将定位坐标发送至主线程 worker.postMessage({ message: 'WORKER_MAIN_CURRENT_LOCATION_COORD', data: coord }) } else {//手机未移动或者出错 console.log("imu move flag:", imuMoveFlag) } } // //向主线程发消息,通知读取音频文件 // worker.postMessage({ // message: 'WORKER_MAIN_NOTICE_READ_AUDIO_FILE',//读取音频文件 // data: '' // }) } } } } return }, //缓存加速度数据 storeAccData(oneAccData) { let item = {} item.timestamp = oneAccData.timestamp item.x = oneAccData.x item.y = oneAccData.y item.z = oneAccData.z this.data.accDataList.push(item)//缓存加速度 }, //组织一帧加速度数据(约250毫秒) organizeOneFrameAccData() { let oneFrameAccData = [] //一帧加速度数据 if (this.data.accDataList.length > 0) { let stopTimestamp = this.data.accDataList[0].timestamp + 260//250毫秒的数据 let accCounter = 0//计数器 for (let k = 0; k < this.data.accDataList.length; k++) { let oneAcc = {} oneAcc = this.data.accDataList[k] if (oneAcc.timestamp <= stopTimestamp) { oneFrameAccData.push(oneAcc) accCounter++ } else { break } } this.data.accDataList = this.data.accDataList.slice(accCounter)//截取加速度队列 } return oneFrameAccData }, //注入加速度到wasm模块 injectAccDataToWasm(dataList) { let ptr = this.wasmDataPtr.accDataPtr / 4//加速度数据在wasm模块中按4字节存储 let wLen = dataList.length; let item = {} if (wLen > 0) { //注入队列长度 HEAPU32[ptr] = wLen //加速度队列长度 ptr++ //注入3轴加速度 for (let i = 0; i < wLen; i++) { item = dataList[i] ptr = ptr + i * 3//修改数据指针 HEAPF32[ptr] = item.x//x轴加速度 HEAPF32[ptr + 1] = item.y//y轴加速度 HEAPF32[ptr + 2] = item.z//z轴加速度 } //保存数据 this.wasmFuncs.exchange_js_notice_wasm_save_acc() } }, //通知wasm模块释放内存 noticeWasmFreeMemory() { this.wasmFuncs.exchange_js_notice_wasm_free_memory() } } export default wasmapi