yiyi-audio-recorder
Version:
js audio recorder plugin
402 lines (351 loc) • 9.91 kB
text/typescript
import { downloadPCM, downloadWAV, download } from "./download/download";
import {
compress,
encodeMP3,
encodePCM,
encodeWAV,
} from "./transform/transform";
import Player from "./player/player";
import Recorder from "./recorder/recorder";
declare let window: any;
declare let Math: any;
declare let navigator: any;
declare let Promise: any;
// 构造函数参数格式
interface recorderConfig {
sampleBits?: number; // 采样位数
sampleRate?: number; // 采样率
numChannels?: number; // 声道数
compiling?: boolean; // 是否边录边播
}
class Index extends Recorder {
private isrecording: boolean = false; // 是否正在录音
private ispause: boolean = false; // 是否是暂停
private isplaying: boolean = false; // 是否正在播放
public onplay: () => void; // 音频播放回调
public onpauseplay: () => void; // 音频暂停回调
public onresumeplay: () => void; // 音频恢复播放回调
public onstopplay: () => void; // 音频停止播放回调
public onplayend: () => void; // 音频正常播放结束
public interval: NodeJS.Timer;
/**
* @param {Object} options 包含以下三个参数:
* sampleBits,采样位数,一般8,16,默认16
* sampleRate,采样率,一般 11025、16000、22050、24000、44100、48000,默认为浏览器自带的采样率
* numChannels,声道,1或2
*/
constructor(options: recorderConfig = {}) {
super(options);
}
/**
* 重新修改配置
*
* @param {recorderConfig} [options={}]
* @memberof Recorder
*/
public setOption(options: recorderConfig = {}) {
this.setNewOption(options);
}
/**
* Start the recording
*/
start(): Promise<{}> {
if (this.isrecording) {
// 正在录音,则不允许
return Promise.reject();
}
this.isrecording = true;
return this.startRecord();
}
/**
* Pause the recording
*/
pause(): void {
if (this.isrecording && !this.ispause) {
this.ispause = true;
// 当前不暂停的时候才可以暂停
this.pauseRecord();
}
}
/**
* 继续录音
*/
resume(): void {
if (this.isrecording && this.ispause) {
this.ispause = false;
this.resumeRecord();
}
}
/**
* 停止录音
*
* @memberof Recorder
*/
stop(): void {
if (this.isrecording) {
this.isrecording = false;
this.ispause = false;
this.stopRecord();
}
}
/**
* 播放录音
*/
play(): void {
this.stop();
// 关闭前一次音频播放
this.isplaying = true;
this.onplay && this.onplay();
Player.addPlayEnd(this.onplayend); // 注册播放完成后的回调事件
const dataV = this.getWAV();
if (dataV.byteLength > 44) {
Player.play(dataV.buffer); // 播放
}
}
/**
* 获取已经播放了多长时间
*/
getPlayTime(): number {
return Player.getPlayTime();
}
/**
* 暂停播放录音
*
* @memberof Recorder
*/
pausePlay(): void {
if (this.isrecording || !this.isplaying) {
// 正在录音或没有播放,暂停无效
return;
}
this.isplaying = false;
this.onpauseplay && this.onpauseplay();
Player.pausePlay();
}
/**
* 恢复播放录音
*
* @memberof Recorder
*/
resumePlay(): void {
if (this.isrecording || this.isplaying) {
// 正在录音或已经播放或没开始播放,恢复无效
return;
}
this.isplaying = true;
this.onresumeplay && this.onresumeplay();
Player.resumePlay();
}
/**
* 停止播放
*
* @memberof Recorder
*/
stopPlay(): void {
if (this.isrecording) {
// 正在录音,停止录音播放无效
return;
}
this.isplaying = false;
this.onstopplay && this.onstopplay();
Player.stopPlay();
}
destroy(): Promise<{}> {
Player.destroyPlay();
return this.destroyRecord();
}
/**
* 获取当前已经录音的PCM音频数据
*
* @returns[DataView]
* @memberof Recorder
*/
// getWholeData() {
// return this.tempPCM;
// }
/**
* 获取余下的新数据,不包括 getNextData 前一次获取的数据
*
* @returns [DataView]
* @memberof Recorder
*/
// getNextData() {
// let length = this.tempPCM.length,
// data = this.tempPCM.slice(this.offset);
// this.offset = length;
// return data;
// }
/**
* 获取当前录音的波形数据,
* 调取频率由外部控制。
*
* @memberof Recorder
*/
getRecordAnalyseData(): any {
return this.getAnalyseData();
}
/**
* 获取录音播放时的波形数据,
*
* @memberof Recorder
*/
getPlayAnalyseData(): any {
// 现在录音和播放不允许同时进行,所有复用的录音的analyser节点。
return Player.getAnalyseData();
}
getPCM(): any {
// 先停止
this.stop();
//边录边转
if (this.getEncodedPCM()) {
return this.getEncodedPCM();
}
// 获取pcm数据
let data: any = this.getData();
// 根据输入输出比例 压缩或扩展
data = compress(data, this.inputSampleRate, this.outputSampleRate);
// 按采样位数重新编码
return encodePCM(data, this.oututSampleBits, this.littleEdian);
}
/**
* 获取PCM格式的blob数据
*
* @returns { blob } PCM格式的blob数据
* @memberof Recorder
*/
getPCMBlob(): any {
return new Blob([this.getPCM()]);
}
/**
* 下载录音pcm数据
*
* @param {string} [name='recorder'] 重命名的名字
* @memberof Recorder
*/
downloadPCM(name: string = "recorder"): void {
let pcmBlob = this.getPCMBlob();
downloadPCM(pcmBlob, name);
}
/**
* 获取WAV编码的二进制数据(dataview)
*
* @returns {dataview} WAV编码的二进制数据
* @memberof Recorder
*/
getWAV(): any {
let pcmTemp = this.getPCM();
// PCM增加44字节的头就是WAV格式了
return encodeWAV(
pcmTemp,
this.inputSampleRate,
this.outputSampleRate,
this.config.numChannels,
this.oututSampleBits,
this.littleEdian
);
}
/**
* 获取WAV音频的blob数据
*
* @returns { blob } wav格式blob数据
* @memberof Recorder
*/
getWAVBlob(): any {
return new Blob([this.getWAV()], { type: "audio/wav" });
}
/**
* 下载录音的wav数据
*
* @param {string} [name='recorder'] 重命名的名字
* @memberof Recorder
*/
downloadWAV(name: string = "recorder"): void {
let wavBlob = this.getWAVBlob();
downloadWAV(wavBlob, name);
}
/**
* 获取MP3编码的二进制数据(dataview)
*
* @returns {dataview} WAV编码的二进制数据
* @memberof Recorder
*/
getMP3(): any {
let pcmTemp = this.getChannelData();
return encodeMP3(
pcmTemp,
this.inputSampleRate,
this.outputSampleRate,
this.config.numChannels,
this.oututSampleBits,
this.littleEdian
);
}
/**
* 获取WAV音频的blob数据
*
* @returns { blob } MP3格式blob数据
* @memberof Recorder
*/
getMP3Blob(): any {
return new Blob(this.getMP3(), { type: "audio/mp3" });
}
/**
* 下载录音的mp3数据
*
* @param {string} [name='recorder'] 重命名的名字
* @memberof Recorder
*/
downloadMP3(name: string = "recorder"): void {
let mp3Blob = this.getMP3Blob();
download(mp3Blob, name, "mp3");
}
/**
* 通用的下载接口
*/
download(blob, name: string, type: string): void {
download(blob, name, type);
}
/**
* 获取左和右声道的数据
*
* @returns [DataView]
*/
getChannelData(): any {
const all = this.getPCM();
const length = all.byteLength;
const littleEdian = this.littleEdian;
const res = { left: null, right: null };
if (this.config.numChannels === 2) {
// 双通道,劈开
const lD = new DataView(new ArrayBuffer(length / 2));
const rD = new DataView(new ArrayBuffer(length / 2));
// 双声道,需要拆分下数据
if (this.config.sampleBits === 16) {
for (var i = 0; i < length / 2; i += 2) {
lD.setInt16(
i,
all.getInt16(i * 2, littleEdian),
littleEdian
);
rD.setInt16(
i,
all.getInt16(i * 2 + 2, littleEdian),
littleEdian
);
}
} else {
for (var i = 0; i < length / 2; i += 2) {
lD.setInt8(i, all.getInt8(i * 2));
rD.setInt8(i, all.getInt8(i * 2 + 1));
}
}
res.left = lD;
res.right = rD;
} else {
// 单通道
res.left = all;
}
return res;
}
}
export default Index;