火山引擎流式语音识别大模型 做持续监听持续记录 场景怎么写逻辑好
目前是麦克风实时说话,通过静音检测分割成临时wav文件传给模型再返回,速度慢效率低。如果用麦克风流式持续传输包,我发现这个模型会不断返回增量的语句,也就是停止后才给最终结果,发现不适合于持续监听快速持续,一次性返回结果的场景。
是我的技术路线走错了吗,还有好多不明白的地方,这个场景怎么写逻辑比较好呢?
业务场景描述:麦克风一直开启状态。随着说话,屏幕上不断快速出现识别的文字结果。
火山引擎流式语音识别大模型 做持续监听持续记录 场景怎么写逻辑好
目前是麦克风实时说话,通过静音检测分割成临时wav文件传给模型再返回,速度慢效率低。如果用麦克风流式持续传输包,我发现这个模型会不断返回增量的语句,也就是停止后才给最终结果,发现不适合于持续监听快速持续,一次性返回结果的场景。
是我的技术路线走错了吗,还有好多不明白的地方,这个场景怎么写逻辑比较好呢?
业务场景描述:麦克风一直开启状态。随着说话,屏幕上不断快速出现识别的文字结果。
关注让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。
如果答案让您满意,请采纳、点赞、关注,非常感谢!
.
每日一句正能量,让它带给您好心情:把生活过得热气腾腾,就是最好的状态。
.
针对您描述的"持续监听、实时显示"场景,我认为您的技术路线确实需要调整。火山引擎流式语音识别大模型的设计就是为了实时返回增量结果的,这恰恰符合您的需求。让我为您提供一个更优化的技术方案。
您当前方案的问题:
class RealtimeSpeechRecognition {
constructor() {
this.isListening = false;
this.mediaRecorder = null;
this.audioChunks = [];
this.recognitionStream = null;
}
// 开始监听
async startListening() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.setupMediaRecorder(stream);
this.connectToVolcanoEngine();
this.isListening = true;
} catch (error) {
console.error('无法访问麦克风:', error);
}
}
// 设置媒体录制器
setupMediaRecorder(stream) {
const options = {
mimeType: 'audio/webm;codecs=opus',
audioBitsPerSecond: 16000
};
this.mediaRecorder = new MediaRecorder(stream, options);
this.mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
this.sendAudioData(event.data);
}
};
// 每100ms发送一次数据,保证低延迟
this.mediaRecorder.start(100);
}
}
import websocket
import threading
import queue
from collections import deque
class VolcanoStreamingClient:
def __init__(self):
self.ws = None
self.audio_queue = queue.Queue()
self.text_buffer = deque(maxlen=10) # 保存最近10条识别结果
self.is_connected = False
def connect(self):
"""连接火山引擎WebSocket服务"""
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp(
"wss://your-volcano-websocket-endpoint",
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close
)
# 在单独线程中运行WebSocket
ws_thread = threading.Thread(target=self.ws.run_forever)
ws_thread.daemon = True
ws_thread.start()
def on_message(self, ws, message):
"""处理识别结果"""
try:
result = json.loads(message)
if result.get('type') == 'partial': # 增量结果
text = result.get('text', '')
self.update_display(text, is_final=False)
elif result.get('type') == 'final': # 最终结果
text = result.get('text', '')
self.update_display(text, is_final=True)
except Exception as e:
print(f"解析结果错误: {e}")
def send_audio_data(self, audio_chunk):
"""发送音频数据"""
if self.ws and self.ws.sock and self.ws.sock.connected:
# 将音频数据转换为base64或直接发送二进制
self.ws.send(audio_chunk, opcode=websocket.ABNF.OPCODE_BINARY)
class DisplayManager {
constructor() {
this.displayElement = document.getElementById('speech-text');
this.currentText = '';
this.isUpdating = false;
}
// 更新显示内容
updateDisplay(text, isFinal = false) {
if (isFinal) {
// 最终结果,清空当前内容,开始新行
this.addFinalText(text);
this.currentText = '';
} else {
// 增量结果,更新当前行
this.currentText = text;
this.renderText();
}
}
// 渲染文本
renderText() {
const timestamp = new Date().toLocaleTimeString();
const displayText = `${timestamp} - ${this.currentText}`;
// 使用requestAnimationFrame保证流畅更新
requestAnimationFrame(() => {
this.displayElement.textContent = displayText;
this.displayElement.scrollIntoView({ behavior: 'smooth' });
});
}
addFinalText(text) {
const finalElement = document.createElement('div');
finalElement.className = 'final-text';
finalElement.textContent = `${new Date().toLocaleTimeString()} - ${text}`;
this.displayElement.parentNode.insertBefore(finalElement, this.displayElement);
}
}
public class ContinuousSpeechRecognition {
private volatile boolean isRunning = false;
private AudioFormat audioFormat;
private TargetDataLine targetDataLine;
public void startContinuousRecognition() {
try {
// 设置音频格式
audioFormat = new AudioFormat(16000, 16, 1, true, false);
// 获取麦克风输入
DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
targetDataLine.open(audioFormat);
targetDataLine.start();
isRunning = true;
// 启动处理线程
new Thread(this::processAudioStream).start();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
private void processAudioStream() {
byte[] buffer = new byte[4096];
VolcanoStreamClient streamClient = new VolcanoStreamClient();
while (isRunning) {
int bytesRead = targetDataLine.read(buffer, 0, buffer.length);
if (bytesRead > 0) {
// 发送到火山引擎流式识别
streamClient.sendAudioData(buffer, bytesRead);
}
}
}
// 处理识别结果回调
public void onRecognitionResult(String text, boolean isFinal) {
if (isFinal) {
// 最终结果,保存到历史
saveToHistory(text);
}
// 实时更新UI显示
updateDisplay(text, isFinal);
}
}
# 音频配置
audio_config:
sample_rate: 16000
channels: 1
format: pcm_s16le
chunk_size: 1024 # 每帧大小
# 网络配置
streaming_config:
reconnect_interval: 2000 # 重连间隔(ms)
timeout: 30000 # 超时时间(ms)
buffer_size: 8192 # 缓冲区大小
这个方案充分利用了火山引擎流式识别的特性,能够实现您要求的"随着说话,屏幕上不断快速出现识别的文字结果"的效果。关键是要正确处理增量结果,而不是等待最终结果。