|
@@ -44,24 +44,35 @@ const stopCountDown = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 开始录音
|
|
// 开始录音
|
|
|
-const startRecording = () => {
|
|
|
|
|
|
|
+const startRecording = async () => {
|
|
|
|
|
+ if (!recordEsm) {
|
|
|
|
|
+ ElMessage.error('录音插件未初始化')
|
|
|
|
|
+ cancelRecording()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- recordEsm.startRecording({deviceId: deviceId.value}).then(() => {
|
|
|
|
|
- isRecording.value = true
|
|
|
|
|
- isProcessing.value = true
|
|
|
|
|
- useTime.value = 0
|
|
|
|
|
- startCountDown()
|
|
|
|
|
- }).catch(() => {
|
|
|
|
|
- ElMessage.warning('未找到或未授权录音设备')
|
|
|
|
|
- availableAudioDevicesFlag.value = false
|
|
|
|
|
- cancelRecording()
|
|
|
|
|
- })
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- console.error(err)
|
|
|
|
|
- ElMessage.error('录音错误')
|
|
|
|
|
- recordingDialog.value = false
|
|
|
|
|
- isProcessing.value = false
|
|
|
|
|
- recordingDialog.value = false
|
|
|
|
|
|
|
+ await recordEsm.startRecording({deviceId: deviceId.value})
|
|
|
|
|
+ isRecording.value = true
|
|
|
|
|
+ isProcessing.value = true
|
|
|
|
|
+ useTime.value = 0
|
|
|
|
|
+ startCountDown()
|
|
|
|
|
+ console.log('录音已启动')
|
|
|
|
|
+ } catch (err: any) {
|
|
|
|
|
+ console.error('录音启动失败:', err)
|
|
|
|
|
+ availableAudioDevicesFlag.value = false
|
|
|
|
|
+
|
|
|
|
|
+ // 根据错误类型提供更友好的提示
|
|
|
|
|
+ if (err.name === 'NotAllowedError') {
|
|
|
|
|
+ ElMessage.warning('麦克风权限被拒绝,请在浏览器设置中允许访问麦克风')
|
|
|
|
|
+ } else if (err.name === 'NotFoundError') {
|
|
|
|
|
+ ElMessage.warning('未找到可用的麦克风设备')
|
|
|
|
|
+ } else if (err.name === 'NotReadableError') {
|
|
|
|
|
+ ElMessage.warning('麦克风被其他应用占用,请关闭其他使用麦克风的程序')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.warning(`无法启动录音: ${err.message || '未知错误'}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ cancelRecording()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -82,7 +93,10 @@ const cancelRecording = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const openRecordingDialog = () => {
|
|
const openRecordingDialog = () => {
|
|
|
|
|
+ console.log('打开录音对话框')
|
|
|
isActiveCancel.value = false
|
|
isActiveCancel.value = false
|
|
|
|
|
+ isRecording.value = false
|
|
|
|
|
+ isProcessing.value = false
|
|
|
recordingDialog.value = true
|
|
recordingDialog.value = true
|
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
|
initRecord(() => startRecording());
|
|
initRecord(() => startRecording());
|
|
@@ -95,39 +109,69 @@ const setMessage = (message: string): void => {
|
|
|
emit('setMessage', message)
|
|
emit('setMessage', message)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const scanAvailableAudioDevices = (success: Function | null = null, fail: Function | null = null) => {
|
|
|
|
|
- if (deviceId.value != '') {
|
|
|
|
|
- if (success) success()
|
|
|
|
|
- } else {
|
|
|
|
|
- RecordPlugin.getAvailableAudioDevices().then((devices) => {
|
|
|
|
|
- if (devices.length <= 0) {
|
|
|
|
|
- availableAudioDevicesFlag.value = false
|
|
|
|
|
- if (fail) fail()
|
|
|
|
|
- } else {
|
|
|
|
|
- devices.forEach((device) => {
|
|
|
|
|
- if (deviceId.value == '') deviceId.value = device.deviceId
|
|
|
|
|
- })
|
|
|
|
|
- if (success) success();
|
|
|
|
|
|
|
+const scanAvailableAudioDevices = async (success: Function | null = null, fail: Function | null = null) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 先请求麦克风权限,这样才能获取到设备列表
|
|
|
|
|
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
|
|
|
|
+
|
|
|
|
|
+ // 获取权限后立即停止流,避免占用设备
|
|
|
|
|
+ stream.getTracks().forEach(track => track.stop())
|
|
|
|
|
+
|
|
|
|
|
+ // 现在可以获取设备列表了
|
|
|
|
|
+ const devices = await RecordPlugin.getAvailableAudioDevices()
|
|
|
|
|
+
|
|
|
|
|
+ if (devices.length <= 0) {
|
|
|
|
|
+ availableAudioDevicesFlag.value = false
|
|
|
|
|
+ ElMessage.error('未找到可用的音频设备,请检查麦克风连接')
|
|
|
|
|
+ if (fail) fail()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 选择第一个可用设备
|
|
|
|
|
+ if (deviceId.value === '') {
|
|
|
|
|
+ deviceId.value = devices[0].deviceId
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
|
|
+ console.log('找到音频设备:', devices.length, '个')
|
|
|
|
|
+ if (success) success()
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err: any) {
|
|
|
|
|
+ console.error('获取音频设备失败:', err)
|
|
|
|
|
+ availableAudioDevicesFlag.value = false
|
|
|
|
|
+
|
|
|
|
|
+ // 根据错误类型提供更友好的提示
|
|
|
|
|
+ if (err.name === 'NotAllowedError') {
|
|
|
|
|
+ ElMessage.error('麦克风权限被拒绝,请在浏览器设置中允许访问麦克风')
|
|
|
|
|
+ } else if (err.name === 'NotFoundError') {
|
|
|
|
|
+ ElMessage.error('未找到可用的麦克风设备,请检查设备连接')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.error('无法访问麦克风,请检查浏览器权限设置')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (fail) fail()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const initRecord = (callback: Function) => {
|
|
|
|
|
- if (waveformRef.value) {
|
|
|
|
|
|
|
+const initRecord = async (callback: Function) => {
|
|
|
|
|
+ if (!waveformRef.value) {
|
|
|
|
|
+ cancelRecording()
|
|
|
|
|
+ ElMessage.warning('初始化失败')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
wavesurfer = WaveSurfer.create({
|
|
wavesurfer = WaveSurfer.create({
|
|
|
container: waveformRef.value,
|
|
container: waveformRef.value,
|
|
|
waveColor: '#5A92F8',
|
|
waveColor: '#5A92F8',
|
|
|
progressColor: 'transparent',
|
|
progressColor: 'transparent',
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
recordEsm = wavesurfer.registerPlugin(
|
|
recordEsm = wavesurfer.registerPlugin(
|
|
|
- RecordPlugin.create({
|
|
|
|
|
- renderRecordedAudio: false,
|
|
|
|
|
- scrollingWaveform: false,
|
|
|
|
|
- continuousWaveform: false,
|
|
|
|
|
- continuousWaveformDuration: 30
|
|
|
|
|
- }),
|
|
|
|
|
|
|
+ RecordPlugin.create({
|
|
|
|
|
+ renderRecordedAudio: false,
|
|
|
|
|
+ scrollingWaveform: false,
|
|
|
|
|
+ continuousWaveform: false,
|
|
|
|
|
+ continuousWaveformDuration: 30
|
|
|
|
|
+ }),
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
recordEsm.on('record-end', async (audioBlob: any) => {
|
|
recordEsm.on('record-end', async (audioBlob: any) => {
|
|
|
try {
|
|
try {
|
|
|
stopCountDown();
|
|
stopCountDown();
|
|
@@ -150,13 +194,15 @@ const initRecord = (callback: Function) => {
|
|
|
recordingDialog.value = false
|
|
recordingDialog.value = false
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
- scanAvailableAudioDevices(callback, () => {
|
|
|
|
|
- ElMessage.error('未找到音频设备')
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 扫描设备并启动录音
|
|
|
|
|
+ await scanAvailableAudioDevices(callback, () => {
|
|
|
cancelRecording()
|
|
cancelRecording()
|
|
|
})
|
|
})
|
|
|
- } else {
|
|
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('初始化录音插件失败:', err)
|
|
|
|
|
+ ElMessage.error('录音功能初始化失败')
|
|
|
cancelRecording()
|
|
cancelRecording()
|
|
|
- ElMessage.warning('初始化失败')
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -184,14 +230,14 @@ onUnmounted(() => {
|
|
|
:close-on-press-escape="false"
|
|
:close-on-press-escape="false"
|
|
|
>
|
|
>
|
|
|
<div v-if="isProcessing" class="waveformRef-title">录音将在{{ countDown }}秒后自动结束</div>
|
|
<div v-if="isProcessing" class="waveformRef-title">录音将在{{ countDown }}秒后自动结束</div>
|
|
|
- <div v-else class="waveformRef-title">正则扫描麦克风设备</div>
|
|
|
|
|
|
|
+ <div v-else class="waveformRef-title">正在检测麦克风设备...</div>
|
|
|
<div ref="waveformRef" class="waveformRef"></div>
|
|
<div ref="waveformRef" class="waveformRef"></div>
|
|
|
<template #footer>
|
|
<template #footer>
|
|
|
<el-button round @click="cancelRecording">取消</el-button>
|
|
<el-button round @click="cancelRecording">取消</el-button>
|
|
|
- <el-button type="primary" round class="stop-recording" @click="stopRecording">
|
|
|
|
|
|
|
+ <el-button type="primary" round class="stop-recording" :disabled="!isRecording" @click="stopRecording">
|
|
|
<el-image v-if="isRecording" class="record-icon" fit="contain" src="/images/recording-stop.png"/>
|
|
<el-image v-if="isRecording" class="record-icon" fit="contain" src="/images/recording-stop.png"/>
|
|
|
<el-image v-else class="record-icon loading" fit="contain" src="/images/recording-loading.png"/>
|
|
<el-image v-else class="record-icon loading" fit="contain" src="/images/recording-loading.png"/>
|
|
|
- {{ isRecording ? '我说完了' : '音频处理中' }}
|
|
|
|
|
|
|
+ {{ isRecording ? '我说完了' : '正在准备...' }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|