Beethoven 是一个音频处理Swift库,提供了一个易于使用的接口来解决音乐信号音调检测的古老问题。您可以在 维基百科 上阅读更多关于这个主题的内容。
基本工作流程是从输入/输出源获取音频缓冲区,将其转换为可处理格式,并应用一种音调估计算法以找到基频。对于最终用户来说,这仅关系到选择估计算法和实现代理方法。
Beethoven 被设计成灵活、可定制且高度可扩展。
库的主要目的是收集Swift实现的多种时域和频域算法,针对单音调音高提取,不同速率的精度和速度,以覆盖尽可能多的音调检测场景、乐器和人类声音。当前的实现可能也不完美,并且显然还有改进的空间。这意味着 贡献 非常重要,并且备受欢迎!
目录
关键特性
- 使用
AVAudioEngine
和音频节点进行音频信号跟踪。 - 通过一个可用的 "转换器" 预处理音频缓冲区。
- 音调估计。
使用
配置
使用Config
结构体配置缓冲区和估计策略,该结构体用于初始化PitchEngine
。对于需要从设备输出跟踪信号的情况,存在一个名为audioUrl
的参数,其目的是指向您的音频文件的URL。
// Creates a configuration for the input signal tracking (by default).
let config = Config(
bufferSize: 4096,
estimationStrategy: .yin
)
// Creates a configuration for the output signal tracking.
let config = Config(
bufferSize: 4096,
estimationStrategy: .yin,
audioUrl: URL
)
Config
也可以不带有任何参数进行实例化
// Input signal tracking with YIN algorithm.
let config = Config()
音高引擎
PitchEngine
是您将要使用的用于查找音高的主要类。它可以使用配置和委托实例化。
let pitchEngine = PitchEngine(config: config, delegate: pitchEngineDelegate)
这两个参数都是可选的,默认使用标准配置,并且可以在稍后设置delegate
let pitchEngine = PitchEngine()
pitchEngine.delegate = pitchEngineDelegate
PitchEngine
使用PitchEngineDelegate
在开始音高检测时报告结果或错误
func pitchEngine(_ pitchEngine: PitchEngine, didReceivePitch pitch: Pitch)
func pitchEngine(_ pitchEngine: PitchEngine, didReceiveError error: Error)
func pitchEngineWentBelowLevelThreshold(_ pitchEngine: PitchEngine)
要开始或停止音高跟踪过程,只需使用相应的PitchEngine
方法即可
pitchEngine.start()
pitchEngine.stop()
信号跟踪
存在2个信号跟踪类
InputSignalTracker
使用AVAudioInputNode
从实时录音输入(麦克风)中获取音频缓冲区。OutputSignalTracker
使用AVAudioOutputNode
和AVAudioFile
播放音频文件并从播放输出获取音频缓冲区。
变换
变换是音频处理的第一步,其中将 AVAudioPCMBuffer
对象转换为浮点数数组。它也是一个实现不同种类优化的地方。然后数组会保存在内部 Buffer
结构的 elements
属性中,该结构还包含可选的 realElements
和 imagElements
属性,这些属性在后续计算中可能非常有用。
目前有3种变换方式
通过实现 Transformer
协议,可以轻松添加新的变换策略
public protocol Transformer {
func transform(buffer: AVAudioPCMBuffer) -> Buffer
}
估计
音高检测算法(PDA)是一种用于估计音高或基频的算法。音高是一种心理声学现象;在选择 algorithms 时需要考虑输入源、允许的误差率和所需性能。
已实现的算法列表
maxValue
- 音频缓冲区中最大值的索引,用作峰值quadradic
- 对光谱峰值的二次插值barycentric
- 重心的纠正quinnsFirst
- Quinn的第一次估计quinnsSecond
- Quinn的第二次估计jains
- Jain的方法hps
- 谐波产品谱yin
- YIN
通过实现 Estimator
或 LocationEstimator
协议,可以轻松添加新的估计算法
protocol Estimator {
var transformer: Transformer { get }
func estimateFrequency(sampleRate: Float, buffer: Buffer) throws -> Float
func estimateFrequency(sampleRate: Float, location: Int, bufferCount: Int) -> Float
}
protocol LocationEstimator: Estimator {
func estimateLocation(buffer: Buffer) throws -> Int
}
然后应将其添加到 EstimationStrategy
枚举类型,并在 EstimationFactory
结构的 create
方法中加入。通常,缓冲区变换应在单独的结构或类中执行,以保持代码库更加整洁和可读。
错误处理
由于一些困难,比如攻击瞬态、低频和高频等,音高检测不是一个简单任务。同时,它是一个实时处理,所以我们无法完全避免各种错误。为此,有一系列的错误类型需要妥善处理。
信号跟踪错误
public enum InputSignalTrackerError: Error {
case inputNodeMissing
}
录制权限错误
PitchEngine
在启动时请求 AVAudioSessionRecordPermission
,但如果权限被拒绝,它会产生相应的错误
public enum PitchEngineError: Error {
case recordPermissionDenied
}
音高估计错误
在音高估计过程中可能会发生一些错误
public enum EstimationError: Error {
case emptyBuffer
case unknownMaxIndex
case unknownLocation
case unknownFrequency
}
音高检测的具体细节
目前,贝多芬仅在单声录音上执行音高检测。
基于Stackoverflow 答案
音高检测很大程度上取决于您想要处理的音乐内容。从单声录音中提取音高(即单个乐器或声音)与从多声部混合中提取单个乐器(例如,从多声部录音中提取旋律)不同。
对于单声部音高提取,有各种算法可以在时域和频域中实现(维基百科)。
然而,如果您想要从多声部材料中提取旋律,这两者都不会很好地工作。多声部音乐的旋律提取仍然是一个研究问题。
示例
查看调音器示例,了解如何在实际场景中使用贝多芬进行音调调整。它使用YIN
估计算法,由@glaurent采用,在电吉他和原声吉他的弦音高检测中似乎相当准确。
安装
贝多芬可通过CocoaPods获取。要安装它,只需将以下行添加到Podfile中
pod 'Beethoven'
贝多芬也可通过Carthage获取。要安装,只需将以下内容写入Cartfile中
github "vadymmarkov/Beethoven"
贝多芬也可手动安装。只需下载并将源
文件夹拖放到您的项目中。
组件
贝多芬使用Pitchy库从指定的频率中获取音乐音高,包括音符、音阶和偏差。
作者
Вадим Марков,[email protected]
贡献
有关更多信息,请查看CONTRIBUTING文件。
许可
Beethoven遵循MIT许可。有关更多信息,请查看LICENSE文件。