实现一个mp3格式的录音器

实现一个mp3格式的录音器

先说下背景: 要实现一个人工智能聊天对话的功能,前端录音转成文本通过http发给服务端去回复,转文本这块是调用科大讯飞的接口,我前端录出来通过webSocket传给后端,因为是websocket传输,后端需要的格式是mp3格式二进制字节数组。

我一开始用浏览器的MediaRecorder API,代码实现如下:(这个实现有问题,记录下踩坑过程希望帮到读者,可快速跳过直接看最终实现)

整体流程设计(这里只做参考,我们现在只讨论录音器的封装)

前端录音 >>> 得到wav格式的blob >>> 转成MP3格式的blob >>> blob转二进制数据binaryArray >>> websocket发送给服务端 >>> 得到text文本

由于浏览器通过这个API录制的音频不是mp3格式的,只支持ogg、wav 等等,而科大讯飞那边需要的是mp3格式的,因此封装了一个将wav格式的blob数据转成MP3格式的音频blob的方法:wavBlob2Mp3Blob。

//wavBlob 转 mp3Blob
export function wavBlob2Mp3Blob(wavBlob) {
 return new Promise((resolve, reject) => {
   const reader = new FileReader();
   reader.onload = function (event) {
     const arrayBuffer = event.target.result;
     const dataView = new DataView(arrayBuffer);
     try {
       const mp3Data = encodeWavToMp3(dataView);
       const mp3Blob = new Blob([mp3Data], { type: "audio/mp3" });
       resolve(mp3Blob);
     } catch (error) {
       reject(error);
     }
   };
   reader.onerror = function (error) {
     reject(error);
   };
   reader.readAsArrayBuffer(wavBlob);
 });
}

由于使用websocket传输数据,文件的传输一般用binaryArray(字节数组),因此封装了将blob转成二进制数据的方法:blobToBinary:

//blob转二进制
export function blobToBinary(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const arrayBuffer = reader.result;
      const binaryData = new Uint8Array(arrayBuffer);
      resolve(binaryData);
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(blob);
  });
}
import { wavBlob2Mp3Blob } from "@/views/virtualTeacher/utils/wavBlob2Mp3Blob";
import { blobToBinary } from "@/views/virtualTeacher/utils/tool";

class Recorder {
  constructor() {
    this.recorder = null;
    this.audioChunks = []; // blob格式录音数据切片组
    this.audioBlob = null; // blob格式录音数据切片组
    this.isRecording = false;
    this.mp3BlobData = null;
  }
	
  //初始化
  async init() {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: true,
    });
    this.recorder = new MediaRecorder(stream, { mimeType: "audio/wav" });
    this.recorder.ondataavailable = (e) => {
      console.log("e.data录音data", e.data);
      this.audioChunks.push(e.data);
    };
  }
  //开始录音
  startRecord() {
    this.audioChunks.length = 0;
    this.audioBlob = null;
    if (this.mediaRecorder && this.mediaRecorder.state === "recording") {
      this.stopRecord(); // 如果已经在录音,先停止
    }
    this.recorder.start();
    this.isRecording = true;
  }
  // 结束录音
  stopRecord() {
    return new Promise((resolve) => {
      this.recorder.onstop = async (e) => {
        console.log("录音结束");
        this.audioBlob = new Blob(this.audioChunks, { type: "audio/wav" });
        this.mp3BlobData = await wavBlob2Mp3Blob(this.audioBlob);
        this.binaryData = await blobToBinary(this.audioBlob);
        resolve(); //  在 onstop 回调中 resolve Promise,不然是异步的,有可能你stop停止录制后立马拿到的audioBlob是空
      };
      this.recorder.stop();
      this.isRecording = false;
    });
  }
  // 下载录音文件
  //(顺手做了下这个下载功能,我们其实使用不到,可以忽略,不过帮助了我调试发现问题)
  downloadRecord() {
    if (!this.audioBlob) {
      console.error("没有可用的MP3录音数据");
      return;
    }
    const url = URL.createObjectURL(this.audioBlob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "recording.wav"; // 设置下载文件名
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url); // 释放内存
  }

  // 播放录音
  play() {
    if (!this.audioBlob) {
      console.error("没有可用的录音数据");
      return;
    }
    const audio = new Audio(URL.createObjectURL(this.audioBlob));
    audio.play();
    // URL.revokeObjectURL(url); // 释放内存
    // 我怕释放内存就不能播放了,我没试就注释掉了
  }
}

export { Recorder, isRecording };

这样做完用websocket把数据传过去,websocket没有返回,查看科大讯飞文档发现格式不对的时候可能会返回为空,于是添加了downloadRecord方法,去下载录音文件确定文件是否ok,下载下来后发现不能播放,只能在浏览器内部播放,拿到浏览器外边的系统播放器和其他播放软件都无法播放。那我知道了,就是这个文件有问题。

千难万险之后知道了:浏览器的mediaRecorder录出来的录音数据有问题,拿到外部播放不了。。。。
有人懂行的可以讲一讲。

然后我就找了个库:recorder-core,然后封装一个Recorder的类。

以下是最终行得通的实现

安装recorder-core

npm i recorder-core
import RecorderCore from "recorder-core";
//引入mp3格式支持文件;如果需要多个格式支持,把这些格式的编码引擎js文件放到后面统统引入进来即可
import "recorder-core/src/engine/mp3";
import "recorder-core/src/engine/mp3-engine";
import { blobToBinary } from "../../../utils/tool";
const config = {
  sampleRate: 16000, // 采样率 16kHz 或者你可以设置为 8000
  bitDepth: 16, // 位长 16bit
  channelCount: 1, // 单声道
};

class Recorder {
  constructor() {
    this.isRecording = false;
    this.recorder = null;
    this.audioBlob = null; // blob格式录音数据切片组
    this.mp3BlobData = null;
  }
  // 
  init() {
    this.recorder = new RecorderCore(config);
    // 录音打开
    this.recorder.open(() => {
      console.log("Recording opened");
    });
  }

  startRecord() {
    this.audioBlob = null;
    // 录音开始回调
    this.recorder.start(() => {
      console.log("Recording started");
      this.isRecording = true;
    });
  }
  // 结束录音
  stopRecord() {
    // 录音结束回调
    return new Promise((resolve, reject) => {
      this.recorder.stop(async (blob, duration) => {
        this.audioBlob = blob;
        this.audioBinary = await blobToBinary(blob);
        resolve(blob);
      });
    });
  }
  // 播放录音(也用不到,仅做参考,感觉可能以后能用到就写了)
  play() {
    const audio = new Audio(URL.createObjectURL(this.audioBlob));
    audio.play();
  }

  // 下载录音
  downloadRecord() {
    if (!this.audioBlob) {
      console.error("No recording available to download.");
      return;
    }
    const blob = new Blob([this.audioBlob], { type: "audio/mp3" });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "recording.mp3";
    document.body.appendChild(a);
    a.click();
    a.remove();
    window.URL.revokeObjectURL(url);
  }
}

export { Recorder };

外部使用的时候只需要导入这个类,实例化一个录音器,就可以对录音进行管理,获取录音数据,需要的同学可以按照自己需求改造封装。

参考链接http://www.cnblogs.com/Amandaliu/archive/2013/02/04/2891604.html 在链接内容基础上修改了amr编码格式为aac编码格式 Android提供了两个API用于实现录音功能:android.media.AudioRecord、android.media.MediaRecorder。 网上有很多谈论这两个类的资料。现在大致总结下: 1、AudioRecord 主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理(如会说话的汤姆猫、语音) 优点:语音的实时处理,可以用代码实现各种音频的封装 缺点:输出是PCM语音数据,如果保存成音频文件,是不能够被播放器播放的,所以必须先写代码实现数据编码以及压缩 示例: 使用AudioRecord类录音,并实现WAV格式封装。录音20s,输出的音频文件大概为3.5M左右(已写测试代码) 2、MediaRecorder 已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp 优点:大部分以及集成,直接调用相关接口即可,代码量小 缺点:无法实时处理音频;输出的音频格式不是很多,例如没有输出mp3格式文件 示例: 使用MediaRecorder类录音,输出amr格式文件。录音20s,输出的音频文件大概为33K(已写测试代码) 3、音频格式比较 WAV格式:录音质量高,但是压缩率小,文件大 AAC格式:相对于mp3,AAC格式的音质更佳,文件更小;有损压缩;一般苹果或者Android SDK4.1.2(API 16)及以上版本支持播放 AMR格式:压缩比比较大,但相对其他的压缩格式质量比较差,多用于人声,通话录音 至于常用的mp3格式,使用MediaRecorder没有该视频格式输出。一些人的做法是使用AudioRecord录音,然后编码成wav格式,再转换成mp3格式 再贴上一些测试工程。 功能描述: 1、点击“录音WAV文件”,开始录音。录音完成后,生成文件/sdcard/FinalAudio.wav 2、点击“录音AMR文件”,开始录音。录音完成后,生成文件/sdcard/FinalAudio.amr 3、点击“停止录音”,停止录音,并显示录音输出文件以及该文件大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值