在H5端实现实时翻译并导出

570 阅读2分钟

科大讯飞语音拼写 + 导出音频文件


重要 不支持的手机浏览器

  • 小米手机自带浏览器 15 版本以上不支持录音

  • 阿里系浏览器不支持浏览器录音

  • vivo 和 oppo 手机自带浏览不支持录音(待测试)


科大讯飞语音拼写 demo 下载

image.png


引入到自己的项目的时候需要注意的地方

index.js

import TransWorker from 'js/transcode.worker.js'

webpack.config.js

module.exports = function (env, argv) {
  env = env || {}
  return {
    mode: 'development',
    devtool: conf.devtool,
    module: {
      rules: [
        {
          test: /\.worker\.js$/,
          use: { loader: 'worker-loader' }
        },
      ]
    },
  }
}
  • 看着就是一个正常引入 其实在 webpack.config.js 中做了设置

  • 是用 worker-loader 引入的worker.js文件

  • 我当时就上当了(引入到项目的时候怎么弄就是不好使)

// 我引入的时候就是这样的

import TransWorker from "worker-loader!./transcode.worker.js";

把输入的录音导出来

  • Recorder 发现这个非常好用的插件

  • Recorder录音 在线demo

  • 和科大讯飞的录音权限有重复获取 体验上没什么,不过我认为完全没必要引入Recorder 我只要获取到音频文件就行了

接下来就是找音频文件了

  • 科大讯飞的官网上有个base64的文件,每次请求的时候都会带着,我想着就把这个base64转换一下转成我想要的,我没成功我放弃了

  • 找到 inputBuffer.getChannelData(0) 这个就是获取音频一帧的文件,把每一帧的文件保存起来,等结束的时候转成音频文件就行了

this.iatRecorder.stop(); // 音频结束录制的时候
// this.iatRecorder.audiobase64 这个是我的音频文件
this.audiobase64 = this.iatRecorder.audiobase64;
let alldata = this.mergeArray(this.iatRecorder.audiobase64);
let wavBuffer = this.createWavFile(alldata);
let audioblob = new Blob([new Uint8Array(wavBuffer)]);
let audiosrc = window.URL.createObjectURL(audioblob);
  • 这个就是我的音频文件

image.png

    mergeArray(list) {
        const length = list.length * list[0].length
        const data = new Float32Array(length)
        let offset = 0
        for (let i = 0; i < list.length; i++) {
        data.set(list[i], offset)
        offset += list[i].length
        }
        return data
    },
    createWavFile (audioData) {
        const WAV_HEAD_SIZE = 44;
        let buffer = new ArrayBuffer(audioData.length * 2 + WAV_HEAD_SIZE),
            // 需要用一个view来操控buffer
        view = new DataView(buffer);
        // 写入wav头部信息
        // RIFF chunk descriptor/identifier
        this.writeUTFBytes(view, 0, 'RIFF');
        // RIFF chunk length
        view.setUint32(4, 44 + audioData.length * 2, true);
        // RIFF type
        this.writeUTFBytes(view, 8, 'WAVE');
        // format chunk identifier
        // FMT sub-chunk
        this.writeUTFBytes(view, 12, 'fmt ');
        // format chunk length
        view.setUint32(16, 16, true);
        // sample format (raw)
        view.setUint16(20, 1, true);
        // stereo (2 channels)
        view.setUint16(22, 2, true);
        // sample rate
        view.setUint32(24, 44100, true);
        // byte rate (sample rate * block align)
        view.setUint32(28, 44100 * 2, true);
        // block align (channel count * bytes per sample)
        view.setUint16(32, 2 * 2, true);
        // bits per sample
        view.setUint16(34, 16, true);
        // data sub-chunk
        // data chunk identifier
        this.writeUTFBytes(view, 36, 'data');
        // data chunk length
        view.setUint32(40, audioData.length * 2, true);
        let length = audioData.length;
        let index = 44;
        for (let i = 0; i < length; i++) {
            // view.setInt16(index, audioData[i] * (0x7FFF * volume), true);
            var s = Math.max(-1, Math.min(1, audioData[i]))
            view.setInt16(index, s < 0 ? s * 0x8000 : s * 0x7fff, true)
            index += 2;
        }
        return buffer;
    },
    writeUTFBytes (view, offset, string) {
        var lng = string.length;
        for (var i = 0; i < lng; i++) { 
            view.setUint8(offset + i, string.charCodeAt(i));
        }

image.png

演示地址

git源码地址