Tauri JS应用中断开外部耳机后Vosk语音转文本失效的问题排查求助
Tauri JS应用中断开外部耳机后Vosk语音转文本失效的问题排查求助
大家好,我在开发Tauri应用时遇到了一个特别棘手的问题,想请各位帮忙分析排查:
我的应用流程是前端JS录制音频→处理后通过Rust handler传给Python子进程→用Vosk做实时语音转文本。目前的奇怪现象是:
- 当华为FreeBuds Pro耳机连接时(哪怕我明确指定使用MacBook内置麦克风的ID),语音转文本工作完全正常
- 断开耳机并刷新页面重新录制后,音频录制本身是正常的(能保存成有效音频文件),但Vosk返回的识别结果始终是空字符串
"",完全无法识别语音
下面是我的相关代码,麻烦各位帮忙看看哪里可能出问题了:
前端JS代码
获取麦克风流
const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: micId, sampleRate: 16000, channelCount: 1 }, video: false, }); streamAudioToPython(micStream);
音频流处理并发送给Rust
const streamAudioToPython = async (micStream?: any) => { try { const audioContext = new AudioContext(); await audioContext.audioWorklet.addModule('/processor.js'); const mediaStreamSource = audioContext.createMediaStreamSource( micStream || stream ); setMediaStreamSource(mediaStreamSource); const audioProcessor = new AudioWorkletNode( audioContext, 'audio-processor' ); setAudioProcessor(audioProcessor); audioProcessor.port.onmessage = (event) => { const regularArray = event.data; invoke('send_audio_to_python', { audioData: regularArray, }); }; mediaStreamSource.connect(audioProcessor); } catch (error) { console.error('Error~~~>', error); } };
AudioWorklet处理器(processor.js)
class AudioProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { const input = inputs[0]; if (input) { const channelData = input[0]; const int16Data = new Int16Array(channelData.length); for (let i = 0; i < channelData.length; i++) { int16Data[i] = Math.min(1, Math.max(-1, channelData[i])) * 0x7fff; } const byteArray = new Uint8Array(int16Data.buffer); const regularArray = Array.from(byteArray); this.port.postMessage(regularArray); } return true; } } registerProcessor('audio-processor', AudioProcessor);
Rust后端代码
main.rs
mod execute_python_scripts; mod show_in_folder; fn main() { tauri::Builder::default() .setup(|app| { let app_handle = app.handle(); execute_python_scripts::force_initialize_python_process(app_handle); Ok(()) }) .invoke_handler(tauri::generate_handler![ show_in_folder::show_in_folder, execute_python_scripts::send_audio_to_python, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
execute_python_scripts.rs
use chrono::Local; use std::fs::{self, OpenOptions}; use std::io::{BufRead, BufReader, Write}; use std::path::Path; use std::process::{Child, Command, Stdio}; use std::sync::{Arc, Mutex}; use tauri::{AppHandle, Manager}; #[tauri::command] pub async fn send_audio_to_python(audio_data: Vec<u8>) -> Result<(), String> { let mut child_guard = PYTHON_PROCESS .lock() .map_err(|_poisoned| "Mutex poisoned.".to_string())?; if let Some(child_process) = child_guard.as_mut() { match child_process.try_wait() { Ok(Some(_)) => { return Err("Python process is no longer running.".to_string()); } Ok(None) => { let stdin = child_process.stdin.as_mut().ok_or("Failed to open stdin")?; stdin.write_all(&audio_data).map_err(|e| e.to_string())?; } Err(e) => { return Err(format!("Failed to check Python process status: {}", e)); } } } else { return Err("No Python process is currently running.".to_string()); } Ok(()) } pub fn start_python_process(app_handle: AppHandle) -> std::process::Child { let log_dir = "log"; let exists = Path::new(log_dir).exists(); if !exists { fs::create_dir_all(log_dir).expect("Failed to create log directory"); } let now = Local::now(); let log_file_name = format!("{}/logs_{}.txt", log_dir, now.format("%Y-%m-%d_%H-%M-%S")); let mut log_file = OpenOptions::new() .create(true) .append(true) .open(&log_file_name) .expect("Failed to open or create log file"); let mut child = Command::new("python3") .arg("python/audio_to_speech.py") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("Failed to start Python process"); if let Some(stdout) = child.stdout.take() { let reader = BufReader::new(stdout); let app_handle_clone = app_handle.clone(); std::thread::spawn(move || { for line in reader.lines() { if let Ok(text) = line { writeln!(log_file, "{}", text).expect("Failed to write to log file"); app_handle_clone .emit_all("python-log", format!("{}", text)) .expect("Failed to emit Python log"); } } }); } if let Some(stderr) = child.stderr.take() { let reader = BufReader::new(stderr); std::thread::spawn(move || { for line in reader.lines() { if let Ok(text) = line { eprintln!("Down===> {}", text); } } }); } return child; } pub fn force_initialize_python_process(app_handle: AppHandle) { let mut process_guard = PYTHON_PROCESS.lock().unwrap(); if process_guard.is_none() { *process_guard = Some(start_python_process(app_handle)); } } lazy_static::lazy_static! { static ref PYTHON_PROCESS: Arc<Mutex<Option<Child>>> = Arc::new(Mutex::new(None)); }
Python语音识别代码(audio_to_speech.py)
import sys import vosk import numpy as np import json fs = 16000 model = vosk.Model(lang="en-us") recognizer = vosk.KaldiRecognizer(model, fs) def process_audio_data(audio_data): """Process audio data with Vosk recognizer.""" if recognizer.AcceptWaveform(audio_data): text = recognizer.Result() text_json = json.loads(text) print("text-", text_json["text"], flush=True) else: partial = recognizer.PartialResult() partial_json = json.loads(partial) print("partial-", partial_json["partial"], flush=True) def handle_stream(): audio_data = sys.stdin.buffer.read(4096) if len(audio_data) == 0: print("No audio data received.", flush=True) return audio_data = np.frombuffer(audio_data, dtype=np.int16) audio_data_bytes = audio_data.tobytes() process_audio_data(audio_data_bytes) if __name__ == "__main__": while True: handle_stream()
我已经确认录制的音频是有效的,但Vosk就是识别不出内容,有没有朋友遇到过类似的问题?或者能帮我分析下可能的原因?
备注:内容来源于stack exchange,提问作者Zeeshan Ahmad Khalil
相关产品推荐
相关产品推荐

