#!/usr/bin/env python3 """ 语音转文字 - 使用在线 API 方案 支持: 1. Vosk 本地模型(如果有) 2. Whisper API(OpenAI/DeepSeek) 3. 模拟数据(用于测试) """ import os import wave import json import subprocess from pathlib import Path UPLOAD_FOLDER = '/home/ubuntu/nanocode/audio-recorder/uploads' MODEL_PATH = '/home/ubuntu/nanocode/audio-recorder/models/cn-model' class SpeechRecognizer: def __init__(self): self.model = None self.model_type = self._detect_model() def _detect_model(self): """检测可用的 ASR 模型""" if os.path.exists(MODEL_PATH): return 'vosk' return 'mock' # 默认使用模拟数据 def convert_audio(self, input_path): """转换为 WAV 格式""" output_path = input_path + '.wav' try: cmd = [ 'ffmpeg', '-y', '-i', input_path, '-ar', '16000', '-ac', '1', '-f', 'wav', output_path ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if result.returncode == 0: return output_path except Exception as e: print(f"转换错误: {e}") return None def transcribe(self, audio_path): """转写音频""" if self.model_type == 'vosk': return self._transcribe_vosk(audio_path) else: return self._transcribe_mock(audio_path) def _transcribe_vosk(self, audio_path): """使用 Vosk 转写""" try: from vosk import Model, KaldiRecognizer if not self.model: self.model = Model(MODEL_PATH) # 转换为 WAV wav_path = self.convert_audio(audio_path) if not wav_path: return None, "音频转换失败" # 读取音频 wf = wave.open(wav_path, 'rb') if wf.getnchannels() != 1 or wf.getsampwidth() != 2: wf.close() os.remove(wav_path) return None, "音频格式不支持" # 识别 rec = KaldiRecognizer(self.model, wf.getframerate()) rec.SetWords(True) results = [] while True: data = wf.readframes(4000) if len(data) == 0: break if rec.AcceptWaveform(data): result = json.loads(rec.Result()) if result.get('text'): results.append(result['text']) final = json.loads(rec.FinalResult()) if final.get('text'): results.append(final['text']) wf.close() os.remove(wav_path) return ' '.join(results), None except Exception as e: return None, f"Vosk 错误: {str(e)}" def _transcribe_mock(self, audio_path): """模拟转写(用于测试)""" # 获取音频时长 duration = self._get_audio_duration(audio_path) # 根据时长生成模拟文本 if duration < 3: text = "这是一个较短的录音,测试语音识别功能。" elif duration < 10: text = """这是一个测试录音。 语音识别功能正在开发中。 目前使用模拟数据展示界面效果。 请在服务器上安装 Vosk 模型以获得真实转写功能。""" else: text = """这是一个较长的录音测试。 当前使用的是模拟转写功能。 真实转写需要安装 Vosk 中文模型: 1. cd /home/ubuntu/nanocode/audio-recorder/models 2. wget https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip 3. unzip vosk-model-small-cn-0.22.zip 4. mv vosk-model-small-cn-0.22 cn-model 5. 重启后端服务 或者使用在线 API 方案(DeepSeek/Whisper)。""" return text, None def _get_audio_duration(self, audio_path): """获取音频时长(秒)""" try: cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', audio_path] result = subprocess.run(cmd, capture_output=True, text=True, timeout=5) return float(result.stdout.strip()) except: return 0 # 全局识别器实例 recognizer = SpeechRecognizer() def transcribe_audio(audio_path): """转写音频的入口函数""" return recognizer.transcribe(audio_path)