首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在定制AUAudioUnit中发现裂纹和pops的原因

在定制AUAudioUnit中发现裂纹和pops的原因
EN

Stack Overflow用户
提问于 2020-02-11 08:53:39
回答 1查看 216关注 0票数 0

我正在努力学习一些核心音频,并开始,我已经尝试了一个基本的音频单元,只是产生一个可调频率的正弦波。我现在的问题是,每当我调整频率时,我就会有严重的裂纹和爆裂,我不知道在哪里找出原因。

我甚至不知道这是否仅仅是因为我需要做一些平滑在频率之间移动,虽然我还没有在任何例子中看到。

总之,这是我的音频单元的实现:

代码语言:javascript
复制
#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滑块:

代码语言:javascript
复制
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绑定进行更新有关,但对我来说确实有点牵强。更可能的问题是在渲染块,但我现在看不到它。:)

无论如何,任何输入都是非常感谢的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-11 18:39:41

这是一个典型的错误,因为每次循环迭代都会增加时间和计算一个新相位(对于正弦函数参数),在改变频率时会产生相位不连续。

一个简单的解决方案是增加阶段而不是时间。在给定频率下,很容易计算单位时间的相位增量,并在内环外进行计算。由于该相δ较小,因此不会有大的相间断产生裂纹和pops。

用这种方法计算一个新阶段,每次迭代只需要加法,而不是乘法(由2.0 * M_PI等等)。

您还可能希望绑定+- M_PI或+-2pi内的相位范围,而不是让它任意增大,这会导致正弦函数中的范围缩小错误。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60164998

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档