<!-- 听说组件 -->
<template>
<div class="lestening-speaking-container">
<p class="middleSizeBlob">Type the statement that you hear.</p>
<img style="width: 1.32rem;height:1.24rem;margin-top: .07rem;" src="../assets/images/secondary/lesting.png" alt="">
<!-- <p><span>可播放次数:</span><span style="color: #4581e1;font-weight: blob;">{{ count }}</span></p> -->
<div class="btn-trim">
<!-- <tiny-button type="primary" :disabled="disabled" :icon="iconStartCircle" @click="Play"> Play </tiny-button> -->
<span>{{ audioData.starTime }} / {{ audioData.endTime }}</span>
<audio src="" :muted="audioData.muted"></audio>
</div>
<div class="function-box">
<canvas id="canvas"></canvas>
<canvas id="playChart"></canvas>
<tiny-button :disabled="disabled" type="primary" native-type="submit" @click="nextStep"> Speaking/Stop
</tiny-button>
</div>
<tiny-dialog-box v-model:visible="dialogData.visible" top="42%" title="音频将在倒计时结束后自动播放" width="30%">
<span style="font-size: .14rem;font-weight: 700;">{{ dialogData.countNum }}</span>
</tiny-dialog-box>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Button as TinyButton, DialogBox as TinyDialogBox } from '@opentiny/vue'
import Recorder from 'js-audio-recorder'
const lamejs = require('lamejs')
// 子组件向父组件传参数
const emits = defineEmits(['nextQuestion'])
onMounted(() => {
data.startCanvas()
data.voice()
})
// 录音所需的信息
const data = reactive({
//用于存储创建的语音对象
recorder: new Recorder({
sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
numChannels: 1, // 声道,支持 1 或 2, 默认是1
// compiling: false,(0.x版本中生效,1.x增加中) // 是否边录边转换,默认是false
}),
formData: null,
// 控制录音动画的显示隐藏
showAnima: false,
mation: true,
isHistory: true,
soundSize: 0,
// 录音时长
duration: 0,
//波浪图-录音
drawRecordId: null,
oCanvas: null,
ctx: null,
//波浪图-播放
drawPlayId: null,
pCanvas: null,
pCtx: null,
startCanvas() {
//录音波浪
data.oCanvas = document.getElementById('canvas');
data.ctx = data.oCanvas.getContext("2d");
//播放波浪
data.pCanvas = document.getElementById('playChart');
data.pCtx = data.pCanvas.getContext("2d");
},
drawRecord() {
// 用requestAnimationFrame稳定60fps绘制
data.drawRecordId = requestAnimationFrame(data.drawRecord);
// 实时获取音频大小数据
// console.log(data.recorder.getRecordAnalyseData())
// return
let dataArray = data.recorder.getRecordAnalyseData()
let bufferLength = dataArray.length;
// 填充背景色
data.ctx.fillStyle = 'rgb(200, 200, 200)';
data.ctx.fillRect(0, 0, data.oCanvas.width, data.oCanvas.height);
// 设定波形绘制颜色
data.ctx.lineWidth = 2;
data.ctx.strokeStyle = 'rgb(0, 0, 0)';
data.ctx.beginPath();
var sliceWidth = data.oCanvas.width * 1.0 / bufferLength, // 一个点占多少位置,共有bufferLength个点要绘制
x = 0; // 绘制点的x轴位置
for (var i = 0; i < bufferLength; i++) {
var v = dataArray[i] / 128.0;
var y = v * data.oCanvas.height / 2;
if (i === 0) {
// 第一个点
data.ctx.moveTo(x, y);
} else {
// 剩余的点
data.ctx.lineTo(x, y);
}
// 依次平移,绘制所有点
x += sliceWidth;
}
data.ctx.lineTo(data.oCanvas.width, data.oCanvas.height / 2);
data.ctx.stroke();
},
submit() { // 发送语音的方法
data.recorder.pause() // 暂停录音
data.timer = null
console.log('上传录音')// 上传录音
var formData = new FormData()
var blob = data.recorder.getWAVBlob()//获取wav格式音频数据
//此处获取到blob对象后需要设置fileName满足当前项目上传需求,其它项目可直接传把blob作为 file塞入formData
var newbolb = new Blob([blob], { type: 'audio/wav' })
var fileOfBlob = new File([newbolb], new Date().getTime() + '.wav')
//formData是传给后端的对象,
formData.append('file', fileOfBlob)
//计算出录音时长
data.duration = Math.ceil((new Date() - data.duration) / 1000);
console.log(data.duration);
//发送给后端的方法
sendAudio(formData).then(res => {
console.log(res);
})
},
// 录音按钮的点击事件
voice() {
//实例化语音对象
// data.recorder = new Recorder({
// sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
// sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
// numChannels: 1 // 声道,支持 1 或 2, 默认是1
// })
//记录开始录音的时间
data.duration = new Date();
Recorder.getPermission().then(() => {
console.log('开始录音')
data.recorder.start().then(() => {
data.drawRecord();//开始绘制图片
}, (error) => {
// 出错了
console.log(`${error.name} : ${error.message}`);
}); // 开始录音
data.recorder.onprogress = function (params) {
// console.log('录音时长(秒)', params.duration);
// console.log('录音大小(字节)', params.fileSize);
data.soundSize = params.vol
// console.log('录音音量百分比(%)', params.vol);
// console.log('当前录音的总数据([DataView, DataView...])', params.data);
}
}, (error) => {
console.log(`${error.name} : ${error.message}`)
})
},
handleStop() {
console.log('停止录音')
data.recorder.stop() // 停止录音
data.mation = false;
},
handlePlay() {
console.log('播放录音')
data.recorder.play() // 播放录音
},
handleDestroy() {
console.log('销毁实例')
if (data.recorder) {
data.recorder.destroy() // 毁实例
}
},
})
// 本地音频播放所需要的信息
const audioData = reactive({
starTime: '00:00',
endTime: '00:00',
muted: true,
})
// 弹出层的信息
const dialogData = reactive({
visible: false,
countNum: 2,
})
// 录音和停止录音的的按钮
const disabled = ref(true)
// 点击按钮时触发
function nextStep() {
emits('nextQuestion')
}
</script>
<style lang="scss" scoped>
.lestening-speaking-container {
width: 100%;
height: 100%;
text-align: center;
font-size: .11rem;
border: 1px solid red;
.btn-trim {
display: flex;
align-items: center;
justify-content: center;
font-size: .1rem;
font-weight: normal;
height: .18rem;
margin-top: .14rem;
.tiny-button {
width: .72rem;
height: .18rem;
// margin: 0 auto;
font-size: .08rem;
border-radius: .1rem;
font-weight: 700;
margin-top: .04rem;
margin-right: .04rem;
}
::v-deep .tiny-svg {
width: .1rem !important;
height: .1rem !important;
}
}
.function-box {
width: 5.59rem;
height: 1.15rem;
opacity: 1;
background: rgba(252, 252, 252, 0.94);
box-shadow: 0rem .01rem .03rem rgba(0, 0, 0, 0.25);
margin: .07rem auto 0rem;
#playChart,
#canvas {
height: .28rem;
width: 100%;
border: 1px solid aqua;
margin-top: .29rem;
}
.tiny-button {
width: 1.42rem;
height: .24rem;
margin: .21rem auto 0;
font-size: .11rem;
border-radius: .13rem;
}
}
::v-deep .tiny-dialog-box__body {
padding-bottom: .21rem;
text-align: center;
}
::v-deep .tiny-dialog-box__headerbtn {
display: none;
}
}
</style>
js-audio-recorder录音带声波图
最新推荐文章于 2024-07-11 08:59:04 发布