Skip to content

Instantly share code, notes, and snippets.

@Samigos
Last active July 20, 2024 13:25
Show Gist options
  • Save Samigos/00edfa3d42bff52520f504a6e7146c83 to your computer and use it in GitHub Desktop.
Save Samigos/00edfa3d42bff52520f504a6e7146c83 to your computer and use it in GitHub Desktop.
import SwiftUI
import AudioKit
import AudioKitEX
import AVFoundation
import SoundpipeAudioKit
import DunneAudioKit
class AudioEffectsConductorAudioKitService {
private let engine = AudioEngine()
private let leadingVocalAudioPlayer = AudioPlayer()
private let mixer = Mixer()
private var inputType: AudioEffectInputType
// -------------------
private var leadingVocalAudioFader: Fader!
private var reverbSmallRoom: Reverb!
private var reverbPlate: Reverb!
private var reverbMediumChamber: Reverb!
private var reverbMediumChamberDryWetMixer: DryWetMixer!
private var flanger: Flanger!
private var flangerDryWetMixer: DryWetMixer!
private var highShelfFilter: HighShelfFilter!
private var highShelfFilterDryWetMixer: DryWetMixer!
private var highPassFilter: HighPassFilter!
private var lowPassFilter: LowPassFilter!
private var peakingParametricEqualizerFilter: PeakingParametricEqualizerFilter!
private var bandRejectButterworthFilter: BandRejectButterworthFilter!
private var pitchShifter: PitchShifter!
private var stereoDelay: StereoDelay!
private var delay: Delay!
private var delayDryWetMixer: DryWetMixer!
private var pitchCorrection: PitchCorrectionAudioKitNode!
private var pitchCorrectionDryWetMixer: DryWetMixer!
private var compressor: Compressor!
private var compressorDryWetMixer: DryWetMixer!
private var bandPassButterworth: BandPassButterworthFilter!
private var bandPassButterworthDryWetMixer: DryWetMixer!
// ------------------
private var harmoniesLowPitchShiftFilter1: PitchShifter!
private var harmoniesLowPitchShiftFilterDryWetMixer1: DryWetMixer!
private var harmoniesHighPitchShiftFilter1: PitchShifter!
private var harmoniesHighPitchShiftFilterDryWetMixer1: DryWetMixer!
private var harmoniesPitchesFilterDryWetMixer1: DryWetMixer!
private var harmoniesFinalFilterDryWetMixer1: DryWetMixer!
// ------------------
private var harmoniesLowPitchShiftFilter2: PitchShifter!
private var harmoniesLowPitchShiftFilterDryWetMixer2: DryWetMixer!
private var harmoniesHighPitchShiftFilter2: PitchShifter!
private var harmoniesHighPitchShiftFilterDryWetMixer2: DryWetMixer!
private var harmoniesPitchesFilterDryWetMixer2: DryWetMixer!
private var harmoniesFinalFilterDryWetMixer2: DryWetMixer!
// ------------------
@Injected private var config: ConfigRepository
@Injected private var supportService: SupportRepository
// ------------------
private var isInitialized = false
private var selectedVolume: Float = 1
private var selectedPitchCorrectionSpeed: Float = 0
private var selectedDecibelCutoff: Float = -60
// ------------------
init(inputType: AudioEffectInputType) {
self.inputType = inputType
// if isLooping = false (default) seek doesn’t work properly
leadingVocalAudioPlayer.isLooping = true
}
private func initializeEffects() {
let inputNode = engine.input
guard let inputNode else { return }
guard !isInitialized else { return }
isInitialized = true
// ------------------
leadingVocalAudioFader = Fader(inputNode, gain: 1)
pitchCorrection = PitchCorrectionAudioKitNode(leadingVocalAudioFader)
pitchCorrectionDryWetMixer = DryWetMixer(leadingVocalAudioFader, pitchCorrection, balance: 0)
// ------------------
highPassFilter = HighPassFilter(pitchCorrectionDryWetMixer)
highPassFilter.resonance = 0
// ------------------
lowPassFilter = LowPassFilter(highPassFilter)
lowPassFilter.resonance = 0
// ------------------
compressor = Compressor(lowPassFilter)
compressor.masterGain = 0
compressor.attackTime = 0.2
compressor.releaseTime = 1.5
compressorDryWetMixer = DryWetMixer(lowPassFilter, compressor, balance: 0)
// ------------------
peakingParametricEqualizerFilter = PeakingParametricEqualizerFilter(compressorDryWetMixer)
peakingParametricEqualizerFilter.centerFrequency = 3000
peakingParametricEqualizerFilter.q = 0.6
peakingParametricEqualizerFilter.gain = 1
// ------------------
bandRejectButterworthFilter = BandRejectButterworthFilter(peakingParametricEqualizerFilter)
bandRejectButterworthFilter.bandwidth = 100
// ------------------
bandPassButterworth = BandPassButterworthFilter(bandRejectButterworthFilter)
bandPassButterworth.bandwidth = 4000
bandPassButterworth.centerFrequency = 0
bandPassButterworthDryWetMixer = DryWetMixer(bandRejectButterworthFilter, bandPassButterworth, balance: 0)
// ------------------
highShelfFilter = HighShelfFilter(bandPassButterworthDryWetMixer, gain: 0)
highShelfFilterDryWetMixer = DryWetMixer(bandPassButterworthDryWetMixer, highShelfFilter, balance: 0)
// ------------------
stereoDelay = StereoDelay(highShelfFilterDryWetMixer)
stereoDelay.feedback = 0
stereoDelay.time = 0.5
stereoDelay.dryWetMix = 0.8
stereoDelay.pingPong = 1
// ------------------
reverbSmallRoom = Reverb(stereoDelay)
reverbSmallRoom.dryWetMix = 0
reverbSmallRoom.loadFactoryPreset(.smallRoom)
// ------------------
reverbPlate = Reverb(reverbSmallRoom)
reverbPlate.dryWetMix = 0
reverbPlate.loadFactoryPreset(.plate)
// ------------------
delay = Delay(reverbPlate)
delay.feedback = 10
delay.time = 0.125
delay.dryWetMix = 0
delayDryWetMixer = DryWetMixer(reverbPlate, delay, balance: 0.5)
// ------------------
reverbMediumChamber = Reverb(delayDryWetMixer)
reverbMediumChamber.dryWetMix = 0
reverbMediumChamber.loadFactoryPreset(.mediumChamber)
reverbMediumChamberDryWetMixer = DryWetMixer(delayDryWetMixer, reverbMediumChamber, balance: 0)
// ------------------
pitchShifter = PitchShifter(reverbMediumChamberDryWetMixer)
// ------------------
harmoniesLowPitchShiftFilter1 = PitchShifter(pitchShifter, shift: -12)
harmoniesLowPitchShiftFilterDryWetMixer1 = DryWetMixer(pitchShifter, harmoniesLowPitchShiftFilter1, balance: 0.7)
harmoniesHighPitchShiftFilter1 = PitchShifter(pitchShifter, shift: 12)
harmoniesHighPitchShiftFilterDryWetMixer1 = DryWetMixer(pitchShifter, harmoniesHighPitchShiftFilter1, balance: 0.3)
harmoniesPitchesFilterDryWetMixer1 = DryWetMixer(harmoniesHighPitchShiftFilterDryWetMixer1, harmoniesLowPitchShiftFilterDryWetMixer1, balance: 0.5)
harmoniesFinalFilterDryWetMixer1 = DryWetMixer(pitchShifter, harmoniesPitchesFilterDryWetMixer1, balance: 0)
// ------------------
harmoniesLowPitchShiftFilter2 = PitchShifter(harmoniesFinalFilterDryWetMixer1, shift: -3)
harmoniesHighPitchShiftFilter2 = PitchShifter(harmoniesFinalFilterDryWetMixer1, shift: 8)
harmoniesPitchesFilterDryWetMixer2 = DryWetMixer(harmoniesLowPitchShiftFilter2, harmoniesHighPitchShiftFilter2, balance: 0.5)
harmoniesFinalFilterDryWetMixer2 = DryWetMixer(harmoniesFinalFilterDryWetMixer1, harmoniesPitchesFilterDryWetMixer2, balance: 0)
// ------------------
flanger = Flanger(harmoniesFinalFilterDryWetMixer2)
flanger.frequency = 1
flanger.depth = 1
flanger.feedback = 0
flanger.dryWetMix = 1
flangerDryWetMixer = DryWetMixer(harmoniesFinalFilterDryWetMixer2, flanger)
flangerDryWetMixer.balance = 0.5
}
func start() {
if engine.avEngine.isRunning {
return
}
initializeEffects()
startMic()
}
}
extension AudioEffectsConductorAudioKitService {
private func startMic() {
if engine.output == nil {
engine.output = flangerDryWetMixer
engine.input?.avAudioNode.installTap(onBus: 0, bufferSize: 8, format: nil) { [weak self] buffer, time in
guard let self else { return }
let decibels = buffer.calculateDecibelValue()
if decibels < selectedDecibelCutoff {
if leadingVocalAudioFader.gain > 0.2 {
leadingVocalAudioFader.gain = selectedVolume < 0.2 ? selectedVolume : 0.2
}
} else if leadingVocalAudioFader.gain < selectedVolume {
leadingVocalAudioFader.gain = selectedVolume
}
}
}
do {
try engine.start()
} catch {
print("poop mic test Audio Engine failed to start: \(error)")
}
}
private func stopMic() {
engine.stop()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment