锅铲声保住了,人声却糊了:HarmonyOS 7 AudioCapturer 三种降噪模式怎么选

同一个录音入口被拿来做三件事:录炒菜的锅气声、录菜谱讲解、在嘈杂厨房里记一条语音备忘。旧实现只开一个固定“降噪”开关,结果锅铲和油爆声被抹平,纯人声仍有空调底噪,切换场景后页面还不知道设备最终采用了哪种模式。

HarmonyOS 7(API 26)为 AudioCapturer 增加了三种可区分的降噪模式,以及“查询支持、设置模式、读回当前模式”的完整链路。本文交付一套按录音意图选模式的控制器、两种行为明显不同的核心案例、一个嘈杂环境补充案例,以及可复用验收矩阵。

HarmonyOS 7 AudioCapturer 降噪模式能力查询、场景选择与读回验证

图中的顺序不能颠倒:先查询设备支持的模式,再按录音意图选择,设置后读回确认;请求模式不受支持时走明确降级,而不是假装已经生效。

官方依据与版本边界

官方变更页:https://developer.huawei.com/consumer/cn/doc/doccenter-release-notes/js-apidiff-audiokit-7003

  1. API 26 新增 NoiseReductionMode 枚举。
  2. 枚举包含 FIDELITY = 0、PURE_VOCALS = 1、STANDARD = 2。
  3. AudioCapturer.setNoiseReductionMode(mode) 用于设置降噪模式。
  4. AudioCapturer.getNoiseReductionMode() 用于读取当前模式。
  5. AudioCapturer.getSupportedNoiseReductionModes() 返回当前设备支持的模式集合。

本文依据 HarmonyOS 7.0.0 Release(API 26)Audio Kit 官方变更说明核对接口,并对模式选择、能力交集和降级逻辑做宿主侧 TypeScript 断言。当前本机 SDK 为 API 24,且没有连接 HDC 设备,因此不把示例描述为 API 26 编译通过、设备录音效果或信噪比实测。接入前请在 API 26 工程和目标设备上完成编译、听感盲测、波形与读回验证。

不要把三种模式写成“强、中、弱”

三种模式的差别不是简单的降噪强度滑杆,而是目标不同:

  • FIDELITY:优先保留环境和细节,适合记录烹饪声、器具声等内容本身。
  • PURE_VOCALS:录音目标是清晰人声,环境声不属于交付物。
  • STANDARD:没有单一主角,或当前场景只需要通用的噪声控制。

如果产品只显示“降噪:开”,用户无法理解为什么一段录音保留了锅铲声,另一段却把环境压下去了。更合适的交互是让业务场景决定首选模式,同时把最终读回模式记入录音元数据。

两种接入架构:固定模式与运行时协商

本篇属于 Audio Kit 采集可靠性系列的“模式协商”一环。模式选择发生在录制开始前,属于采集器配置;录音页只持有用户意图和最终读回值,不直接保存“设备一定支持某模式”的假设。

  • 方案 A:在每个录音页面写死一个模式。只有设备范围已锁定、功能单一且支持集合经过逐机验证时才适用。实现短,但换设备或切换录音意图时容易出现请求值和实际值分离;每个页面还会重复处理错误。
  • 方案 B:由 NoiseReductionPolicy.ets 统一把录音意图映射为候选序列,AudioCaptureFactory.ets 在录制前查询设备支持集合、设置、读回,再把 requested/applied/degraded 交给页面。多场景和多设备优先用此方案;代价是多一次能力探测与显式状态管理。

比较的关键不是哪一档“更强”,而是谁拥有设备能力事实。无论采用哪种方案,页面上显示的都必须是读回的 applied,而非按钮刚发出的 requested。

可复用的 ArkTS 模式控制器

下面的控制器接收一个已经创建好的 AudioCapturer。它先查询支持列表,再从候选顺序中选择第一个可用模式;设置后必须读回,避免 UI 只显示“请求成功”。

import { audio } from '@kit.AudioKit'

type RecordingIntent = 'cooking-sound' | 'recipe-narration' | 'kitchen-memo'

type AppliedNoiseReduction = {
  requested: audio.NoiseReductionMode
  applied: audio.NoiseReductionMode
  degraded: boolean
}

function preference(intent: RecordingIntent): audio.NoiseReductionMode[] {
  switch (intent) {
    case 'cooking-sound':
      return [
        audio.NoiseReductionMode.FIDELITY,
        audio.NoiseReductionMode.STANDARD
      ]
    case 'recipe-narration':
      return [
        audio.NoiseReductionMode.PURE_VOCALS,
        audio.NoiseReductionMode.STANDARD
      ]
    case 'kitchen-memo':
      return [
        audio.NoiseReductionMode.STANDARD,
        audio.NoiseReductionMode.PURE_VOCALS
      ]
  }
}

export function applyNoiseReduction(
  capturer: audio.AudioCapturer,
  intent: RecordingIntent
): AppliedNoiseReduction {
  const supported = capturer.getSupportedNoiseReductionModes()
  const candidates = preference(intent)
  const requested = candidates[0]
  const selected = candidates.find(mode => supported.includes(mode))

  if (selected === undefined) {
    throw new Error('No supported noise reduction mode for current intent')
  }

  capturer.setNoiseReductionMode(selected)
  const applied = capturer.getNoiseReductionMode()
  if (applied !== selected) {
    throw new Error("Noise reduction readback mismatch selected=" + selected + " applied=" + applied)
  }
  return {
    requested,
    applied,
    degraded: applied !== requested
  }
}

这里没有用枚举数值比较“大小”。模式是语义选择,不存在数值越大效果越强的官方结论。候选顺序来自业务意图,支持集合来自设备,最终结果来自读回。

案例一:烹饪声教程优先 FIDELITY

煎、炸、翻炒时的声音本身就是内容。若把持续油声和锅铲碰撞都当作噪声,观众会失去判断火候的重要线索。

这个场景的主路径是:

  1. 首选 FIDELITY。
  2. 设备不支持时降级到 STANDARD。
  3. 录音页明确展示“保真”或“标准降噪”,不能仍显示首选值。
  4. 试听样本要同时包含轻微翻炒和瞬时锅铲声,不能只用一段说话声验收。

它的成功标准不是“背景越安静越好”,而是关键烹饪细节没有被错误抹除,同时没有不可接受的底噪。

案例二:菜谱旁白优先 PURE_VOCALS

旁白的交付物是语句清晰度。风扇、抽油烟机和远处器具声不属于内容主体,因此首选 PURE_VOCALS,不支持时再用 STANDARD。

import { hilog } from '@kit.PerformanceAnalysisKit'

const result = applyNoiseReduction(capturer, 'recipe-narration')

AppStorage.setOrCreate('noiseReductionApplied', result.applied)
AppStorage.setOrCreate('noiseReductionDegraded', result.degraded)

hilog.info(
  0x1200,
  'RecipeAudio',
  'requested=%{public}d applied=%{public}d degraded=%{public}s',
  result.requested,
  result.applied,
  String(result.degraded)
)

旁白场景还要增加一个反例:说话人短暂停顿时,系统不能把恢复的环境底噪误判为内容开始;验收应包含有声、停顿、再次说话三段,而不是只听一句完整句子。

补充案例:厨房语音备忘优先 STANDARD

语音备忘既要听清人声,又不值得为每条记录做复杂调参。STANDARD 是更稳妥的默认选择。若设备不支持,再根据支持集合选择 PURE_VOCALS,并把降级状态记录下来。

这个案例与旁白不同:旁白用于正式内容,应该阻止“不受支持且无候选”的录制;备忘是即时工具,即使只能使用可用模式也应允许继续,但必须把实际模式写入元数据,便于后续解释音质差异。

失败与恢复:读回不一致不能继续录制

setNoiseReductionMode 没有返回“听感已符合预期”的证据。设置抛错、支持集合为空或读回值不是刚选中的模式时,录音入口应进入可解释的失败状态:撤回本次模式标签,保留 sessionId、intent、supported、selected、applied 和错误码的结构化日志,再让用户选择通用模式或停止录制。不要在异常后沿用上一段录音的 UI 标签。页面退出时仍须停止并释放采集器,避免旧实例继续占用麦克风。

诊断日志只记录模式元数据,不记录音频内容或个人信息。可用 hilog 将同一个 sessionId 下的“支持集合、请求模式、读回模式、降级原因”关联;上机时分别注入空支持集合、设置异常、读回不一致和页面中途退出,检查是否都能得到明确终态。宿主侧断言只能覆盖选择和状态分支,音质与设备行为仍需 API 26 工程和目标设备验证。

复现步骤

  1. 在 API 26 工程创建 AudioCapturer,保存设备型号、系统版本和采样配置。
  2. 调用 getSupportedNoiseReductionModes(),记录原始返回数组。
  3. 分别选择烹饪声、菜谱旁白和厨房备忘三种意图。
  4. 调用 setNoiseReductionMode() 后立即用 getNoiseReductionMode() 读回。
  5. 每种模式录制同样长度的样本,保留原始文件、模式、时间和场景标签。
  6. 用主观听感、波形和可复现步骤比较,不编造信噪比提升数值。
  7. 构造“首选模式不受支持”的测试替身,确认 UI 和日志都显示实际降级。

验收矩阵

场景/输入首选模式验收信号
烹饪声教程FIDELITY锅铲和油爆细节保留
正式菜谱旁白PURE_VOCALS人声清楚,环境被抑制
厨房语音备忘STANDARD通用降噪,允许明确降级
首选受支持设置首选读回等于 requested
首选不支持选择候选degraded=true
所有候选不支持停止设置返回明确错误
切换录音意图重新设置并读回UI 不保留旧模式
页面销毁停止录制并释放无悬挂采集器

宿主侧选择逻辑测试

当前机器不能运行 API 26 的音频采集,因此先验证“意图、支持集合、候选顺序”的纯逻辑。它不能证明降噪听感,但能发现把不支持模式直接下发或把枚举当强度排序的问题。

type HostMode = 'FIDELITY' | 'PURE_VOCALS' | 'STANDARD'

function pickMode(preferred: HostMode[], supported: HostMode[]): HostMode | undefined {
  return preferred.find(mode => supported.includes(mode))
}

const cases = [
  { preferred: ['FIDELITY', 'STANDARD'], supported: ['STANDARD'], expected: 'STANDARD' },
  { preferred: ['PURE_VOCALS', 'STANDARD'], supported: ['PURE_VOCALS'], expected: 'PURE_VOCALS' },
  { preferred: ['STANDARD', 'PURE_VOCALS'], supported: [], expected: undefined }
] as const

for (const item of cases) {
  const actual = pickMode([...item.preferred], [...item.supported])
  if (actual !== item.expected) {
    throw new Error(`mode selection failed: ${JSON.stringify(item)}`)
  }
}

文件级检查表

  • AudioCaptureFactory.ets 只负责创建和释放采集器。
  • NoiseReductionPolicy.ets 维护意图到候选模式的映射。
  • 设置前查询设备支持列表,不硬编码“所有设备都支持”。
  • 设置后读回实际模式,并把 requested/applied/degraded 写入日志。
  • 三类录音使用不同验收素材,不拿旁白样本替代烹饪声测试。
  • API 26 工程完成编译,目标设备完成录音、听感和生命周期验证。
  • 不用宿主断言冒充设备音质、性能或降噪效果数据。

小结

FIDELITY、PURE_VOCALS 和 STANDARD 不是三档强弱,而是三种录音目标。真正稳妥的接入链路是:先查支持列表,按场景选候选,设置后读回,失败时明确降级。

这样做多了几行状态代码,却能避免最难解释的两类问题:用户以为已经启用某模式,设备实际没有采用;以及为了追求“更安静”,把本来属于内容的声音一起抹掉。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐