CzRger hace 2 meses
padre
commit
a85e79fed2
Se han modificado 1 ficheros con 24 adiciones y 193 borrados
  1. 24 193
      src/views/manage/app/index_audio.vue

+ 24 - 193
src/views/manage/app/index_audio.vue

@@ -1,210 +1,41 @@
 <template>
-  <div class="audio">
-    <template v-if="state.isStart">
-      <el-tooltip content="停止语音输入" placement="top">
-        <div class="audio-main-voice __hover" @click="onStop">
-          <div v-for="item in 4" ref="ref_bars" />
-        </div>
-      </el-tooltip>
+  <div>
+    <template v-if="state.isSpeak">
+      <el-button @click="stop">停止录音</el-button>
     </template>
     <template v-else>
-      <el-tooltip content="语音输入" placement="top">
-        <div class="audio-main __hover" @click="onStart">
-          <img src="@/assets/images/logo.png" />
-        </div>
-      </el-tooltip>
-    </template>
-    <template v-if="state.isStart">
-      <div class="duration">
-        {{ durationCpt }}
-      </div>
+      <el-button @click="speak">开始录音</el-button>
     </template>
+
+    {{ volume }}
+    <div class="relative h-6 w-full bg-[#fff]">
+      <div class="absolute h-6 bg-[red]" :style="{ width: volume + '%' }"></div>
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  onMounted,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
+import { reactive } from 'vue'
 import { ElMessage } from 'element-plus'
-// import {audioToText} from "@/views/smart-ask-answer/assistant-2/dify/share";
+import useSpeechToAudio from '@/utils/useSpeechToAudio'
 
-const emit = defineEmits(['onLoading', 'onAudio'])
 const props = defineProps({})
 const state: any = reactive({
-  isStart: false,
-  duration: 0,
-  mediaRecorder: null,
-  audioBlob: null,
-  timer: null,
-  analyser: null,
-  animationId: null,
-  phase: 0,
+  isSpeak: false,
 })
-const ref_bars = ref()
-const durationCpt = computed(() => {
-  const minutes = Math.floor(state.duration / 60)
-  const seconds = Math.floor(state.duration % 60)
-  return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
-})
-const onStart = async () => {
-  state.isStart = true
-  try {
-    // 请求麦克风权限
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
-
-    const audioContext = new (window.AudioContext ||
-      window.webkitAudioContext)()
-    state.analyser = audioContext.createAnalyser()
-    state.analyser.fftSize = 256
-    const microphone = audioContext.createMediaStreamSource(stream)
-    microphone.connect(state.analyser)
-    updateVolumeBars()
-
-    state.mediaRecorder = new MediaRecorder(stream)
-    const audioChunks: any = []
-    state.mediaRecorder.ondataavailable = (event) => {
-      audioChunks.push(event.data)
-    }
-    state.mediaRecorder.onstop = async () => {
-      clearInterval(state.timer)
-      state.audioBlob = new Blob(audioChunks, { type: 'audio/mp3' })
-      // this.audioUrl = URL.createObjectURL(this.audioBlob)
-      stream.getTracks().forEach((track) => track.stop())
-      if (microphone) {
-        microphone.disconnect()
-      }
-      if (audioContext && audioContext.state !== 'closed') {
-        audioContext.close()
-      }
-      cancelAnimationFrame(state.animationId)
-      // 重置柱状图
-      ref_bars.value.forEach((bar) => {
-        bar.style.height = '4px'
-      })
-      if (!state.audioBlob) {
-        ElMessage.error('没有可上传的录音文件')
-        return
-      }
-      try {
-        const formData = new FormData()
-        formData.append('file', state.audioBlob)
-        console.log(state.audioBlob)
-        // const audioResponse = await audioToText(`/installed-apps/${import.meta.env.VITE_DIFY_APPID}/audio-to-text`, false, formData)
-        // emit('onAudio', audioResponse.text)
-      } catch (err) {
-        emit('onAudio', '')
-        ElMessage.error('上传错误:' + err)
-      } finally {
-        emit('onAudio', '')
-      }
-    }
-
-    state.mediaRecorder.start()
-    const startTime = Date.now()
-    state.duration = 0
-    // 更新录音时长
-    state.timer = setInterval(() => {
-      state.duration = Math.floor((Date.now() - startTime) / 1000)
-    }, 1000)
-  } catch (err: any) {
-    ElMessage.error('无法访问麦克风: ' + err.message)
-    console.error('录音错误:', err)
-  }
-}
-const onStop = async () => {
-  emit('onLoading')
-  state.isStart = false
-  if (state.mediaRecorder) {
-    state.mediaRecorder.stop()
-  }
-}
-const updateVolumeBars = () => {
-  if (!state.isStart) return
-
-  const array = new Uint8Array(state.analyser.frequencyBinCount)
-  state.analyser.getByteFrequencyData(array)
-  let sum = 0
-  for (let i = 0; i < array.length; i++) {
-    sum += array[i]
-  }
-
-  const average = sum / array.length
-  const baseVolume = Math.min(1, average / 70)
-
-  // 更新相位
-  state.phase += 0.2
-  if (state.phase > Math.PI * 2) state.phase -= Math.PI * 2
-
-  // 更新每个柱子的高度
-  ref_bars.value.forEach((bar, index) => {
-    // 每个柱子有轻微相位差
-    const barPhase = state.phase + (index * Math.PI) / 3
-    // 波浪因子 (0.5-1.5范围)
-    const waveFactor = 0.8 + Math.sin(barPhase) * 0.2
-
-    // 基础高度 + 音量影响 * 波浪因子
-    const height =
-      4 +
-      baseVolume *
-        (index > 0 && index < ref_bars.value.length - 1 ? 15 : 5) *
-        waveFactor
-    bar.style.height = `${height}px`
-  })
-
-  state.animationId = requestAnimationFrame(updateVolumeBars)
+const onEnd = (audio) => {
+  state.isSpeak = false
+  console.log(audio)
 }
-const calculateBarLevels = (volume) => {
-  // 根据音量计算4个柱子的高度比例
-  // 无声音时全部为0,有声音时从低到高依次点亮
-  const thresholds = [0.25, 0.5, 0.75, 1.0]
-  return thresholds.map((t) =>
-    Math.max(0, Math.min(1, (volume - (t - 0.25)) * 4)),
-  )
+const onSpeak = ({ duration }) => {
+  state.isSpeak = true
+  // console.log('duration', duration)
 }
-onMounted(() => {})
+const { speak, stop, volume } = useSpeechToAudio({
+  onEnd,
+  onSpeak,
+  timeout: 3,
+})
 </script>
 
-<style lang="scss" scoped>
-.audio {
-  display: flex;
-  align-items: center;
-  .audio-main,
-  .audio-main-voice {
-    width: 32px;
-    height: 32px;
-    border-radius: 8px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-  }
-  .audio-main {
-    &:hover {
-      background-color: rgba(0, 0, 0, 0.04);
-    }
-  }
-  .audio-main-voice {
-    background-color: rgba(0, 87, 255, 0.1);
-    gap: 2px;
-    > div {
-      width: 2px;
-      height: 4px;
-      background-color: #06f;
-      transition: height 0.15s ease-out;
-      &:nth-child(2) {
-        margin-right: 1px;
-      }
-    }
-  }
-  .duration {
-    color: #4f4f4f;
-    font-size: 14px;
-    margin-left: 6px;
-  }
-}
-</style>
+<style lang="scss" scoped></style>