You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在AudioKit 5.6中实现自定义音高校正效果节点?

基于AudioKit 5.6的实时麦克风音效链与自定义音高校正节点实现

我正在开发一个基于AudioKit 5.6的功能,用于对麦克风输入施加实时音效,且支持用户调整参数。

现有效果链代码

private func initializeEffects() {
        guard let inputNode = engine.input else { return }
        fader = Fader(inputNode, gain: 1)
        
        compressor = Compressor(fader)
        compressor.masterGain = 0

        dynamicRangeCompressor = DynamicRangeCompressor(compressor)
        dynamicRangeCompressor.ratio = 100
        dynamicRangeCompressor.attackDuration = 0.01
        dynamicRangeCompressor.releaseDuration = 0.1
        
        dynamicRangeCompressorDryWetMixer = DryWetMixer(compressor, dynamicRangeCompressor)
        dynamicRangeCompressorDryWetMixer.balance = 0.75
        
        bandPassButterworth = BandPassButterworthFilter(dynamicRangeCompressorDryWetMixer)
        bandPassButterworth.bandwidth = 4000
        bandPassButterworth.centerFrequency = 0
        
        delay = Delay(bandPassButterworth)
        delay.feedback = 20
        delay.time = 0.25
        delay.dryWetMix = 100
        
        delayDryWetMixer = DryWetMixer(bandPassButterworth, delay)
        delayDryWetMixer.balance = 0
        
        reverb = Reverb(delayDryWetMixer)
        reverb.dryWetMix = 0
        reverb.loadFactoryPreset(.largeChamber)
        
        pitchShifter = PitchShifter(reverb)
        
        flanger = Flanger(pitchShifter)
        flanger.frequency = 1
        flanger.depth = 1
        flanger.feedback = 0
        flanger.dryWetMix = 1
        
        flangerDryWetMixer = DryWetMixer(pitchShifter, flanger)
        flangerDryWetMixer.balance = 0.5
        
        stringResonator = StringResonator(flangerDryWetMixer)
        stringResonator.feedback = 0.9
        
        stringResonatorDryWetMixer = DryWetMixer(flangerDryWetMixer, stringResonator)
        stringResonatorDryWetMixer.balance = 0
        
        engine.output = stringResonatorDryWetMixer
    }

自定义音高校正效果需求

现在需要创建一个自定义音高校正效果并加入该效果链,我有一个Objective-C++库,其提供的音频缓冲区处理方法如下:

- (void)processStereo:(const float *)srcL leftDestination:(float *)dstL rightSource:(const float *)srcR rightDestination:(float *)dstR numSamples:(int)nsmp;

自定义DSP层实现

经过参考资料后,完成了DSP层的实现代码:

#include "SoundpipeDSPBase.h"
#include "ParameterRamper.h"
#include "Soundpipe.h"
#include "CSoundpipeAudioKit.h"
#include "tuna.h"

enum TunaParameter : AUParameterAddress {
    TunaFilterPitchCorrectionParameterSpeed,
};

class TunaDSP : public SoundpipeDSPBase {
private:
    float speed = 0;
    ParameterRamper speedRamp;
    tuna *tuner;
    
    
public:
    TunaDSP() {
        parameters[TunaFilterPitchCorrectionParameterSpeed] = &speedRamp;
    }
    
    void setSpeed(float speed) {
        this->speed = speed;
        tuner->setpar(tuna::TUNA_SPEED, speed);
        reset();
    }
    
    void init(int channelCount, double sampleRate) override {
        SoundpipeDSPBase::init(channelCount, sampleRate);
        tuner = new tuna();
        tuner->setpar(tuna::TUNA_SPEED, 1); 
    }
    
    void deinit() override { 
        SoundpipeDSPBase::deinit(); 
    }
    
    void reset() override {
        SoundpipeDSPBase::reset();
        if (!isInitialized) return;
        tuner = new tuna(); 
    }
    
#define CHUNKSIZE 8     // defines ramp interval
    
    void process(FrameRange range) override {
        const float *inBuffers[2];
        float *outBuffers[2];
        inBuffers[0]  = (const float *)inputBufferLists[0]->mBuffers[0].mData + range.start;
        inBuffers[1]  = (const float *)inputBufferLists[0]->mBuffers[1].mData + range.start;
        outBuffers[0] = (float *)outputBufferList->mBuffers[0].mData + range.start;
        outBuffers[1] = (float *)outputBufferList->mBuffers[1].mData + range.start;
        //unsigned inChannelCount = inputBufferLists[0]->mNumberBuffers;
        //unsigned outChannelCount = outputBufferList->mNumberBuffers;
        
        if (!isStarted)
        {
            // effect bypassed: just copy input to output
            memcpy(outBuffers[0], inBuffers[0], range.count * sizeof(float));
            memcpy(outBuffers[1], inBuffers[1], range.count * sizeof(float));
            return;
        }
        
        // process in chunks of maximum length CHUNKSIZE
        for (int frameIndex = 0; frameIndex < range.count; frameIndex += CHUNKSIZE)
        {
            int chunkSize = range.count - frameIndex;
            if (chunkSize > CHUNKSIZE) chunkSize = CHUNKSIZE;
            
            tuner->process(inBuffers[0], inBuffers[1], outBuffers[0], outBuffers[1], chunkSize);
            
            // advance pointers
            inBuffers[0] += chunkSize;
            inBuffers[1] += chunkSize;
            outBuffers[0] += chunkSize;
            outBuffers[1] += chunkSize;
        }
    }
};

void akTunaFilterPitchCorrectionSetSpeed(DSPRef dspRef, float speed) {
    auto dsp = dynamic_cast<TunaDSP *>(dspRef);
    assert(dsp);
    dsp->setSpeed(speed);
}

AK_REGISTER_DSP(TunaDSP, "tuna")
AK_REGISTER_PARAMETER(TunaFilterPitchCorrectionParameterSpeed)

自定义AudioKit Node实现

对应的Swift节点实现代码如下,目前该自定义音高校正效果节点已可正常工作:

import Foundation
import AudioKit
import AudioKitEX
import CAudioKitEX

public class PitchCorrectionAudioKitNode: Node {
    let input: Node
    
    /// Connected nodes
    public var connections: [Node] { [input] }
    
    /// Underlying AVAudioNode
    public var avAudioNode = instantiate(effect: "tuna")
    
    // MARK: - Parameters
    
    /// Specification details for feedback
    public static let speedDef = NodeParameterDef(
        identifier: "speed",
        name: "Speed",
        address: akGetParameterAddress("TunaFilterPitchCorrectionParameterSpeed"),
        defaultValue: 0.6,
        range: 0.0 ... 1.0,
        unit: .percent
    )
    
    @Parameter(speedDef) public var speed: AUValue
    
    // MARK: - Initialization
    
    /// Initialize this pitch correction node
    ///
    /// - Parameters:
    ///   - input: Input node to process
    ///
    public init(
        _ input: Node,
        speed: AUValue = speedDef.defaultValue
    ) {
        self.input = input
        
        setupParameters()
        
        self.speed = speed
    }
}

内容的提问来源于stack exchange,提问作者Sotiris Kaniras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 14:45:56