在背景嘈杂的环境中(如办公室、咖啡馆、多人车内),环境噪声或背景人声等会导致 AI 误响应或被误打断。您可以通过调整音量增益、开启 AI 降噪或声纹降噪等方式抑制噪音,以提升语音识别的准确率。
选择降噪方式
降噪方式 | 适用场景 |
|---|
调整音量增益 | 基础音量控制,抑制轻微噪声,适用于相对安静环境下的基础优化。 |
AI 降噪 | 滤除非人声环境噪声,如空调、风扇、键盘声。 |
声纹降噪 | 滤除背景人声干扰。精准识别目标人声,并抑制旁人说话声,适用于多人交谈的嘈杂环境,如办公室、公共场所。 |
远场人声抑制 | 滤除背景中的远场人声干扰。 远场人声,指在有背景噪声的环境下,声源(用户)距离麦克风 3-5 米以上时所产生的人声,如会议室、车载场景等。
|
调整音量增益
通过调整发送到服务端的音频音量大小来影响信噪比。增益值越低,采集音量越低,有助于滤除部分环境噪音。适用于用户环境相对安静,只有轻微、不明显的背景噪音。
配置方法:
调用 StartVoiceChat 接口时,设置 ASRConfig.VolumeGain。
注意:过度降低 VolumeGain 可能会导致用户的正常语音音量过小,反而影响识别。
"ASRConfig": {
"VolumeGain": 0.3 // 推荐值,默认值为 1.0
}
AI 降噪
当环境噪声较为明显时(如空调、风扇、键盘声),推荐使用 AI 降噪。支持通过服务端或客户端实现。
注意
- 服务端和客户端不建议同时开启 AI 降噪,以避免过度处理可能带来的音质损伤。
- 声纹降噪和 AI 降噪不能同时使用。
方式一:服务端 AI 降噪
在服务端对音频进行智能降噪处理,适用于不具备或不便开启端上 AI 降噪能力的终端(例如:物联网、智能硬件设备)。
在调用 StartVoiceChat 接口时,设置 AgentConfig.AnsMode。
"AgentConfig": {
"AnsMode": 2 // 根据噪声类型选择模式
}
模式选择
1:轻度降噪。适用于有微弱、平稳噪声的环境。2:中度降噪。适用于最常见的持续性噪声,如空调、风扇声。3:重度降噪。适用于有嘈杂、非平稳噪声的环境,如键盘敲击声、物体碰撞声。
方式二:客户端 AI 降噪
直接使用 ByteRTC SDK 的客户端降噪 API。降噪算法受调用 joinRoom 时设置的房间模式影响。详细说明参见:
声纹降噪(公测中)
基于声纹信息,从混合人声中保留目标说话者的语音,并抑制他人声音,让 AI “听得更清”。支持使用预注册声纹或自动提取声纹。
使用限制
- 仅支持 1 个目标用户的声音。
- 该功能目前为限时免费公测阶段,目前针对远场人声屏蔽效果较好。
- 开启声纹降噪时,建议不要开启 AI 降噪,以免影响声纹降噪的效果。
模式 1:使用预注册声纹
使用预先为用户注册好的声纹 ID 进行降噪。适用于用户身份固定(如个人设备),且对降噪精度和身份识别要求极高的场景。
实现方法:
- 调用接口 RegisterVoicePrint 为目标用户注册声纹(对应字段
VoicePrintId)。
注意:注册声纹使用的音频,其采集设备需要和用户真实通话时使用的音频采集设备保持一致,以保证最佳效果。
- 调用
StartVoiceChat 接口时,配置 AgentConfig.VoicePrint。"VoicePrint": {
"Mode": 1, // 开启声纹降噪
"IdList": ["vp_id_123"], // 传入已注册的声纹 ID
"EnableSV": true // 开启说话人验证,防止非目标用户误触发
}
模式 2:实时注册声纹
无需用户提前注册,系统会在通话开始后自动学习用户的声音特征。适用于用户不固定或首次体验的场景。
实现方法:
调用 StartVoiceChat 接口时,按照以下示例配置 AgentConfig.VoicePrint。
"VoicePrint": {
"Mode": 1, // 必填,开启声纹降噪
"EnableSV": true, // 可选,开启说话人验证,防止非目标用户误触发
"VoiceDuration": 20 // 可选,设置注册声纹所需的有效语音时长
// "IdList" 无需传入
}
等待声纹生成。
用户在通话过程中的累计有效说话时长达到 VoiceDuration 指定的时长后,声纹才能成功生成并开始生效。
获取实时声纹注册状态
在使用 “实时声纹注册” 模式进行声纹降噪时,你可以在客户端监听回调,实时获取声纹的注册进度及生效状态。
- 调用
StartVoiceChat 接口时,确保已开启声纹降噪(AgentConfig.VoicePrint.Mode 为 1 且不传 IdList)。 - 在客户端监听 RTC SDK 回调 onRoomBinaryMessageReceived,并解析收到的二进制消息。
- 不同端回调名称可能有差异,具体请参见客户端 API 参考。
- 其中,C 语言端接口名称为
on_message_received。
当实时声纹注册发生变化时,您将通过上述回调收到一个二进制的 message(stat 类型)。您需要对其进行解析,以获取具体的状态信息。
消息格式

参数名 | 类型 | 描述 |
|---|
magic number | binary | 消息格式标识,固定为 stat。 |
length | binary | 消息体长度,单位为 bytes,采用大端序(Big-endian)存储。 |
body | binary | 状态信息,JSON 字符串格式。详见下方说明。 |
body 字段说明
字段名 | 类型 | 描述 |
|---|
event | String | 事件类型,固定为 VoicePrintStatus。 |
status | String | 实时声纹注册状态,取值如下: Disabled:实时声纹功能未开启。Registering:声纹注册中。Active:声纹已启用。表示声纹特征已提取完成,声纹降噪正式生效。
|
解析示例
/**
* @brief 解析并处理声纹状态消息 (stat)
*/
function handleVoicePrintStatus(message: ArrayBuffer) {
if (!message || message.byteLength < 8) return;
const dataView = new DataView(message);
const magic = new TextDecoder().decode(message.slice(0, 4));
if (magic === 'stat') {
const length = dataView.getUint32(4, false); // false for big-endian
if (message.byteLength < 8 + length) return; // 健壮性:检查包完整性
const payload = new TextDecoder().decode(message.slice(8, 8 + length));
let data: any;
try {
data = JSON.parse(payload);
} catch {
console.error('Failed to parse stat message payload as JSON.');
return; // 健壮性:JSON 解析失败则退出
}
if (data.event === 'VoicePrintStatus') {
console.log('当前声纹状态:', data.status); // 'Disabled', 'Registering', or 'Active'
}
}
}
import org.json.JSONObject;
import org.json.JSONException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
/**
* @brief 解析并处理声纹状态消息 (stat)
* @param buffer 从 onRoomBinaryMessageReceived 回调中获取的 ByteBuffer
*/
public static void handleVoicePrintStatus(ByteBuffer buffer) {
if (buffer == null || buffer.remaining() < 8) {
return;
}
// 关键:为保证能重复读取,先记录当前位置
int initialPosition = buffer.position();
buffer.order(ByteOrder.BIG_ENDIAN);
byte[] magicBytes = new byte[4];
buffer.get(magicBytes);
String magic = new String(magicBytes, StandardCharsets.UTF_8);
if ("stat".equals(magic)) {
int length = buffer.getInt();
if (length < 0 || buffer.remaining() < length) {
buffer.position(initialPosition); // 恢复位置
return; // 健壮性:检查包完整性
}
byte[] jsonBytes = new byte[length];
buffer.get(jsonBytes);
String jsonContent = new String(jsonBytes, StandardCharsets.UTF_8);
try {
JSONObject jsonObject = new JSONObject(jsonContent);
if ("VoicePrintStatus".equals(jsonObject.optString("event"))) {
System.out.println("当前声纹状态: " + jsonObject.optString("status"));
}
} catch (JSONException e) {
// 健壮性:JSON 解析失败
System.err.println("Failed to parse stat message payload as JSON.");
}
}
// 恢复 buffer 的位置,以防其他代码需要处理此消息
buffer.position(initialPosition);
}
#include <iostream>
#include <string>
#include <vector>
#include <arpa/inet.h> // for ntohl
#include "nlohmann/json.hpp"
/**
* @brief 解析并处理声纹状态消息 (stat)
* @param message 从 onRoomBinaryMessageReceived 回调中获取的二进制数据指针
* @param size 数据大小
*/
void handleVoicePrintStatus(const uint8_t* message, int size) {
if (message == nullptr || size < 8) {
return;
}
// 1. 校验 magic number "stat" (0x73746174)
uint32_t magic = ntohl(*reinterpret_cast<const uint32_t*>(message));
if (magic != 0x73746174U) {
return;
}
// 2. 获取长度并检查边界
uint32_t length = ntohl(*reinterpret_cast<const uint32_t*>(message + 4));
if (static_cast<uint32_t>(size) < 8 + length) {
return; // 健壮性:包不完整
}
// 3. 提取 JSON 字符串并安全解析
std::string json_str(reinterpret_cast<const char*>(message + 8), length);
nlohmann::json data = nlohmann::json::parse(json_str, nullptr, false);
if (data.is_discarded()) {
std::cerr << "Failed to parse stat message payload as JSON." << std::endl;
return; // 健壮性:JSON 解析失败
}
if (data.value("event", "") == "VoicePrintStatus") {
std::cout << "当前声纹状态: " << data.value("status", "N/A") << std::endl;
}
}
/**
* @brief 解析并处理声纹状态消息 (stat)
* @param data 从 onRoomBinaryMessageReceived 回调中获取的 NSData 对象
*/
- (void)handleVoicePrintStatus:(NSData *)data {
if (data == nil || data.length < 8) {
return;
}
const uint8_t *bytes = data.bytes;
// 1. 检查 Magic Number "stat" (0x73746174)
// 使用 C-style 移位操作,注意字节序
uint32_t magic = (uint32_t)OSReadBigInt32(bytes, 0);
if (magic != 0x73746174) {
return;
}
// 2. 获取长度并检查边界
uint32_t length = (uint32_t)OSReadBigInt32(bytes, 4);
if (data.length < 8 + length) {
return; // 健壮性:包不完整
}
// 3. 解析 JSON Body
NSData *bodyData = [data subdataWithRange:NSMakeRange(8, length)];
NSError *error = nil;
id json = [NSJSONSerialization JSONObjectWithData:bodyData options:0 error:&error];
if (error || ![json isKindOfClass:[NSDictionary class]]) {
NSLog(@"Error: Failed to parse stat message payload as JSON.");
return; // 健壮性:JSON 解析失败
}
if ([json[@"event"] isEqualToString:@"VoicePrintStatus"]) {
NSLog(@"当前声纹状态: %@", json[@"status"]);
}
}
远场人声抑制
通过识别音频能量差异,精准过滤背景中(通常 3-5 米外)的低能量干扰人声,同时保留近场目标语音。该功能有效解决了多人环境下 AI 误识别、被误打断或因背景音干扰导致的判停延迟问题。适用于手机、耳机及车载等近距离收音场景。
注意
- 该功能仅在使用火山语音识别大模型或火山声音复刻大模型时生效。
- 由于不同硬件设备的收音特性差异较大,必须根据实际设备类型和应用场景选择合适的
Level 或 Threshold 。若参数设置不当(如抑制过强),可能会导致正常用户的声音也被抑制,影响收音质量。建议根据实际业务场景进行细致调试。
调用 StartVoiceChat 时,通过 ASRConfig.FarfieldConfig 字段配置。建议优先使用 Level 进行自适应调节。
"FarfieldConfig": {
"Enable": true, // 是否开启远场人声抑制
// 抑制等级:可选 High, Medium, Low。系统将根据环境底噪自适应调整
// 注意:若下方的 Threshold 取值 > 0,则本参数失效
"Level": "Medium",
// 能量阈值:取值 0-127。数值越小,过滤的能量越大(127接近静音)
// 默认 0,表示不启用手动阈值,由上面的 Level 参数控制
"Threshold": 0,
// 音源是否固定:
// - true:音源与麦克风位置固定(如佩戴耳机、听筒模式)
// - false:音源位置不固定(如使用扬声器)
"FixedSource": false
}