wasmapi.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. var printCharBuffers = [null, [], []]
  2. var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined
  3. var HEAP8 = null // Int8Array
  4. var HEAPU8 = null // Uint8Array
  5. var HEAP16 = null // Int16Array
  6. var HEAPU16 = null // Uint16Array
  7. var HEAP32 = null // Int32Array
  8. var HEAPU32 = null // Uint32Array
  9. var HEAPF32 = null // Float32Array
  10. var HEAPF64 = null // Float64Array
  11. //注入函数,打印字符串,C代码中打印字符串需要以\n结束
  12. function printChar(stream, curr) {
  13. var buffer = printCharBuffers[stream];
  14. //assert(buffer);
  15. if (curr === 0 || curr === 10) {
  16. //(stream === 1 ? out : err)(this.UTF8ArrayToString(buffer, 0));
  17. //buffer.length = 0;
  18. let str = UTF8ArrayToString(buffer, 0)
  19. console.log(str)//打印字符串
  20. buffer.length = 0;
  21. } else {
  22. buffer.push(curr);
  23. }
  24. }
  25. //注入函数,将字节数组转成字符数组
  26. function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) {
  27. var endIdx = idx + maxBytesToRead;
  28. var endPtr = idx;
  29. // TextDecoder needs to know the byte length in advance, it doesn't stop on
  30. // null terminator by itself. Also, use the length info to avoid running tiny
  31. // strings through TextDecoder, since .subarray() allocates garbage.
  32. // (As a tiny code save trick, compare endPtr against endIdx using a negation,
  33. // so that undefined means Infinity)
  34. while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
  35. if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
  36. return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
  37. }
  38. var str = '';
  39. // If building with TextDecoder, we have already computed the string length
  40. // above, so test loop end condition against that
  41. while (idx < endPtr) {
  42. // For UTF8 byte structure, see:
  43. // http://en.wikipedia.org/wiki/UTF-8#Description
  44. // https://www.ietf.org/rfc/rfc2279.txt
  45. // https://tools.ietf.org/html/rfc3629
  46. var u0 = heapOrArray[idx++];
  47. if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }
  48. var u1 = heapOrArray[idx++] & 63;
  49. if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }
  50. var u2 = heapOrArray[idx++] & 63;
  51. if ((u0 & 0xF0) == 0xE0) {
  52. u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
  53. } else {
  54. //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!');
  55. u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63);
  56. }
  57. if (u0 < 0x10000) {
  58. str += String.fromCharCode(u0);
  59. } else {
  60. var ch = u0 - 0x10000;
  61. str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
  62. }
  63. }
  64. return str;
  65. }
  66. const wasmapi = {
  67. wasmInstance: null,//wasm实例
  68. wasmFuncs: null,//wasm导出函数
  69. wasmDataPtr: {//wasm模块的数据指针
  70. installBtsDataPtr: 0,//安装基站数据指针
  71. bleBtsDataPtr: 0,//蓝牙基站数据指针
  72. audioDataPtr: 0,//音频数据指针
  73. currentLocationCoordPtr: 0,//定位坐标数据指针
  74. accDataPtr: 0,//加速度指针
  75. softNameAndVersionPtr: 0,//软件名称和版本数据指针
  76. },
  77. data: {
  78. audioDataList: [],//音频数据队列
  79. oneFrameLen: 12000,//一帧音频数据长度
  80. bleBtsMonitorSuccessFlag: false,//蓝牙基站监听成功标识符
  81. bleBtsDataList: [], //蓝牙基站队列
  82. installBtsInjectFlag: false,//安装基站已注入标识符
  83. accDataList: [],//加速度数据队列
  84. },
  85. ////wasm注入函数,将数据从源地址src拷贝长度为num的数据到目的地址dest
  86. _emscripten_memcpy_js(dest, src, num) {
  87. HEAPU8.copyWithin(dest, src, src + num)
  88. },
  89. _emscripten_resize_heap(requestedSize) {
  90. let oldSize = HEAPU8.length
  91. // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned.
  92. requestedSize >>>= 0
  93. this.abortOnCannotGrowMemory(requestedSize)
  94. },
  95. abortOnCannotGrowMemory(requestedSize) {
  96. 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`);
  97. },
  98. _fd_write(fd, iov, iovcnt, pnum) {
  99. let num = 0
  100. for (let i = 0; i < iovcnt; i++) {
  101. let ptr = HEAP32[((iov) >> 2)]
  102. let len = HEAP32[(((iov) + (4)) >> 2)]
  103. iov += 8
  104. for (let j = 0; j < len; j++) {
  105. printChar(fd, HEAPU8[ptr + j])
  106. }
  107. num += len
  108. }
  109. HEAP32[((pnum) >> 2)] = num
  110. return 0
  111. },
  112. _fd_close(fd) {
  113. abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM');
  114. return 0
  115. },
  116. _fd_seek(fd, offset_low, offset_high, whence, newOffset) {
  117. abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM')
  118. return 0;
  119. },
  120. _emscripten_date_now() {
  121. return new Date().getTime()
  122. },
  123. // __emscripten_get_now_is_monotonic() {
  124. // return 1
  125. // },
  126. // _emscripten_get_now() {
  127. // return new Date().getTime()
  128. // },
  129. //加载微信wasm模块
  130. loadWxWasm() {
  131. let self = this
  132. //let configParam = { d: this._emscripten_date_now, b: this._emscripten_memcpy_js, a: this._emscripten_resize_heap, c: this._fd_write }
  133. let configParam = {}
  134. configParam.emscripten_memcpy_js = this._emscripten_memcpy_js
  135. configParam.emscripten_resize_heap = this._emscripten_resize_heap
  136. configParam.fd_write = this._fd_write
  137. configParam.fd_close = this._fd_close
  138. configParam.fd_seek = this._fd_seek
  139. configParam.emscripten_date_now = this._emscripten_date_now
  140. // configParam._emscripten_get_now_is_monotonic = this.__emscripten_get_now_is_monotonic
  141. // configParam.emscripten_get_now = this._emscripten_get_now
  142. let importObject = {}
  143. importObject.env = configParam
  144. importObject.wasi_snapshot_preview1 = configParam
  145. //importObject.a = configParam
  146. //返回值res:{instance{}, Module{}}:包含instance和Module对象,instance中也包含Module。
  147. //js使用instance或者Module中Exports对象中的函数(包括用户自定义的函数和系统函数)完成既定功能
  148. //instantiate的第一个参数为绝对路径
  149. WXWebAssembly.instantiate("wasm/aplm8000sdk.wasm", importObject).then((res) => {
  150. self.wasmInstance = res.instance//WASM模块实例
  151. self.wasmFuncs = self.wasmInstance.exports//WASM模块接口
  152. //初始化内存
  153. if (self.wasmInstance.exports['memory']) {
  154. let buf = self.wasmInstance.exports['memory'].buffer
  155. HEAP8 = new Int8Array(buf)
  156. HEAPU8 = new Uint8Array(buf)
  157. HEAP16 = new Int16Array(buf)
  158. HEAPU16 = new Uint16Array(buf)
  159. HEAP32 = new Int32Array(buf)
  160. HEAPU32 = new Uint32Array(buf)
  161. HEAPF32 = new Float32Array(buf)
  162. HEAPF64 = new Float64Array(buf)
  163. }
  164. //wasm模块初始化
  165. self.wasmFuncs.main()
  166. //获得wasm模块路由基站数据指针
  167. self.wasmDataPtr.bleBtsDataPtr = self.wasmFuncs.exchange_js_get_route_bts_save_address()
  168. //获得wasm模块安装基站的数据指针
  169. self.wasmDataPtr.installBtsDataPtr = self.wasmFuncs.exchange_js_get_install_bts_save_address()
  170. //获得wasm模块音频数据指针
  171. self.wasmDataPtr.audioDataPtr = self.wasmFuncs.exchange_js_get_audio_data_save_address()
  172. //获得wasm模块定位坐标数据指针
  173. self.wasmDataPtr.currentLocationCoordPtr = self.wasmFuncs.exchange_js_get_current_location_coord_save_address()
  174. //获得wasm模块加速度数据指针
  175. self.wasmDataPtr.accDataPtr = self.wasmFuncs.exchange_js_get_acc_save_address()
  176. //获得wasm模块软件名称和版本数据指针
  177. self.wasmDataPtr.softNameAndVersionPtr = self.wasmFuncs.exchange_js_get_soft_name_and_version_save_address()
  178. //通知主控模块开始监听蓝牙
  179. worker.postMessage({
  180. message: 'WORKER_MAIN_START_MONITOR_BLE_BTS',
  181. data: ''
  182. })
  183. //TODO打印软件名称与版本
  184. let softNameAndVersionPtr = self.wasmDataPtr.softNameAndVersionPtr
  185. let softName = []
  186. let softVersion = []
  187. for (let i = 0; i < 18; i++) {
  188. if (i < 11) {
  189. softName.push(HEAPU8[softNameAndVersionPtr + i])
  190. } else {
  191. softVersion.push(HEAPU8[softNameAndVersionPtr + i])
  192. }
  193. }
  194. console.log("软件版本:" + softName.map(num => String.fromCharCode(num)).join('') + " " + softVersion.map(num => String.fromCharCode(num)).join(''))
  195. }).catch((err) => {
  196. console.log("加载wasm对象出错", err)
  197. })
  198. },
  199. //保存蓝牙基站数据至本地
  200. saveBleBtsDataToLocal(dataList) {
  201. if (!dataList || dataList.length < 4) {
  202. return
  203. }
  204. //蓝牙基站监听成功,保存数据
  205. this.data.bleBtsMonitorSuccessFlag = true//蓝牙基站监听成功标识符
  206. this.data.bleBtsDataList.splice(0, this.data.bleBtsDataList.length)
  207. this.data.bleBtsDataList = dataList//保存蓝牙基站数据,并覆盖以前的数据
  208. //通知主控模块注入安装基站数据(只注入一次)
  209. if (!this.data.installBtsInjectFlag) {
  210. worker.postMessage({
  211. message: 'WORKER_MAIN_ORGANIZE_INSTALL_BTS_DATA',//通知主线程组织安装基站数据
  212. data: ''
  213. })
  214. }
  215. },
  216. //清除蓝牙基站设备(没有监听到本系统的蓝牙基站)
  217. clearBleBtsData() {
  218. this.data.bleBtsMonitorSuccessFlag = false//蓝牙基站监听失败
  219. this.data.bleBtsDataList.splice(0, this.data.bleBtsDataList.length)
  220. },
  221. //发送蓝牙基站数据至wasm模块
  222. sendBleBtsDataToWasm() {
  223. let dataList = []
  224. //监听蓝牙基站失败
  225. if (!this.data.bleBtsMonitorSuccessFlag) {
  226. return
  227. }
  228. //将数据保存至wasm模块
  229. dataList = this.data.bleBtsDataList
  230. let dataLen = dataList.length
  231. if (this.wasmDataPtr.bleBtsDataPtr > 0) {
  232. let ptr = this.wasmDataPtr.bleBtsDataPtr / 4//蓝牙基站数据在wasm模块按4字节存储
  233. for (let i = 0; i < dataLen; i++) {
  234. HEAP32[ptr + i] = dataList[i]//写入数据
  235. }
  236. } else {
  237. console.log("蓝牙指针尚未初始化")
  238. return
  239. }
  240. //清除本地蓝牙基站数据
  241. this.data.bleBtsDataList.splice(0, this.data.bleBtsDataList.length)
  242. this.data.bleBtsMonitorSuccessFlag = false
  243. // // //TODO,打印一下看看效果,正式版本要去掉
  244. // if (this.wasmFuncs != null) {
  245. // this.wasmFuncs.exchange_js_notice_wasm_show_route_bts();
  246. // }
  247. return
  248. },
  249. //发送安装基站数据至wasm模块
  250. sendInstallBtsDataToWasm(dataList) {
  251. //将数据保存至wasm模块
  252. if (this.wasmDataPtr.installBtsDataPtr > 0 && dataList.length > 0) {
  253. this.data.installBtsInjectFlag = true//注入标识符置1
  254. //向wasm模块注入数据
  255. let ptr = this.wasmDataPtr.installBtsDataPtr / 4//基站数据在wasm模块按4字节存储
  256. for (let i = 0; i < dataList.length; i++) {
  257. HEAP32[ptr + i] = dataList[i]
  258. }
  259. //向主线程发消息开始录音
  260. worker.postMessage({
  261. message: 'WORKER_MAIN_START_REC',
  262. data: ''
  263. })
  264. }
  265. // // TODO,打印一下看看效果,正式版本要去掉
  266. // if (this.wasmFuncs != null) {
  267. // this.wasmFuncs.exchange_js_notice_wasm_show_install_bts()
  268. // }
  269. },
  270. //处理音频数据,实施定位操作
  271. //buffer:录音数据,为二进制单字节(byte)数据数组
  272. processAudioData(buffer) {
  273. //注入蓝牙基站数据
  274. this.sendBleBtsDataToWasm()
  275. ////创建数据视图。
  276. //录音数据是arraryBuffer类型,该类型数据为二进制单字节(byte)数据数组,不能直接读写,需要在buffer上创建数据视图后方可操作数据
  277. const dataView = new DataView(buffer);//创建一个数据视图
  278. //遍历数据视图
  279. let sample = 0
  280. let dataLen = Math.floor(dataView.byteLength / 2) //一个采样点为两个字节
  281. if (dataLen > 0) {
  282. //把数据插入全局队列,以便实施成帧处理
  283. for (let i = 0; i < dataLen; i++) {
  284. sample = dataView.getInt16(2 * i, true)//数据大小端模式,true:小端模式,false:大端模式
  285. this.data.audioDataList.push(sample)
  286. }
  287. //计算全局队列中数据的帧数
  288. let frameCounter = Math.floor(this.data.audioDataList.length / this.data.oneFrameLen)
  289. if (frameCounter) {//至少有一帧数据
  290. //截取音频数据帧(可能是1帧,也可能是多帧。若是多帧,有可能导致处理超时)
  291. const dataList = this.data.audioDataList.splice(0, frameCounter * this.data.oneFrameLen)
  292. //把音频数据逐帧注入wasm模块
  293. let ptr = this.wasmDataPtr.audioDataPtr / 2//音频数据在wasm模块中按2字节存储
  294. for (let i = 0; i < frameCounter; i++) {
  295. let beginTime = new Date().getTime()//开始计时
  296. //1、把数据注入wasm模块
  297. let baseIndex = i * this.data.oneFrameLen
  298. for (let j = 0; j < this.data.oneFrameLen; j++) {
  299. let item = dataList[baseIndex + j]
  300. HEAP16[ptr + j] = item//把音频数据注入wasm模块
  301. }
  302. //2、注入加速度数据
  303. let oneFrameAccDataList = []
  304. oneFrameAccDataList = this.organizeOneFrameAccData()//组织一秒加速度数据
  305. this.injectAccDataToWasm(oneFrameAccDataList)//注入数据
  306. //3、通知wasm模块处理音频数据
  307. let result = this.wasmFuncs.exchange_js_notice_wasm_start_work()
  308. let stopTime = new Date().getTime() - beginTime//结束计时
  309. console.log("耗时:", stopTime)
  310. //4、获取wasm模块处理音频数据的结果
  311. if (result != 1) {
  312. console.log("音频数据处理错误")
  313. } else {
  314. let item = []
  315. //定位坐标数据在wasm模块中按4字节存储
  316. let locationCoordPtr = this.wasmDataPtr.currentLocationCoordPtr / 4
  317. //a、从wasm模块读取音频数据处理结果
  318. for (let i = 0; i < 18; i++) {
  319. item.push(HEAP32[locationCoordPtr + i])
  320. }
  321. //TODO,b、使用定位结果这部分,需要二次开放商根据业务需求,做定制开发
  322. //使用定位结果
  323. if (item[0] === 1 && item[1] === 1) {
  324. let coord = {}
  325. coord.x = item[2]
  326. coord.y = item[3]
  327. //imu移动标识符
  328. const imuMoveFlag = item[17]//0:惯导计算出错,1:惯导计算成功,但手机未移动,50:惯导计算能成功,手机有移动
  329. if (imuMoveFlag == 50) {//手机有移动
  330. //将定位坐标发送至主线程
  331. worker.postMessage({
  332. message: 'WORKER_MAIN_CURRENT_LOCATION_COORD',
  333. data: coord
  334. })
  335. } else {//手机未移动或者出错
  336. console.log("imu move flag:", imuMoveFlag)
  337. }
  338. }
  339. // //向主线程发消息,通知读取音频文件
  340. // worker.postMessage({
  341. // message: 'WORKER_MAIN_NOTICE_READ_AUDIO_FILE',//读取音频文件
  342. // data: ''
  343. // })
  344. }
  345. }
  346. }
  347. }
  348. return
  349. },
  350. //缓存加速度数据
  351. storeAccData(oneAccData) {
  352. let item = {}
  353. item.timestamp = oneAccData.timestamp
  354. item.x = oneAccData.x
  355. item.y = oneAccData.y
  356. item.z = oneAccData.z
  357. this.data.accDataList.push(item)//缓存加速度
  358. },
  359. //组织一帧加速度数据(约250毫秒)
  360. organizeOneFrameAccData() {
  361. let oneFrameAccData = [] //一帧加速度数据
  362. if (this.data.accDataList.length > 0) {
  363. let stopTimestamp = this.data.accDataList[0].timestamp + 260//250毫秒的数据
  364. let accCounter = 0//计数器
  365. for (let k = 0; k < this.data.accDataList.length; k++) {
  366. let oneAcc = {}
  367. oneAcc = this.data.accDataList[k]
  368. if (oneAcc.timestamp <= stopTimestamp) {
  369. oneFrameAccData.push(oneAcc)
  370. accCounter++
  371. } else {
  372. break
  373. }
  374. }
  375. this.data.accDataList = this.data.accDataList.slice(accCounter)//截取加速度队列
  376. }
  377. return oneFrameAccData
  378. },
  379. //注入加速度到wasm模块
  380. injectAccDataToWasm(dataList) {
  381. let ptr = this.wasmDataPtr.accDataPtr / 4//加速度数据在wasm模块中按4字节存储
  382. let wLen = dataList.length;
  383. let item = {}
  384. if (wLen > 0) {
  385. //注入队列长度
  386. HEAPU32[ptr] = wLen //加速度队列长度
  387. ptr++
  388. //注入3轴加速度
  389. for (let i = 0; i < wLen; i++) {
  390. item = dataList[i]
  391. ptr = ptr + i * 3//修改数据指针
  392. HEAPF32[ptr] = item.x//x轴加速度
  393. HEAPF32[ptr + 1] = item.y//y轴加速度
  394. HEAPF32[ptr + 2] = item.z//z轴加速度
  395. }
  396. //保存数据
  397. this.wasmFuncs.exchange_js_notice_wasm_save_acc()
  398. }
  399. },
  400. //通知wasm模块释放内存
  401. noticeWasmFreeMemory() {
  402. this.wasmFuncs.exchange_js_notice_wasm_free_memory()
  403. }
  404. }
  405. export default wasmapi