Recording.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <!-- 语音识别 -->
  2. <template>
  3. <div class="ai-recording">
  4. <div v-for="(_i, index) in 31" :key="index" class="ai-recording-bar"></div>
  5. </div>
  6. </template>
  7. <script setup lang="ts">
  8. import { IatRecorder } from '../utils/voice'
  9. const iatRecorder:Recordable = new IatRecorder()
  10. import { ref, onMounted, nextTick, onUnmounted } from 'vue'
  11. const text = ref('')
  12. let mediaStream // 保存麦克风流
  13. // 获取麦克风权限并开始录音
  14. async function startRecording() {
  15. mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true })
  16. console.log('麦克风已开启')
  17. }
  18. // 停止麦克风访问
  19. function stopRecording() {
  20. if (mediaStream) {
  21. const tracks = mediaStream.getTracks() // 获取所有音轨
  22. tracks.forEach((track) => track.stop()) // 停止每个音轨
  23. console.log('麦克风已关闭')
  24. }
  25. }
  26. const emit = defineEmits(['on-complete'])
  27. onMounted(() => {
  28. nextTick(() => {
  29. iatRecorder.onWillStatusChange = (_oldStatus, status, _audioData) => {
  30. console.log(`status===${status}`)
  31. // 可以在这里进行页面中一些交互逻辑处理:倒计时(听写只有60s),录音的动画,按钮交互等
  32. // status: null init ing end
  33. if (status === 'init') {
  34. startRecording()
  35. }
  36. if (status === 'end') {
  37. console.log(text.value)
  38. stopRecording()
  39. // 转写结束 事件
  40. emit('on-complete', text.value)
  41. }
  42. }
  43. iatRecorder.onTextChange = (text) => {
  44. console.log(`text===${text}`)
  45. if (text) {
  46. sendMessage(text)
  47. }
  48. }
  49. // 开始识别
  50. iatRecorder.start()
  51. })
  52. })
  53. onUnmounted(() => {
  54. // 停止转写
  55. iatRecorder.stop()
  56. })
  57. const sendMessage = (msg) => {
  58. if (msg) {
  59. // 转写结果都会带个标点符号 移除之
  60. const last = msg[msg.length - 1]
  61. const arr = ['.', ',', '。', ',']
  62. if (arr.indexOf(last) !== -1) {
  63. msg = msg.substring(0, msg.length - 1)
  64. }
  65. }
  66. text.value = msg || ''
  67. }
  68. </script>
  69. <style lang="less" scoped>
  70. .ai-recording {
  71. display: flex;
  72. align-items: center;
  73. &-bar {
  74. width: 2px;
  75. height: 20px;
  76. display: inline-block;
  77. background-color: #3786fd;
  78. animation: ani 1s infinite;
  79. & + .ai-recording-bar {
  80. margin-left: 2px;
  81. }
  82. &:nth-child(6n + 1) {
  83. height: 2px;
  84. }
  85. &:nth-child(6n + 2) {
  86. height: 6px;
  87. }
  88. &:nth-child(6n + 3) {
  89. height: 10px;
  90. }
  91. &:nth-child(6n + 4) {
  92. height: 18px;
  93. }
  94. &:nth-child(6n + 5) {
  95. height: 10px;
  96. }
  97. &:nth-child(6n + 6) {
  98. height: 6px;
  99. }
  100. }
  101. @keyframes ani {
  102. 0% {
  103. transform: scale(1);
  104. }
  105. 50% {
  106. transform: scale(0.4);
  107. }
  108. 100% {
  109. transform: scale(1);
  110. }
  111. }
  112. }
  113. </style>