> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/audio-api.md).

# 音频 API

`ConvaiAudio` 是音频外观门面 `ConvaiManager`，提供对麦克风采集、按角色远程音频输出以及音频播放解锁的脚本化控制。它清晰地分离了关注点：麦克风输入（你发送给 Convai 的内容）、角色音频输出（你从角色那里听到的内容）以及 WebGL 音频播放闸门。

**访问：** `ConvaiManager.ActiveManager.Audio`

```csharp
var audio = ConvaiManager.ActiveManager?.Audio;
if (audio == null) return; // 管理器尚未引导完成
```

***

### 麦克风控制

#### 属性

| 属性                       | 类型     | 描述                                  |
| ------------------------ | ------ | ----------------------------------- |
| `IsMicMuted`             | `bool` | 当麦克风已静音且未向 Convai 发送音频时为 True       |
| `RequiresUserGesture`    | `bool` | 当音频播放因等待用户交互而被阻止时为 True（仅 WebGL）    |
| `IsAudioPlaybackActive`  | `bool` | 当音频播放当前处于活动且已解锁时为 True              |
| `CanEnableAudioPlayback` | `bool` | 当 `EnableAudioPlayback()` 可被调用以解锁音频 |

#### 方法

| 方法                                                                             | 返回                       | 描述                  |
| ------------------------------------------------------------------------------ | ------------------------ | ------------------- |
| `SetMicMuted(bool muted)`                                                      | `void`                   | 显式设置麦克风静音状态         |
| `ToggleMicMuted()`                                                             | `bool`                   | 切换麦克风静音状态。返回新的静音状态。 |
| `StartListeningAsync(int microphoneIndex = 0, CancellationToken ct = default)` | `IConvaiOperation<Unit>` | 在指定的设备索引上开始麦克风采集    |
| `StopListeningAsync(CancellationToken ct = default)`                           | `IConvaiOperation<Unit>` | 停止麦克风采集             |

{% hint style="info" %}
该 `microphoneIndex` 参数在 `StartListeningAsync` 对应于 `返回的列表中的一个索引`. 使用 `ConvaiManager.ActiveManager.TryGetMicrophoneDeviceService(out var svc)` 在选择索引之前按名称枚举设备。索引 `0` 使用系统默认麦克风。
{% endhint %}

#### 事件

| 事件                 | 签名             | 触发时机                              |
| ------------------ | -------------- | --------------------------------- |
| `OnMicMuteChanged` | `Action<bool>` | 麦克风静音状态发生变化。参数：新的静音状态（true = 静音）。 |

```csharp
using Convai.Runtime.Facades;
using UnityEngine;

public class MicMuteIndicator : MonoBehaviour
{
    [SerializeField] private GameObject _muteIcon;

    private void OnEnable()
    {
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio == null) return;
        audio.OnMicMuteChanged += OnMuteChanged;
        _muteIcon.SetActive(audio.IsMicMuted);
    }

    private void OnDisable()
    {
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio != null)
            audio.OnMicMuteChanged -= OnMuteChanged;
    }

    private void OnMuteChanged(bool isMuted) => _muteIcon.SetActive(isMuted);
}
```

***

### 按角色音频控制

这些方法控制特定角色的音频输出是否在本地播放。使用来自 `ConvaiCharacter.CharacterId` 或 `ConvaiManager.ActiveManager.Characters`.

| 方法                                                        | 返回     | 描述                                |
| --------------------------------------------------------- | ------ | --------------------------------- |
| `SetCharacterMuted(string characterId, bool muted)`       | `bool` | 设置特定角色的静音状态。返回 `true` 如果更改已应用。    |
| `MuteCharacter(string characterId)`                       | `bool` | 使特定角色的音频输出静音。返回 `true` 如果已应用。     |
| `UnmuteCharacter(string characterId)`                     | `bool` | 取消特定角色音频输出的静音。返回 `true` 如果已应用。    |
| `IsCharacterMuted(string characterId)`                    | `bool` | 返回 `true` 如果该角色的音频输出当前处于静音状态。     |
| `SetRemoteAudioEnabled(string characterId, bool enabled)` | `bool` | 启用或禁用某个角色的远程音频输出。返回 `true` 如果已应用。 |
| `IsRemoteAudioEnabled(string characterId)`                | `bool` | 返回 `true` 如果该角色的远程音频输出已启用。        |

{% hint style="info" %}
**静音 vs. 启用远程音频：** 这两者是独立的控制。启用/禁用远程音频控制 SDK 是否 საერთოდ为该角色流式输出音频。静音控制本地接收到的音频是否通过你的音频输出设备播放。禁用远程音频可节省带宽；静音只是本地范围的音量控制。
{% endhint %}

#### 事件

| 事件                            | 签名                     | 触发时机                                        |
| ----------------------------- | ---------------------- | ------------------------------------------- |
| `OnRemoteAudioEnabledChanged` | `Action<string, bool>` | 某个角色的远程音频启用状态发生变化。参数：characterId，isEnabled。 |

***

### 音频播放 — WebGL

在 WebGL 上，浏览器会阻止音频播放，直到用户与页面交互为止。SDK 会在这一要求之下对音频输出进行门控。

| 成员                       | 类型     | 描述                                  |
| ------------------------ | ------ | ----------------------------------- |
| `RequiresUserGesture`    | `bool` | 当音频因等待用户交互而被阻止时为 True               |
| `CanEnableAudioPlayback` | `bool` | 当 `EnableAudioPlayback()` 可被调用      |
| `IsAudioPlaybackActive`  | `bool` | 当音频播放已解锁并处于活动状态时为 True              |
| `EnableAudioPlayback()`  | `void` | 解锁音频播放。 **必须在用户手势处理程序内调用** （按钮点击等）。 |

{% hint style="danger" %}
在 WebGL 上，在 `EnableAudioPlayback()` 用户手势处理程序之外调用 `Start()` 或 `OnEnable()`）不会产生任何效果——浏览器会阻止解锁。将其绑定到 UI 按钮的 `onClick` 事件，或在 `OnPointerClick`.
{% endhint %}

```csharp
using Convai.Runtime.Facades;
using UnityEngine;
using UnityEngine.UI;

public class WebGLStartButton : MonoBehaviour
{
    [SerializeField] private Button     _startButton;
    [SerializeField] private GameObject _startPanel;

    private void OnEnable()  => _startButton.onClick.AddListener(OnStartClicked);
    private void OnDisable() => _startButton.onClick.RemoveListener(OnStartClicked);

    private void OnStartClicked()
    {
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio == null) return;

        // 在用户手势中解锁音频
        if (audio.CanEnableAudioPlayback)
            audio.EnableAudioPlayback();

        // 连接并开始监听
        ConvaiManager.ActiveManager.EnableAudioAndStartListening();

        _startPanel.SetActive(false);
    }
}
```

***

### 使用示例

#### 示例 1 — 训练模拟 HUD 中的静音按钮

一款军事训练模拟在 HUD 中提供了一个按键式麦克风静音功能，因此受训者可以在向观察员发言前将自己静音，而无需断开连接。

```csharp
using Convai.Runtime.Facades;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class MuteButton : MonoBehaviour
{
    [SerializeField] private Button   _button;
    [SerializeField] private TMP_Text _label;

    private void OnEnable()
    {
        _button.onClick.AddListener(OnClicked);
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio != null)
        {
            audio.OnMicMuteChanged += RefreshLabel;
            RefreshLabel(audio.IsMicMuted);
        }
    }

    private void OnDisable()
    {
        _button.onClick.RemoveListener(OnClicked);
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio != null)
            audio.OnMicMuteChanged -= RefreshLabel;
    }

    private void OnClicked() => ConvaiManager.ActiveManager?.Audio?.ToggleMicMuted();

    private void RefreshLabel(bool isMuted) =>
        _label.text = isMuted ? "取消静音" : "静音";
}
```

#### 示例 2 — 多 NPC 评估场景中的按角色音频切换

一个企业入职培训模拟有两名 AI 讲师。评估管理脚本会在主讲师的评估环节中使副讲师静音，然后再恢复两者。

```csharp
using Convai.Runtime.Components;
using Convai.Runtime.Facades;
using System.Collections;
using UnityEngine;

public class AssessmentAudioManager : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _primaryInstructor;
    [SerializeField] private ConvaiCharacter _secondaryInstructor;

    public void BeginPrimaryEvaluation()
    {
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio == null) return;

        // 让副讲师静音，这样只会听到主讲师
        audio.MuteCharacter(_secondaryInstructor.CharacterId);
        audio.UnmuteCharacter(_primaryInstructor.CharacterId);
    }

    public void RestoreBothInstructors()
    {
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio == null) return;

        audio.UnmuteCharacter(_primaryInstructor.CharacterId);
        audio.UnmuteCharacter(_secondaryInstructor.CharacterId);
    }
}
```

#### 示例 3 — 连接前选择麦克风设备

一个交互式体验允许用户在会话开始前从下拉菜单中选择自己偏好的麦克风，然后在所选设备上开始监听。

```csharp
using Convai.Runtime.Facades;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class MicrophoneSelector : MonoBehaviour
{
    [SerializeField] private TMP_Dropdown _dropdown;

    private List<string> _deviceNames = new();

    private void Start()
    {
        var manager = ConvaiManager.ActiveManager;
        if (manager == null || !manager.TryGetMicrophoneDeviceService(out var svc)) return;

        _deviceNames.Clear();
        _dropdown.ClearOptions();

        foreach (var device in svc.GetAvailableDevices())
        {
            _deviceNames.Add(device.Name);
            _dropdown.options.Add(new TMP_Dropdown.OptionData(device.Name));
        }

        _dropdown.RefreshShownValue();
    }

    public async void OnConfirmDevice()
    {
        int index = _dropdown.value;
        var audio = ConvaiManager.ActiveManager?.Audio;
        if (audio == null) return;

        await audio.StartListeningAsync(index, destroyCancellationToken);
        Debug.Log($"正在监听：{_deviceNames[index]}");
    }
}
```

***

### 故障排查

| 症状                                     | 可能原因                                           | 修复方法                                                                                                          |
| -------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| WebGL 上没有音频输出                          | `EnableAudioPlayback()` 未在用户手势中调用              | 绑定 `EnableAudioPlayback()` 到按钮的 `onClick`；不要在 `Start()` 或 `Awake()`                                           |
| `ToggleMicMuted()` 返回 `true` 但角色仍能听到玩家 | `IsMicMuted` 控制的是 SDK 的静音——请确认角色会话已连接且音频管线正在运行 | 检查 `IsSessionConnected` 在当前激活的角色上；静音只会在会话中生效                                                                  |
| `MuteCharacter()` 返回 `false`           | 未识别角色 ID——该角色可能尚未注册                            | 验证 ID 是否匹配 `ConvaiCharacter.CharacterId`；该角色必须位于 `ConvaiManager.Characters`                                   |
| `StartListeningAsync` 在移动端失败           | 平台麦克风权限未授予                                     | 使用 `TryGetPermissionService` 在调用之前请求麦克风权限 `StartListeningAsync`                                               |
| `IsRemoteAudioEnabled` 返回 `false` 在连接后 | `EnableRemoteAudioOnStart` 在角色上的是 `false`      | 设置 `ConvaiCharacter.EnableRemoteAudioOnStart = true` 在 Inspector 中，或调用 `character.EnableRemoteAudio()` 在会话开始后 |

***

### 下一步

有关按角色音频事件和会话控制，请参阅 [角色与玩家 API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/character-and-player-api.md)。有关 WebGL 特定的音频要求和限制，请参阅 [WebGL 平台指南](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/platform-guides/webgl.md)。有关连接和会话控制，请参阅 [ConvaiManager API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/convaimanager-api.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/audio-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
