发现 SoundAnalysis 中的内置声音分类
Discover built-in sound classification in SoundAnalysis
2021年6月10日
一句话判断
SoundAnalysis 框架现在内置了一个预训练的声音分类器——不需要你自己训练模型,300+ 种日常声音的识别开箱即用,延迟低到可以实时运行。
这场 Session 讲了什么
SoundAnalysis 框架在 iOS 15 中获得了一个重大更新:内置的声音分类器(Built-in Sound Classifier)。之前要用 SoundAnalysis,你需要自己用 Create ML 训练一个模型,然后导入到 app 中。现在 Apple 提供了一个预训练好的模型,可以直接识别超过 300 种日常声音。
覆盖的声音类别非常广:人声(说话、笑声、哭泣)、环境音(雨声、风声、雷声)、动物叫声(狗叫、猫叫、鸟鸣)、乐器(钢琴、吉他、鼓)、交通工具(汽车、火车、飞机)、警报声(火警、警笛、闹钟)等。
使用方式极其简单。你只需要创建一个 SNAudioStreamAnalyzer,然后添加一个 SNClassifySoundRequest,设置好回调就能开始接收分类结果。整个流程不需要一行 Core ML 代码。
值得深挖的点
实时分析的能耗与精度平衡
内置分类器支持两种精度模式:SNClassifySoundRequest 默认使用标准精度,在 Apple Neural Engine 上运行,功耗非常低。如果你需要更高的精度(比如区分不同类型的乐器),可以使用自定义 Core ML 模型替代内置分类器。
实时分析的推荐缓冲区大小是 1.5 秒,每次分析间隔 0.5 秒。这意味着延迟大约 1.5 秒,对于大多数应用场景足够了。但如果你需要更低延迟(比如节拍检测),需要自定义分析管线。
隐私敏感的使用场景
声音分类能力很强,但也意味着隐私风险。Session 特别强调:你的 app 在使用麦克风录音进行声音分类之前,必须获得用户的明确授权(AVAudioApplication.shared.requestRecordPermission)。如果你的 app 在后台持续监听声音,需要在 Info.plist 中声明 audio 后台模式,并且向用户解释为什么需要这个权限。
代码片段
// 使用内置声音分类器进行实时识别
import SoundAnalysis
class SoundClassifier: NSObject, SNResultsObserving {
let audioEngine = AVAudioEngine()
var analyzer: SNAudioStreamAnalyzer?
func startListening() {
let audioSession = AVAudioSession.sharedInstance()
try? audioSession.setCategory(.record, mode: .measurement)
try? audioSession.setActive(true)
let inputNode = audioEngine.inputNode
let format = inputNode.outputFormat(forBus: 0)
analyzer = SNAudioStreamAnalyzer(format: format)
// 创建内置分类请求
let request = try! SNClassifySoundRequest()
request.windowDuration = CMTime(seconds: 1.5, preferredTimescale: 44100)
request.overlapFactor = 0.5 // 50% 重叠,提高检测灵敏度
try! analyzer?.add(request, withObserver: self)
// 安装音频处理节点
inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) {
[weak self] buffer, time in
self?.analyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
}
try? audioEngine.start()
}
// SNResultsObserving 回调
func request(_ request: SNRequest, didProduce result: SNResult) {
guard let classificationResult = result as? SNClassificationResult else { return }
// 获取置信度最高的三个分类
let topResults = classificationResult.classifications
.filter { $0.confidence > 0.5 }
.prefix(3)
for classification in topResults {
print("识别到: \(classification.identifier), 置信度: \(classification.confidence)")
}
}
}
// 分析音频文件中的声音(非实时)
func classifyAudioFile(url: URL) {
let request = try! SNClassifySoundRequest()
SNClassifySoundRequest.classifySound(
at: url,
with: request
) { result in
switch result {
case .success(let classificationResult):
// 打印所有时间段的分类结果
for interval in classificationResult.classificationsByTime {
print("时间段: \(interval.timeRange)")
for classification in interval.classifications.prefix(3) {
print(" \(classification.identifier): \(classification.confidence)")
}
}
case .failure(let error):
print("分析失败: \(error)")
}
}
}
最佳实践
不要在后台持续运行声音分类器而不告诉用户。如果你的 app 需要后台监听(比如婴儿哭声检测),在 UI 上必须显示一个持续的指示器,告诉用户麦克风正在工作。Apple 的审核对这类 app 的隐私审查非常严格。
对于需要识别特定声音的场景(比如只关心「警笛声」),在回调中过滤 confidence > 0.6 的结果,避免误报。内置分类器在安静环境下准确率很高,但在嘈杂环境中会有明显的误分类。
还有什么值得关注
- 内置分类器支持离线运行,不需要网络连接。
- 如果内置的 300+ 种类别不满足需求,可以用 Create ML 训练自定义声音分类模型,然后通过
SNClassifySoundRequest(mlModel:)加载。 SNResultsObserving的回调不在主线程,UI 更新需要手动DispatchQueue.main.async。