You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure C#语音双向交互自监听问题解决方案问询

问题

在使用Azure Speech to Text与TTS的C#实现双向语音交互时,麦克风会拾取应用自身合成的语音,引发无限循环。需要在开放式扬声器场景下实现:过滤应用自身语音,避免被麦克风识别,同时支持用户打断正在进行的语音合成并处理输入音频。

已知使用耳机可解决该问题,但需适配扬声器场景。当前已实现标准的麦克风监听与语音合成函数,代码如下:

监听函数

public async Task Listen()
{
    var stopRecognition = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
    using var audioProcessingOptions = AudioProcessingOptions.Create(AudioProcessingConstants.AUDIO_INPUT_PROCESSING_ENABLE_DEFAULT);
    using var audioInput = AudioConfig.FromDefaultMicrophoneInput(audioProcessingOptions);

    using var recognizer = new SpeechRecognizer(Config, audioInput);

    recognizer.Recognized += Recognizer_Recognized;

    await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);

    // Waits for completion.
    // Use Task.WaitAny to keep the task rooted.
    Task.WaitAny(new[] { stopRecognition.Task });

    // Stops recognition.
    await recognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}

识别回调

private void Recognizer_Recognized(object? sender, SpeechRecognitionEventArgs e)
{
    // 将识别出的文本推送至事件以显示在屏幕上...
}

语音合成函数

public async Task Talk(string text)
{
    // 为Windows平台支持中文字符
    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
    {
        Console.InputEncoding = System.Text.Encoding.Unicode;
        Console.OutputEncoding = System.Text.Encoding.Unicode;
    }

    // 设置语音名称
    Config.SpeechSynthesisVoiceName = "en-AU-CarlyNeural";

    // 使用默认扬声器作为音频输出创建语音合成器
    using var synthesizer = new SpeechSynthesizer(Config);
    using var result = await synthesizer.SpeakTextAsync(text);
    if (result.Reason == ResultReason.SynthesizingAudioCompleted)
    {
        // 合成完成处理
    }
    else if (result.Reason == ResultReason.Canceled)
    {
        var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
    }
}

核心矛盾:如果在语音合成时停止监听,无法支持用户打断长时间播报;如果持续监听,麦克风会拾取自身合成的语音,陷入循环。


解决方案

1. 启用Azure Speech SDK内置回声消除功能

Azure Speech SDK的音频处理选项中包含回声消除能力,默认配置已启用基础处理,但可以明确指定开启针对性的回声消除与噪声抑制,增强过滤效果:

// 明确开启回声消除和噪声抑制
using var audioProcessingOptions = AudioProcessingOptions.Create(
    AudioProcessingConstants.AUDIO_INPUT_PROCESSING_ENABLE_ECHO_CANCELLATION |
    AudioProcessingConstants.AUDIO_INPUT_PROCESSING_ENABLE_NOISE_SUPPRESSION);

注意:该功能依赖硬件和系统底层支持,部分设备可能需要额外配置优化。

2. 结合合成状态过滤识别结果

通过标记合成状态,在识别回调中直接忽略合成期间的麦克风输入,同时支持实时打断:

// 全局变量标记是否正在合成语音
private bool _isSpeaking = false;
private CancellationTokenSource? _talkCts;

private void Recognizer_Recognized(object? sender, SpeechRecognitionEventArgs e)
{
    // 识别到有效输入时,立即取消当前合成任务
    _talkCts?.Cancel();
    
    // 如果处于合成状态,忽略本次识别结果
    if (_isSpeaking) return;
    
    // 处理用户输入逻辑...
}

public async Task Talk(string text)
{
    _talkCts = new CancellationTokenSource();
    var cancellationToken = _talkCts.Token;
    
    try
    {
        _isSpeaking = true;
        Config.SpeechSynthesisVoiceName = "en-AU-CarlyNeural";
        using var synthesizer = new SpeechSynthesizer(Config);
        
        // 支持取消令牌的合成方法,实现实时打断
        using var result = await synthesizer.SpeakTextAsync(text, cancellationToken);
        
        if (result.Reason == ResultReason.Canceled)
        {
            var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
            // 处理取消后的逻辑,比如日志或状态重置
        }
    }
    finally
    {
        _isSpeaking = false;
        _talkCts.Dispose();
        _talkCts = null;
    }
}

3. 本地音频旁路与实时匹配过滤

如果SDK内置功能不足以满足需求,可以通过手动处理合成音频流的方式实现精准过滤:

  1. 在合成时,先将音频数据输出到内存流而非直接播放;
  2. 使用第三方音频库播放内存流中的音频;
  3. 在监听模块中,将麦克风输入的音频与当前播放的合成音频进行实时对比,过滤掉完全匹配的片段。

修改后的合成函数示例:

public async Task Talk(string text, CancellationToken cancellationToken)
{
    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
    {
        Console.InputEncoding = System.Text.Encoding.Unicode;
        Console.OutputEncoding = System.Text.Encoding.Unicode;
    }

    Config.SpeechSynthesisVoiceName = "en-AU-CarlyNeural";
    // 不直接绑定扬声器输出,手动处理音频流
    using var synthesizer = new SpeechSynthesizer(Config, null);

    var result = await synthesizer.SpeakTextAsync(text);
    if (result.Reason != ResultReason.SynthesizingAudioCompleted)
    {
        var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
        return;
    }

    // 使用自定义音频播放器播放合成的音频数据(需自行实现或使用第三方库)
    var audioPlayer = new CustomAudioPlayer(result.AudioData);
    await audioPlayer.PlayAsync(cancellationToken);
}

此方法需要额外开发音频对比逻辑,适合对过滤精度要求极高的场景。


内容的提问来源于stack exchange,提问作者Blakey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:22:08