在自定义 AUAudioUnit 中查找爆裂声和爆裂声的原因
Posted
技术标签:
【中文标题】在自定义 AUAudioUnit 中查找爆裂声和爆裂声的原因【英文标题】:Finding cause of crackle and pops in custom AUAudioUnit 【发布时间】:2020-02-11 08:53:39 【问题描述】:我正在尝试学习一些核心音频,并开始尝试制作一个基本的音频单元,它只生成一个可调节频率的正弦波。我现在遇到的问题是,每当我调整频率时,我都会有严重的噼啪声和爆裂声,我不知道在哪里可以找到导致这种情况的原因。
我什至无法判断这是否只是因为在频率之间移动时我需要进行一些平滑处理,尽管到目前为止我还没有在任何示例中看到这一点。
无论如何,这是我的音频单元的实现:
#import "VCOUnit.h"
@interface VCOUnit ()
AUParameterTree *_parameterTree;
AUAudioUnitBusArray *_outputBusses;
AVAudioPCMBuffer *_outputBuffer;
@end
@implementation VCOUnit
- (instancetype)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options error:(NSError *__autoreleasing _Nullable *)outError
self = [super initWithComponentDescription:componentDescription options:options error:outError];
return self;
- (AUParameterTree *)parameterTree
if (!_parameterTree)
AUParameter *frequency = [AUParameterTree createParameterWithIdentifier:@"frequency"
name:@"Frequency"
address:0
min:10
max:500
unit:kAudioUnitParameterUnit_Hertz
unitName:nil
flags:0
valueStrings:nil
dependentParameters:nil];
frequency.value = 220.0;
_parameterTree = [AUParameterTree createTreeWithChildren:@[frequency]];
return _parameterTree;
- (AUAudioUnitBusArray *)outputBusses
if (!_outputBusses)
AVAudioFormat *format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:1];
AUAudioUnitBus *bus = [[AUAudioUnitBus alloc] initWithFormat:format error:nil];
_outputBusses = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[bus]];
return _outputBusses;
- (BOOL)allocateRenderResourcesAndReturnError:(NSError *__autoreleasing _Nullable *)outError
if (![super allocateRenderResourcesAndReturnError:outError])
return NO;
AVAudioFormat *format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:1];
_outputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:1024];
return YES;
- (AUInternalRenderBlock)internalRenderBlock
AUParameter *frequencyParam = [self.parameterTree parameterWithAddress:0];
float deltaTime = 1.0 / 44100.0;
__block float time = 0;
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList *outputData, const AURenderEvent *realtimeEventListHead, AURenderPullInputBlock pullInputBlock)
AudioBufferList *abl = outputData;
for (int frame = 0; frame < frameCount; frame++)
float value = sin(2.0 * M_PI * frequencyParam.value * time);
for (int i = 0; i < abl->mNumberBuffers; i++)
float *output = abl->mBuffers[i].mData;
output[frame] = value;
time += deltaTime;
return noErr;
;
@end
我有一个包装它的 Swift 类,我用它来将频率绑定到 SwiftUI 滑块:
class VCO: ObservableObject
@Published var frequency = 220.0
didSet
guard
let parameterTree = unit.auAudioUnit.parameterTree,
let freqParam = parameterTree.parameter(withAddress: 0) else
return
freqParam.setValue(AUValue(frequency), originator: nil)
var unit: AVAudioUnit!
private let componentDescription = AudioComponentDescription(componentType: kAudioUnitType_Generator,
componentSubType: OSType(1),
componentManufacturer: 0x666f6f20,
componentFlags: 0,
componentFlagsMask: 0
)
init()
AUAudioUnit.registerSubclass(VCOUnit.self, as: componentDescription, name: "VCO", version: 1)
func setup(sampleRate: Double)
let opts = AudioComponentInstantiationOptions.init(rawValue: 0)
AVAudioUnit.instantiate(with: componentDescription, options: opts) [weak self] (unit, error) in
guard let unit = unit else
print(error!)
return
self?.unit = unit
当然这不是现阶段的生产代码,但我怀疑裂缝和爆裂的原因是非常具体的。我什至开始考虑这是否与我使用 SwiftUI 绑定来更新或其他什么有关,但这对我来说确实有点牵强。问题更可能出在渲染块中,但我目前看不到。 :)
无论如何,任何意见都值得赞赏。
【问题讨论】:
【参考方案1】:这是一个典型的错误,由于每次循环迭代都会增加时间并计算一个新的相位(对于正弦函数参数),这会在改变频率时产生相位不连续性。
一个简单的解决方案是增加相位而不是时间。很容易计算给定频率的每单位时间的相位增量,并在内环之外进行计算。由于这个相位增量很小,因此不会有大的相位不连续性产生爆裂声和爆裂声。
以这种方式计算一个新阶段,每次迭代只需要加法,而不是乘法(乘以 2.0 * M_PI 等)。
您可能还希望将相位范围限制在 +- M_PI 或 +-2pi 内,而不是让它任意增长,这会导致正弦函数内的范围缩小错误。
【讨论】:
这是一些很好的信息,实际上你证实了我的一些模糊的怀疑。我上面使用的正弦计算来自一个不允许“实时”改变频率的 Apple 示例,并且作为事实上,我有一个增加相位的旧实验。非常感谢您澄清并指出要寻找的方向。 :)以上是关于在自定义 AUAudioUnit 中查找爆裂声和爆裂声的原因的主要内容,如果未能解决你的问题,请参考以下文章
在自定义数据结构上查找JavaScript中两个节点之间的路径