Qwen3-ASR-0.6B开发实战:基于Vue3的语音识别前端界面

1. 引言

语音识别技术正在改变我们与设备交互的方式,从智能助手到实时字幕,应用场景越来越广泛。Qwen3-ASR-0.6B作为一款轻量级但功能强大的语音识别模型,支持30种语言和22种中文方言,为开发者提供了出色的语音转文字能力。

本文将带你从零开始,使用Vue3框架构建一个美观实用的语音识别Web前端界面。无论你是前端开发新手还是有一定经验的开发者,都能通过本教程快速掌握如何将先进的语音识别技术集成到Web应用中。

2. 环境准备与项目搭建

2.1 前置要求

在开始之前,确保你的开发环境满足以下要求:

  • Node.js 16.0 或更高版本
  • npm 或 yarn 包管理器
  • 基本的JavaScript和Vue3知识

2.2 创建Vue3项目

使用Vite快速创建Vue3项目,这是目前最快速的Vue项目构建工具:

npm create vite@latest qwen-asr-frontend -- --template vue
cd qwen-asr-frontend
npm install

2.3 安装必要依赖

除了Vue3基础依赖,我们还需要安装一些处理音频和UI的库:

npm install axios element-plus
npm install --save-dev @types/webrtc

3. 核心功能实现

3.1 音频录制组件

首先创建一个用于录制音频的组件,这是语音识别的基础:

<template>
  <div class="audio-recorder">
    <el-button 
      :type="isRecording ? 'danger' : 'primary'" 
      @click="toggleRecording"
      :icon="isRecording ? 'Stop' : 'Microphone'"
    >
      {{ isRecording ? '停止录制' : '开始录制' }}
    </el-button>
    
    <div v-if="audioUrl" class="audio-preview">
      <audio :src="audioUrl" controls></audio>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const isRecording = ref(false)
const mediaRecorder = ref(null)
const audioChunks = ref([])
const audioUrl = ref('')

const toggleRecording = async () => {
  if (isRecording.value) {
    stopRecording()
  } else {
    await startRecording()
  }
}

const startRecording = async () => {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
    audioChunks.value = []
    
    mediaRecorder.value = new MediaRecorder(stream, {
      mimeType: 'audio/webm;codecs=opus'
    })
    
    mediaRecorder.value.ondataavailable = (event) => {
      if (event.data.size > 0) {
        audioChunks.value.push(event.data)
      }
    }
    
    mediaRecorder.value.onstop = () => {
      const audioBlob = new Blob(audioChunks.value, { type: 'audio/webm' })
      audioUrl.value = URL.createObjectURL(audioBlob)
      stream.getTracks().forEach(track => track.stop())
    }
    
    mediaRecorder.value.start()
    isRecording.value = true
  } catch (error) {
    ElMessage.error('无法访问麦克风:' + error.message)
  }
}

const stopRecording = () => {
  if (mediaRecorder.value) {
    mediaRecorder.value.stop()
    isRecording.value = false
  }
}
</script>

3.2 语音识别服务调用

创建与Qwen3-ASR-0.6B后端服务通信的逻辑:

// src/services/asrService.js
import axios from 'axios'

const API_BASE_URL = 'http://your-backend-url/api'

export const asrService = {
  async transcribeAudio(audioBlob) {
    try {
      const formData = new FormData()
      formData.append('audio', audioBlob, 'recording.webm')
      formData.append('model', 'qwen3-asr-0.6b')
      
      const response = await axios.post(`${API_BASE_URL}/transcribe`, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
        timeout: 30000 // 30秒超时
      })
      
      return response.data
    } catch (error) {
      throw new Error(`语音识别失败: ${error.response?.data?.message || error.message}`)
    }
  },
  
  async getSupportedLanguages() {
    try {
      const response = await axios.get(`${API_BASE_URL}/languages`)
      return response.data
    } catch (error) {
      console.error('获取支持语言失败:', error)
      return []
    }
  }
}

3.3 主界面组件

整合所有功能的主界面组件:

<template>
  <div class="asr-container">
    <el-card class="main-card">
      <template #header>
        <div class="card-header">
          <h2>Qwen3-ASR-0.6B 语音识别</h2>
          <el-select v-model="selectedLanguage" placeholder="选择语言">
            <el-option
              v-for="lang in supportedLanguages"
              :key="lang.code"
              :label="lang.name"
              :value="lang.code"
            />
          </el-select>
        </div>
      </template>

      <AudioRecorder 
        @audio-recorded="handleAudioRecorded"
        ref="audioRecorder"
      />

      <div class="result-section">
        <h3>识别结果</h3>
        <el-input
          v-model="transcriptionResult"
          type="textarea"
          :rows="6"
          placeholder="识别结果将显示在这里..."
          readonly
        />
        
        <div class="action-buttons">
          <el-button 
            type="success" 
            :loading="isTranscribing"
            :disabled="!audioBlob"
            @click="transcribeAudio"
          >
            {{ isTranscribing ? '识别中...' : '开始识别' }}
          </el-button>
          
          <el-button 
            v-if="transcriptionResult"
            type="primary"
            @click="copyToClipboard"
          >
            复制文本
          </el-button>
        </div>
      </div>
    </el-card>

    <div class="stats" v-if="transcriptionStats">
      <el-tag type="info">识别时间: {{ transcriptionStats.processingTime }}s</el-tag>
      <el-tag type="success">置信度: {{ transcriptionStats.confidence }}%</el-tag>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import AudioRecorder from './components/AudioRecorder.vue'
import { asrService } from './services/asrService'

const audioBlob = ref(null)
const transcriptionResult = ref('')
const isTranscribing = ref(false)
const transcriptionStats = ref(null)
const supportedLanguages = ref([])
const selectedLanguage = ref('zh') // 默认中文

const handleAudioRecorded = (blob) => {
  audioBlob.value = blob
  transcriptionResult.value = ''
  transcriptionStats.value = null
}

const transcribeAudio = async () => {
  if (!audioBlob.value) return
  
  isTranscribing.value = true
  try {
    const result = await asrService.transcribeAudio(audioBlob.value, selectedLanguage.value)
    transcriptionResult.value = result.text
    transcriptionStats.value = {
      processingTime: result.processing_time,
      confidence: result.confidence
    }
    ElMessage.success('识别完成')
  } catch (error) {
    ElMessage.error(error.message)
  } finally {
    isTranscribing.value = false
  }
}

const copyToClipboard = async () => {
  try {
    await navigator.clipboard.writeText(transcriptionResult.value)
    ElMessage.success('已复制到剪贴板')
  } catch (error) {
    ElMessage.error('复制失败')
  }
}

onMounted(async () => {
  supportedLanguages.value = await asrService.getSupportedLanguages()
})
</script>

<style scoped>
.asr-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.result-section {
  margin-top: 20px;
}

.action-buttons {
  margin-top: 15px;
  display: flex;
  gap: 10px;
}

.stats {
  margin-top: 15px;
  display: flex;
  gap: 10px;
}
</style>

4. 高级功能扩展

4.1 实时语音识别

添加实时语音识别功能,提供更流畅的用户体验:

// src/services/realtimeAsrService.js
class RealtimeASRService {
  constructor() {
    this.socket = null
    this.isConnected = false
  }

  connect() {
    return new Promise((resolve, reject) => {
      this.socket = new WebSocket('ws://your-backend-url/realtime-asr')
      
      this.socket.onopen = () => {
        this.isConnected = true
        resolve()
      }
      
      this.socket.onerror = (error) => {
        reject(error)
      }
      
      this.socket.onmessage = (event) => {
        const data = JSON.parse(event.data)
        this.handleMessage(data)
      }
    })
  }

  sendAudioChunk(audioData) {
    if (this.isConnected) {
      this.socket.send(audioData)
    }
  }

  handleMessage(data) {
    // 处理实时识别结果
    if (data.type === 'partial_result') {
      this.emit('partial-result', data.text)
    } else if (data.type === 'final_result') {
      this.emit('final-result', data.text)
    }
  }
}

4.2 历史记录功能

添加本地存储功能,保存识别历史:

// src/utils/historyManager.js
export const historyManager = {
  getHistory() {
    const history = localStorage.getItem('asrHistory')
    return history ? JSON.parse(history) : []
  },

  addToHistory(transcription) {
    const history = this.getHistory()
    history.unshift({
      text: transcription,
      timestamp: new Date().toISOString(),
      id: Date.now()
    })
    
    // 只保留最近50条记录
    const limitedHistory = history.slice(0, 50)
    localStorage.setItem('asrHistory', JSON.stringify(limitedHistory))
  },

  clearHistory() {
    localStorage.removeItem('asrHistory')
  }
}

5. 界面优化与用户体验

5.1 响应式设计

确保界面在不同设备上都能良好显示:

/* 响应式调整 */
@media (max-width: 768px) {
  .asr-container {
    padding: 10px;
  }
  
  .card-header {
    flex-direction: column;
    gap: 10px;
  }
  
  .action-buttons {
    flex-direction: column;
  }
}

5.2 加载状态和错误处理

增强用户体验的加载状态和错误提示:

<template>
  <div class="loading-overlay" v-if="isLoading">
    <el-icon class="loading-icon"><Loading /></el-icon>
    <span>处理中...</span>
  </div>
</template>

<script>
// 在组件中添加加载状态管理
const isLoading = ref(false)

const showError = (message) => {
  ElNotification.error({
    title: '错误',
    message,
    duration: 3000
  })
}
</script>

6. 部署与优化

6.1 构建生产版本

使用Vite构建优化后的生产版本:

npm run build

6.2 性能优化建议

  • 使用Web Worker处理音频数据
  • 实现音频数据压缩
  • 添加请求重试机制
  • 使用CDN加速静态资源

7. 总结

通过本教程,我们成功构建了一个基于Vue3的Qwen3-ASR-0.6B语音识别前端界面。这个应用不仅具备了基本的录音和识别功能,还包含了实时识别、历史记录等高级特性。

实际开发中,这个前端界面表现相当不错。Vue3的响应式系统让状态管理变得简单,Element Plus组件库提供了美观的UI元素,整体开发体验很流畅。语音识别的准确度也令人满意,特别是对中文和英文的支持相当出色。

如果你想要进一步扩展功能,可以考虑添加语音合成、多语言界面支持,或者集成更多的AI服务。这个项目为基础提供了一个很好的起点,你可以根据自己的需求进行定制和扩展。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐