From 3509ed55b2b98173a5e9bf74e106e5da37c65b37 Mon Sep 17 00:00:00 2001 From: NickCulbertson Date: Mon, 26 May 2025 19:10:06 -0400 Subject: [PATCH 1/4] Add Vocoder and Talkbox examples --- Cookbook/CookbookCommon/Package.swift | 2 +- .../Sources/CookbookCommon/ContentView.swift | 2 + .../Recipes/Effects/Talkbox.swift | 65 +++++++++++++++++ .../Recipes/Effects/Vocoder.swift | 69 +++++++++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift create mode 100644 Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift diff --git a/Cookbook/CookbookCommon/Package.swift b/Cookbook/CookbookCommon/Package.swift index 9e883245..393ba4e0 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 4ea681d5..1c0a3850 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 00000000..6b6b3d8b --- /dev/null +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift @@ -0,0 +1,65 @@ +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() + + 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() + } + } +} 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 00000000..210dda2c --- /dev/null +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift @@ -0,0 +1,69 @@ +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() + + 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() + } + } +} From 7781cb0505c34401053707f926f3aef9454354cf Mon Sep 17 00:00:00 2001 From: NickCulbertson Date: Mon, 26 May 2025 19:18:51 -0400 Subject: [PATCH 2/4] Update Keyboard --- .../Sources/CookbookCommon/Recipes/Effects/Talkbox.swift | 3 +++ .../Sources/CookbookCommon/Recipes/Effects/Vocoder.swift | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift index 6b6b3d8b..bf13656e 100644 --- a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift @@ -40,6 +40,7 @@ class TalkboxConductor: ObservableObject, ProcessesPlayerInput { struct TalkboxView: View { @StateObject var conductor = TalkboxConductor() + @Environment(\.colorScheme) var colorScheme var body: some View { VStack { @@ -61,5 +62,7 @@ struct TalkboxView: View { .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 index 210dda2c..ea913ad8 100644 --- a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift @@ -44,6 +44,7 @@ class VocoderConductor: ObservableObject, ProcessesPlayerInput { struct VocoderView: View { @StateObject var conductor = VocoderConductor() + @Environment(\.colorScheme) var colorScheme var body: some View { VStack { @@ -65,5 +66,7 @@ struct VocoderView: View { .onDisappear { conductor.stop() } + .background(colorScheme == .dark ? + Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) } } From af42d0766d47d4fcc008257debd192a6a550843d Mon Sep 17 00:00:00 2001 From: NickCulbertson Date: Mon, 26 May 2025 19:23:35 -0400 Subject: [PATCH 3/4] hounds --- .../Sources/CookbookCommon/Recipes/Effects/Talkbox.swift | 9 ++++----- .../Sources/CookbookCommon/Recipes/Effects/Vocoder.swift | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift index bf13656e..549d0339 100644 --- a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift @@ -11,22 +11,21 @@ class TalkboxConductor: ObservableObject, ProcessesPlayerInput { 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 diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift index ea913ad8..896cbaf0 100644 --- a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift @@ -11,22 +11,21 @@ class VocoderConductor: ObservableObject, ProcessesPlayerInput { 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 @@ -35,7 +34,7 @@ class VocoderConductor: ObservableObject, ProcessesPlayerInput { vocoder = Vocoder(player, excitation: osc) engine.output = vocoder - + vocoder.attackTime = 0.001 vocoder.releaseTime = 0.02 vocoder.bandwidthRatio = 0.1 From f0ffa978f3db7796fb314ebf5369a41578bd0b87 Mon Sep 17 00:00:00 2001 From: NickCulbertson Date: Mon, 26 May 2025 19:24:16 -0400 Subject: [PATCH 4/4] hounds --- .../Sources/CookbookCommon/Recipes/Effects/Talkbox.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift index 549d0339..a1f14aab 100644 --- a/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift +++ b/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift @@ -12,7 +12,7 @@ class TalkboxConductor: ObservableObject, ProcessesPlayerInput { let talkbox: Talkbox let buffer: AVAudioPCMBuffer var osc = DynamicOscillator() - + func noteOn(pitch: Pitch, point _: CGPoint) { isPlaying = true osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency()