语音识别和合成的延迟可能是创建无缝和高效应用程序的一个重大障碍。减少延迟不仅可以改善用户体验,还可以提升实时应用程序的整体性能。本文将探讨在一般转录、实时转录、文件转录和语音合成中减少延迟的策略。

1. 网络延迟:将语音资源移近应用程序

导致语音识别延迟的主要因素之一是网络延迟。为了减轻这一延迟,关键是减少应用程序与语音识别资源之间的距离。以下是一些建议:

  • 语音容器 :它提供了在本地或边缘运行模型的灵活性,从而无需将音频数据发送到云端,进而减少网络延迟。

    在 Docker 中安装和运行语音容器 - Speech 服务 - Azure AI 服务 | Microsoft Learn

  • 利用云服务提供商:选择在靠近用户地区的区域拥有数据中心的云服务提供商。这样可以显著减少网络延迟。

  • 使用嵌入式语音:这是一种专为在设备上场景设计的紧凑型模型,在这些场景中互联网连接有限或不可用,从而显著减少网络延迟。然而,这可能会导致准确度略有下降。因此,为了获得最佳准确度,考虑采用混合方法:在有网络连接时通过云端使用 Azure AI 语音,在没有网络时切换到嵌入式语音。这提供了高质量、准确的语音处理以及可靠的备份选项。

    嵌入式语音 - Speech 服务 - Azure AI 服务 | Microsoft Learn

2. 实时转录:

实时转录需要立即处理音频输入以提供即时反馈。以下是一些建议,以实现低延迟的实时转录:

2.1 使用实时流
不要录制整个音频然后处理,而是使用实时流将音频数据以小片段的形式发送到语音识别服务。这允许立即处理并减少整体延迟。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def speech_recognize_continuous_async_from_microphone():
    """异步从麦克风输入进行连续语音识别"""
    speech_config = speechsdk.SpeechConfig(subscription=os.getenv("SUBSCRIPTION_KEY"), 
    region="centralIndia")
    speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config)
    done = False
    def recognized_cb(evt: speechsdk.SpeechRecognitionEventArgs):
        print('RECOGNIZED: {}'.format(evt.result.text))

    def stop_cb(evt: speechsdk.SessionEventArgs):
        """回调函数,用于停止连续识别"""
        print('CLOSING on {}'.format(evt))
        nonlocal done
        done = True

    # 连接语音识别器触发的事件的回调函数
    speech_recognizer.recognized.connect(recognized_cb)
    speech_recognizer.session_stopped.connect(stop_cb)


    # 在识别开始时,可以在此线程上执行其他任务...
    result_future = speech_recognizer.start_continuous_recognition_async()
    result_future.get()  # 等待 voidfuture,这样我们就知道引擎初始化已完成。
    print('Continuous Recognition is now running, say something.')
    while not done:
        print('type "stop" then enter when done')
        stop = input()
        if (stop.lower() == "stop"):
            print('Stopping async recognition.')
            speech_recognizer.stop_continuous_recognition_async()
            break
    print("recognition stopped, main thread can exit now.")

speech_recognize_continuous_async_from_microphone()

Azure Speech SDK 还提供了将音频流输入识别器的方式,作为对麦克风或文件输入的替代。您可以根据需求选择 PushAudioInputStream 和 PullAudioInputStream。

详情请见 - Speech SDK 音频输入流概念 - Azure AI 服务 | Microsoft Learn

2.2 定义默认语言
如果已知默认语言,请在转录过程开始时定义。这样可以消除检测输入语言所需的额外处理时间。如果不知道默认语言,请使用 “SpeechServiceConnection_LanguageIdMode” 在转录开始时检测语言,并指定预期语言的列表以减少处理时间

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
speech_config = speechsdk.SpeechConfig(subscription="YourSubscriptionKey", 
region="YourServiceRegion") 
speech_config.speech_recognition_language = "zh-CN" # 设置默认语言
## 或者 
speech_config.set_property(property_id=speechsdk.PropertyId
.SpeechServiceConnection_LanguageIdMode, value = "AtStart")
auto_detect_source_language_config = speechsdk.languageconfig
.AutoDetectSourceLanguageConfig(languages=["zh-CN", "en-US"])
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config, 
audio_config=audio_config,
auto_detect_source_language_config=auto_detect_source_language_config)

2.3 使用异步方法
使用 start_continuous_recognition_async 而不是 start_continuous_recognition,以及 stop_continuous_recognition_async 而不是 stop_continuous_recognition 等异步方法。这些方法允许非阻塞操作并减少延迟。

1
2
3
speech_recognizer.start_continuous_recognition_async(); # 执行其他任务

speech_recognizer.stop_continuous_recognition_async()

2.4 使用快速转录
快速转录比实时流转录快得多,适用于需要即时转录的场景,如呼叫中心分析、会议摘要、语音配音等。它可以在不到一分钟的时间内转录 30 分钟的音频。尽管这还在公共预览中,并且只支持少数语言环境。支持的完整语言列表,请查看 语言支持 - Speech 服务 - Azure AI 服务 | Microsoft Learn

3. 文件转录

对于文件转录,处理大型音频文件可能会引入显著的延迟。以下是一些减少延迟的策略:

3.1 将音频分割成小片段
将音频文件分割成更小的片段,并并行运行每个片段的转录。这允许更快地处理并减少整体转录时间。音频分块的一个缺点是,根据分块策略,它可能会导致转录质量略有下降,但如果转录层之后是用于分析洞察、后期处理等的 LLM 智能层,那么质量下降应该会被 LLM 智能抵消。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from pydub import AudioSegment  
import concurrent.futures  

def transcribe_chunk(chunk):  
    # 每个片段的转录逻辑  
    pass  
audio = AudioSegment.from_file("large_audio_file.wav")  
chunk_length_ms = 10000  # 10秒  
chunks = [audio[i:i + chunk_length_ms] 
for i in range(0, len(audio), chunk_length_ms)]  
  
with concurrent.futures.ThreadPoolExecutor() as executor:  
    futures = [executor.submit(transcribe_chunk, chunk) for chunk in chunks]  
    results = [f.result() for f in concurrent.futures.as_completed(futures)]  

3.2 提高音频速度
在发送转录之前,提高音频文件的播放速度。这样可以减少处理整个文件的时间,而对转录的准确性影响很小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def increase_audio_speed(filename, 
output_filename = "modified_audio_file.wav", speed_change_factor = 1.7):  
    # 加载音频文件  
    audio = AudioSegment.from_file(filename)  # 更改为您的文件格式  
    # 改变速度:加速(例如,1.5倍)  
    speed_change_factor = speed_change_factor  # 增加,使其变快,减少以放慢  
    new_audio = audio._spawn(audio.raw_data, 
    overrides={'frame_rate': int(audio.frame_rate * speed_change_factor)})  
    # 设置新音频的帧率  
    new_audio = new_audio.set_frame_rate(audio.frame_rate)  
    # 导出修改后的音频  
    new_audio.export(output_filename, format="wav")  # 更改为您所需的格式  

3.3 压缩输入音频
在发送转录之前,压缩输入音频。这可以减少文件大小,以实现更快的传输,优化带宽使用和存储效率。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pydub import AudioSegment
input_audio = 'raw_tts.wav'
output_audio = 'compressed_audio.mp3'
try:
    # 加载音频文件
    audio = AudioSegment.from_file(input_audio)  
    # 导出音频文件,使用较低的比特率进行压缩
    audio.export(output_audio, format="mp3", bitrate="64k")
    print(f"Compressed audio saved as {output_audio}")
except Exception as e:
    print(f"An error occurred: {e}")

4. 语音合成

语音合成的延迟在实时应用程序中可能是一个瓶颈。以下是一些建议,以减少延迟:

4.1 使用异步方法
不要使用 speak_text_async 进行语音合成,因为它会阻塞流直到整个音频被处理完毕,而是切换到 start_speaking_text_async 方法。此方法在接收到第一个音频块时就开始流式传输音频输出,从而显著减少延迟。

4.2 文本流
流式传输文本允许 TTS 系统在收到文本的初始部分后立即开始处理和生成语音,而不是等待整个文本可用。这减少了语音输出开始前的初始延迟,使其成为交互式应用程序、现场活动和响应式 AI 驱动的对话的理想选择。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# tts 句子结束标记
tts_sentence_end = [ ".", "!", "?", ";", "。", "!", "?", ";", "\
" ]
completion = gpt_client.chat.completions.create(
        model="gpt-4o",
    messages=[,
        {"role": "user", "content": <prompt>}
    ],
    stream=True
)
collected_messages = []
last_tts_request = None

for chunk in completion:
    if len(chunk.choices) > 0:
        chunk_text = chunk.choices[0].delta.content
        if chunk_text:
            collected_messages.append(chunk_text)
            if chunk_text in tts_sentence_end:
                text = "".join(collected_messages)
                .strip() # 将收到的消息一起连接起来构成一个句子
                last_tts_request = speech_synthesizer
                .start_speaking_text_async(text).get()
                collected_messages.clear()

4.3 优化音频输出格式
负载大小影响延迟。使用压缩音频格式来节省网络带宽,这在网络不稳定或带宽有限时至关重要。切换到 Riff-48Khz-16Bit-Mono-Pcm 格式,该格式具有 384 kbps 的比特率,自动使用压缩输出格式进行转录,从而减少延迟。

通过遵循这些策略,您可以在 STT 和 TTS 应用程序中显著减少延迟,从而提供更流畅、更高效的用户体验。实施这些技术将确保您的应用程序即使在实时场景中也能保持响应性和高性能。