智能面试——录音及播放下载js-audio-recorder — post请求,formdata传参

40 篇文章 1 订阅

录音插件 js-audio-recorder

image.png

bug:本地调试调取不起来麦克风

  • 浏览器配置安全域名 chrome://flags/
  • Insecure origins treated as secure
  • 输入域名即可
  • 电脑需要连接上耳机
<template>
  <div class="BaseRecorder">
    <div class="BaseRecorder-record">
      <el-button @click="startRecorder()">开始录音</el-button>
      <el-button @click="pauseRecorder()">暂停录音</el-button>
      <el-button @click="resumeRecorder()">继续录音</el-button>
      <el-button @click="stopRecorder()">结束录音</el-button>
    </div>
    <div class="BaseRecorder-play">
      <el-button @click="playRecorder()">录音播放</el-button>
      <el-button @click="pausePlayRecorder()">暂停录音播放</el-button>
      <el-button @click="resumePlayRecorder()">恢复录音播放</el-button>
      <el-button @click="stopPlayRecorder()">停止录音播放</el-button>
    </div>
    <div class="BaseRecorder-download">
      <el-button @click="downPCM()">下载PCM</el-button>
      <el-button @click="downWAV()">下载WAV</el-button>
    </div>
    <div class="BaseRecorder-destroy">
      <el-button type="error" @click="destroyRecorder()">销毁录音</el-button>
    </div>
    <div class="BaseRecorder-wave">
      <canvas ref="record"></canvas>
      <canvas ref="play"></canvas>
    </div>
  </div>
</template>

<script>
import Recorder from "js-audio-recorder";

export default {
  name: "home",
  data() {
    return {
      recorder: null,
      // 波浪图-录音
      drawRecordId: null,
      // 波浪图-播放
      drawPlayId: null,
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    // 初始化
    init() {
      this.recorder = new Recorder({
        // 采样位数,支持 8 或 16,默认是16
        sampleBits: 16,
        // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值
        sampleRate: 48000,
        // 声道,支持 1 或 2, 默认是1
        numChannels: 1,
        // 是否边录边转换,默认是false
        compiling: false,
      });
    },
    // 开始录音
    startRecorder() {
      this.recorder.start().then(
        () => {
          console.log("开始录音", this.recorder);
          this.drawRecord();
          this.recorder.onprogress = (params) => {
            console.log(params);

            // 此处控制数据的收集频率
            if (this.recorder.config.compiling) {
              console.log("音频总数据:", params.data);
            }
          };
          // 定时获取录音的数据并播放
          this.recorder.config.compiling &&
            (playTimer = setInterval(() => {
              let newData = this.recorder.getNextData();
              if (!newData.length) {
                return;
              }
              let byteLength = newData[0].byteLength;
              let buffer = new ArrayBuffer(newData.length * byteLength);
              let dataView = new DataView(buffer);

              // 数据合并
              for (let i = 0, iLen = newData.length; i < iLen; ++i) {
                for (let j = 0, jLen = newData[i].byteLength; j < jLen; ++j) {
                  dataView.setInt8(i * byteLength + j, newData[i].getInt8(j));
                }
              }

              // 将录音数据转成WAV格式,并播放
              let a = encodeWAV(
                dataView,
                config.sampleRate,
                config.sampleRate,
                config.numChannels,
                config.sampleBits
              );
              let blob = new Blob([a], { type: "audio/wav" });

              blob.arrayBuffer().then((arraybuffer) => {
                console.log(arraybuffer);
                // Player.play(arraybuffer);
              });
            }, 3000));
        },
        (error) => {
          // 出错了
          console.log(`${error.name} : ${error.message}`);
        }
      );
    },
    // 继续录音
    resumeRecorder() {
      this.recorder.resume();
    },
    // 暂停录音
    pauseRecorder() {
      this.recorder.pause();
      this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
      this.drawRecordId = null;
    },
    // 结束录音
    stopRecorder() {
      this.recorder.stop();
      this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
      this.drawRecordId = null;
    },
    // 录音播放
    playRecorder() {
      this.recorder.play();
      this.drawPlay(); // 绘制波浪图
    },
    // 暂停录音播放
    pausePlayRecorder() {
      this.recorder.pausePlay();
    },
    // 恢复录音播放
    resumePlayRecorder() {
      this.recorder.resumePlay();
      this.drawPlay(); // 绘制波浪图
    },
    // 停止录音播放
    stopPlayRecorder() {
      this.recorder.stopPlay();
    },
    // 销毁录音
    destroyRecorder() {
      this.recorder.destroy().then(() => {
        this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
        this.drawRecordId = null;

        this.drawPlayId && cancelAnimationFrame(this.drawPlayId);
        this.drawPlayId = null;

        this.recorder = null;
      });
    },

    /**
     *  下载录音文件
     * */
    // 下载pcm
    downPCM() {
      console.log("pcm: ", this.recorder.getPCMBlob());
      // 这里传参进去的时文件名
      this.recorder.downloadPCM("新文件");
    },
    // 下载wav
    downWAV() {
      console.log("wav: ", this.recorder.getWAVBlob());
      // 这里传参进去的时文件名
      this.recorder.downloadWAV("新文件");
    },

    /**
     * 绘制波浪图-录音
     * */
    drawRecord() {
      this.drawRecordId = requestAnimationFrame(this.drawRecord);
      this.drawWave({
        canvas: this.$refs.record,
        dataArray: this.recorder.getRecordAnalyseData(),
        bgcolor: "rgb(255, 128, 200)",
        lineWidth: 1,
        lineColor: "rgb(0, 128, 255)",
      });
    },
    /**
     * 绘制波浪图-播放
     * */
    drawPlay() {
      this.drawPlayId = requestAnimationFrame(this.drawPlay);
      this.drawWave({
        canvas: this.$refs.play,
        dataArray: this.recorder.getPlayAnalyseData(),
      });
    },
    drawWave({
      canvas,
      dataArray,
      bgcolor = "rgb(200, 200, 200)",
      lineWidth = 2,
      lineColor = "rgb(0, 0, 0)",
    }) {
      if (!canvas) return;

      const ctx = canvas.getContext("2d");
      const bufferLength = dataArray.length;
      // 一个点占多少位置,共有bufferLength个点要绘制
      const sliceWidth = canvas.width / bufferLength;
      // 绘制点的x轴位置
      let x = 0;

      // 填充背景色
      ctx.fillStyle = bgcolor;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // 设定波形绘制颜色
      ctx.lineWidth = lineWidth;
      ctx.strokeStyle = lineColor;

      ctx.beginPath();

      for (let i = 0; i < bufferLength; i++) {
        const v = dataArray[i] / 128;
        const y = (v * canvas.height) / 2;

        if (i === 0) {
          // 第一个点
          ctx.moveTo(x, y);
        } else {
          // 剩余的点
          ctx.lineTo(x, y);
        }
        // 依次平移,绘制所有点
        x += sliceWidth;
      }

      // 最后一个点
      ctx.lineTo(canvas.width, canvas.height / 2);
      ctx.stroke();
    },
  },
};
</script>
<style lang="scss" scoped>
.BaseRecorder {
  & > div {
    margin: 20px 0;
  }
  &-wave {
    canvas {
      width: 100%;
      border: 1px solid #ccc;
    }
  }
}
</style>

智能面试页面

image.png

<template>
  <div class="flex1 w100 h100 bg">
    <div style="width: 300px" class="bg-white h100 flex-column center">
      <div class="blue size-30 mb-60">Java面试专场</div>
      <div class="size-26">张三</div>
      <div class="gray-2 mt-20 mb-100">18378562388</div>
      <el-button type="success" round @click="start()">开始面试</el-button>
    </div>
    <div class="flex-1 h100 over-hidden">
      <div
        class="h100 w65 flex1 flex-column"
        style="margin: 0 auto; min-width: 800px"
      >
        <div class="flex-1 scroll w100 pt-30">
          <div
            v-for="(item, index) in list.filter((item) => item.show)"
            :key="index"
            class="mb-30"
          >
            <div class="flex_l mb-30">
              <i class="el-icon-question size-28 blue mr-10"></i>
              <div class="">
                {{ item.topic }}
              </div>
            </div>
            <div class="flex_l" v-if="item.file">
              <el-avatar
                class="mr-10"
                size="small"
                src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"
              ></el-avatar>
              <el-card class="flex-1"> 语音已发送 </el-card>
            </div>
            <div class="flex_l" v-if="item.answer">
              <el-avatar
                class="mr-10"
                size="small"
                src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"
              ></el-avatar>
              <el-card class="flex-1">
                {{ item.answer }}
              </el-card>
            </div>
          </div>
        </div>
        <div class="w100 flex1 pb-30">
          <el-input
            type="textarea"
            placeholder="请输入内容"
            v-model="textarea"
            maxlength="500"
            show-word-limit
            :autosize="{ minRows: 4 }"
          >
          </el-input>
          <div class="w10 text-center">
            <el-button
              type="success"
              icon="el-icon-microphone"
              circle
              class="size-16 mb-10"
              @click="startRecorder"
              :disabled="disabled"
            ></el-button>
            <el-button
              :disabled="disabled"
              type="primary"
              round
              @click="submit(1)"
              >提交</el-button
            >
          </div>
        </div>
      </div>
    </div>
    <!-- 结果 -->
    <el-dialog
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      title="面试结果"
      :visible.sync="centerDialogVisible"
      width="600px"
      center
      :show-close="false"
      style="top: 16vh"
    >
      <el-result
        :icon="score >= 80 ? 'success' : score >= 60 ? 'warning' : 'error'"
        :title="score >= 80 ? '优秀' : score >= 60 ? '良好' : '不合格'"
        subTitle="面试结果"
      >
        <template slot="extra">
          <el-button type="primary" size="medium" @click="back">返回</el-button>
        </template>
      </el-result>
    </el-dialog>
    <!-- 录音 -->
    <el-dialog
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      title="正在录音..."
      :visible.sync="audioVisible"
      width="600px"
      center
      :show-close="false"
      style="top: 16vh"
    >
      <div class="mb-20 size-18" v-if="list[index]">
        {{ list[index].topic }}
      </div>
      <div class="BaseRecorder-wave">
        <canvas ref="record"></canvas>
        <!-- <canvas ref="play"></canvas> -->
      </div>
      <div class="center mt-20">
        <el-button type="primary" size="medium" @click="submit(2)"
          >提交</el-button
        >
      </div>
    </el-dialog>
  </div>
  <!-- <div class="BaseRecorder">
    <div class="BaseRecorder-record">
      <el-button @click="startRecorder()">开始录音</el-button>
      <el-button @click="pauseRecorder()">暂停录音</el-button>
      <el-button @click="resumeRecorder()">继续录音</el-button>
      <el-button @click="stopRecorder()">结束录音</el-button>
    </div>
    <div class="BaseRecorder-play">
      <el-button @click="playRecorder()">录音播放</el-button>
      <el-button @click="pausePlayRecorder()">暂停录音播放</el-button>
      <el-button @click="resumePlayRecorder()">恢复录音播放</el-button>
      <el-button @click="stopPlayRecorder()">停止录音播放</el-button>
    </div>
    <div class="BaseRecorder-download">
      <el-button @click="downPCM()">下载PCM</el-button>
      <el-button @click="downWAV()">下载WAV</el-button>
    </div>
    <div class="BaseRecorder-destroy">
      <el-button type="error" @click="destroyRecorder()">销毁录音</el-button>
    </div>
    <div class="BaseRecorder-wave">
      <canvas ref="record"></canvas>
      <canvas ref="play"></canvas>
    </div>
  </div> -->
</template>

<script>
import Recorder from "js-audio-recorder";
// import { subText, subAudio } from "../api/test.js";
import axios from "axios";
export default {
  name: "home",
  data() {
    return {
      index: null,
      disabled: true,
      list: [
        {
          topic: "题目1:1+2等于几?",
          show: false,
          answer: "",
          result: "",
          file: "",
        },
        {
          topic: "题目2:2+2等于几?",
          show: false,
          answer: "",
          result: "",
          file: "",
        },
        {
          topic: "题目3:白日依山尽的下一句是什么?",
          show: false,
          answer: "",
          result: "",
          file: "",
        },
      ],
      textarea: "",
      config: {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      },
      centerDialogVisible: false, //结果弹窗
      score: "", //得分
      audioVisible: false, //录音弹窗

      recorder: null,
      // 波浪图-录音
      drawRecordId: null,
      // 波浪图-播放
      drawPlayId: null,
    };
  },
  mounted() {
    this.init();
  },
  beforeDestroy() {
    this.destroyRecorder();
  },
  methods: {
    start(i = 0) {
      this.index = i;
      this.list[this.index].show = true;
      this.disabled = false;
    },
    // type 1 文字  2 语音
    async submit(type) {
      if (type == 1) {
        if (!this.textarea.trim()) {
          this.$message({
            message: "请输入答案",
            type: "warning",
          });
          return;
        }
        this.list[this.index].answer = this.textarea;
        this.disabled = true;
        this.textarea = "";

        const formData = new FormData();
        formData.append("topic", this.list[this.index].topic);
        formData.append("answer", this.list[this.index].answer);
        const { data } = await axios.post(
          "/ququ/recognize-text",
          formData,
          this.config
        );
        console.log(data.result, 99);
        this.list[this.index].result = data.result;
        this.index += 1;
        if (this.index == this.list.length) {
          this.centerDialogVisible = true;
          this.index = null;
          console.log(this.list, 88);
          this.score =
            (this.list.filter((item) => item.result == "对").length * 100) /
            this.list.length;
        } else {
          this.start(this.index);
        }
      } else {
        this.stopRecorder();
        this.audioVisible = false;
        this.list[this.index].file = this.recorder.getWAVBlob();
        this.disabled = true;

        const formData = new FormData();
        formData.append("topic", this.list[this.index].topic);
        formData.append("file", this.list[this.index].file);
        const { data } = await axios.post(
          "/ququ/recognize-video",
          formData,
          this.config
        );
        console.log(data.result, 99);
        this.list[this.index].result = data.result;
        this.index += 1;
        if (this.index == this.list.length) {
          this.centerDialogVisible = true;
          this.index = null;
          console.log(this.list, 88);
          this.score =
            (this.list.filter((item) => item.result == "对").length * 100) /
            this.list.length;
        } else {
          this.start(this.index);
        }
      }
    },
    back() {
      this.centerDialogVisible = false;
      this.list = [
        {
          topic: "题目1:1+2等于几?",
          show: false,
          answer: "",
          result: "",
          file: "",
        },
        {
          topic: "题目2:2+2等于几?",
          show: false,
          answer: "",
          result: "",
          file: "",
        },
        {
          topic: "题目3:白日依山尽的下一句是什么?",
          show: false,
          answer: "",
          result: "",
          file: "",
        },
      ];
    },

    // 初始化
    init() {
      this.recorder = new Recorder({
        // 采样位数,支持 8 或 16,默认是16
        sampleBits: 16,
        // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值
        sampleRate: 48000,
        // 声道,支持 1 或 2, 默认是1
        numChannels: 1,
        // 是否边录边转换,默认是false
        compiling: false,
      });
    },
    // 开始录音
    startRecorder() {
      this.recorder.start().then(
        () => {
          console.log("开始录音", this.recorder);
          this.audioVisible = true;
          this.drawRecord();
          this.recorder.onprogress = (params) => {
            console.log(params);

            // 此处控制数据的收集频率
            if (this.recorder.config.compiling) {
              console.log("音频总数据:", params.data);
            }
          };
          // 定时获取录音的数据并播放
          this.recorder.config.compiling &&
            (playTimer = setInterval(() => {
              let newData = this.recorder.getNextData();
              if (!newData.length) {
                return;
              }
              let byteLength = newData[0].byteLength;
              let buffer = new ArrayBuffer(newData.length * byteLength);
              let dataView = new DataView(buffer);

              // 数据合并
              for (let i = 0, iLen = newData.length; i < iLen; ++i) {
                for (let j = 0, jLen = newData[i].byteLength; j < jLen; ++j) {
                  dataView.setInt8(i * byteLength + j, newData[i].getInt8(j));
                }
              }

              // 将录音数据转成WAV格式,并播放
              let a = encodeWAV(
                dataView,
                config.sampleRate,
                config.sampleRate,
                config.numChannels,
                config.sampleBits
              );
              let blob = new Blob([a], { type: "audio/wav" });

              blob.arrayBuffer().then((arraybuffer) => {
                console.log(arraybuffer);
                // Player.play(arraybuffer);
              });
            }, 3000));
        },
        (error) => {
          // 出错了
          console.log(`${error.name} : ${error.message}`);
        }
      );
    },
    // 继续录音
    resumeRecorder() {
      this.recorder.resume();
    },
    // 暂停录音
    pauseRecorder() {
      this.recorder.pause();
      this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
      this.drawRecordId = null;
    },
    // 结束录音
    stopRecorder() {
      this.recorder.stop();
      this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
      this.drawRecordId = null;
    },
    // 录音播放
    playRecorder() {
      this.recorder.play();
      this.drawPlay(); // 绘制波浪图
    },
    // 暂停录音播放
    pausePlayRecorder() {
      this.recorder.pausePlay();
    },
    // 恢复录音播放
    resumePlayRecorder() {
      this.recorder.resumePlay();
      this.drawPlay(); // 绘制波浪图
    },
    // 停止录音播放
    stopPlayRecorder() {
      this.recorder.stopPlay();
    },
    // 销毁录音
    destroyRecorder() {
      this.recorder.destroy().then(() => {
        this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
        this.drawRecordId = null;

        this.drawPlayId && cancelAnimationFrame(this.drawPlayId);
        this.drawPlayId = null;

        this.recorder = null;
      });
    },

    /**
     *  下载录音文件
     * */
    // 下载pcm
    downPCM() {
      console.log("pcm: ", this.recorder.getPCMBlob());
      // 这里传参进去的时文件名
      this.recorder.downloadPCM("新文件");
    },
    // 下载wav
    downWAV() {
      console.log("wav: ", this.recorder.getWAVBlob());
      // 这里传参进去的时文件名
      this.recorder.downloadWAV("新文件");
    },

    /**
     * 绘制波浪图-录音
     * */
    drawRecord() {
      this.drawRecordId = requestAnimationFrame(this.drawRecord);
      this.drawWave({
        canvas: this.$refs.record,
        dataArray: this.recorder.getRecordAnalyseData(),
        bgcolor: "#a8e1fc",
        lineWidth: 1,
        lineColor: "rgb(255, 128, 200)",
      });
    },
    /**
     * 绘制波浪图-播放
     * */
    drawPlay() {
      this.drawPlayId = requestAnimationFrame(this.drawPlay);
      this.drawWave({
        canvas: this.$refs.play,
        dataArray: this.recorder.getPlayAnalyseData(),
      });
    },
    drawWave({
      canvas,
      dataArray,
      bgcolor = "rgb(200, 200, 200)",
      lineWidth = 2,
      lineColor = "rgb(0, 0, 0)",
    }) {
      if (!canvas) return;

      const ctx = canvas.getContext("2d");
      const bufferLength = dataArray.length;
      // 一个点占多少位置,共有bufferLength个点要绘制
      const sliceWidth = canvas.width / bufferLength;
      // 绘制点的x轴位置
      let x = 0;

      // 填充背景色
      ctx.fillStyle = bgcolor;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // 设定波形绘制颜色
      ctx.lineWidth = lineWidth;
      ctx.strokeStyle = lineColor;

      ctx.beginPath();

      for (let i = 0; i < bufferLength; i++) {
        const v = dataArray[i] / 128;
        const y = (v * canvas.height) / 2;

        if (i === 0) {
          // 第一个点
          ctx.moveTo(x, y);
        } else {
          // 剩余的点
          ctx.lineTo(x, y);
        }
        // 依次平移,绘制所有点
        x += sliceWidth;
      }

      // 最后一个点
      ctx.lineTo(canvas.width, canvas.height / 2);
      ctx.stroke();
    },
  },
};
</script>
<style lang="scss" scoped>
.BaseRecorder {
  & > div {
    margin: 20px 0;
  }
  &-wave {
    canvas {
      width: 100%;
      border: 1px solid #ccc;
    }
  }
}
</style>

post请求,formdata传参

const formData = new FormData();
formData.append("topic", this.list[this.index].topic);
formData.append("answer", this.list[this.index].answer);
const { data } = await axios.post(
     "/ququ/recognize-text",
     formData,
     {
        headers: {
          "Content-Type": "multipart/form-data",
        },
     }
 );
console.log(data.result, 99);
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小曲曲

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值