Last active
July 20, 2024 13:25
-
-
Save Samigos/00edfa3d42bff52520f504a6e7146c83 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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