From 994cd8a16d13ad226c71b7ef1de1d70aba417c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E5=9D=9A=E6=9E=9C?= <753610399@qq.com> Date: Mon, 25 Oct 2021 13:20:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DBufferStreamPlayer=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E5=87=BA=E7=8E=B0=E7=9A=84=E5=B0=8Fbug=EF=BC=8C?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 40 ++++- app-support-sample/README.md | 10 ++ assets/runtime-codes/teach.env_in.follow.js | 163 ++++++++++++++++++ ...0\201\345\210\206\345\217\221Runtime.html" | 1 + src/extensions/buffer_stream.player.js | 4 +- 5 files changed, 207 insertions(+), 11 deletions(-) create mode 100644 assets/runtime-codes/teach.env_in.follow.js diff --git a/README.md b/README.md index 0f0e796..687e88a 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,15 @@ # :open_book:Recorder用于html5录音 -[​](?Ref=Desc&Start)支持在大部分已实现`getUserMedia`的移动端、PC端浏览器录音,主要包括:Chrome、Firefox、Safari、IOS 14.3+、Android WebView、腾讯Android X5内核(QQ、微信)、大部分2021年后更新的Android手机自带浏览器;不支持:~~UC系内核(典型的支付宝),大部分未更新的老旧国产手机自带浏览器,低版本IOS(11.0-14.2)上除Safari外的其他任何形式的浏览器(含PWA、WebClip、任何App内网页)~~。 +[​](?Ref=Desc&Start)支持在大部分已实现`getUserMedia`的移动端、PC端浏览器麦克风录音、实时处理,主要包括:Chrome、Firefox、Safari、IOS 14.3+、Android WebView、腾讯Android X5内核(QQ、微信)、大部分2021年后更新的Android手机自带浏览器;不支持:~~UC系内核(典型的支付宝),大部分未更新的老旧国产手机自带浏览器,低版本IOS(11.0-14.2)上除Safari外的其他任何形式的浏览器(含PWA、WebClip、任何App内网页)~~。 支持对任意`MediaStream`进行音频录制、实时处理,包括:`getUserMedia返回的流`、`WebRTC中的remote流`、`audio、video标签的captureStream方法返回的流`、`自己创建的流` 等等。 +提供多个扩展功能支持:拥有丰富的音频可视化、变速变调处理、音频流播放等;搭配上强大的实时处理支持,可用于各种网页应用:从简单的录音,到复杂的实时语音识别(ASR),甚至音频相关的游戏,都能从容应对。 + +音频文件的播放:可直接使用常规的`Audio HTML标签`来播放完整的音频文件,参考文档下面的【快速使用】部分,有播放例子;上传了的录音直接将音频链接赋值给`audio.src`即可播放;本地的`blob音频文件`可通过`URL.createObjectURL`来生成本地链接赋值给`audio.src`即可播放,或者将blob对象直接赋值给`audio.srcObject`(兼容性没有src高)。实时的音频片段文件播放,可以使用本库自带的`BufferStreamPlayer`扩展来播放,简单高效,或者采用别的途径播放。 + + [​](?) **Recorder H5** : @@ -34,6 +39,8 @@ > [](https://xiangyuecn.gitee.io/recorder/) 扫一扫在线测试,`github.io`可访问性太不尽人意,所以使用`gitee.io`镜像库的速度快多了。 +[​](?) + [​](?) 录音默认输出mp3格式,另外可选wav、pcm格式;有限支持ogg(beta)、webm(beta)、amr(beta)格式;支持任意格式扩展(前提有相应编码器)。 @@ -43,7 +50,9 @@ > mp3使用lamejs编码(CBR),压缩后的recorder.mp3.min.js文件150kb左右(开启gzip后54kb)。如果对录音文件大小没有特别要求,可以仅仅使用录音核心+wav编码器(raw pcm format录音文件超大),压缩后的recorder.wav.min.js不足5kb。录音得到的mp3(CBR)、wav(PCM),均可简单拼接小的二进制录音片段文件来生成长的音频文件,具体参考下面这两种编码器的详细介绍。 -> 如需在Hybrid App内使用(支持IOS、Android),或提供低版本IOS微信的支持,请参阅[app-support-sample](https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample)目录。 +[​](?) + +> 如需在Hybrid App内使用(支持IOS、Android),或提供低版本IOS微信的支持,请参阅下面的App中录音示例,参考示例代码给网页授予录音权限,或直接由App底层提供接口给H5调用([app-support-sample](https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample)目录内有源码)。 > > *低版本IOS兼容、老旧国产手机自带浏览器上的使用限制等问题和兼容请参阅下面的知识库部分;打开录音后对音频播放的影响、录音中途来电话等问题也参阅下面的知识库。* @@ -66,9 +75,10 @@ 9. [【Demo库】【文件合并】-wav多个片段文件合并](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=lib.merge.wav_merge) 10. [【教程】实时多路音频混音](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=teach.realtime.mix_multiple) 11. [【教程】变速变调音频转换](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=teach.sonic.transform) -12. [【教程】DTMF(电话拨号按键信号)解码、编码](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=teach.dtmf.decode_and_encode) -13. [【Demo库】PCM采样率提升](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=lib.samplerate.raise) -14. [【测试】音频可视化相关扩展测试](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=test.extensions.visualization) +12. [【教程】新录音从老录音接续、或录制中途插入音频](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=teach.env_in.follow) +13. [【教程】DTMF(电话拨号按键信号)解码、编码](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=teach.dtmf.decode_and_encode) +14. [【Demo库】PCM采样率提升](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=lib.samplerate.raise) +15. [【测试】音频可视化相关扩展测试](https://xiangyuecn.gitee.io/recorder/assets/工具-代码运行和静态分发Runtime.html?jsname=test.extensions.visualization) ### App Demo @@ -177,8 +187,9 @@ var recOpen=function(success){//一般在显示出录音按钮或相关的录音 type:"mp3",sampleRate:16000,bitRate:16 //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎 ,onProcess:function(buffers,powerLevel,bufferDuration,bufferSampleRate,newBufferIdx,asyncEnd){ //录音实时回调,大约1秒调用12次本回调 - //可利用extensions/waveview.js扩展实时绘制波形 + //可实时绘制波形(extensions目录内的waveview.js、wavesurfer.view.js、frequency.histogram.view.js扩展功能) //可利用extensions/sonic.js扩展实时变速变调,此扩展计算量巨大,onProcess需要返回true开启异步模式 + //可实时上传(发送)数据,配合Recorder.SampleData方法,将buffers中的新数据连续的转换成pcm上传,或使用mock方法将新数据连续的转码成其他格式上传,可以参考文档里面的:Demo片段列表 -> 实时转码并上传-通用版;基于本功能可以做到:实时转发数据、实时保存数据、实时语音识别(ASR)等 } }); @@ -422,13 +433,14 @@ set={ ,onProcess:NOOP //接收到录音数据时的回调函数:fn(buffers,powerLevel,bufferDuration,bufferSampleRate,newBufferIdx,asyncEnd) //返回值:onProcess如果返回true代表开启异步模式,在某些大量运算的场合异步是必须的,必须在异步处理完成时调用asyncEnd(不能真异步时需用setTimeout包裹);返回其他值或者不返回为同步模式(需避免在回调内执行耗时逻辑);如果开启异步模式,在onProcess执行后新增的buffer会全部替换成空数组,因此本回调开头应立即将newBufferIdx到本次回调结尾位置的buffer全部保存到另外一个数组内,处理完成后写回buffers中本次回调的结尾位置。 //buffers=[[Int16,...],...]:缓冲的PCM数据,为从开始录音到现在的所有pcm片段,每次回调可能增加0-n个不定量的pcm片段。 + //注意:buffers数据的采样率为bufferSampleRate,它和set.sampleRate不一定相同,可能为浏览器提供的原始采样率rec.srcSampleRate,也可能为已转换好的采样率set.sampleRate;如需浏览器原始采样率的数据,请使用rec.buffers原始数据,而不是本回调的参数;如需明确和set.sampleRate完全相同采样率的数据,请在onProcess中自行连续调用采样率转换函数Recorder.SampleData(),配合mock方法可实现实时转码和压缩语音传输;修改或替换buffers内的数据将会改变最终生成的音频内容(注意不能改变第一维数组长度),比如简单有限的实现实时静音、降噪、混音等处理,详细参考下面的rec.buffers //powerLevel:当前缓冲的音量级别0-100。 //bufferDuration:已缓冲时长。 - //bufferSampleRate:缓冲使用的采样率(当type支持边录边转码(Worker)时,此采样率和设置的采样率相同,否则不一定相同)。 + //bufferSampleRate:buffers缓存数据的采样率(当type支持边录边转码(Worker)时,此采样率和设置的采样率相同,否则不一定相同)。 //newBufferIdx:本次回调新增的buffer起始索引。 //asyncEnd:fn() 如果onProcess是异步的(返回值为true时),处理完成时需要调用此回调,如果不是异步的请忽略此参数,此方法回调时必须是真异步(不能真异步时需用setTimeout包裹)。 //如果需要绘制波形之类功能,需要实现此方法即可,使用以计算好的powerLevel可以实现音量大小的直观展示,使用buffers可以达到更高级效果 - //注意,buffers数据的采样率和set.sampleRate不一定相同,可能为浏览器提供的原始采样率rec.srcSampleRate,也可能为已转换好的采样率set.sampleRate;如需浏览器原始采样率的数据,请使用rec.buffers原始数据,而不是本回调的参数;如需明确和set.sampleRate完全相同采样率的数据,请在onProcess中自行连续调用采样率转换函数Recorder.SampleData(),配合mock方法可实现实时转码和压缩语音传输;修改或替换buffers内的数据将会改变最终生成的音频内容(注意不能改变第一维数组长度),比如简单有限的实现实时静音、降噪、混音等处理,详细参考下面的rec.buffers + //如果需要实时上传(发送)之类的,可以配合Recorder.SampleData方法,将buffers中的新数据连续的转换成pcm,或使用mock方法将新数据连续的转码成其他格式,可以参考文档里面的:Demo片段列表 -> 实时转码并上传-通用版;基于本功能可以做到:实时转发数据、实时保存数据、实时语音识别(ASR)等 //*******高级设置****** //,sourceStream:MediaStream Object @@ -514,8 +526,18 @@ buffers中的PCM数据为浏览器采集的原始音频数据,采样率为浏 浏览器提供的原始采样率,只有start或mock调用后才会有值,此采样率就是rec.buffers数据的采样率。 +### 【方法】rec.envIn(pcmData,pcmAbsSum) +本方法是一个内部使用的最为核心方法,如果你不知道用途,请勿随意调用,配套的有私有方法`envStart(mockEnvInfo,sampleRate)`(私有方法请自行阅读源码),这两方法控制着录音的开启、实时音频输入逻辑,起到隔离平台环境差异的作用(Recorder、RecordApp共享使用了本机制,实现了录音过程和平台环境无关)。 + +通过调用本方法,会在当前正在录制的录音中追加进新的pcm数据,每次调用本方法都会触发onProcess回调;从而可以做到:在录音过程中插入音频数据、在新的录音中注入之前老的录音的buffers数据可以做到接续录音 等业务逻辑,可参考上面的Demo片段列表中的`新录音从老录音接续、或录制中途插入音频`例子。 + +`pcmData`:`[Int16,...]` 为一维数组,pcm音频数据的采样率必须是`rec.srcSampleRate` (如果不是,请用`Recorder.SampleData()`方法先转换好) + +`pcmAbsSum`:pcmData所有采样的绝对值的和,用来传递给`Recorder.PowerLevel`方法计算音量百分比,最终是给onProcess使用,如果不需要计算音量百分比,直接给0即可。 + + ### 【方法】rec.mock(pcmData,pcmSampleRate) -模拟一段录音数据,后面可以调用stop进行编码。需提供pcm数据 `pcmData` `=` `[Int16,...]` 为一维数组,和pcm数据的采样率 `pcmSampleRate`。 +模拟一段录音数据,后面直接调用stop进行编码得到音频文件。需提供pcm数据 `pcmData` `=` `[Int16,...]` 为一维数组,和pcm数据的采样率 `pcmSampleRate`。调用本方法后无需调用也无法调用open、close、start等方法,只能调用stop,如果之前已经开始了录音,前面的录音数据全部会被丢弃;本方法主要用于音频转码。 提示:在录音实时回调中配合`Recorder.SampleData()`方法使用效果更佳,可实时生成小片段语音文件。 diff --git a/app-support-sample/README.md b/app-support-sample/README.md index 687b512..b7841ff 100644 --- a/app-support-sample/README.md +++ b/app-support-sample/README.md @@ -132,6 +132,9 @@ import 'recorder-core/src/extensions/waveview' [​](?Ref=Codes&Start)然后使用,假设立即运行,只录3秒,会自动根据环境使用Native录音、微信JsSDK录音、H5录音 ``` javascript //var dialog=createDelayDialog(); 开启可选的弹框伪代码,需先于权限请求前执行,因为回调不确定是同步还是异步的 +//RecordApp.AlwaysUseWeixinJS=true; 在微信内强制总是使用微信的JsSDK接口录音,这样也方便于Android微信上进行调试,或者统一使用微信JsSDK来录音 +//RecordApp.AlwaysAppUseJS=true; App里面总是使用H5网页版录音,可用于测试App里面的网页兼容性 + //请求录音权限 RecordApp.RequestPermission(function(){ //dialog&&dialog.Cancel(); 如果开启了弹框,此处需要取消 @@ -364,6 +367,13 @@ IOS-Weixin底层会把从微信素材下载过来的原始音频信息存储在s 此配置只有在组件是通过RecordApp自动加载时才会有效,如果组件是手动引入的时不会生效;会影响的组件有:`RecordApp.Platforms`的`Config.paths`中标记了`lazyBeforeStart=1`、`lazyBeforeStop=1`的js;`lazyBeforeStart`标记的js会在`Start`调用前完成加载,否则会阻塞`Start`,`lazyBeforeStop`标记的js会在`Stop`调用前完成加载,否则会阻塞`Stop`。 +## 【静态属性】RecordApp.AlwaysUseWeixinJS +默认为`false`,设为`true`时:在微信内强制总是使用微信的JsSDK接口录音,这样也方便于Android微信上进行调试,或者统一使用微信JsSDK来录音。 + +## 【静态属性】RecordApp.AlwaysAppUseJS +默认为`false`,设为`true`时:App里面总是使用H5网页版录音,可用于测试App里面的网页兼容性。 + + ## 【静态属性】RecordApp.Current 为`RecordApp.Install`初始化后识别到的底层平台,取值为`RecordApp.Platforms`之一。 diff --git a/assets/runtime-codes/teach.env_in.follow.js b/assets/runtime-codes/teach.env_in.follow.js new file mode 100644 index 0000000..0f50f58 --- /dev/null +++ b/assets/runtime-codes/teach.env_in.follow.js @@ -0,0 +1,163 @@ +/****************** +《【教程】新录音从老录音接续、或录制中途插入音频》 +作者:高坚果 +时间:2021-10-25 10:55:55 + +本教程是Recorder内部使用的最为核心方法 rec.envIn(pcmData,pcmAbsSum) 的使用示例;配套的有私有方法`envStart(mockEnvInfo,sampleRate)`(私有方法请自行阅读源码),这两方法控制着录音的开启、实时音频输入逻辑,起到隔离平台环境差异的作用(Recorder、RecordApp共享使用了本机制,实现了录音过程和平台环境无关)。 + +通过调用 rec.envIn 方法,会在当前正在录制的录音中追加进新的pcm数据,每次调用本方法都会触发onProcess回调;从而可以做到:在录音过程中插入音频数据、在新的录音中注入之前老的录音的buffers数据可以做到接续录音 等业务逻辑。 + +由于 rec.envIn 实现机制复杂,理解起来很困难,不太建议使用;在你不知道确切用途的情况下,请勿随意调用。 +******************/ + +//从素材录音接续录音,之前有一个录音已经stop了,通过这个例子,可以继续恢复录音 +var recEnvInStart=function(){ + if(!srcOk){ + Runtime.Log("请先录制一段素材",1); + return; + } + + _start(function(){ + //将素材录音的buffers二维数组转成一维,就拿到pcm了,注意采样率 + var chunk=Recorder.SampleData(srcRec.buffers, srcRec.srcSampleRate, rec.srcSampleRate); + //直接输入pcm数据到刚start的录音,这样就会接续录音了,因为是这里和start是同步操作,输入的数据会被放到开头 + //pcm的采样率必须和rec的srcSampleRate一致 + rec.envIn(chunk.data);//忽略pcmAbsSum参数,影响不大 + + Runtime.Log("已从素材录音尾部接续开始新的录音,正在录音中..."); + }); +}; +//普通开始录音 +var recStart=function(){ + _start(function(){ + Runtime.Log("已开始普通录音中..."); + }); +}; + +var rec,curDuration; +var _start=function(onStart){ + rec&&rec.close(); + curDuration=0; + + rec=Recorder({ + type:"mp3" + ,sampleRate:16000 + ,bitRate:16 + ,onProcess:function(buffers,powerLevel,bufferDuration,bufferSampleRate){ + curDuration=bufferDuration; + Runtime.Process.apply(null,arguments); + } + }); + var t=setTimeout(function(){ + Runtime.Log("无法录音:权限请求被忽略(超时假装手动点击了确认对话框)",1); + },8000); + + rec.open(function(){//打开麦克风授权获得相关资源 + clearTimeout(t); + + rec.start();//开始录音 + onStart(); + },function(msg,isUserNotAllow){//用户拒绝未授权或不支持 + clearTimeout(t); + Runtime.Log((isUserNotAllow?"UserNotAllow,":"")+"无法录音:"+msg, 1); + }); +}; + +//在当前正在进行的录音中插入素材的音频数据,录音结果会变长 +var append=function(){ + if(!srcOk){ + Runtime.Log("未进行素材录音",1); + return; + } + if(!rec){ + Runtime.Log("未开始录音",1); + return; + } + + //将素材录音的buffers二维数组转成一维,就拿到pcm了,注意采样率 + var chunk=Recorder.SampleData(srcRec.buffers, srcRec.srcSampleRate, rec.srcSampleRate); + //直接输入pcm数据到当前录音,追加到录音后面,pcm的采样率必须和rec的srcSampleRate一致 + rec.envIn(chunk.data);//忽略pcmAbsSum参数,影响不大 + + Runtime.Log("已在"+curDuration+"ms处插入了素材音频"); +}; + +//停止录音 +var recStop=function(){ + if(!rec){ + Runtime.Log("未开始录音",1); + return; + } + rec.stop(function(blob,duration){ + rec.close();//释放录音资源 + + Runtime.LogAudio(blob,duration,rec); + },function(msg){ + Runtime.Log("录音失败:"+msg, 1); + }); +}; + + + +//=====以下代码无关紧要,音频数据源,采集原始音频用的================== +//加载录音框架 +Runtime.Import([ + {url:RootFolder+"/src/recorder-core.js",check:function(){return !window.Recorder}} + ,{url:RootFolder+"/src/engine/mp3.js",check:function(){return !Recorder.prototype.mp3}} + ,{url:RootFolder+"/src/engine/mp3-engine.js",check:function(){return !Recorder.lamejs}} +]); + +//显示控制按钮 +Runtime.Ctrls([ + {name:"先录一段音作为素材",click:"srcStart"} + ,{name:"结束素材录音",click:"srcStop"} + ,{html:"
"} + ,{name:"从素材录音开始接续录音",click:"recEnvInStart"} + ,{name:"普通开始录音",click:"recStart"} + ,{name:"结束录音",click:"recStop"} + ,{html:"
"} + ,{name:"在当前录制中插入素材的音频",click:"append"} + ,{html:"
"} +]); + + +//调用录音 +var srcRec,srcOk=0; +function srcStart(){ + srcRec&&srcRec.close(); + srcOk=0; + + srcRec=Recorder({ + type:"mp3" + ,sampleRate:16000 + ,bitRate:16 + ,onProcess:function(buffers,powerLevel,bufferDuration,bufferSampleRate){ + Runtime.Process.apply(null,arguments); + } + }); + var t=setTimeout(function(){ + Runtime.Log("无法录音:权限请求被忽略(超时假装手动点击了确认对话框)",1); + },8000); + + srcRec.open(function(){//打开麦克风授权获得相关资源 + clearTimeout(t); + srcRec.start();//开始录音 + },function(msg,isUserNotAllow){//用户拒绝未授权或不支持 + clearTimeout(t); + Runtime.Log((isUserNotAllow?"UserNotAllow,":"")+"无法录音:"+msg, 1); + }); +}; +function srcStop(){ + if(!srcRec){ + Runtime.Log("未开始素材录音",1); + return; + } + srcRec.stop(function(blob,duration){ + srcRec.close();//释放录音资源 + srcOk=1; + + Runtime.LogAudio(blob,duration,srcRec,"素材录音"); + },function(msg){ + Runtime.Log("录音失败:"+msg, 1); + }); +}; \ No newline at end of file diff --git "a/assets/\345\267\245\345\205\267-\344\273\243\347\240\201\350\277\220\350\241\214\345\222\214\351\235\231\346\200\201\345\210\206\345\217\221Runtime.html" "b/assets/\345\267\245\345\205\267-\344\273\243\347\240\201\350\277\220\350\241\214\345\222\214\351\235\231\346\200\201\345\210\206\345\217\221Runtime.html" index 9946485..5c832b7 100644 --- "a/assets/\345\267\245\345\205\267-\344\273\243\347\240\201\350\277\220\350\241\214\345\222\214\351\235\231\346\200\201\345\210\206\345\217\221Runtime.html" +++ "b/assets/\345\267\245\345\205\267-\344\273\243\347\240\201\350\277\220\350\241\214\345\222\214\351\235\231\346\200\201\345\210\206\345\217\221Runtime.html" @@ -1112,6 +1112,7 @@ //,{n:"【教程】实时语音识别(语音转文字)",k:"teach.realtime.asr"} ,{n:"【教程】实时多路音频混音",k:"teach.realtime.mix_multiple"} ,{n:"【教程】变速变调音频转换",k:"teach.sonic.transform"} +,{n:"【教程】新录音从老录音接续、或录制中途插入音频",k:"teach.env_in.follow"} ,{n:"【教程】DTMF(电话拨号按键信号)解码、编码",k:"teach.dtmf.decode_and_encode"} ,{n:"【Demo库】PCM采样率提升",k:"lib.samplerate.raise"} diff --git a/src/extensions/buffer_stream.player.js b/src/extensions/buffer_stream.player.js index 5076f01..bcab683 100644 --- a/src/extensions/buffer_stream.player.js +++ b/src/extensions/buffer_stream.player.js @@ -591,11 +591,11 @@ fn.prototype=BufferStreamPlayer.prototype={ /**pcm数据进行首尾1ms淡入淡出处理,播放时可以大幅减弱爆音**/ var FadeInOut=BufferStreamPlayer.FadeInOut=function(arr,sampleRate){ - var sd=sampleRate/1000*1; + var sd=sampleRate/1000*1;//浮点数,arr是Int16或者Float32 for(var i=0;i