基于FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字

22 篇文章 0 订阅
12 篇文章 1 订阅

       终于折腾好了(使用FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字,我的是Vue.js项目),对一个前端仔来说,听到要兼容IE,都是不情愿的,但是需求来了,那就折腾起来吧。(主要这需求砍不了,菜刀没带)。但是经过这几天折腾,记录一下,不足之处,还望指教~

       首先呢,明确下我们的目标,支持IE11浏览器录音后转文字。我们用逆向思维思考下,音频转文字。我们目前用的是科大讯飞的SDK。前端的话,把我们拿到的音频数据,包装好,调用一下后端给的接口,然后后端调讯飞SDK,转化为文字,发回来给我们。所以我们最主要是拿到音频数据,即录音。那我们难点是IE11上录音,因为IE 不支持 navigator.mediaDevices.getUserMedia这样的API。这里用JS调用flash去实现IE上录音。(应该还可以调用ActiveX,没尝试)。

       这里在github找到两个可以运行起来的demo。传送门:recorder.js,FlashWavRecorder 

       这两个提供的example 都可以正常跑起来。recorder.js音质比较好,第二个杂音大点。但recorder.js不知道怎么取到录音后的音频数据,照理说,上传都可以实现了,拿到音频数据,理应可以。或许是我的能力问题,还请指点。FlashWavRecorder 有getBase64的方法,所以就选用FlashWavRecorder 了。

     

      调用别人的SDK,就要符合别人的游戏规则。如下摘自科大讯飞接入规则

1. 接口说明

  1. 授权认证,调用接口需要将 Appid,CurTime, Param 和 CheckSum 信息放在 HTTP 请求头中;
  2. 接口统一为 UTF-8 编码;
  3. 接口支持 http 和 https;
  4. 请求方式为POST。

2. 授权认证

在调用所有业务接口时,都需要在 Http Request Header 中配置以下参数用于授权认证:

参数格式说明必须
X-Appidstring讯飞开放平台注册申请应用的应用ID(appid)
X-CurTimestring当前UTC时间戳,从1970年1月1日0点0 分0 秒开始到现在的秒数
X-Paramstring相关参数JSON串经Base64编码后的字符串,见各接口详细说明
X-CheckSumstring令牌,计算方法:MD5(apiKey + curTime + param),三个值拼接的字符串,进行MD5哈希计算(32位小写),其中apiKey由讯飞提供,调用方管理。

     讯飞demo中有这个字段 文档没写 

bodystringdataUrl 

注:

  • apiKey:接口密钥,由讯飞开放平台提供,调用方注意保管,如泄露,可联系讯飞技术人员重置;
  • checkSum 有效期:出于安全性考虑,每个 checkSum 的有效期为 5 分钟(用 curTime 计算),同时 curTime 要与标准时间同步,否则,时间相差太大,服务端会直接认为 curTime 无效;
  • BASE64 编码采用 MIME 格式,字符包括大小写字母各26个,加上10个数字,和加号 + ,斜杠 / ,一共64个字符。

checkSum 生成示例:

String apiKey="abcd1234"; 
String curTime="1502607694";
String param="eyAiYXVmIjogImF1ZGlvL0wxNjtyYXR...";
String checkSum=MD5(apiKey+curTime+param);
Copy

3. 白名单

在调用所有业务接口时,授权认证通过后,服务端会检查调用方ip是否在讯飞开放平台配置的ip白名单中,对于没有配置到白名单中的IP发来的请求,服务端会拒绝服务。 注:

  • IP白名单可在控制台应用管理卡片上编辑,五分钟左右生效;
  • IP白名单最多可设置5个,更多的需求可通过工单联系技术人员;
  • 如果服务器返回结果如下所示,则表示由于未配置IP白名单,服务端拒绝服务:
{
    "code":"10105",
    "desc":"illegal access|illegal client_ip",
    "data":"",
    "sid":"xxxxxx"
}

我们请求后端接口类似这样:

that.$http.postObj('restful/voice/recognition', {
                'body': sendurl,
                'X-Appid': that.appid,
                'X-CurTime': time,
                'X-Param': xParam,
                'X-CheckSum': xChecksum
            }).then((res) => {
                    //成功识别语音后,在这里拿到文字 eg:res.data.data
                    console.log(res)
                }
            }).catch((err) => {
                console.log(err)
            });
//basic.js  

$(function () {  
//这里的$(function () {})  原生写法是function DOMContentLoaded(){};
//document.addEventListener('DOMContentLoaded', DOMContentLoaded, false);

  var RECORDER_APP_ID = "recorderApp";
  var $level = $('.level .progress');
  var appWidth = 24;
  var appHeight = 24;
  var flashvars = {'upload_image': '../images/upload.png'};
  var params = {};
  var attributes = {'id': RECORDER_APP_ID, 'name': RECORDER_APP_ID};
  swfobject.embedSWF("../recorder.swf", "flashcontent", appWidth, appHeight, "11.0.0", "", flashvars, params, attributes); 
 //注意这句,要确保swfobject.js 已经加载好了 要准备个容器不然报错 <div id="flashcontent" />
 //这里使用  document.getElementById(RECORDER_APP_ID).style.top='-3333px' 
 // 因为把swf对象设置为display:none 或者visibility:hidden 都是使用不了 
  window.fwr_event_handler = function fwr_event_handler() {//flash插件相关状态反馈
    var name, $controls;
    switch (arguments[0]) {
      case "ready":
        FWRecorder.uploadFormId = "#uploadForm";
        FWRecorder.uploadFieldName = "upload_file[filename]";
        FWRecorder.connect(RECORDER_APP_ID, 0);//如果老是报 recoder is null 这里没有调进来
        FWRecorder.recorderOriginalWidth = appWidth;
        FWRecorder.recorderOriginalHeight = appHeight;
        break;
       
      case "permission_panel_closed":
                FWRecorder.recorderOriginalWidth = 0;
                FWRecorder.recorderOriginalHeight = 0;
                document.getElementById(RECORDER_APP_ID).style.top='-3333px'//隐藏白白一块
                    FWRecorder.defaultSize();
                    break;
      case "microphone_user_request":
        FWRecorder.showPermissionWindow();//这里请求后 会弹出 是否 允许 使用麦克风的请求
        break;

        ....这里省略局部代码
      case "microphone_level"://这里用于显示声波。如果成功接入麦克风,录音的时候会一直调用
        $level.css({height: arguments[1] * 100 + '%'});
        break;

        ....这里省略局部代码
  };
 .......这里省略局部代码
});

          以上主要是理解swfobject.embedSWF的用法。这里有详细的传送门: swfobject.js使用

//recoder.js  


(function(global) {
  var Recorder;

  var RECORDED_AUDIO_TYPE = "audio/wav";

  Recorder = {
    recorder: null,
    recorderOriginalWidth: 0,
    recorderOriginalHeight: 0,
    uploadFormId: null,
    uploadFieldName: null,
    isReady: false,

    connect: function(name, attempts) {//连接麦克风 主要是init recorder
      if(navigator.appName.indexOf("Microsoft") != -1) {
        Recorder.recorder = window[name];
      } else {
        Recorder.recorder = document[name];//swfObject对象 ,这里的 name='recorderApp'
      }

      if(attempts >= 40) {
        return;
      }

      // flash app needs time to load and initialize
      if(Recorder.recorder && Recorder.recorder.init) {
        Recorder.recorderOriginalWidth = Recorder.recorder.width;
        Recorder.recorderOriginalHeight = Recorder.recorder.height;
        if(Recorder.uploadFormId && $) {
          var frm = $(Recorder.uploadFormId); 
         Recorder.recorder.init(frm.attr('action').toString(), Recorder.uploadFieldName, frm.serializeArray());
        }
        return;
      }

      setTimeout(function() {Recorder.connect(name, attempts+1);}, 100);
    },

    playBack: function(name) {//播放
      // TODO: Rename to `playback`
      Recorder.recorder.playBack(name);
    },

    stopRecording: function() {//停止录音
      Recorder.recorder.stopRecording();
    },

    resize: function(width, height) {//设置 麦克风权限请求框大小
      Recorder.recorder.width = width + "px";
      Recorder.recorder.height = height + "px";
    },

    defaultSize: function() {
      Recorder.resize(Recorder.recorderOriginalWidth, Recorder.recorderOriginalHeight);
    },

    show: function() {
      Recorder.recorder.show();
    },

    hide: function() {
      Recorder.recorder.hide();
    },

    getBase64: function(name) {//来获取base64数据 如果需要转回来 window.atob("base64")
      var data = Recorder.recorder.getBase64(name);
      return 'data:' + RECORDED_AUDIO_TYPE + ';base64,' + data;
    },

    getBlob: function(name) {//这里转化为blod 后面讯飞转文字的时候要用到。当然可以自己转
      var base64Data = Recorder.getBase64(name).split(',')[1];
      return base64toBlob(base64Data, RECORDED_AUDIO_TYPE);
    },
    showPermissionWindow: function(options) {
      Recorder.resize(240, 160);
      // need to wait until app is resized before displaying permissions screen
      var permissionCommand = function() {
        if (options && options.permanent) {
          Recorder.recorder.permitPermanently();
        } else {
          Recorder.recorder.permit();
        }
      };
      setTimeout(permissionCommand, 1);
    },
  };

  function base64toBlob(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;

    var byteCharacters = atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
      var slice = byteCharacters.slice(offset, offset + sliceSize);

      var byteNumbers = new Array(slice.length);
      for (var i = 0; i < slice.length; i++) {
        byteNumbers[i] = slice.charCodeAt(i);
      }

      var byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }

    return new Blob(byteArrays, {type: contentType});
  }

  global.FWRecorder = Recorder;


})(this);

然后写几个button 接入就好了。      大家可以动手试一试。

注意:如果是在vue.js中当成模块的时候,ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

不能使用arguments.callee;不能使用arguments.caller;禁止this指向全局对象;不能使用fn.caller和fn.arguments获取函数调用的堆栈 等等   所以引入swfobject.js 的时候 要把 arguments.callee 替换掉。 传送门:严格模式下arguments.callee代替思路

例如有用arguments.callee的函数里,

function (){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);


把匿名函数 改为具名函数 ?    swfobject.js 里面好像有7处需要改 

function fn(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(fn,0);

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TE-茶叶蛋

踩坑不易,您的打赏,感谢万分

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值