diff --git a/Cookbook/CookbookCommon/Package.swift b/Cookbook/CookbookCommon/Package.swift index 9e88324..393ba4e 100644 --- a/Cookbook/CookbookCommon/Package.swift +++ b/Cookbook/CookbookCommon/Package.swift @@ -13,7 +13,7 @@ let package = Package( .package(url: "https://github.com/AudioKit/Controls", from: "1.0.0"), .package(url: "https://github.com/AudioKit/DunneAudioKit", from: "5.6.0"), .package(url: "https://github.com/AudioKit/Keyboard", from: "1.3.0"), - .package(url: "https://github.com/AudioKit/SoundpipeAudioKit", from: "5.6.0"), + .package(url: "https://github.com/AudioKit/SoundpipeAudioKit", from: "5.7.1"), .package(url: "https://github.com/AudioKit/SporthAudioKit", from: "5.5.0"), .package(url: "https://github.com/AudioKit/STKAudioKit", from: "5.5.0"), .package(url: "https://github.com/AudioKit/Tonic", from: "1.0.0"), diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/ContentView.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/ContentView.swift index 4ea681d..1c0a385 100644 --- a/Cookbook/CookbookCommon/Sources/CookbookCommon/ContentView.swift +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/ContentView.swift @@ -103,10 +103,12 @@ struct MasterView: View { NavigationLink("String Resonator", destination: StringResonatorView()) } Group { + NavigationLink("Talkbox", destination: TalkboxView()) NavigationLink("Time / Pitch", destination: TimePitchView()) NavigationLink("Transient Shaper", destination: TransientShaperView()) NavigationLink("Tremolo", destination: TremoloView()) NavigationLink("Variable Delay", destination: VariableDelayView()) + NavigationLink("Vocoder", destination: VocoderView()) } } } diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift new file mode 100644 index 0000000..a1f14aa --- /dev/null +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift @@ -0,0 +1,67 @@ +import AudioKit +import AudioKitEX +import AudioKitUI +import AVFoundation +import SoundpipeAudioKit +import SwiftUI +import Tonic + +class TalkboxConductor: ObservableObject, ProcessesPlayerInput { + let engine = AudioEngine() + let player = AudioPlayer() + let talkbox: Talkbox + let buffer: AVAudioPCMBuffer + var osc = DynamicOscillator() + + func noteOn(pitch: Pitch, point _: CGPoint) { + isPlaying = true + osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() + } + + func noteOff(pitch _: Pitch) { + isPlaying = false + } + + @Published var isPlaying: Bool = false { + didSet { isPlaying ? osc.start() : osc.stop() } + } + + init() { + buffer = Cookbook.sourceBuffer + player.buffer = buffer + player.isLooping = true + osc.amplitude = 0.5 + + talkbox = Talkbox(player, excitation: osc) + engine.output = talkbox + } +} + +struct TalkboxView: View { + @StateObject var conductor = TalkboxConductor() + @Environment(\.colorScheme) var colorScheme + + var body: some View { + VStack { + PlayerControls(conductor: conductor) + HStack { + ForEach(conductor.talkbox.parameters) { + ParameterRow(param: $0) + } + } + NodeOutputView(conductor.player) + CookbookKeyboard(noteOn: conductor.noteOn, + noteOff: conductor.noteOff) + } + .padding() + .cookbookNavBarTitle("Talkbox") + .onAppear { + conductor.start() + } + .onDisappear { + conductor.stop() + } + .background(colorScheme == .dark ? + Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) + } +} diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift new file mode 100644 index 0000000..896cbaf --- /dev/null +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift @@ -0,0 +1,71 @@ +import AudioKit +import AudioKitEX +import AudioKitUI +import AVFoundation +import SoundpipeAudioKit +import SwiftUI +import Tonic + +class VocoderConductor: ObservableObject, ProcessesPlayerInput { + let engine = AudioEngine() + let player = AudioPlayer() + let vocoder: Vocoder + let buffer: AVAudioPCMBuffer + var osc = MorphingOscillator(index: 2.5) + + func noteOn(pitch: Pitch, point _: CGPoint) { + isPlaying = true + osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() + } + + func noteOff(pitch _: Pitch) { + isPlaying = false + } + + @Published var isPlaying: Bool = false { + didSet { isPlaying ? osc.start() : osc.stop() } + } + + init() { + buffer = Cookbook.sourceBuffer + player.buffer = buffer + player.isLooping = true + osc.amplitude = 0.5 + + vocoder = Vocoder(player, excitation: osc) + engine.output = vocoder + + vocoder.attackTime = 0.001 + vocoder.releaseTime = 0.02 + vocoder.bandwidthRatio = 0.1 + } +} + +struct VocoderView: View { + @StateObject var conductor = VocoderConductor() + @Environment(\.colorScheme) var colorScheme + + var body: some View { + VStack { + PlayerControls(conductor: conductor) + HStack { + ForEach(conductor.vocoder.parameters) { + ParameterRow(param: $0) + } + } + NodeOutputView(conductor.player) + CookbookKeyboard(noteOn: conductor.noteOn, + noteOff: conductor.noteOff) + } + .padding() + .cookbookNavBarTitle("Vocoder") + .onAppear { + conductor.start() + } + .onDisappear { + conductor.stop() + } + .background(colorScheme == .dark ? + Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) + } +}