├── .github └── workflows │ └── tests.yml ├── .gitignore ├── Cookbook.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Cookbook ├── Configuration │ └── SampleCode.xcconfig ├── Cookbook.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Cookbook.xcscheme ├── Cookbook │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-1024px.png │ │ │ ├── AppIcon-120px-40pt@3x.png │ │ │ ├── AppIcon-120px-60pt@2x.png │ │ │ ├── AppIcon-152px-76pt@2x.png │ │ │ ├── AppIcon-167px-83.5pt@2x.png │ │ │ ├── AppIcon-180px-60pt@3x.png │ │ │ ├── AppIcon-20px-20pt@1x.png │ │ │ ├── AppIcon-29px-29pt@1x.png │ │ │ ├── AppIcon-40px-20pt@2x-1.png │ │ │ ├── AppIcon-40px-20pt@2x.png │ │ │ ├── AppIcon-40px-40pt@1x.png │ │ │ ├── AppIcon-58px-29pt@2x-1.png │ │ │ ├── AppIcon-58px-29pt@2x.png │ │ │ ├── AppIcon-60px-20pt@3x.png │ │ │ ├── AppIcon-76px-76pt@1x.png │ │ │ ├── AppIcon-80px-40pt@2x-1.png │ │ │ ├── AppIcon-80px-40pt@2x.png │ │ │ ├── AppIcon-87px-29pt@3x.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── audiokit-icon.imageset │ │ │ ├── Contents.json │ │ │ ├── audiokit-icon.png │ │ │ ├── audiokit-icon2x.png │ │ │ └── audiokit-icon@3x.png │ │ └── audiokit-logo.imageset │ │ │ ├── Contents.json │ │ │ └── audiokit-logo.png │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Cookbook.entitlements │ ├── CookbookApp.swift │ ├── Info.plist │ └── audio3D.scnassets │ │ └── audio3DTest.scn ├── CookbookCommon │ ├── .swiftpm │ │ └── xcode │ │ │ └── package.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── Package.resolved │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── CookbookCommon │ │ │ ├── About AudioKit │ │ │ ├── AboutAudioKitContentView.swift │ │ │ └── AudioKitInfoView.swift │ │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── AppIcon-1024px.png │ │ │ │ ├── AppIcon-120px-40pt@3x.png │ │ │ │ ├── AppIcon-120px-60pt@2x.png │ │ │ │ ├── AppIcon-152px-76pt@2x.png │ │ │ │ ├── AppIcon-167px-83.5pt@2x.png │ │ │ │ ├── AppIcon-180px-60pt@3x.png │ │ │ │ ├── AppIcon-20px-20pt@1x.png │ │ │ │ ├── AppIcon-29px-29pt@1x.png │ │ │ │ ├── AppIcon-40px-20pt@2x-1.png │ │ │ │ ├── AppIcon-40px-20pt@2x.png │ │ │ │ ├── AppIcon-40px-40pt@1x.png │ │ │ │ ├── AppIcon-58px-29pt@2x-1.png │ │ │ │ ├── AppIcon-58px-29pt@2x.png │ │ │ │ ├── AppIcon-60px-20pt@3x.png │ │ │ │ ├── AppIcon-76px-76pt@1x.png │ │ │ │ ├── AppIcon-80px-40pt@2x-1.png │ │ │ │ ├── AppIcon-80px-40pt@2x.png │ │ │ │ ├── AppIcon-87px-29pt@3x.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── audiokit-icon.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── audiokit-icon.png │ │ │ │ ├── audiokit-icon2x.png │ │ │ │ └── audiokit-icon@3x.png │ │ │ └── audiokit-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── audiokit-logo.png │ │ │ ├── Colors.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── FontColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── ContentView.swift │ │ │ ├── CookbookCommon.swift │ │ │ ├── Impulse Responses │ │ │ ├── dish.wav │ │ │ └── stairwell.wav │ │ │ ├── MIDI Files │ │ │ ├── 4tracks.mid │ │ │ └── Demo.mid │ │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ │ ├── Recipes │ │ │ ├── AdditionalPackages │ │ │ │ ├── ControlsView.swift │ │ │ │ ├── FlowView.swift │ │ │ │ ├── KeyboardView.swift │ │ │ │ ├── PianoRollView.swift │ │ │ │ ├── STKView.swift │ │ │ │ └── WaveformView.swift │ │ │ ├── AudioPlayer │ │ │ │ ├── AudioPlayerCompletionHandler.swift │ │ │ │ ├── MultiSegmentPlayerView.swift │ │ │ │ └── Playlist.swift │ │ │ ├── Distortion │ │ │ │ ├── BitCrusher.swift │ │ │ │ ├── Clipper.swift │ │ │ │ ├── Decimator.swift │ │ │ │ ├── DistortionPresets.swift │ │ │ │ ├── RingModulator.swift │ │ │ │ └── TanhDistortion.swift │ │ │ ├── Effects │ │ │ │ ├── AutoPanner.swift │ │ │ │ ├── AutoWah.swift │ │ │ │ ├── Balancer.swift │ │ │ │ ├── Chorus.swift │ │ │ │ ├── Compressor.swift │ │ │ │ ├── Convolution.swift │ │ │ │ ├── Delay.swift │ │ │ │ ├── DynamicRangeCompressor.swift │ │ │ │ ├── Expander.swift │ │ │ │ ├── Flanger.swift │ │ │ │ ├── MultiTapDelay.swift │ │ │ │ ├── Panner.swift │ │ │ │ ├── PeakLimiter.swift │ │ │ │ ├── PhaseLockedVocoder.swift │ │ │ │ ├── Phaser.swift │ │ │ │ ├── PitchShifter.swift │ │ │ │ ├── PlaybackSpeed.swift │ │ │ │ ├── StereoDelay.swift │ │ │ │ ├── StringResonator.swift │ │ │ │ ├── Talkbox.swift │ │ │ │ ├── TimePitch.swift │ │ │ │ ├── TransientShaper.swift │ │ │ │ ├── Tremolo.swift │ │ │ │ ├── VariableDelay.swift │ │ │ │ └── Vocoder.swift │ │ │ ├── Filters │ │ │ │ ├── BandPassButterworthFilter.swift │ │ │ │ ├── BandRejectButterworthFilter.swift │ │ │ │ ├── CombFilterReverb.swift │ │ │ │ ├── EqualizerFilter.swift │ │ │ │ ├── FormantFilter.swift │ │ │ │ ├── HighPassButterworthFilter.swift │ │ │ │ ├── HighPassFilter.swift │ │ │ │ ├── HighShelfFilter.swift │ │ │ │ ├── HighShelfParametricEqualizerFilter.swift │ │ │ │ ├── KorgLowPassFilter.swift │ │ │ │ ├── LowPassButterworthFilter.swift │ │ │ │ ├── LowPassFilter.swift │ │ │ │ ├── LowShelfFilter.swift │ │ │ │ ├── LowShelfParametricEqualizerFilter.swift │ │ │ │ ├── ModalResonanceFilter.swift │ │ │ │ ├── MoogLadder.swift │ │ │ │ ├── PeakingParametricEqualizerFilter.swift │ │ │ │ ├── ResonantFilter.swift │ │ │ │ ├── ThreePoleLowpassFilter.swift │ │ │ │ ├── ToneComplementFilter.swift │ │ │ │ └── ToneFilter.swift │ │ │ ├── MiniApps │ │ │ │ ├── Arpeggiator.swift │ │ │ │ ├── Audio3D.swift │ │ │ │ ├── DrumSequencer.swift │ │ │ │ ├── DrumSynthesizers.swift │ │ │ │ ├── Drums.swift │ │ │ │ ├── GraphicEqualizer.swift │ │ │ │ ├── InstrumentEXS.swift │ │ │ │ ├── InstrumentSFZ.swift │ │ │ │ ├── MIDIMonitor.swift │ │ │ │ ├── MIDITrack.swift │ │ │ │ ├── MusicToy.swift │ │ │ │ ├── NoiseGenerators.swift │ │ │ │ ├── Recorder.swift │ │ │ │ ├── SpriteKitAudio.swift │ │ │ │ ├── Telephone.swift │ │ │ │ ├── Tuner.swift │ │ │ │ └── VocalTract.swift │ │ │ ├── Operations │ │ │ │ ├── CrossingSignal.swift │ │ │ │ ├── DroneOperation.swift │ │ │ │ ├── InstrumentOperation.swift │ │ │ │ ├── LFOOperation.swift │ │ │ │ ├── PhasorOperation.swift │ │ │ │ ├── PitchShfitOperation.swift │ │ │ │ ├── SegmentOperation.swift │ │ │ │ ├── SmoothDelayOperation.swift │ │ │ │ ├── StereoDelayOperation.swift │ │ │ │ ├── StereoOperation.swift │ │ │ │ ├── VariableDelayOperation.swift │ │ │ │ └── VocalTractOperation.swift │ │ │ ├── Oscillators │ │ │ │ ├── AmplitudeEnvelope.swift │ │ │ │ ├── DynamicOscillator.swift │ │ │ │ ├── FMOscillator.swift │ │ │ │ ├── MorphingOscillator.swift │ │ │ │ ├── Oscillator.swift │ │ │ │ ├── PWMOscillator.swift │ │ │ │ └── PhaseDistortionOscillator.swift │ │ │ ├── PhysicalModels │ │ │ │ └── PluckedString.swift │ │ │ ├── Reverbs │ │ │ │ ├── ChowningReverb.swift │ │ │ │ ├── CostelloReverb.swift │ │ │ │ ├── FlatFrequencyResponseReverb.swift │ │ │ │ ├── Reverb.swift │ │ │ │ └── ZitaReverb.swift │ │ │ ├── UncategorizedDemos │ │ │ │ ├── AudioFileView.swift │ │ │ │ ├── CallbackInstrument.swift │ │ │ │ └── Table.swift │ │ │ └── WIP │ │ │ │ ├── BaseTapDemo.swift │ │ │ │ ├── ChannelDeviceRouting.swift │ │ │ │ ├── DunneSynth.swift │ │ │ │ ├── InputDeviceDemo.swift │ │ │ │ ├── MIDIPortTest.swift │ │ │ │ ├── MIDIPortTestConductor.swift │ │ │ │ ├── PolyphonicOscillator.swift │ │ │ │ ├── PolyphonicSTK+MIDIKit.swift │ │ │ │ └── RolandTB303Filter.swift │ │ │ ├── Reusable Components │ │ │ ├── CallbackLoop.swift │ │ │ ├── Cookbook.swift │ │ │ ├── CookbookKeyboard.swift │ │ │ ├── CookbookKnob.swift │ │ │ ├── NavigationHelpers.swift │ │ │ ├── ParameterRow.swift │ │ │ └── PlayerControls.swift │ │ │ ├── Samples │ │ │ ├── Bass Synth.mp3 │ │ │ ├── Counting.mp3 │ │ │ ├── Guitar.mp3 │ │ │ ├── Piano.mp3 │ │ │ ├── Strings.mp3 │ │ │ ├── Synth.mp3 │ │ │ ├── alphabet.mp3 │ │ │ ├── bass_drum_C1.wav │ │ │ ├── beat.aiff │ │ │ ├── clap_D#1.wav │ │ │ ├── closed_hi_hat_F#1.wav │ │ │ ├── hi_tom_D2.wav │ │ │ ├── lo_tom_F1.wav │ │ │ ├── mid_tom_B1.wav │ │ │ ├── open_hi_hat_A#1.wav │ │ │ └── snare_D1.wav │ │ │ └── TestAudioURLs.swift │ └── Tests │ │ └── CookbookCommonTests │ │ └── CookbookCommonTests.swift └── Sounds │ ├── Sampler Instruments │ ├── drumSimp.exs │ ├── funkyWow.exs │ ├── nes-syn1.exs │ ├── noisyRez.exs │ ├── sawPad1.exs │ ├── sawPiano1.exs │ └── sqrTone1.exs │ ├── basicSamples │ ├── noise-wht2.wav │ ├── saw220-ana1.wav │ ├── saw220.wav │ └── sqr220.wav │ ├── cheeb-bd.wav │ ├── cheeb-ch.wav │ ├── cheeb-hat.wav │ ├── cheeb-snr.wav │ ├── cheeb-stick.wav │ ├── closed_hi_hat_F#1.wav │ └── sqr.SFZ ├── LICENSE ├── README.md ├── Xcode-config ├── DEVELOPMENT_TEAM.template.xcconfig └── Shared.xcconfig └── images ├── Cookbook.png ├── Cookbook2.png └── CookbookMac.png /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions for AudioKit Cookbook 2 | name: CI 3 | 4 | on: [push, pull_request] 5 | env: 6 | XCODE_VER: 14.2 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | matrix: 12 | xcode_version: ['14.2'] 13 | runs-on: macos-12 14 | env: 15 | DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode_version }}.app 16 | steps: 17 | - name: Check out AudioKit Cookbook 18 | uses: actions/checkout@v2 19 | - name: Build AudioKit Cookbook 20 | run: | 21 | set -euo pipefail 22 | xcodebuild -project Cookbook/Cookbook.xcodeproj -sdk iphonesimulator -scheme Cookbook -arch x86_64 ONLY_ACTIVE_ARCH=YES CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" clean build | xcpretty -c 23 | 24 | # Send notification to Discord on failure. 25 | send_notification: 26 | name: Send Notification 27 | uses: AudioKit/ci/.github/workflows/send_notification.yml@main 28 | needs: [build] 29 | if: ${{ failure() && github.ref == 'refs/heads/main' }} 30 | secrets: inherit 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | .DS_Store 93 | 94 | # User-specific xcconfig files 95 | Xcode-config/DEVELOPMENT_TEAM.xcconfig 96 | 97 | Package.resolved -------------------------------------------------------------------------------- /Cookbook.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Cookbook.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Cookbook/Configuration/SampleCode.xcconfig: -------------------------------------------------------------------------------- 1 | // The `SAMPLE_CODE_DISAMBIGUATOR` configuration is to make it easier to build 2 | // and run a sample code project. Once you set your project's development team, 3 | // you'll have a unique bundle identifier. This is because the bundle identifier 4 | // is derived based on the 'SAMPLE_CODE_DISAMBIGUATOR' value. Do not use this 5 | // approach in your own projects—it's only useful for sample code projects because 6 | // they are frequently downloaded and don't have a development team set. 7 | SAMPLE_CODE_DISAMBIGUATOR=${DEVELOPMENT_TEAM} 8 | -------------------------------------------------------------------------------- /Cookbook/Cookbook.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Cookbook/Cookbook.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-1024px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-1024px.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-40pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-40pt@3x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-60pt@2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-152px-76pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-152px-76pt@2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-167px-83.5pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-167px-83.5pt@2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-180px-60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-180px-60pt@3x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-20px-20pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-20px-20pt@1x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-29px-29pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-29px-29pt@1x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x-1.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-40pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-40pt@1x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x-1.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-60px-20pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-60px-20pt@3x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-76px-76pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-76px-76pt@1x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x-1.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-87px-29pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/AppIcon.appiconset/AppIcon-87px-29pt@3x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "audiokit-icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "audiokit-icon2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "audiokit-icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/audiokit-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/audiokit-icon.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/audiokit-icon2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/audiokit-icon2x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/audiokit-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/audiokit-icon.imageset/audiokit-icon@3x.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/audiokit-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "audiokit-logo.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/Assets.xcassets/audiokit-logo.imageset/audiokit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/Assets.xcassets/audiokit-logo.imageset/audiokit-logo.png -------------------------------------------------------------------------------- /Cookbook/Cookbook/Cookbook.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.device.audio-input 8 | 9 | com.apple.security.files.user-selected.read-write 10 | 11 | com.apple.security.network.client 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/CookbookApp.swift: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. 2 | 3 | import AudioKit 4 | import AVFoundation 5 | import CookbookCommon 6 | import SwiftUI 7 | 8 | @main 9 | struct CookbookApp: App { 10 | init() { 11 | #if os(iOS) 12 | do { 13 | Settings.bufferLength = .short 14 | 15 | // Settings.sampleRate default is 44_100 16 | if #available(iOS 18.0, *) { 17 | if !ProcessInfo.processInfo.isMacCatalystApp && !ProcessInfo.processInfo.isiOSAppOnMac { 18 | // Set samplerRate for iOS 18 and newer 19 | Settings.sampleRate = 48_000 20 | } 21 | } 22 | 23 | try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(Settings.bufferLength.duration) 24 | try AVAudioSession.sharedInstance().setCategory(.playAndRecord, 25 | options: [.defaultToSpeaker, .mixWithOthers, .allowBluetoothA2DP]) 26 | try AVAudioSession.sharedInstance().setActive(true) 27 | } catch let err { 28 | print(err) 29 | } 30 | #endif 31 | } 32 | 33 | var body: some Scene { 34 | WindowGroup { 35 | ContentView() 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSSpeechRecognitionUsageDescription 6 | We use Speech Recognition to demonstrate BaseTap capabilities. 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | AudioKit 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSMicrophoneUsageDescription 28 | We use the microphone to demonstrate processing input. 29 | UIApplicationSceneManifest 30 | 31 | UIApplicationSupportsMultipleScenes 32 | 33 | 34 | UIApplicationSupportsIndirectInputEvents 35 | 36 | UIBackgroundModes 37 | 38 | audio 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Cookbook/Cookbook/audio3D.scnassets/audio3DTest.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Cookbook/audio3D.scnassets/audio3DTest.scn -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "CookbookCommon", 7 | platforms: [.macOS(.v12), .iOS(.v16), .tvOS(.v15)], 8 | products: [.library(name: "CookbookCommon", targets: ["CookbookCommon"])], 9 | dependencies: [ 10 | .package(url: "https://github.com/AudioKit/AudioKit", from: "5.6.4"), 11 | .package(url: "https://github.com/AudioKit/AudioKitUI", branch: "visionos"), 12 | .package(url: "https://github.com/AudioKit/AudioKitEX", from: "5.6.0"), 13 | .package(url: "https://github.com/AudioKit/Controls", from: "1.0.0"), 14 | .package(url: "https://github.com/AudioKit/DunneAudioKit", from: "5.6.0"), 15 | .package(url: "https://github.com/AudioKit/Keyboard", from: "1.3.0"), 16 | .package(url: "https://github.com/AudioKit/SoundpipeAudioKit", from: "5.7.1"), 17 | .package(url: "https://github.com/AudioKit/SporthAudioKit", from: "5.5.0"), 18 | .package(url: "https://github.com/AudioKit/STKAudioKit", from: "5.5.0"), 19 | .package(url: "https://github.com/AudioKit/Tonic", from: "1.0.0"), 20 | .package(url: "https://github.com/AudioKit/Waveform", branch: "visionos"), 21 | .package(url: "https://github.com/AudioKit/Flow", from: "1.0.0"), 22 | .package(url: "https://github.com/AudioKit/PianoRoll", from: "1.0.0"), 23 | .package(url: "https://github.com/orchetect/MIDIKit", from: "0.9.7"), 24 | ], 25 | targets: [ 26 | .target( 27 | name: "CookbookCommon", 28 | dependencies: ["AudioKit", "AudioKitUI", "AudioKitEX", "Keyboard", "SoundpipeAudioKit", 29 | "SporthAudioKit", "STKAudioKit", "DunneAudioKit", "Tonic", "Controls", "Waveform", "Flow", "PianoRoll", "MIDIKit"], 30 | resources: [ 31 | .copy("MIDI Files"), 32 | .copy("Samples"), 33 | .copy("Impulse Responses"), 34 | ] 35 | ), 36 | .testTarget(name: "CookbookCommonTests", dependencies: ["CookbookCommon"]), 37 | ] 38 | ) 39 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/README.md: -------------------------------------------------------------------------------- 1 | # CookbookCommon 2 | 3 | This contains the recipes so that operating system-specific wrapper applications can be made. 4 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/About AudioKit/AboutAudioKitContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct AboutAudioKitContentView: View { 4 | private let maxWidth: CGFloat = 200 5 | var stackSpacing: CGFloat 6 | 7 | var body: some View { 8 | audioKitArtwork 9 | audioKitText 10 | } 11 | 12 | private var audioKitArtwork: some View { 13 | return VStack(spacing: stackSpacing) { 14 | Image("audiokit-icon") 15 | .resizable() 16 | .aspectRatio(contentMode: .fit) 17 | .frame(maxWidth: maxWidth) 18 | Image("audiokit-logo") 19 | .resizable() 20 | .aspectRatio(contentMode: .fit) 21 | .frame(maxWidth: maxWidth) 22 | } 23 | } 24 | 25 | private var audioKitText: some View { 26 | Text("AudioKit is an audio synthesis, processing, and analysis platform for iOS, macOS, and tvOS.\n\nMost of the examples that were inside of AudioKit are now in this application.\n\nIn addition to the resources found here, there are various open-source example projects on GitHub and YouTube created by AudioKit contributors.") 27 | .padding() 28 | } 29 | } 30 | 31 | #Preview { 32 | AboutAudioKitContentView(stackSpacing: 25) 33 | } 34 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/About AudioKit/AudioKitInfoView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct AudioKitInfoView: View { 4 | @Environment(\.dismiss) var dismiss 5 | @Environment(\.verticalSizeClass) var verticalSizeClass 6 | @Environment(\.dynamicTypeSize) var dynamicTypeSize 7 | let stackSpacing: CGFloat = 25 8 | var body: some View { 9 | NavigationStack { 10 | ScrollView { 11 | if verticalSizeClass == .regular { 12 | VStack(spacing: stackSpacing) { 13 | AboutAudioKitContentView(stackSpacing: stackSpacing) 14 | } 15 | } else { 16 | HStack(spacing: stackSpacing) { AboutAudioKitContentView(stackSpacing: stackSpacing) 17 | } 18 | } 19 | } 20 | .toolbar { 21 | ToolbarItem(placement: .primaryAction) { 22 | Button { 23 | dismiss() 24 | } label: { 25 | Text("Done") 26 | .fontWeight(.semibold) 27 | } 28 | .accessibilityHint("Tap to close this screen.") 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | #Preview { 36 | AudioKitInfoView() 37 | } 38 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-1024px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-1024px.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-40pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-40pt@3x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-120px-60pt@2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-152px-76pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-152px-76pt@2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-167px-83.5pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-167px-83.5pt@2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-180px-60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-180px-60pt@3x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-20px-20pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-20px-20pt@1x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-29px-29pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-29px-29pt@1x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x-1.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-40pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-40px-40pt@1x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x-1.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-60px-20pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-60px-20pt@3x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-76px-76pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-76px-76pt@1x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x-1.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-87px-29pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/AppIcon.appiconset/AppIcon-87px-29pt@3x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "audiokit-icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "audiokit-icon2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "audiokit-icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/audiokit-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/audiokit-icon.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/audiokit-icon2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/audiokit-icon2x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/audiokit-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-icon.imageset/audiokit-icon@3x.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "audiokit-logo.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-logo.imageset/audiokit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Assets.xcassets/audiokit-logo.imageset/audiokit-logo.png -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Colors.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.860", 9 | "green" : "0.500", 10 | "red" : "0.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "0.766", 28 | "red" : "0.249" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Colors.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Colors.xcassets/FontColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "1.000", 13 | "alpha" : "1.000", 14 | "blue" : "1.000", 15 | "green" : "1.000" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "1.000", 31 | "alpha" : "1.000", 32 | "blue" : "1.000", 33 | "green" : "1.000" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0.000", 49 | "alpha" : "1.000", 50 | "blue" : "0.000", 51 | "green" : "0.000" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/CookbookCommon.swift: -------------------------------------------------------------------------------- 1 | public struct CookbookCommon { 2 | public private(set) var text = "Hello, World!" 3 | 4 | public init() {} 5 | } 6 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Impulse Responses/dish.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Impulse Responses/dish.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Impulse Responses/stairwell.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Impulse Responses/stairwell.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/MIDI Files/4tracks.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/MIDI Files/4tracks.mid -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/MIDI Files/Demo.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/MIDI Files/Demo.mid -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/AdditionalPackages/FlowView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Flow 3 | 4 | func simplePatch() -> Patch { 5 | let generator = Node(name: "generator", titleBarColor: Color.cyan, outputs: ["out"]) 6 | let processor = Node(name: "processor", titleBarColor: Color.red, inputs: ["in"], outputs: ["out"]) 7 | let mixer = Node(name: "mixer", titleBarColor: Color.gray, inputs: ["in1", "in2"], outputs: ["out"]) 8 | let output = Node(name: "output", titleBarColor: Color.purple, inputs: ["in"]) 9 | 10 | let nodes = [generator, processor, generator, processor, mixer, output] 11 | 12 | let wires = Set([Wire(from: OutputID(0, 0), to: InputID(1, 0)), 13 | Wire(from: OutputID(1, 0), to: InputID(4, 0)), 14 | Wire(from: OutputID(2, 0), to: InputID(3, 0)), 15 | Wire(from: OutputID(3, 0), to: InputID(4, 1)), 16 | Wire(from: OutputID(4, 0), to: InputID(5, 0))]) 17 | 18 | var patch = Patch(nodes: nodes, wires: wires) 19 | patch.recursiveLayout(nodeIndex: 5, at: CGPoint(x: 800, y: 50)) 20 | return patch 21 | } 22 | 23 | /// Bit of a stress test to show how Flow performs with more nodes. 24 | func randomPatch() -> Patch { 25 | var randomNodes: [Node] = [] 26 | for n in 0 ..< 50 { 27 | let randomPoint = CGPoint(x: 1000 * Double.random(in: 0 ... 1), 28 | y: 1000 * Double.random(in: 0 ... 1)) 29 | randomNodes.append(Node(name: "node\(n)", 30 | position: randomPoint, 31 | inputs: ["In"], 32 | outputs: ["Out"])) 33 | } 34 | 35 | var randomWires: Set = [] 36 | for n in 0 ..< 50 { 37 | randomWires.insert(Wire(from: OutputID(n, 0), to: InputID(Int.random(in: 0 ... 49), 0))) 38 | } 39 | return Patch(nodes: randomNodes, wires: randomWires) 40 | } 41 | 42 | struct FlowView: View { 43 | @State var patch = simplePatch() 44 | @State var selection = Set() 45 | 46 | var body: some View { 47 | NodeEditor(patch: $patch, selection: $selection) 48 | .navigationTitle("Flow Demo") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/AdditionalPackages/PianoRollView.swift: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKitUI/ 2 | 3 | import PianoRoll 4 | import SwiftUI 5 | 6 | public struct PianoRollView: View { 7 | public init() {} 8 | 9 | @State var model = PianoRollModel(notes: [ 10 | PianoRollNote(start: 1, length: 2, pitch: 3), 11 | PianoRollNote(start: 5, length: 1, pitch: 4), 12 | ], length: 128, height: 128) 13 | 14 | public var body: some View { 15 | VStack(alignment: .leading) { 16 | Text("Tap inside of the scrolling grid to set a note.") 17 | .padding([.top, .horizontal]) 18 | ScrollView([.horizontal, .vertical], showsIndicators: true) { 19 | PianoRoll(model: $model, noteColor: .cyan, gridColor: .primary, layout: .horizontal) 20 | } 21 | .padding() 22 | } 23 | .foregroundStyle(.primary) 24 | .navigationTitle("Piano Roll Demo") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/AudioPlayer/AudioPlayerCompletionHandler.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AVFoundation 3 | import SwiftUI 4 | 5 | class CompletionHandlerConductor: ObservableObject, HasAudioEngine { 6 | let engine = AudioEngine() 7 | var player = AudioPlayer() 8 | var fileURL = [URL]() 9 | @Published var playDuration = 0.0 10 | var currentFileIndex = 0 11 | 12 | // Load the files to play 13 | func getPlayerFiles() { 14 | let files = ["Bass Synth.mp3", "Piano.mp3", 15 | "Synth.mp3", "Strings.mp3", "Guitar.mp3"] 16 | for filename in files { 17 | guard let url = Bundle.module.resourceURL?.appendingPathComponent( 18 | "Samples/\(filename)") 19 | else { 20 | Log("failed to load sample", filename) 21 | return 22 | } 23 | fileURL.append(url) 24 | } 25 | } 26 | 27 | /* Completion handler function: 28 | a function returning void */ 29 | func playNextFile() { 30 | if currentFileIndex < 4 { 31 | currentFileIndex += 1 32 | } else { 33 | currentFileIndex = 0 34 | } 35 | startPlaying() 36 | } 37 | 38 | init() { 39 | getPlayerFiles() 40 | engine.output = player 41 | 42 | /* Assign the function 43 | to the completion handler */ 44 | player.completionHandler = playNextFile 45 | } 46 | 47 | func startPlaying() { 48 | try? player.load(url: fileURL[currentFileIndex]) 49 | player.play() 50 | if let duration = player.file?.duration { 51 | playDuration = duration 52 | } 53 | } 54 | 55 | } 56 | 57 | struct AudioPlayerCompletionHandler: View { 58 | @StateObject var conductor = CompletionHandlerConductor() 59 | 60 | var body: some View { 61 | Text("AudioPlayer Completion Handler") 62 | .padding() 63 | Text("This will play one file. Once it completes, it will play another.") 64 | Text("That's one thing a completion handler can do.") 65 | VStack { 66 | let playLabel = "Playing: " + conductor.fileURL[conductor.currentFileIndex] 67 | .deletingPathExtension().lastPathComponent 68 | let playTimeRange = Date()...Date().addingTimeInterval(conductor.playDuration) 69 | ProgressView(timerInterval: playTimeRange, countsDown: false) { 70 | Text(playLabel) 71 | } 72 | } 73 | .onAppear { 74 | conductor.start() 75 | conductor.startPlaying() 76 | } 77 | .onDisappear { 78 | conductor.stop() 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Distortion/BitCrusher.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class BitCrusherConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let bitcrusher: BitCrusher 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | bitcrusher = BitCrusher(player) 21 | dryWetMixer = DryWetMixer(player, bitcrusher) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct BitCrusherView: View { 27 | @StateObject var conductor = BitCrusherConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.bitcrusher.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.bitcrusher, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Bit Crusher") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Distortion/Clipper.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ClipperConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let clipper: Clipper 12 | let amplifier: Fader 13 | let dryWetMixer: DryWetMixer 14 | let buffer: AVAudioPCMBuffer 15 | 16 | init() { 17 | buffer = Cookbook.sourceBuffer 18 | player.buffer = buffer 19 | player.isLooping = true 20 | 21 | clipper = Clipper(player) 22 | amplifier = Fader(clipper) 23 | dryWetMixer = DryWetMixer(player, amplifier) 24 | engine.output = dryWetMixer 25 | } 26 | } 27 | 28 | struct ClipperView: View { 29 | @StateObject var conductor = ClipperConductor() 30 | 31 | var body: some View { 32 | VStack { 33 | PlayerControls(conductor: conductor) 34 | HStack { 35 | ForEach(conductor.clipper.parameters) { 36 | ParameterRow(param: $0) 37 | } 38 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 39 | } 40 | DryWetMixView(dry: conductor.player, 41 | wet: conductor.clipper, 42 | mix: conductor.dryWetMixer) 43 | } 44 | .padding() 45 | .cookbookNavBarTitle("Clipper") 46 | .onAppear { 47 | conductor.start() 48 | } 49 | .onDisappear { 50 | conductor.stop() 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Distortion/Decimator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: Decimation is a type of digital distortion like bit crushing, 9 | //: but instead of directly stating what bit depth and sample rate you want, 10 | //: it is done through setting "decimation" and "rounding" parameters. 11 | 12 | class DecimatorConductor: ObservableObject, ProcessesPlayerInput { 13 | let engine = AudioEngine() 14 | let player = AudioPlayer() 15 | let decimator: Decimator 16 | let dryWetMixer: DryWetMixer 17 | let buffer: AVAudioPCMBuffer 18 | 19 | init() { 20 | buffer = Cookbook.sourceBuffer 21 | player.buffer = buffer 22 | player.isLooping = true 23 | 24 | decimator = Decimator(player) 25 | decimator.finalMix = 100 26 | dryWetMixer = DryWetMixer(player, decimator) 27 | engine.output = dryWetMixer 28 | } 29 | } 30 | 31 | struct DecimatorView: View { 32 | @StateObject var conductor = DecimatorConductor() 33 | 34 | var body: some View { 35 | VStack { 36 | PlayerControls(conductor: conductor) 37 | HStack { 38 | ForEach(conductor.decimator.parameters) { 39 | ParameterRow(param: $0) 40 | } 41 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 42 | } 43 | DryWetMixView(dry: conductor.player, 44 | wet: conductor.decimator, 45 | mix: conductor.dryWetMixer) 46 | } 47 | .padding() 48 | .cookbookNavBarTitle("Decimator") 49 | .onAppear { 50 | conductor.start() 51 | } 52 | .onDisappear { 53 | conductor.stop() 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Distortion/RingModulator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class RingModulatorConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let ringModulator: RingModulator 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | ringModulator = RingModulator(player) 21 | dryWetMixer = DryWetMixer(player, ringModulator) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct RingModulatorView: View { 27 | @StateObject var conductor = RingModulatorConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.ringModulator.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.ringModulator, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Ring Modulator") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Distortion/TanhDistortion.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class TanhDistortionConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let distortion: TanhDistortion 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | distortion = TanhDistortion(player) 21 | dryWetMixer = DryWetMixer(player, distortion) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct TanhDistortionView: View { 27 | @StateObject var conductor = TanhDistortionConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.distortion.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.distortion, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Tanh Distortion") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/AutoPanner.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class AutoPannerConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let panner: AutoPanner 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | panner = AutoPanner(player) 21 | dryWetMixer = DryWetMixer(player, panner) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct AutoPannerView: View { 27 | @StateObject var conductor = AutoPannerConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.panner.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.panner, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Auto Panner") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/AutoWah.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class AutoWahConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let autowah: AutoWah 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | autowah = AutoWah(player) 21 | dryWetMixer = DryWetMixer(player, autowah) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct AutoWahView: View { 27 | @StateObject var conductor = AutoWahConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.autowah.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.autowah, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Auto Wah") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Balancer.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import Controls 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class BalancerConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let buffer: AVAudioPCMBuffer 13 | let balancer: Balancer 14 | let variSpeed: VariSpeed 15 | let osc = Oscillator() 16 | let dryWetMixer: DryWetMixer 17 | 18 | @Published var frequency: AUValue = 440 { 19 | didSet { 20 | osc.$frequency.ramp(to: frequency, duration: 0.5) 21 | } 22 | } 23 | 24 | @Published var rate: AUValue = 1 { 25 | didSet { 26 | variSpeed.rate = rate 27 | } 28 | } 29 | 30 | @Published var balance: AUValue = 0.5 { 31 | didSet { 32 | dryWetMixer.balance = balance 33 | } 34 | } 35 | 36 | init() { 37 | buffer = Cookbook.sourceBuffer 38 | player.buffer = buffer 39 | player.isLooping = true 40 | 41 | osc.play() 42 | variSpeed = VariSpeed(player) 43 | let fader = Fader(variSpeed) 44 | balancer = Balancer(osc, comparator: fader) 45 | dryWetMixer = DryWetMixer(fader, balancer) 46 | engine.output = dryWetMixer 47 | } 48 | } 49 | 50 | struct BalancerView: View { 51 | @StateObject var conductor = BalancerConductor() 52 | 53 | var body: some View { 54 | VStack { 55 | PlayerControls(conductor: conductor) 56 | HStack { 57 | CookbookKnob(text: "Rate", parameter: $conductor.rate, range: 0.3125 ... 5) 58 | CookbookKnob(text: "Frequency", parameter: $conductor.frequency, range: 220 ... 880) 59 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 60 | } 61 | DryWetMixView(dry: conductor.player, 62 | wet: conductor.balancer, 63 | mix: conductor.dryWetMixer) 64 | } 65 | .padding() 66 | .cookbookNavBarTitle("Balancer") 67 | .onAppear { 68 | conductor.start() 69 | } 70 | .onDisappear { 71 | conductor.stop() 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Chorus.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import DunneAudioKit 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class ChorusConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let chorus: Chorus 13 | var dryWetMixer: DryWetMixer 14 | let buffer: AVAudioPCMBuffer 15 | 16 | init() { 17 | buffer = Cookbook.sourceBuffer 18 | player.buffer = buffer 19 | player.isLooping = true 20 | 21 | chorus = Chorus(player) 22 | dryWetMixer = DryWetMixer(player, chorus) 23 | engine.output = dryWetMixer 24 | } 25 | } 26 | 27 | struct ChorusView: View { 28 | @StateObject var conductor = ChorusConductor() 29 | 30 | var body: some View { 31 | VStack { 32 | PlayerControls(conductor: conductor) 33 | HStack { 34 | ForEach(conductor.chorus.parameters) { 35 | ParameterRow(param: $0) 36 | } 37 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 38 | } 39 | DryWetMixView(dry: conductor.player, 40 | wet: conductor.chorus, 41 | mix: conductor.dryWetMixer) 42 | } 43 | .padding() 44 | .cookbookNavBarTitle("Chorus") 45 | .onAppear { 46 | conductor.start() 47 | } 48 | .onDisappear { 49 | conductor.stop() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Compressor.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class CompressorConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let compressor: Compressor 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | compressor = Compressor(player) 21 | dryWetMixer = DryWetMixer(player, compressor) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct CompressorView: View { 27 | @StateObject var conductor = CompressorConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.compressor.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, wet: conductor.compressor, mix: conductor.dryWetMixer) 39 | } 40 | .padding() 41 | .cookbookNavBarTitle("Compressor") 42 | .onAppear { 43 | conductor.start() 44 | } 45 | .onDisappear { 46 | conductor.stop() 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Delay.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | // It's very common to mix exactly two inputs, one before processing occurs, 9 | // and one after, resulting in a combination of the two. This is so common 10 | // that many of the AudioKit nodes have a dry/wet mix parameter built in. 11 | // But, if you are building your own custom effects, or making a long chain 12 | // of effects, you can use DryWetMixer to blend your signals. 13 | 14 | class DelayConductor: ObservableObject, ProcessesPlayerInput { 15 | let engine = AudioEngine() 16 | let player = AudioPlayer() 17 | let delay: Delay 18 | let dryWetMixer: DryWetMixer 19 | let buffer: AVAudioPCMBuffer 20 | 21 | init() { 22 | buffer = Cookbook.sourceBuffer 23 | player.buffer = buffer 24 | player.isLooping = true 25 | 26 | delay = Delay(player) 27 | delay.feedback = 0.9 28 | delay.time = 0.01 29 | 30 | // We're not using delay's built in dry wet mix because 31 | // we are tapping the wet result so it can be plotted. 32 | delay.dryWetMix = 100 33 | dryWetMixer = DryWetMixer(player, delay) 34 | engine.output = dryWetMixer 35 | } 36 | } 37 | 38 | struct DelayView: View { 39 | @StateObject var conductor = DelayConductor() 40 | 41 | var body: some View { 42 | VStack { 43 | PlayerControls(conductor: conductor) 44 | HStack { 45 | ForEach(conductor.delay.parameters) { 46 | ParameterRow(param: $0) 47 | } 48 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 49 | } 50 | DryWetMixView(dry: conductor.player, 51 | wet: conductor.delay, 52 | mix: conductor.dryWetMixer) 53 | } 54 | .padding() 55 | .cookbookNavBarTitle("Delay") 56 | .onAppear { 57 | conductor.start() 58 | } 59 | .onDisappear { 60 | conductor.stop() 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/DynamicRangeCompressor.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class DynamicRangeCompressorConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let compressor: DynamicRangeCompressor 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | compressor = DynamicRangeCompressor(player) 21 | dryWetMixer = DryWetMixer(player, compressor) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct DynamicRangeCompressorView: View { 27 | @StateObject var conductor = DynamicRangeCompressorConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.compressor.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.compressor, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Dynamic Range Compressor") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Expander.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ExpanderConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let expander: Expander 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | expander = Expander(player) 21 | dryWetMixer = DryWetMixer(player, expander) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ExpanderView: View { 27 | @StateObject var conductor = ExpanderConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.expander.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.expander, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Expander") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Flanger.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import DunneAudioKit 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class FlangerConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let flanger: Flanger 13 | let dryWetMixer: DryWetMixer 14 | let buffer: AVAudioPCMBuffer 15 | 16 | init() { 17 | buffer = Cookbook.sourceBuffer 18 | player.buffer = buffer 19 | player.isLooping = true 20 | 21 | flanger = Flanger(player) 22 | dryWetMixer = DryWetMixer(player, flanger) 23 | engine.output = dryWetMixer 24 | } 25 | } 26 | 27 | struct FlangerView: View { 28 | @StateObject var conductor = FlangerConductor() 29 | 30 | var body: some View { 31 | VStack { 32 | PlayerControls(conductor: conductor) 33 | HStack { 34 | ForEach(conductor.flanger.parameters) { 35 | ParameterRow(param: $0) 36 | } 37 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 38 | } 39 | DryWetMixView(dry: conductor.player, 40 | wet: conductor.flanger, 41 | mix: conductor.dryWetMixer) 42 | } 43 | .padding() 44 | .cookbookNavBarTitle("Flanger") 45 | .onAppear { 46 | conductor.start() 47 | } 48 | .onDisappear { 49 | conductor.stop() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/MultiTapDelay.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: ## Multi-tap Delay 9 | //: A multi-tap delay is a delay line where multiple 'taps' or outputs are 10 | //: taken from a delay buffer at different points, and the taps are then 11 | //: summed with the original. Multi-tap delays are great for creating 12 | //: rhythmic delay patterns, but they can also be used to create sound 13 | //: fields of such density that they start to take on some of the qualities 14 | //: we'd more usually associate with reverb. - Geoff Smith, Sound on Sound 15 | 16 | class MultiTapDelayConductor: ObservableObject, ProcessesPlayerInput { 17 | let engine = AudioEngine() 18 | let player = AudioPlayer() 19 | let buffer: AVAudioPCMBuffer 20 | 21 | init() { 22 | buffer = Cookbook.sourceBuffer 23 | player.buffer = buffer 24 | player.isLooping = true 25 | 26 | var delays = [VariableDelay]() 27 | 28 | func multitapDelay(_ input: Node, times: [AUValue], gains: [AUValue]) -> Mixer { 29 | let mix = Mixer(input) 30 | var counter = 0 31 | zip(times, gains).forEach { time, gain in 32 | delays.append(VariableDelay(input, time: time)) 33 | mix.addInput(Fader(delays[counter], gain: gain)) 34 | counter += 1 35 | } 36 | return mix 37 | } 38 | 39 | engine.output = multitapDelay(player, times: [0.1, 0.2, 0.4], gains: [0.5, 2.0, 0.5]) 40 | } 41 | } 42 | 43 | struct MultiTapDelayView: View { 44 | @StateObject var conductor = MultiTapDelayConductor() 45 | 46 | var body: some View { 47 | VStack(spacing: 20) { 48 | Text(""" 49 | A multi-tap delay is a delay line where multiple 'taps' or outputs are taken from a delay buffer at different points, and the taps are then summed with the original. Multi-tap delays are great for creating rhythmic delay patterns, but they can also be used to create sound fields of such density that they start to take on some of the qualities we'd more usually associate with reverb. 50 | 51 | - Geoff Smith, Sound on Sound 52 | """) 53 | PlayerControls(conductor: conductor) 54 | } 55 | .padding() 56 | .cookbookNavBarTitle("MultiTap Delay Operation") 57 | .onAppear { 58 | conductor.start() 59 | } 60 | .onDisappear { 61 | conductor.stop() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Panner.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class PannerConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let panner: Panner 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | panner = Panner(player) 21 | dryWetMixer = DryWetMixer(player, panner) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct PannerView: View { 27 | @StateObject var conductor = PannerConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.panner.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.panner, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Panner") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/PeakLimiter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: A peak limiter will set a hard limit on the amplitude of an audio signal. 9 | //: They're especially useful for any type of live input processing, when you 10 | //: may not be in total control of the audio signal you're recording or processing. 11 | 12 | class PeakLimiterConductor: ObservableObject, ProcessesPlayerInput { 13 | let engine = AudioEngine() 14 | let player = AudioPlayer() 15 | let peakLimiter: PeakLimiter 16 | let dryWetMixer: DryWetMixer 17 | let buffer: AVAudioPCMBuffer 18 | 19 | init() { 20 | buffer = Cookbook.sourceBuffer 21 | player.buffer = buffer 22 | player.isLooping = true 23 | 24 | peakLimiter = PeakLimiter(player) 25 | dryWetMixer = DryWetMixer(player, peakLimiter) 26 | engine.output = dryWetMixer 27 | } 28 | } 29 | 30 | struct PeakLimiterView: View { 31 | @StateObject var conductor = PeakLimiterConductor() 32 | 33 | var body: some View { 34 | VStack { 35 | PlayerControls(conductor: conductor) 36 | HStack { 37 | ForEach(conductor.peakLimiter.parameters) { 38 | ParameterRow(param: $0) 39 | } 40 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 41 | } 42 | DryWetMixView(dry: conductor.player, 43 | wet: conductor.peakLimiter, 44 | mix: conductor.dryWetMixer) 45 | } 46 | .padding() 47 | .cookbookNavBarTitle("PeakLimiter") 48 | .onAppear { 49 | conductor.start() 50 | } 51 | .onDisappear { 52 | conductor.stop() 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/PhaseLockedVocoder.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import Controls 5 | import AVFoundation 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class PhaseLockedVocoderConductor: ObservableObject, HasAudioEngine { 10 | @Published var position: Float = 0.0 { 11 | didSet { 12 | phaseLockedVocoder.position = position 13 | } 14 | } 15 | 16 | let engine = AudioEngine() 17 | var phaseLockedVocoder: PhaseLockedVocoder 18 | 19 | init() { 20 | let url = Bundle.module.resourceURL?.appendingPathComponent("Samples/beat.aiff") 21 | let file = try! AVAudioFile(forReading: url!) 22 | phaseLockedVocoder = PhaseLockedVocoder(file: file) 23 | phaseLockedVocoder.amplitude = 1 24 | phaseLockedVocoder.pitchRatio = 1 25 | phaseLockedVocoder.start() 26 | engine.output = phaseLockedVocoder 27 | } 28 | } 29 | 30 | struct PhaseLockedVocoderView: View { 31 | @StateObject var conductor = PhaseLockedVocoderConductor() 32 | 33 | var body: some View { 34 | VStack { 35 | Text("Position: \(conductor.position)") 36 | Ribbon(position: $conductor.position) 37 | .cornerRadius(10) 38 | .frame(height: 50) 39 | NodeOutputView(conductor.phaseLockedVocoder) 40 | } 41 | .padding() 42 | .cookbookNavBarTitle("Phase Locked Vocoder") 43 | .onAppear { 44 | conductor.start() 45 | } 46 | .onDisappear { 47 | conductor.stop() 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Phaser.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class PhaserConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let phaser: Phaser 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | phaser = Phaser(player) 21 | dryWetMixer = DryWetMixer(player, phaser) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct PhaserView: View { 27 | @StateObject var conductor = PhaserConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.phaser.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.phaser, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Phaser") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/PitchShifter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class PitchShifterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let pitchshifter: PitchShifter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | pitchshifter = PitchShifter(player) 21 | dryWetMixer = DryWetMixer(player, pitchshifter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct PitchShifterView: View { 27 | @StateObject var conductor = PitchShifterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.pitchshifter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.pitchshifter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Pitch Shifter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/PlaybackSpeed.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SwiftUI 6 | 7 | // This recipe uses the VariSpeed node to change the playback speed of a file (which also affects the pitch) 8 | class PlaybackSpeedConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let variSpeed: VariSpeed 12 | let buffer: AVAudioPCMBuffer 13 | 14 | init() { 15 | buffer = Cookbook.sourceBuffer 16 | player.buffer = buffer 17 | player.isLooping = true 18 | 19 | variSpeed = VariSpeed(player) 20 | variSpeed.rate = 2.0 21 | engine.output = variSpeed 22 | } 23 | 24 | @Published var rate: AUValue = 2.0 { 25 | didSet { 26 | variSpeed.rate = rate 27 | } 28 | } 29 | } 30 | 31 | struct PlaybackSpeedView: View { 32 | @StateObject var conductor = PlaybackSpeedConductor() 33 | 34 | var body: some View { 35 | VStack { 36 | PlayerControls(conductor: conductor) 37 | CookbookKnob(text: "Rate", 38 | parameter: $conductor.rate, 39 | range: 0.3125 ... 5, 40 | units: "Generic") 41 | NodeRollingView(conductor.variSpeed) 42 | } 43 | .padding() 44 | .cookbookNavBarTitle("Playback Speed") 45 | .onAppear { 46 | conductor.start() 47 | } 48 | .onDisappear { 49 | conductor.stop() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/StereoDelay.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import DunneAudioKit 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class StereoDelayConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let delay: StereoDelay 13 | var dryWetMixer: DryWetMixer 14 | let buffer: AVAudioPCMBuffer 15 | 16 | init() { 17 | buffer = Cookbook.sourceBuffer 18 | player.buffer = buffer 19 | player.isLooping = true 20 | 21 | delay = StereoDelay(player) 22 | dryWetMixer = DryWetMixer(player, delay) 23 | engine.output = dryWetMixer 24 | } 25 | } 26 | 27 | struct StereoDelayView: View { 28 | @StateObject var conductor = StereoDelayConductor() 29 | 30 | var body: some View { 31 | VStack { 32 | PlayerControls(conductor: conductor) 33 | HStack { 34 | ForEach(conductor.delay.parameters) { 35 | ParameterRow(param: $0) 36 | } 37 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 38 | } 39 | DryWetMixView(dry: conductor.player, 40 | wet: conductor.delay, 41 | mix: conductor.dryWetMixer) 42 | } 43 | .padding() 44 | .cookbookNavBarTitle("Stereo Delay") 45 | .onAppear { 46 | conductor.start() 47 | } 48 | .onDisappear { 49 | conductor.stop() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/StringResonator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class StringResonatorConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: StringResonator 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = StringResonator(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct StringResonatorView: View { 27 | @StateObject var conductor = StringResonatorConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("String Resonator") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Talkbox.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | import Tonic 8 | 9 | class TalkboxConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let talkbox: Talkbox 13 | let buffer: AVAudioPCMBuffer 14 | var osc = DynamicOscillator() 15 | 16 | func noteOn(pitch: Pitch, point _: CGPoint) { 17 | isPlaying = true 18 | osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 19 | } 20 | 21 | func noteOff(pitch _: Pitch) { 22 | isPlaying = false 23 | } 24 | 25 | @Published var isPlaying: Bool = false { 26 | didSet { isPlaying ? osc.start() : osc.stop() } 27 | } 28 | 29 | init() { 30 | buffer = Cookbook.sourceBuffer 31 | player.buffer = buffer 32 | player.isLooping = true 33 | osc.amplitude = 0.5 34 | 35 | talkbox = Talkbox(player, excitation: osc) 36 | engine.output = talkbox 37 | } 38 | } 39 | 40 | struct TalkboxView: View { 41 | @StateObject var conductor = TalkboxConductor() 42 | @Environment(\.colorScheme) var colorScheme 43 | 44 | var body: some View { 45 | VStack { 46 | PlayerControls(conductor: conductor) 47 | HStack { 48 | ForEach(conductor.talkbox.parameters) { 49 | ParameterRow(param: $0) 50 | } 51 | } 52 | NodeOutputView(conductor.player) 53 | CookbookKeyboard(noteOn: conductor.noteOn, 54 | noteOff: conductor.noteOff) 55 | } 56 | .padding() 57 | .cookbookNavBarTitle("Talkbox") 58 | .onAppear { 59 | conductor.start() 60 | } 61 | .onDisappear { 62 | conductor.stop() 63 | } 64 | .background(colorScheme == .dark ? 65 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/TimePitch.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SwiftUI 6 | 7 | // With TimePitch you can easily change the pitch and speed of a player-generated sound. It does not work on live input or generated signals. 8 | 9 | struct TimePitchData { 10 | var rate: AUValue = 2.0 11 | var pitch: AUValue = -400 12 | } 13 | 14 | class TimePitchConductor: ObservableObject, ProcessesPlayerInput { 15 | let engine = AudioEngine() 16 | let player = AudioPlayer() 17 | let timePitch: TimePitch 18 | let buffer: AVAudioPCMBuffer 19 | 20 | init() { 21 | buffer = Cookbook.sourceBuffer 22 | player.buffer = buffer 23 | player.isLooping = true 24 | 25 | timePitch = TimePitch(player) 26 | timePitch.rate = 2.0 27 | timePitch.pitch = -400.0 28 | engine.output = timePitch 29 | } 30 | 31 | @Published var data = TimePitchData() { 32 | didSet { 33 | // When AudioKit uses an Apple AVAudioUnit, like the case here, the values can't be ramped 34 | timePitch.rate = data.rate 35 | timePitch.pitch = data.pitch 36 | } 37 | } 38 | 39 | } 40 | 41 | struct TimePitchView: View { 42 | @StateObject var conductor = TimePitchConductor() 43 | 44 | var body: some View { 45 | VStack { 46 | PlayerControls(conductor: conductor) 47 | 48 | HStack { 49 | CookbookKnob(text: "Rate", 50 | parameter: self.$conductor.data.rate, 51 | range: 0.3125 ... 5, 52 | units: "Generic") 53 | CookbookKnob(text: "Pitch", 54 | parameter: self.$conductor.data.pitch, 55 | range: -2400 ... 2400, 56 | units: "Cents") 57 | } 58 | NodeOutputView(conductor.timePitch) 59 | } 60 | .padding() 61 | .cookbookNavBarTitle("Time / Pitch") 62 | .onAppear { 63 | conductor.start() 64 | } 65 | .onDisappear { 66 | conductor.stop() 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/TransientShaper.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import DunneAudioKit 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class TransientShaperConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let transientshaper: TransientShaper 13 | let dryWetMixer: DryWetMixer 14 | let buffer: AVAudioPCMBuffer 15 | 16 | init() { 17 | buffer = Cookbook.sourceBuffer 18 | player.buffer = buffer 19 | player.isLooping = true 20 | 21 | transientshaper = TransientShaper(player) 22 | dryWetMixer = DryWetMixer(player, transientshaper) 23 | engine.output = dryWetMixer 24 | } 25 | } 26 | 27 | struct TransientShaperView: View { 28 | @StateObject var conductor = TransientShaperConductor() 29 | 30 | var body: some View { 31 | VStack { 32 | PlayerControls(conductor: conductor) 33 | HStack { 34 | ForEach(conductor.transientshaper.parameters) { 35 | ParameterRow(param: $0) 36 | } 37 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 38 | } 39 | DryWetMixView(dry: conductor.player, 40 | wet: conductor.transientshaper, 41 | mix: conductor.dryWetMixer) 42 | } 43 | .padding() 44 | .cookbookNavBarTitle("Transient Shaper") 45 | .onAppear { 46 | conductor.start() 47 | } 48 | .onDisappear { 49 | conductor.stop() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Tremolo.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class TremoloConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let tremolo: Tremolo 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | tremolo = Tremolo(player) 21 | dryWetMixer = DryWetMixer(player, tremolo) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct TremoloView: View { 27 | @StateObject var conductor = TremoloConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.tremolo.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.tremolo, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Tremolo") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/VariableDelay.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class VariableDelayConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let delay: VariableDelay 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | delay = VariableDelay(player) 21 | dryWetMixer = DryWetMixer(player, delay) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct VariableDelayView: View { 27 | @StateObject var conductor = VariableDelayConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.delay.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.delay, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Variable Delay") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Effects/Vocoder.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | import Tonic 8 | 9 | class VocoderConductor: ObservableObject, ProcessesPlayerInput { 10 | let engine = AudioEngine() 11 | let player = AudioPlayer() 12 | let vocoder: Vocoder 13 | let buffer: AVAudioPCMBuffer 14 | var osc = MorphingOscillator(index: 2.5) 15 | 16 | func noteOn(pitch: Pitch, point _: CGPoint) { 17 | isPlaying = true 18 | osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 19 | } 20 | 21 | func noteOff(pitch _: Pitch) { 22 | isPlaying = false 23 | } 24 | 25 | @Published var isPlaying: Bool = false { 26 | didSet { isPlaying ? osc.start() : osc.stop() } 27 | } 28 | 29 | init() { 30 | buffer = Cookbook.sourceBuffer 31 | player.buffer = buffer 32 | player.isLooping = true 33 | osc.amplitude = 0.5 34 | 35 | vocoder = Vocoder(player, excitation: osc) 36 | engine.output = vocoder 37 | 38 | vocoder.attackTime = 0.001 39 | vocoder.releaseTime = 0.02 40 | vocoder.bandwidthRatio = 0.1 41 | } 42 | } 43 | 44 | struct VocoderView: View { 45 | @StateObject var conductor = VocoderConductor() 46 | @Environment(\.colorScheme) var colorScheme 47 | 48 | var body: some View { 49 | VStack { 50 | PlayerControls(conductor: conductor) 51 | HStack { 52 | ForEach(conductor.vocoder.parameters) { 53 | ParameterRow(param: $0) 54 | } 55 | } 56 | NodeOutputView(conductor.player) 57 | CookbookKeyboard(noteOn: conductor.noteOn, 58 | noteOff: conductor.noteOff) 59 | } 60 | .padding() 61 | .cookbookNavBarTitle("Vocoder") 62 | .onAppear { 63 | conductor.start() 64 | } 65 | .onDisappear { 66 | conductor.stop() 67 | } 68 | .background(colorScheme == .dark ? 69 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/BandPassButterworthFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: Band-pass filters allow audio above a specified frequency range and 9 | //: bandwidth to pass through to an output. The center frequency is the starting point 10 | //: from where the frequency limit is set. Adjusting the bandwidth sets how far out 11 | //: above and below the center frequency the frequency band should be. 12 | //: Anything above that band should pass through. 13 | 14 | class BandPassButterworthFilterConductor: ObservableObject, ProcessesPlayerInput { 15 | let engine = AudioEngine() 16 | let player = AudioPlayer() 17 | let filter: BandPassButterworthFilter 18 | let dryWetMixer: DryWetMixer 19 | let buffer: AVAudioPCMBuffer 20 | 21 | init() { 22 | buffer = Cookbook.sourceBuffer 23 | player.buffer = buffer 24 | player.isLooping = true 25 | 26 | filter = BandPassButterworthFilter(player) 27 | dryWetMixer = DryWetMixer(player, filter) 28 | engine.output = dryWetMixer 29 | } 30 | } 31 | 32 | struct BandPassButterworthFilterView: View { 33 | @StateObject var conductor = BandPassButterworthFilterConductor() 34 | 35 | var body: some View { 36 | VStack { 37 | PlayerControls(conductor: conductor) 38 | HStack { 39 | ForEach(conductor.filter.parameters) { 40 | ParameterRow(param: $0) 41 | } 42 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 43 | } 44 | DryWetMixView(dry: conductor.player, 45 | wet: conductor.filter, 46 | mix: conductor.dryWetMixer) 47 | } 48 | .padding() 49 | .cookbookNavBarTitle("Band Pass Butterworth Filter") 50 | .onAppear { 51 | conductor.start() 52 | } 53 | .onDisappear { 54 | conductor.stop() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/BandRejectButterworthFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class BandRejectButterworthFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: BandRejectButterworthFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = BandRejectButterworthFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct BandRejectButterworthFilterView: View { 27 | @StateObject var conductor = BandRejectButterworthFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Band Reject Butterworth Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/CombFilterReverb.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class CombFilterReverbConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: CombFilterReverb 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = CombFilterReverb(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct CombFilterReverbView: View { 27 | @StateObject var conductor = CombFilterReverbConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Comb Filter Reverb") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/EqualizerFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class EqualizerFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: EqualizerFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = EqualizerFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct EqualizerFilterView: View { 27 | @StateObject var conductor = EqualizerFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Equalizer Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/FormantFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class FormantFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: FormantFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = FormantFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct FormantFilterView: View { 27 | @StateObject var conductor = FormantFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Formant Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/HighPassButterworthFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: A high-pass filter takes an audio signal as an input, and cuts out the 9 | //: low-frequency components of the audio signal, allowing for the higher frequency 10 | //: components to "pass through" the filter. 11 | 12 | class HighPassButterworthFilterConductor: ObservableObject, ProcessesPlayerInput { 13 | let engine = AudioEngine() 14 | let player = AudioPlayer() 15 | let filter: HighPassButterworthFilter 16 | let dryWetMixer: DryWetMixer 17 | let buffer: AVAudioPCMBuffer 18 | 19 | init() { 20 | buffer = Cookbook.sourceBuffer 21 | player.buffer = buffer 22 | player.isLooping = true 23 | 24 | filter = HighPassButterworthFilter(player) 25 | dryWetMixer = DryWetMixer(player, filter) 26 | engine.output = dryWetMixer 27 | } 28 | } 29 | 30 | struct HighPassButterworthFilterView: View { 31 | @StateObject var conductor = HighPassButterworthFilterConductor() 32 | 33 | var body: some View { 34 | VStack { 35 | PlayerControls(conductor: conductor) 36 | HStack { 37 | ForEach(conductor.filter.parameters) { 38 | ParameterRow(param: $0) 39 | } 40 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 41 | } 42 | DryWetMixView(dry: conductor.player, 43 | wet: conductor.filter, 44 | mix: conductor.dryWetMixer) 45 | } 46 | .padding() 47 | .cookbookNavBarTitle("High Pass Butterworth Filter") 48 | .onAppear { 49 | conductor.start() 50 | } 51 | .onDisappear { 52 | conductor.stop() 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/HighPassFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class HighPassFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: HighPassFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = HighPassFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct HighPassFilterView: View { 27 | @StateObject var conductor = HighPassFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("High Pass Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/HighShelfFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class HighShelfFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: HighShelfFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = HighShelfFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct HighShelfFilterView: View { 27 | @StateObject var conductor = HighShelfFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("High Shelf Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/HighShelfParametricEqualizerFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class HighShelfParametricEqualizerFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let equalizer: HighShelfParametricEqualizerFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | equalizer = HighShelfParametricEqualizerFilter(player) 21 | dryWetMixer = DryWetMixer(player, equalizer) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct HighShelfParametricEqualizerFilterView: View { 27 | @StateObject var conductor = HighShelfParametricEqualizerFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.equalizer.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.equalizer, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("High Shelf Parametric Equalizer Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/KorgLowPassFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class KorgLowPassFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: KorgLowPassFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = KorgLowPassFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct KorgLowPassFilterView: View { 27 | @StateObject var conductor = KorgLowPassFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Korg Low Pass Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/LowPassButterworthFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: A low-pass filter takes an audio signal as an input, and cuts out the 9 | //: high-frequency components of the audio signal, allowing for the 10 | //: lower frequency components to "pass through" the filter. 11 | 12 | class LowPassButterworthFilterConductor: ObservableObject, ProcessesPlayerInput { 13 | let engine = AudioEngine() 14 | let player = AudioPlayer() 15 | let filter: LowPassButterworthFilter 16 | let dryWetMixer: DryWetMixer 17 | let buffer: AVAudioPCMBuffer 18 | 19 | init() { 20 | buffer = Cookbook.sourceBuffer 21 | player.buffer = buffer 22 | player.isLooping = true 23 | 24 | filter = LowPassButterworthFilter(player) 25 | dryWetMixer = DryWetMixer(player, filter) 26 | engine.output = dryWetMixer 27 | } 28 | } 29 | 30 | struct LowPassButterworthFilterView: View { 31 | @StateObject var conductor = LowPassButterworthFilterConductor() 32 | 33 | var body: some View { 34 | VStack { 35 | PlayerControls(conductor: conductor) 36 | HStack { 37 | ForEach(conductor.filter.parameters) { 38 | ParameterRow(param: $0) 39 | } 40 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 41 | } 42 | DryWetMixView(dry: conductor.player, 43 | wet: conductor.filter, 44 | mix: conductor.dryWetMixer) 45 | } 46 | .padding() 47 | .cookbookNavBarTitle("Low Pass Butterworth Filter") 48 | .onAppear { 49 | conductor.start() 50 | } 51 | .onDisappear { 52 | conductor.stop() 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/LowPassFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class LowPassFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: LowPassFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = LowPassFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct LowPassFilterView: View { 27 | @StateObject var conductor = LowPassFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Low Pass Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/LowShelfFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class LowShelfFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: LowShelfFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = LowShelfFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct LowShelfFilterView: View { 27 | @StateObject var conductor = LowShelfFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Low Shelf Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/LowShelfParametricEqualizerFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class LowShelfParametricEqualizerFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: LowShelfParametricEqualizerFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = LowShelfParametricEqualizerFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct LowShelfParametricEqualizerFilterView: View { 27 | @StateObject var conductor = LowShelfParametricEqualizerFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Low Shelf Parametric Equalizer Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/ModalResonanceFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ModalResonanceFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: ModalResonanceFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = ModalResonanceFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ModalResonanceFilterView: View { 27 | @StateObject var conductor = ModalResonanceFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Modal Resonance Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/MoogLadder.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | //: One of the coolest filters available in AudioKit is the Moog Ladder. 9 | //: It's based off of Robert Moog's iconic ladder filter, which was the 10 | //: first implementation of a voltage - controlled filter used in an 11 | //: analog synthesizer. As such, it was the first filter that gave the 12 | //: ability to use voltage control to determine the cutoff frequency of the 13 | //: filter. As we're dealing with a software implementation, and not an 14 | //: analog synthesizer, we don't have to worry about dealing with 15 | //: voltage control directly. However, by using this node, you can 16 | //: emulate some of the sounds of classic analog synthesizers in your app. 17 | 18 | class MoogLadderConductor: ObservableObject, ProcessesPlayerInput { 19 | let engine = AudioEngine() 20 | let player = AudioPlayer() 21 | let filter: MoogLadder 22 | let dryWetMixer: DryWetMixer 23 | let buffer: AVAudioPCMBuffer 24 | 25 | init() { 26 | buffer = Cookbook.sourceBuffer 27 | player.buffer = buffer 28 | player.isLooping = true 29 | 30 | filter = MoogLadder(player) 31 | dryWetMixer = DryWetMixer(player, filter) 32 | engine.output = dryWetMixer 33 | } 34 | } 35 | 36 | struct MoogLadderView: View { 37 | @StateObject var conductor = MoogLadderConductor() 38 | 39 | var body: some View { 40 | VStack { 41 | PlayerControls(conductor: conductor) 42 | HStack { 43 | ForEach(conductor.filter.parameters) { 44 | ParameterRow(param: $0) 45 | } 46 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 47 | } 48 | DryWetMixView(dry: conductor.player, 49 | wet: conductor.filter, 50 | mix: conductor.dryWetMixer) 51 | } 52 | .padding() 53 | .cookbookNavBarTitle("Moog Ladder") 54 | .onAppear { 55 | conductor.start() 56 | } 57 | .onDisappear { 58 | conductor.stop() 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/PeakingParametricEqualizerFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class PeakingParametricEqualizerFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: PeakingParametricEqualizerFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = PeakingParametricEqualizerFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct PeakingParametricEqualizerFilterView: View { 27 | @StateObject var conductor = PeakingParametricEqualizerFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Peaking Parametric Equalizer Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/ResonantFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ResonantFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: ResonantFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = ResonantFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ResonantFilterView: View { 27 | @StateObject var conductor = ResonantFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Resonant Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/ThreePoleLowpassFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ThreePoleLowpassFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: ThreePoleLowpassFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = ThreePoleLowpassFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ThreePoleLowpassFilterView: View { 27 | @StateObject var conductor = ThreePoleLowpassFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Three Pole Lowpass Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/ToneComplementFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ToneComplementFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: ToneComplementFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = ToneComplementFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ToneComplementFilterView: View { 27 | @StateObject var conductor = ToneComplementFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Tone Complement Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Filters/ToneFilter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ToneFilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: ToneFilter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = ToneFilter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ToneFilterView: View { 27 | @StateObject var conductor = ToneFilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Tone Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/DrumSynthesizers.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class DrumSynthesizersConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | let kick = SynthKick() 10 | let snare = SynthSnare(duration: 0.07) 11 | var reverb: Reverb 12 | var loop: CallbackLoop! 13 | var counter = 0 14 | 15 | @Published var isRunning = false { 16 | didSet { 17 | isRunning ? loop.start() : loop.stop() 18 | } 19 | } 20 | 21 | init() { 22 | let mix = Mixer(kick, snare) 23 | reverb = Reverb(mix) 24 | engine.output = reverb 25 | 26 | loop = CallbackLoop(frequency: 5) { 27 | let randomVelocity = MIDIVelocity(AUValue.random(in: 0 ... 127)) 28 | let onFirstBeat = self.counter % 4 == 0 29 | let everyOtherBeat = self.counter % 4 == 2 30 | let randomHit = Array(0 ... 3).randomElement() == 0 31 | 32 | if onFirstBeat || randomHit { 33 | print("play kick") 34 | self.kick.play(noteNumber: 60, velocity: randomVelocity) 35 | self.kick.stop(noteNumber: 60) 36 | } 37 | 38 | if everyOtherBeat { 39 | print("play snare") 40 | let velocity = MIDIVelocity(Array(0 ... 100).randomElement()!) 41 | self.snare.play(noteNumber: 60, velocity: velocity, channel: 0) 42 | self.snare.stop(noteNumber: 60) 43 | } 44 | self.counter += 1 45 | } 46 | } 47 | } 48 | 49 | struct DrumSynthesizersView: View { 50 | @StateObject var conductor = DrumSynthesizersConductor() 51 | 52 | var body: some View { 53 | VStack { 54 | Text(conductor.isRunning ? "Stop" : "Start") 55 | .foregroundColor(.blue) 56 | .onTapGesture { 57 | conductor.isRunning.toggle() 58 | } 59 | NodeOutputView(conductor.reverb) 60 | } 61 | .padding() 62 | .cookbookNavBarTitle("Drum Synthesizers") 63 | .onAppear { 64 | conductor.start() 65 | } 66 | .onDisappear { 67 | conductor.isRunning = false 68 | conductor.stop() 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/InstrumentEXS.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import Keyboard 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | import Tonic 9 | 10 | class InstrumentEXSConductor: ObservableObject, HasAudioEngine { 11 | let engine = AudioEngine() 12 | var instrument = AppleSampler() 13 | 14 | func noteOn(pitch: Pitch, point _: CGPoint) { 15 | instrument.play(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), velocity: 90, channel: 0) 16 | } 17 | 18 | func noteOff(pitch: Pitch) { 19 | instrument.stop(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), channel: 0) 20 | } 21 | 22 | init() { 23 | engine.output = instrument 24 | 25 | // Load EXS file (you can also load SoundFonts and WAV files too using the AppleSampler Class) 26 | do { 27 | if let fileURL = Bundle.main.url(forResource: "Sounds/Sampler Instruments/sawPiano1", withExtension: "exs") { 28 | try instrument.loadInstrument(url: fileURL) 29 | } else { 30 | Log("Could not find file") 31 | } 32 | } catch { 33 | Log("Could not load instrument") 34 | } 35 | } 36 | } 37 | 38 | struct InstrumentEXSView: View { 39 | @StateObject var conductor = InstrumentEXSConductor() 40 | @Environment(\.colorScheme) var colorScheme 41 | 42 | var body: some View { 43 | NodeOutputView(conductor.instrument) 44 | CookbookKeyboard(noteOn: conductor.noteOn, 45 | noteOff: conductor.noteOff) 46 | .cookbookNavBarTitle("Instrument EXS") 47 | .onAppear { 48 | conductor.start() 49 | } 50 | .onDisappear { 51 | conductor.stop() 52 | } 53 | .background(colorScheme == .dark ? 54 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/MIDITrack.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SwiftUI 5 | 6 | struct MIDITrackDemo: View { 7 | @StateObject var viewModel = MIDITrackViewModel() 8 | @State var fileURL: URL? = Bundle.module.url(forResource: "MIDI Files/Demo", withExtension: "mid") 9 | @State var isPlaying = false 10 | public var body: some View { 11 | VStack { 12 | GeometryReader { geometry in 13 | ScrollView { 14 | if let fileURL = fileURL { 15 | ForEach( 16 | MIDIFile(url: fileURL).tracks.indices, id: \.self 17 | ) { number in 18 | MIDITrackView(fileURL: $fileURL, 19 | trackNumber: number, 20 | trackWidth: geometry.size.width, 21 | trackHeight: 200.0) 22 | .background(Color.primary) 23 | .cornerRadius(10.0) 24 | } 25 | } 26 | } 27 | } 28 | } 29 | .padding() 30 | .navigationBarTitle(Text("MIDI Track View")) 31 | .onTapGesture { 32 | isPlaying.toggle() 33 | } 34 | .onChange(of: isPlaying, perform: { newValue in 35 | if newValue == true { 36 | viewModel.play() 37 | } else { 38 | viewModel.stop() 39 | } 40 | }) 41 | .onAppear(perform: { 42 | viewModel.startEngine() 43 | if let fileURL = Bundle.module.url(forResource: "MIDI Files/Demo", withExtension: "mid") { 44 | viewModel.loadSequencerFile(fileURL: fileURL) 45 | } 46 | }) 47 | .onDisappear(perform: { 48 | viewModel.stop() 49 | viewModel.stopEngine() 50 | }) 51 | .environmentObject(viewModel) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/NoiseGenerators.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Controls 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | struct NoiseData { 10 | var brownianAmplitude: AUValue = 0.0 11 | var pinkAmplitude: AUValue = 0.0 12 | var whiteAmplitude: AUValue = 0.0 13 | } 14 | 15 | class NoiseGeneratorsConductor: ObservableObject, HasAudioEngine { 16 | let engine = AudioEngine() 17 | var brown = BrownianNoise() 18 | var pink = PinkNoise() 19 | var white = WhiteNoise() 20 | var mixer = Mixer() 21 | 22 | @Published var data = NoiseData() { 23 | didSet { 24 | brown.amplitude = data.brownianAmplitude 25 | pink.amplitude = data.pinkAmplitude 26 | white.amplitude = data.whiteAmplitude 27 | } 28 | } 29 | 30 | init() { 31 | mixer.addInput(brown) 32 | mixer.addInput(pink) 33 | mixer.addInput(white) 34 | 35 | brown.amplitude = data.brownianAmplitude 36 | pink.amplitude = data.pinkAmplitude 37 | white.amplitude = data.whiteAmplitude 38 | brown.start() 39 | pink.start() 40 | white.start() 41 | 42 | engine.output = mixer 43 | } 44 | } 45 | 46 | struct NoiseGeneratorsView: View { 47 | @StateObject var conductor = NoiseGeneratorsConductor() 48 | 49 | var body: some View { 50 | VStack { 51 | HStack { 52 | CookbookKnob(text: "Brownian", parameter: self.$conductor.data.brownianAmplitude, range: 0...1) 53 | CookbookKnob(text: "Pink", parameter: self.$conductor.data.pinkAmplitude, range: 0...1) 54 | CookbookKnob(text: "White", parameter: self.$conductor.data.whiteAmplitude, range: 0...1) 55 | }.padding(5) 56 | NodeOutputView(conductor.mixer) 57 | }.cookbookNavBarTitle("Noise Generators") 58 | .onAppear { 59 | conductor.start() 60 | } 61 | .onDisappear { 62 | conductor.stop() 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/Recorder.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SwiftUI 6 | 7 | struct RecorderData { 8 | var isRecording = false 9 | var isPlaying = false 10 | } 11 | 12 | class RecorderConductor: ObservableObject, HasAudioEngine { 13 | let engine = AudioEngine() 14 | var recorder: NodeRecorder? 15 | let player = AudioPlayer() 16 | var silencer: Fader? 17 | let mixer = Mixer() 18 | 19 | @Published var data = RecorderData() { 20 | didSet { 21 | if data.isRecording { 22 | do { 23 | try recorder?.record() 24 | } catch let err { 25 | print(err) 26 | } 27 | } else { 28 | recorder?.stop() 29 | } 30 | 31 | if data.isPlaying { 32 | if let file = recorder?.audioFile { 33 | try? player.load(file: file) 34 | player.play() 35 | } 36 | } else { 37 | player.stop() 38 | } 39 | } 40 | } 41 | 42 | init() { 43 | guard let input = engine.input else { 44 | fatalError() 45 | } 46 | 47 | do { 48 | recorder = try NodeRecorder(node: input) 49 | } catch let err { 50 | fatalError("\(err)") 51 | } 52 | let silencer = Fader(input, gain: 0) 53 | self.silencer = silencer 54 | mixer.addInput(silencer) 55 | mixer.addInput(player) 56 | engine.output = mixer 57 | } 58 | } 59 | 60 | struct RecorderView: View { 61 | @StateObject var conductor = RecorderConductor() 62 | 63 | var body: some View { 64 | VStack { 65 | Spacer() 66 | Text(conductor.data.isRecording ? "STOP RECORDING" : "RECORD") 67 | .foregroundColor(.blue) 68 | .onTapGesture { 69 | conductor.data.isRecording.toggle() 70 | } 71 | Spacer() 72 | Text(conductor.data.isPlaying ? "STOP" : "PLAY") 73 | .foregroundColor(.blue) 74 | .onTapGesture { 75 | conductor.data.isPlaying.toggle() 76 | } 77 | Spacer() 78 | } 79 | 80 | .padding() 81 | .cookbookNavBarTitle("Recorder") 82 | .onAppear { 83 | conductor.start() 84 | } 85 | .onDisappear { 86 | conductor.stop() 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/VocalTract.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Combine 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | 9 | class VocalTractConductor: ObservableObject, HasAudioEngine { 10 | let engine = AudioEngine() 11 | 12 | @Published var isPlaying: Bool = false { 13 | didSet { isPlaying ? voc.start() : voc.stop() } 14 | } 15 | 16 | var voc = VocalTract() 17 | 18 | init() { 19 | engine.output = voc 20 | } 21 | } 22 | 23 | struct Button2: View { 24 | var text: String 25 | var onTap: () -> Void 26 | 27 | var body: some View { 28 | ZStack { 29 | RoundedRectangle(cornerRadius: 10).foregroundColor(.gray) 30 | Text(text).onTapGesture { 31 | self.onTap() 32 | } 33 | } 34 | } 35 | } 36 | 37 | struct VocalTractView: View { 38 | @StateObject var conductor = VocalTractConductor() 39 | 40 | var body: some View { 41 | VStack { 42 | Text(conductor.isPlaying ? "STOP" : "START") 43 | .foregroundColor(.blue) 44 | .onTapGesture { 45 | conductor.isPlaying.toggle() 46 | } 47 | 48 | Button2(text: "Randomize") { 49 | conductor.voc.frequency = AUValue.random(in: 0 ... 2000) 50 | conductor.voc.tonguePosition = AUValue.random(in: 0 ... 1) 51 | conductor.voc.tongueDiameter = AUValue.random(in: 0 ... 1) 52 | conductor.voc.tenseness = AUValue.random(in: 0 ... 1) 53 | conductor.voc.nasality = AUValue.random(in: 0 ... 1) 54 | } 55 | 56 | HStack { 57 | ForEach(conductor.voc.parameters) { 58 | ParameterRow(param: $0) 59 | } 60 | }.frame(height: 150) 61 | NodeOutputView(conductor.voc) 62 | }.cookbookNavBarTitle("Vocal Tract") 63 | .padding() 64 | .onAppear { 65 | conductor.start() 66 | } 67 | .onDisappear { 68 | conductor.stop() 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/CrossingSignal.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class CrossingSignalConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator { 17 | // Generate a sine wave at the right frequency 18 | let crossingSignalTone = Operation.sineWave(frequency: 2500) 19 | 20 | // Periodically trigger an envelope around that signal 21 | let crossingSignalTrigger = Operation.periodicTrigger(period: 0.2) 22 | let crossingSignal = crossingSignalTone.triggeredWithEnvelope( 23 | trigger: crossingSignalTrigger, 24 | attack: 0.01, 25 | hold: 0.1, 26 | release: 0.01 27 | ) 28 | 29 | // scale the volume 30 | return crossingSignal * 0.2 31 | } 32 | 33 | init() { 34 | engine.output = generator 35 | } 36 | } 37 | 38 | struct CrossingSignalView: View { 39 | @StateObject var conductor = CrossingSignalConductor() 40 | 41 | var body: some View { 42 | VStack(spacing: 50) { 43 | Text("A British crossing signal implemented with AudioKit, an example from Andy Farnell's excellent book \"Designing Sound\"") 44 | Text(conductor.isRunning ? "Stop" : "Start") 45 | .foregroundColor(.blue) 46 | .onTapGesture { 47 | conductor.isRunning.toggle() 48 | } 49 | NodeOutputView(conductor.generator) 50 | } 51 | .padding() 52 | .cookbookNavBarTitle("Crossing Signal") 53 | .onAppear { 54 | conductor.start() 55 | } 56 | .onDisappear { 57 | conductor.stop() 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/DroneOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class DroneOperationConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator { 17 | func drone(frequency: Double, rate: Double) -> OperationParameter { 18 | let metro = Operation.metronome(frequency: rate) 19 | let tone = Operation.sineWave(frequency: frequency, amplitude: 0.2) 20 | return tone.triggeredWithEnvelope(trigger: metro, attack: 0.01, hold: 0.1, release: 0.1) 21 | } 22 | 23 | let drone1 = drone(frequency: 440, rate: 3) 24 | let drone2 = drone(frequency: 330, rate: 5) 25 | let drone3 = drone(frequency: 450, rate: 7) 26 | 27 | return (drone1 + drone2 + drone3) / 3 28 | } 29 | 30 | init() { 31 | engine.output = generator 32 | } 33 | } 34 | 35 | struct DroneOperationView: View { 36 | @StateObject var conductor = DroneOperationConductor() 37 | 38 | var body: some View { 39 | VStack(spacing: 50) { 40 | Text("Encapsualating functionality of operations into functions") 41 | Text(conductor.isRunning ? "Stop" : "Start") 42 | .foregroundColor(.blue) 43 | .onTapGesture { 44 | conductor.isRunning.toggle() 45 | } 46 | NodeOutputView(conductor.generator) 47 | } 48 | .padding() 49 | .cookbookNavBarTitle("Drone Operation") 50 | .onAppear { 51 | conductor.start() 52 | } 53 | .onDisappear { 54 | conductor.stop() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/InstrumentOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class InstrumentOperationConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator { 17 | func instrument(noteNumber: MIDINoteNumber, rate: Double, amplitude: Double) -> OperationParameter { 18 | let metro = Operation.metronome(frequency: 82.0 / (60.0 * rate)) 19 | let frequency = Double(noteNumber.midiNoteToFrequency()) 20 | let fm = Operation.fmOscillator(baseFrequency: frequency, amplitude: amplitude) 21 | 22 | return fm.triggeredWithEnvelope(trigger: metro, attack: 0.5, hold: 1, release: 1) 23 | } 24 | 25 | let instrument1 = instrument(noteNumber: 60, rate: 4, amplitude: 0.5) 26 | let instrument2 = instrument(noteNumber: 62, rate: 5, amplitude: 0.4) 27 | let instrument3 = instrument(noteNumber: 65, rate: 7, amplitude: 1.3 / 4.0) 28 | let instrument4 = instrument(noteNumber: 67, rate: 7, amplitude: 0.125) 29 | 30 | let instruments = (instrument1 + instrument2 + instrument3 + instrument4) * 0.13 31 | 32 | let reverb = instruments.reverberateWithCostello(feedback: 0.9, cutoffFrequency: 10000).toMono() 33 | 34 | return mixer(instruments, reverb, balance: 0.4) 35 | } 36 | 37 | init() { 38 | engine.output = generator 39 | } 40 | } 41 | 42 | struct InstrumentOperationView: View { 43 | @StateObject var conductor = InstrumentOperationConductor() 44 | 45 | var body: some View { 46 | VStack(spacing: 50) { 47 | Text("Encapsualating functionality of operations into functions") 48 | Text(conductor.isRunning ? "Stop" : "Start") 49 | .foregroundColor(.blue) 50 | .onTapGesture { 51 | conductor.isRunning.toggle() 52 | } 53 | NodeOutputView(conductor.generator) 54 | } 55 | .padding() 56 | .cookbookNavBarTitle("Instrument Operation") 57 | .onAppear { 58 | conductor.start() 59 | } 60 | .onDisappear { 61 | conductor.stop() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/LFOOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class LFOOperationConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator { 17 | let frequencyLFO = Operation.square(frequency: 1) 18 | .scale(minimum: 440, maximum: 880) 19 | let carrierLFO = Operation.triangle(frequency: 1) 20 | .scale(minimum: 1, maximum: 2) 21 | let modulatingMultiplierLFO = Operation.sawtooth(frequency: 1) 22 | .scale(minimum: 0.1, maximum: 2) 23 | let modulatingIndexLFO = Operation.reverseSawtooth(frequency: 1) 24 | .scale(minimum: 0.1, maximum: 20) 25 | 26 | return Operation.fmOscillator( 27 | baseFrequency: frequencyLFO, 28 | carrierMultiplier: carrierLFO, 29 | modulatingMultiplier: modulatingMultiplierLFO, 30 | modulationIndex: modulatingIndexLFO, 31 | amplitude: 0.2 32 | ) 33 | } 34 | 35 | init() { 36 | engine.output = generator 37 | } 38 | } 39 | 40 | struct LFOOperationView: View { 41 | @StateObject var conductor = LFOOperationConductor() 42 | 43 | var body: some View { 44 | VStack(spacing: 50) { 45 | Text("Often we want rhythmic changing of parameters that varying in a standard way. This is traditionally done with Low-Frequency Oscillators, LFOs.") 46 | Text(conductor.isRunning ? "Stop" : "Start") 47 | .foregroundColor(.blue) 48 | .onTapGesture { 49 | conductor.isRunning.toggle() 50 | } 51 | NodeOutputView(conductor.generator) 52 | } 53 | .padding() 54 | .cookbookNavBarTitle("LFO Operation") 55 | .onAppear { 56 | conductor.start() 57 | } 58 | .onDisappear { 59 | conductor.stop() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/PhasorOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class PhasorOperationConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator { 17 | let interval: Double = 2 18 | let noteCount: Double = 24 19 | let startingNote: Double = 48 // C 20 | 21 | let phasing = Operation.phasor(frequency: 0.5) * Operation.randomNumberPulse(minimum: 0.9, maximum: 2, updateFrequency: 0.5) 22 | let frequency = (floor(phasing * noteCount) * interval + startingNote) 23 | .midiNoteToFrequency() 24 | 25 | var amplitude = (phasing - 1).portamento() // prevents the click sound 26 | 27 | var oscillator = Operation.sineWave(frequency: frequency, amplitude: amplitude) 28 | let reverb = oscillator.reverberateWithChowning() 29 | return mixer(oscillator, reverb, balance: 0.6) 30 | } 31 | 32 | init() { 33 | engine.output = generator 34 | } 35 | } 36 | 37 | struct PhasorOperationView: View { 38 | @StateObject var conductor = PhasorOperationConductor() 39 | 40 | var body: some View { 41 | VStack(spacing: 50) { 42 | Text("Using the phasor to sweep amplitude and frequencies") 43 | Text(conductor.isRunning ? "Stop" : "Start") 44 | .foregroundColor(.blue) 45 | .onTapGesture { 46 | conductor.isRunning.toggle() 47 | } 48 | NodeOutputView(conductor.generator) 49 | } 50 | .padding() 51 | .cookbookNavBarTitle("Phasor Operation") 52 | .onAppear { 53 | conductor.start() 54 | } 55 | .onDisappear { 56 | conductor.stop() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/SegmentOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class SegmentOperationConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator { parameters in 17 | let updateRate = parameters[0] 18 | 19 | // Vary the starting frequency and duration randomly 20 | let start = Operation.randomNumberPulse() * 2000 + 300 21 | let duration = Operation.randomNumberPulse() 22 | let frequency = Operation.lineSegment(trigger: Operation.metronome(frequency: updateRate), 23 | start: start, 24 | end: 0, 25 | duration: duration) 26 | 27 | // Decrease the amplitude exponentially 28 | let amplitude = Operation.exponentialSegment(trigger: Operation.metronome(frequency: updateRate), 29 | start: 0.3, 30 | end: 0.01, 31 | duration: 1.0 / updateRate) 32 | return Operation.sineWave(frequency: frequency, amplitude: amplitude) 33 | } 34 | 35 | init() { 36 | let delay = Delay(generator) 37 | generator.parameter1 = 2.0 38 | 39 | //: Add some effects for good fun 40 | delay.time = 0.125 41 | delay.feedback = 0.8 42 | let reverb = Reverb(delay) 43 | reverb.loadFactoryPreset(.largeHall) 44 | 45 | engine.output = reverb 46 | } 47 | } 48 | 49 | struct SegmentOperationView: View { 50 | @StateObject var conductor = SegmentOperationConductor() 51 | 52 | var body: some View { 53 | VStack(spacing: 50) { 54 | Text("Creating segments that vary parameters in operations linearly or exponentially over a certain duration") 55 | Text(conductor.isRunning ? "Stop" : "Start") 56 | .foregroundColor(.blue) 57 | .onTapGesture { 58 | conductor.isRunning.toggle() 59 | } 60 | NodeOutputView(conductor.generator) 61 | } 62 | .padding() 63 | .cookbookNavBarTitle("Segment Operation") 64 | .onAppear { 65 | conductor.start() 66 | } 67 | .onDisappear { 68 | conductor.stop() 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/SmoothDelayOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SporthAudioKit 6 | import SwiftUI 7 | 8 | struct SmoothDelayOperationData { 9 | var time: AUValue = 0.1 10 | var feedback: AUValue = 0.7 11 | var rampDuration: AUValue = 0.1 12 | } 13 | 14 | class SmoothDelayOperationConductor: ObservableObject, ProcessesPlayerInput { 15 | let engine = AudioEngine() 16 | let player = AudioPlayer() 17 | let buffer: AVAudioPCMBuffer 18 | let effect: OperationEffect 19 | 20 | @Published var data = SmoothDelayOperationData() { 21 | didSet { 22 | effect.$parameter1.ramp(to: data.time, duration: data.rampDuration) 23 | effect.$parameter2.ramp(to: data.feedback, duration: data.rampDuration) 24 | } 25 | } 26 | 27 | init() { 28 | buffer = Cookbook.sourceBuffer 29 | player.buffer = buffer 30 | player.isLooping = true 31 | 32 | effect = OperationEffect(player) { player, parameters in 33 | let delayedPlayer = player.smoothDelay( 34 | time: parameters[0], 35 | feedback: parameters[1], 36 | samples: 1024, 37 | maximumDelayTime: 2.0 38 | ) 39 | return mixer(player.toMono(), delayedPlayer) 40 | } 41 | effect.parameter1 = 0.1 42 | effect.parameter2 = 0.7 43 | 44 | engine.output = effect 45 | } 46 | } 47 | 48 | struct SmoothDelayOperationView: View { 49 | @StateObject var conductor = SmoothDelayOperationConductor() 50 | 51 | var body: some View { 52 | VStack(spacing: 20) { 53 | PlayerControls(conductor: conductor) 54 | HStack { 55 | CookbookKnob(text: "Time", 56 | parameter: $conductor.data.time, 57 | range: 0 ... 0.3, 58 | units: "Seconds") 59 | CookbookKnob(text: "Feedback", 60 | parameter: $conductor.data.feedback, 61 | range: 0 ... 1, 62 | units: "%") 63 | } 64 | NodeOutputView(conductor.effect) 65 | } 66 | .padding() 67 | .cookbookNavBarTitle("Smooth Delay Operation") 68 | .onAppear { 69 | conductor.start() 70 | } 71 | .onDisappear { 72 | conductor.stop() 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/StereoOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import SporthAudioKit 5 | import SwiftUI 6 | 7 | class StereoOperationConductor: ObservableObject, HasAudioEngine { 8 | let engine = AudioEngine() 9 | 10 | @Published var isRunning = false { 11 | didSet { 12 | isRunning ? generator.start() : generator.stop() 13 | } 14 | } 15 | 16 | let generator = OperationGenerator(channelCount: 2) { _ in 17 | 18 | let slowSine = round(Operation.sineWave(frequency: 1) * 12) / 12 19 | let vibrato = slowSine.scale(minimum: -1200, maximum: 1200) 20 | 21 | let fastSine = Operation.sineWave(frequency: 10) 22 | let volume = fastSine.scale(minimum: 0, maximum: 0.5) 23 | 24 | let leftOutput = Operation.sineWave(frequency: 440 + vibrato, amplitude: volume) 25 | let rightOutput = Operation.sineWave(frequency: 220 + vibrato, amplitude: volume) 26 | 27 | return [leftOutput, rightOutput] 28 | } 29 | 30 | init() { 31 | engine.output = generator 32 | } 33 | } 34 | 35 | struct StereoOperationView: View { 36 | @StateObject var conductor = StereoOperationConductor() 37 | 38 | var body: some View { 39 | VStack(spacing: 50) { 40 | Text("This is an example of building a stereo sound generator.") 41 | Text(conductor.isRunning ? "Stop" : "Start") 42 | .foregroundColor(.blue) 43 | .onTapGesture { 44 | conductor.isRunning.toggle() 45 | } 46 | NodeOutputView(conductor.generator) 47 | } 48 | .padding() 49 | .cookbookNavBarTitle("Stereo Operation") 50 | .onAppear { 51 | conductor.start() 52 | } 53 | .onDisappear { 54 | conductor.stop() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Operations/VocalTractOperation.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import SporthAudioKit 6 | import SwiftUI 7 | 8 | class VocalTractOperationConductor: ObservableObject, HasAudioEngine { 9 | @Published var isPlaying = false { 10 | didSet { 11 | isPlaying ? generator.start() : generator.stop() 12 | } 13 | } 14 | 15 | let engine = AudioEngine() 16 | 17 | let generator = OperationGenerator { 18 | let frequency = Operation.sineWave(frequency: 1).scale(minimum: 100, maximum: 300) 19 | let jitter = Operation.jitter(amplitude: 300, minimumFrequency: 1, maximumFrequency: 3) 20 | let position = Operation.sineWave(frequency: 0.1).scale() 21 | let diameter = Operation.sineWave(frequency: 0.2).scale() 22 | let tenseness = Operation.sineWave(frequency: 0.3).scale() 23 | let nasality = Operation.sineWave(frequency: 0.35).scale() 24 | return Operation.vocalTract(frequency: frequency + jitter, 25 | tonguePosition: position, 26 | tongueDiameter: diameter, 27 | tenseness: tenseness, 28 | nasality: nasality) 29 | } 30 | 31 | init() { 32 | engine.output = generator 33 | } 34 | } 35 | 36 | struct VocalTractOperationView: View { 37 | @StateObject var conductor = VocalTractOperationConductor() 38 | 39 | var body: some View { 40 | VStack(spacing: 50) { 41 | Text(conductor.isPlaying ? "Stop!" : "More!") 42 | .foregroundColor(.blue) 43 | .onTapGesture { 44 | conductor.isPlaying.toggle() 45 | } 46 | NodeOutputView(conductor.generator) 47 | } 48 | .cookbookNavBarTitle("Vocal Fun") 49 | .padding() 50 | .onAppear { 51 | conductor.start() 52 | } 53 | .onDisappear { 54 | conductor.stop() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Oscillators/MorphingOscillator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Keyboard 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | import Tonic 9 | 10 | class MorphingOscillatorConductor: ObservableObject, HasAudioEngine { 11 | var engine = AudioEngine() 12 | var osc = MorphingOscillator() 13 | 14 | func noteOn(pitch: Pitch, point _: CGPoint) { 15 | isPlaying = true 16 | osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 17 | } 18 | 19 | func noteOff(pitch _: Pitch) { 20 | isPlaying = false 21 | } 22 | 23 | @Published var isPlaying: Bool = false { 24 | didSet { isPlaying ? osc.start() : osc.stop() } 25 | } 26 | 27 | init() { 28 | osc.amplitude = 0.2 29 | engine.output = osc 30 | } 31 | } 32 | 33 | struct MorphingOscillatorView: View { 34 | @StateObject var conductor = MorphingOscillatorConductor() 35 | @Environment(\.colorScheme) var colorScheme 36 | 37 | var body: some View { 38 | VStack { 39 | Text(conductor.isPlaying ? "STOP" : "START") 40 | .foregroundColor(.blue) 41 | .onTapGesture { 42 | conductor.isPlaying.toggle() 43 | } 44 | HStack { 45 | ForEach(conductor.osc.parameters) { 46 | ParameterRow(param: $0) 47 | } 48 | } 49 | 50 | NodeOutputView(conductor.osc) 51 | CookbookKeyboard(noteOn: conductor.noteOn, 52 | noteOff: conductor.noteOff) 53 | } 54 | .padding() 55 | .cookbookNavBarTitle("Morphing Oscillator") 56 | .onAppear { 57 | conductor.start() 58 | } 59 | .onDisappear { 60 | conductor.stop() 61 | } 62 | .background(colorScheme == .dark ? 63 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Oscillators/Oscillator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Keyboard 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | import Tonic 9 | 10 | class OscillatorConductor: ObservableObject, HasAudioEngine { 11 | let engine = AudioEngine() 12 | var osc = Oscillator() 13 | 14 | func noteOn(pitch: Pitch, point _: CGPoint) { 15 | isPlaying = true 16 | osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 17 | } 18 | 19 | func noteOff(pitch _: Pitch) { 20 | isPlaying = false 21 | } 22 | 23 | @Published var isPlaying: Bool = false { 24 | didSet { isPlaying ? osc.start() : osc.stop() } 25 | } 26 | 27 | init() { 28 | osc.amplitude = 0.2 29 | engine.output = osc 30 | } 31 | } 32 | 33 | struct OscillatorView: View { 34 | @StateObject var conductor = OscillatorConductor() 35 | @Environment(\.colorScheme) var colorScheme 36 | 37 | var body: some View { 38 | VStack { 39 | Text(conductor.isPlaying ? "STOP" : "START") 40 | .foregroundColor(.blue) 41 | .onTapGesture { 42 | conductor.isPlaying.toggle() 43 | } 44 | HStack { 45 | ForEach(conductor.osc.parameters) { 46 | ParameterRow(param: $0) 47 | } 48 | } 49 | NodeOutputView(conductor.osc) 50 | CookbookKeyboard(noteOn: conductor.noteOn, 51 | noteOff: conductor.noteOff) 52 | 53 | }.cookbookNavBarTitle("Oscillator") 54 | .onAppear { 55 | conductor.start() 56 | } 57 | .onDisappear { 58 | conductor.stop() 59 | } 60 | .background(colorScheme == .dark ? 61 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Oscillators/PWMOscillator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Keyboard 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | import Tonic 9 | 10 | class PWMOscillatorConductor: ObservableObject, HasAudioEngine { 11 | let engine = AudioEngine() 12 | var osc = PWMOscillator() 13 | 14 | func noteOn(pitch: Pitch, point _: CGPoint) { 15 | isPlaying = true 16 | osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 17 | } 18 | 19 | func noteOff(pitch _: Pitch) { 20 | isPlaying = false 21 | } 22 | 23 | @Published var isPlaying: Bool = false { 24 | didSet { isPlaying ? osc.start() : osc.stop() } 25 | } 26 | 27 | init() { 28 | osc.amplitude = 0.2 29 | engine.output = osc 30 | } 31 | } 32 | 33 | struct PWMOscillatorView: View { 34 | @StateObject var conductor = PWMOscillatorConductor() 35 | @Environment(\.colorScheme) var colorScheme 36 | 37 | var body: some View { 38 | VStack { 39 | Text(conductor.isPlaying ? "STOP" : "START") 40 | .foregroundColor(.blue) 41 | .onTapGesture { 42 | conductor.isPlaying.toggle() 43 | } 44 | Spacer() 45 | HStack { 46 | ForEach(conductor.osc.parameters) { 47 | ParameterRow(param: $0) 48 | } 49 | }.padding(5) 50 | NodeOutputView(conductor.osc) 51 | CookbookKeyboard(noteOn: conductor.noteOn, 52 | noteOff: conductor.noteOff) 53 | 54 | }.cookbookNavBarTitle("PWM Oscillator") 55 | .onAppear { 56 | conductor.start() 57 | } 58 | .onDisappear { 59 | conductor.stop() 60 | } 61 | .background(colorScheme == .dark ? 62 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Oscillators/PhaseDistortionOscillator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Keyboard 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | import Tonic 9 | 10 | class PhaseDistortionOscillatorConductor: ObservableObject, HasAudioEngine { 11 | let engine = AudioEngine() 12 | var osc = PhaseDistortionOscillator() 13 | 14 | func noteOn(pitch: Pitch, point _: CGPoint) { 15 | isPlaying = true 16 | osc.frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 17 | } 18 | 19 | func noteOff(pitch _: Pitch) { 20 | isPlaying = false 21 | } 22 | 23 | @Published var isPlaying: Bool = false { 24 | didSet { isPlaying ? osc.start() : osc.stop() } 25 | } 26 | 27 | init() { 28 | osc.amplitude = 0.2 29 | engine.output = osc 30 | } 31 | } 32 | 33 | struct PhaseDistortionOscillatorView: View { 34 | @StateObject var conductor = PhaseDistortionOscillatorConductor() 35 | @Environment(\.colorScheme) var colorScheme 36 | 37 | var body: some View { 38 | VStack { 39 | Text(conductor.isPlaying ? "STOP" : "START") 40 | .foregroundColor(.blue) 41 | .onTapGesture { 42 | conductor.isPlaying.toggle() 43 | } 44 | HStack { 45 | ForEach(conductor.osc.parameters) { 46 | ParameterRow(param: $0) 47 | } 48 | } 49 | 50 | NodeOutputView(conductor.osc) 51 | CookbookKeyboard(noteOn: conductor.noteOn, 52 | noteOff: conductor.noteOff) 53 | 54 | }.cookbookNavBarTitle("Phase Distortion Oscillator") 55 | .onAppear { 56 | conductor.start() 57 | } 58 | .onDisappear { 59 | conductor.stop() 60 | } 61 | .background(colorScheme == .dark ? 62 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Reverbs/ChowningReverb.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ChowningReverbConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let reverb: ChowningReverb 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | reverb = ChowningReverb(player) 21 | dryWetMixer = DryWetMixer(player, reverb) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct ChowningReverbView: View { 27 | @StateObject var conductor = ChowningReverbConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 33 | DryWetMixView(dry: conductor.player, 34 | wet: conductor.reverb, 35 | mix: conductor.dryWetMixer) 36 | } 37 | .padding() 38 | .cookbookNavBarTitle("Chowning Reverb") 39 | .onAppear { 40 | conductor.start() 41 | } 42 | .onDisappear { 43 | conductor.stop() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Reverbs/CostelloReverb.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class CostelloReverbConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let reverb: CostelloReverb 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | reverb = CostelloReverb(player) 21 | dryWetMixer = DryWetMixer(player, reverb) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct CostelloReverbView: View { 27 | @StateObject var conductor = CostelloReverbConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.reverb.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.reverb, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Costello Reverb") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Reverbs/FlatFrequencyResponseReverb.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class FlatFrequencyResponseReverbConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let reverb: FlatFrequencyResponseReverb 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | reverb = FlatFrequencyResponseReverb(player) 21 | dryWetMixer = DryWetMixer(player, reverb) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct FlatFrequencyResponseReverbView: View { 27 | @StateObject var conductor = FlatFrequencyResponseReverbConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.reverb.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.reverb, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Flat Frequency Response Reverb") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/Reverbs/ZitaReverb.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class ZitaReverbConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let reverb: ZitaReverb 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | let url = Bundle.module.resourceURL?.appendingPathComponent("Samples/beat.aiff") 17 | do { 18 | let file = try AVAudioFile(forReading: url!) 19 | buffer = try AVAudioPCMBuffer(file: file)! 20 | } catch { 21 | fatalError() 22 | } 23 | reverb = ZitaReverb(player) 24 | dryWetMixer = DryWetMixer(player, reverb) 25 | engine.output = dryWetMixer 26 | } 27 | } 28 | 29 | struct ZitaReverbView: View { 30 | @StateObject var conductor = ZitaReverbConductor() 31 | 32 | var body: some View { 33 | VStack { 34 | PlayerControls(conductor: conductor) 35 | HStack { 36 | ForEach(0..<6) { 37 | ParameterRow(param: conductor.reverb.parameters[$0]) 38 | } 39 | } 40 | HStack { 41 | ForEach(6..<10) { 42 | ParameterRow(param: conductor.reverb.parameters[$0]) 43 | } 44 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 45 | } 46 | DryWetMixView(dry: conductor.player, 47 | wet: conductor.reverb, 48 | mix: conductor.dryWetMixer) 49 | } 50 | .padding() 51 | .cookbookNavBarTitle("Zita Reverb") 52 | .onAppear { 53 | conductor.start() 54 | } 55 | .onDisappear { 56 | conductor.stop() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/UncategorizedDemos/AudioFileView.swift: -------------------------------------------------------------------------------- 1 | import AudioKitUI 2 | import SwiftUI 3 | 4 | struct AudioFileRecipeView: View { 5 | var body: some View { 6 | VStack { 7 | ForEach(TestAudioURLs.allCases, id: \.self) { testURL in 8 | Text(testURL.rawValue) 9 | if let url = testURL.url() { 10 | AudioFileWaveform(url: url) 11 | .background(Color.black) 12 | } 13 | } 14 | } 15 | .padding() 16 | #if os(iOS) 17 | .navigationBarTitle(Text("Audio Files")) 18 | #endif 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/UncategorizedDemos/Table.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SwiftUI 6 | 7 | class TableConductor { 8 | let square: AudioKit.Table 9 | let triangle: AudioKit.Table 10 | let sine: AudioKit.Table 11 | let sineHarmonic: AudioKit.Table 12 | let fileTable: AudioKit.Table 13 | let custom: AudioKit.Table 14 | 15 | init() { 16 | square = Table(.square, count: 128) 17 | triangle = Table(.triangle, count: 128) 18 | sine = Table(.sine, count: 256) 19 | let url = Bundle.module.resourceURL?.appendingPathComponent("Samples/beat.aiff") 20 | let file = try! AVAudioFile(forReading: url!) 21 | fileTable = Table(file: file)! 22 | let harmonicOvertoneAmplitudes: [Float] = [0.0, 0.0, 0.016, 0.301] 23 | sineHarmonic = Table(.harmonic(harmonicOvertoneAmplitudes), phase: 0.75) 24 | custom = Table(.sine, count: 256) 25 | for i in custom.indices { 26 | custom[i] += Float.random(in: -0.3 ... 0.3) + Float(i) / 2048.0 27 | } 28 | } 29 | } 30 | 31 | struct TableRecipeView: View { 32 | var conductor = TableConductor() 33 | 34 | var body: some View { 35 | VStack { 36 | Text("Square") 37 | TableDataView(view: TableView(conductor.square)) 38 | Text("Triangle") 39 | TableDataView(view: TableView(conductor.triangle)) 40 | Text("Sine") 41 | TableDataView(view: TableView(conductor.sine)) 42 | Text("Sine Harmonic") 43 | TableDataView(view: TableView(conductor.sineHarmonic)) 44 | Text("File") 45 | TableDataView(view: TableView(conductor.fileTable)) 46 | Text("Custom Data") 47 | TableDataView(view: TableView(conductor.custom)) 48 | } 49 | .padding() 50 | .cookbookNavBarTitle("Tables") 51 | } 52 | } 53 | 54 | struct TableDataView: UIViewRepresentable { 55 | typealias UIViewType = TableView 56 | var view: TableView 57 | 58 | func makeUIView(context _: Context) -> TableView { 59 | view.backgroundColor = UIColor.black 60 | return view 61 | } 62 | 63 | func updateUIView(_: TableView, context _: Context) { 64 | // 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/WIP/DunneSynth.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import DunneAudioKit 3 | import AudioKitEX 4 | import AudioKitUI 5 | import AVFAudio 6 | import Keyboard 7 | import SwiftUI 8 | import Controls 9 | import Tonic 10 | 11 | class DunneSynthConductor: ObservableObject, HasAudioEngine { 12 | let engine = AudioEngine() 13 | var instrument = Synth() 14 | 15 | func noteOn(pitch: Pitch, point _: CGPoint) { 16 | instrument.play(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), velocity: 120, channel: 0) 17 | } 18 | 19 | func noteOff(pitch: Pitch) { 20 | instrument.stop(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), channel: 0) 21 | } 22 | 23 | init() { 24 | engine.output = PeakLimiter(instrument, attackTime: 0.001, decayTime: 0.001, preGain: 0) 25 | 26 | //Remove pops 27 | instrument.releaseDuration = 0.01 28 | instrument.filterReleaseDuration = 10.0 29 | instrument.filterStrength = 40.0 30 | } 31 | } 32 | 33 | struct DunneSynthView: View { 34 | @StateObject var conductor = DunneSynthConductor() 35 | @Environment(\.colorScheme) var colorScheme 36 | 37 | var body: some View { 38 | NodeOutputView(conductor.instrument) 39 | HStack { 40 | ForEach(0...6, id: \.self){ 41 | ParameterRow(param: conductor.instrument.parameters[$0]) 42 | } 43 | }.padding(5) 44 | HStack { 45 | ForEach(7...13, id: \.self){ 46 | ParameterRow(param: conductor.instrument.parameters[$0]) 47 | } 48 | }.padding(5) 49 | CookbookKeyboard(noteOn: conductor.noteOn, 50 | noteOff: conductor.noteOff) 51 | .cookbookNavBarTitle("Dunne Synth") 52 | .onAppear { 53 | conductor.start() 54 | } 55 | .onDisappear { 56 | conductor.stop() 57 | } 58 | .background(colorScheme == .dark ? 59 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 60 | } 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/WIP/PolyphonicOscillator.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AudioToolbox 5 | import Keyboard 6 | import SoundpipeAudioKit 7 | import SwiftUI 8 | import Tonic 9 | 10 | class PolyphonicOscillatorConductor: ObservableObject, HasAudioEngine { 11 | let engine = AudioEngine() 12 | var notes = Array(repeating: 0, count: 11) 13 | var osc = [Oscillator(), Oscillator(), Oscillator(), Oscillator(), Oscillator(), 14 | Oscillator(), Oscillator(), Oscillator(), Oscillator(), Oscillator(), Oscillator()] 15 | 16 | func noteOn(pitch: Pitch, point _: CGPoint) { 17 | for num in 0 ... 10 { 18 | if notes[num] == 0 { 19 | osc[num].frequency = AUValue(pitch.midiNoteNumber).midiNoteToFrequency() 20 | osc[num].$amplitude.ramp(to: 0.2, duration: 0.005) 21 | notes[num] = pitch.intValue 22 | break 23 | } 24 | } 25 | } 26 | 27 | func noteOff(pitch: Pitch) { 28 | for num in 0 ... 10 { 29 | if notes[num] == pitch.intValue { 30 | osc[num].$amplitude.ramp(to: 0, duration: 0.005) 31 | notes[num] = 0 32 | break 33 | } 34 | } 35 | } 36 | 37 | init() { 38 | for num in 0 ... 10 { 39 | osc[num].amplitude = 0.0 40 | osc[num].start() 41 | } 42 | engine.output = Mixer(osc[0], osc[1], osc[2], osc[3], osc[4], osc[5], 43 | osc[6], osc[7], osc[8], osc[9], osc[10]) 44 | } 45 | } 46 | 47 | struct PolyphonicOscillatorView: View { 48 | @StateObject var conductor = PolyphonicOscillatorConductor() 49 | @Environment(\.colorScheme) var colorScheme 50 | 51 | var body: some View { 52 | if conductor.engine.output != nil { 53 | NodeOutputView(conductor.engine.output!) 54 | } 55 | CookbookKeyboard(noteOn: conductor.noteOn, 56 | noteOff: conductor.noteOff) 57 | .cookbookNavBarTitle("Polyphonic Oscillator") 58 | .onAppear { 59 | conductor.start() 60 | } 61 | .onDisappear { 62 | conductor.stop() 63 | } 64 | .background(colorScheme == .dark ? 65 | Color.clear : Color(red: 0.9, green: 0.9, blue: 0.9)) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/WIP/RolandTB303Filter.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SoundpipeAudioKit 6 | import SwiftUI 7 | 8 | class RolandTB303FilterConductor: ObservableObject, ProcessesPlayerInput { 9 | let engine = AudioEngine() 10 | let player = AudioPlayer() 11 | let filter: RolandTB303Filter 12 | let dryWetMixer: DryWetMixer 13 | let buffer: AVAudioPCMBuffer 14 | 15 | init() { 16 | buffer = Cookbook.sourceBuffer 17 | player.buffer = buffer 18 | player.isLooping = true 19 | 20 | filter = RolandTB303Filter(player) 21 | dryWetMixer = DryWetMixer(player, filter) 22 | engine.output = dryWetMixer 23 | } 24 | } 25 | 26 | struct RolandTB303FilterView: View { 27 | @StateObject var conductor = RolandTB303FilterConductor() 28 | 29 | var body: some View { 30 | VStack { 31 | PlayerControls(conductor: conductor) 32 | HStack { 33 | ForEach(conductor.filter.parameters) { 34 | ParameterRow(param: $0) 35 | } 36 | ParameterRow(param: conductor.dryWetMixer.parameters[0]) 37 | } 38 | DryWetMixView(dry: conductor.player, 39 | wet: conductor.filter, 40 | mix: conductor.dryWetMixer) 41 | } 42 | .padding() 43 | .cookbookNavBarTitle("Roland Tb303 Filter") 44 | .onAppear { 45 | conductor.start() 46 | } 47 | .onDisappear { 48 | conductor.stop() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Reusable Components/CallbackLoop.swift: -------------------------------------------------------------------------------- 1 | // Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/ 2 | import Foundation 3 | 4 | /// Class to handle an updating loop 5 | public class CallbackLoop: NSObject { 6 | private var internalHandler: () -> Void = {} 7 | 8 | /// Period of loop in seconds 9 | public var duration = 1.0 10 | 11 | /// Frequency of callbacks in Hertz 12 | public var frequency: Double { 13 | get { 14 | 1.0 / duration 15 | } 16 | set { 17 | duration = 1.0 / newValue 18 | } 19 | } 20 | 21 | private var isRunning = false 22 | 23 | /// Repeat this loop at a given period with a code block 24 | /// 25 | /// - parameter period: Interval between block executions 26 | /// - parameter handler: Code block to execute 27 | /// 28 | public init(every period: Double, handler: @escaping () -> Void) { 29 | duration = period 30 | internalHandler = handler 31 | super.init() 32 | update() 33 | } 34 | 35 | /// Repeat this loop at a given frequency with a code block 36 | /// 37 | /// - parameter frequency: Frequency of block executions in Hz 38 | /// - parameter handler: Code block to execute 39 | /// 40 | public init(frequency: Double, handler: @escaping () -> Void) { 41 | duration = 1.0 / frequency 42 | internalHandler = handler 43 | super.init() 44 | update() 45 | } 46 | 47 | /// Start the loop 48 | public func start() { 49 | isRunning = true 50 | update() 51 | } 52 | 53 | /// Stop the loop 54 | public func stop() { 55 | isRunning = false 56 | } 57 | 58 | /// Callback function 59 | @objc func update() { 60 | if isRunning { 61 | if !Thread.isMainThread { 62 | DispatchQueue.main.async { [self] in 63 | self.updateInternal() 64 | } 65 | } else { 66 | updateInternal() 67 | } 68 | } 69 | } 70 | 71 | /// Call the callback function. 72 | /// self.perform only works when executed on the main thread. Called from self.update() 73 | @objc internal func updateInternal() { 74 | if isRunning { 75 | internalHandler() 76 | perform(#selector(updateInternal), 77 | with: nil, 78 | afterDelay: duration, 79 | inModes: [.common]) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Reusable Components/Cookbook.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import AudioKitEX 3 | import AudioKitUI 4 | import AVFoundation 5 | import SwiftUI 6 | 7 | // Helper functions 8 | class Cookbook { 9 | static var sourceBuffer: AVAudioPCMBuffer { 10 | let url = Bundle.module.resourceURL?.appendingPathComponent("Samples/beat.aiff") 11 | let file = try! AVAudioFile(forReading: url!) 12 | return try! AVAudioPCMBuffer(file: file)! 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Reusable Components/CookbookKeyboard.swift: -------------------------------------------------------------------------------- 1 | import AudioKit 2 | import SwiftUI 3 | import Keyboard 4 | import Tonic 5 | 6 | struct CookbookKeyboard: View { 7 | var noteOn: (Pitch, CGPoint) -> Void = { _, _ in } 8 | var noteOff: (Pitch) -> Void 9 | var body: some View { 10 | Keyboard(layout: .piano(pitchRange: Pitch(48) ... Pitch(64)), 11 | noteOn: noteOn, noteOff: noteOff) 12 | } 13 | } 14 | 15 | struct MIDIKitKeyboard: View { 16 | var noteOn: (Pitch, CGPoint) -> Void = { _, _ in } 17 | var noteOff: (Pitch) -> Void 18 | var body: some View { 19 | Keyboard(layout: .piano(pitchRange: Pitch(48) ... Pitch(64)), 20 | noteOn: noteOn, noteOff: noteOff){ pitch, isActivated in 21 | MIDIKitKeyboardKey(pitch: pitch, 22 | isActivated: isActivated, color: .red) 23 | }.cornerRadius(5) 24 | } 25 | } 26 | 27 | struct MIDIKitKeyboardKey: View { 28 | @State var MIDIKeyPressed = [Bool](repeating: false, count: 128) 29 | var pitch : Pitch 30 | var isActivated : Bool 31 | var color: Color 32 | 33 | var body: some View { 34 | VStack{ 35 | KeyboardKey(pitch: pitch, 36 | isActivated: isActivated, 37 | text: "", 38 | whiteKeyColor: .white, 39 | blackKeyColor: .black, 40 | pressedColor: color, 41 | flatTop: true, 42 | isActivatedExternally: MIDIKeyPressed[pitch.intValue]) 43 | }.onReceive(NotificationCenter.default.publisher(for: .MIDIKey), perform: { obj in 44 | if let userInfo = obj.userInfo, let info = userInfo["info"] as? UInt8, let val = userInfo["bool"] as? Bool { 45 | self.MIDIKeyPressed[Int(info)] = val 46 | } 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Reusable Components/CookbookKnob.swift: -------------------------------------------------------------------------------- 1 | import AVFoundation 2 | import Controls 3 | import SwiftUI 4 | 5 | public struct CookbookKnob: View { 6 | var text: String 7 | @Binding var parameter: AUValue 8 | var range: ClosedRange 9 | var format: String = "%0.2f" 10 | var units: String = "" 11 | 12 | public init(text: String, 13 | parameter: Binding, 14 | range: ClosedRange, 15 | units: String = "") { 16 | _parameter = parameter 17 | self.text = text 18 | self.range = range 19 | self.units = units 20 | } 21 | 22 | public var body: some View { 23 | VStack { 24 | VStack { 25 | Text(text) 26 | .minimumScaleFactor(0.2) 27 | .lineLimit(2) 28 | .multilineTextAlignment(.center) 29 | if units == "" || units == "Generic" { 30 | Text("\(parameter, specifier: format)") 31 | .lineLimit(1) 32 | } else if units == "%" || units == "Percent" { 33 | Text("\(parameter * 100, specifier: "%0.f")%") 34 | .lineLimit(1) 35 | } else if units == "Percent-0-100" { // for audio units that use 0-100 instead of 0-1 36 | Text("\(parameter, specifier: "%0.f")%") 37 | .lineLimit(1) 38 | } else if units == "Hertz" { 39 | Text("\(parameter, specifier: "%0.2f") Hz") 40 | .lineLimit(1) 41 | } else { 42 | Text("\(parameter, specifier: format) \(units)") 43 | .lineLimit(1) 44 | } 45 | } 46 | .frame(height: 50) 47 | SmallKnob(value: $parameter, range: range) 48 | }.frame(maxWidth: 150, maxHeight: 200).frame(minHeight: 100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Reusable Components/NavigationHelpers.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CookbookNavBarTitle: ViewModifier { 4 | var text: String 5 | func body(content: Content) -> some View { 6 | content 7 | #if !os(macOS) 8 | .navigationBarTitle(Text(text)) 9 | #endif 10 | } 11 | } 12 | 13 | extension View { 14 | func cookbookNavBarTitle(_ text: String) -> some View { 15 | modifier(CookbookNavBarTitle(text: text)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Bass Synth.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Bass Synth.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Counting.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Counting.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Guitar.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Guitar.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Piano.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Piano.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Strings.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Strings.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Synth.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/Synth.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/alphabet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/alphabet.mp3 -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/bass_drum_C1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/bass_drum_C1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/beat.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/beat.aiff -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/clap_D#1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/clap_D#1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/closed_hi_hat_F#1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/closed_hi_hat_F#1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/hi_tom_D2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/hi_tom_D2.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/lo_tom_F1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/lo_tom_F1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/mid_tom_B1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/mid_tom_B1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/open_hi_hat_A#1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/open_hi_hat_A#1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/snare_D1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/CookbookCommon/Sources/CookbookCommon/Samples/snare_D1.wav -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Sources/CookbookCommon/TestAudioURLs.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum TestAudioURLs: String, CaseIterable { 4 | case beat = "beat.aiff", 5 | counting = "Counting.mp3", 6 | guitar = "Guitar.mp3", 7 | bassDrum = "bass_drum_C1.wav", 8 | clap = "clap_D#1.wav", 9 | snare = "snare_D1.wav", 10 | lowTom = "lo_tom_F1.wav", 11 | midTom = "mid_tom_B1.wav", 12 | highTom = "hi_tom_D2.wav" 13 | 14 | func url() -> URL? { 15 | return Bundle.module.url(forResource: "Samples/\(rawValue)", withExtension: "") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Cookbook/CookbookCommon/Tests/CookbookCommonTests/CookbookCommonTests.swift: -------------------------------------------------------------------------------- 1 | @testable import CookbookCommon 2 | import XCTest 3 | 4 | final class CookbookCommonTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(CookbookCommon().text, "Hello, World!") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/drumSimp.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/drumSimp.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/funkyWow.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/funkyWow.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/nes-syn1.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/nes-syn1.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/noisyRez.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/noisyRez.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/sawPad1.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/sawPad1.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/sawPiano1.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/sawPiano1.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/Sampler Instruments/sqrTone1.exs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/Sampler Instruments/sqrTone1.exs -------------------------------------------------------------------------------- /Cookbook/Sounds/basicSamples/noise-wht2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/basicSamples/noise-wht2.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/basicSamples/saw220-ana1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/basicSamples/saw220-ana1.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/basicSamples/saw220.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/basicSamples/saw220.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/basicSamples/sqr220.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/basicSamples/sqr220.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/cheeb-bd.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/cheeb-bd.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/cheeb-ch.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/cheeb-ch.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/cheeb-hat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/cheeb-hat.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/cheeb-snr.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/cheeb-snr.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/cheeb-stick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/cheeb-stick.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/closed_hi_hat_F#1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/Cookbook/Sounds/closed_hi_hat_F#1.wav -------------------------------------------------------------------------------- /Cookbook/Sounds/sqr.SFZ: -------------------------------------------------------------------------------- 1 | lokey=0 hikey=127 pitch_keycenter=57 pitch_keytrack=100 2 | lovel=000 hivel=127 amp_velcurve_127=1 loop_mode=loop_continuous loop_start=0 loop_end=220 sample=basicSamples/saw220.wav 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aurelius Prochazka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cookbook 2 | 3 | # AudioKit Cookbook for iOS and macOS (via Catalyst) 4 | 5 | [![Build Status](https://github.com/AudioKit/Cookbook/workflows/CI/badge.svg)](https://github.com/AudioKit/Cookbook/actions?query=workflow%3ACI) 6 | [![License](https://img.shields.io/cocoapods/l/AudioKit.svg?style=flat)](https://github.com/AudioKit/AudioKit/blob/v5-main/LICENSE) 7 | [![Platform](https://img.shields.io/cocoapods/p/AudioKit.svg?style=flat)](https://github.com/AudioKit/AudioKit/) 8 | [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) 9 | [![Twitter Follow](https://img.shields.io/twitter/follow/AudioKitPro.svg?style=social)](http://twitter.com/AudioKitPro) 10 | 11 | ## Canonical Examples for Using the AudioKit 5 Swift Package 12 | 13 | Most of the examples that were inside of [AudioKit](https://github.com/AudioKit/AudioKit/) are now in this single iOS / macOS Catalyst application. 14 | 15 | ## Top Level Overview 16 | 17 | * `ContentView.swift` contains the menu screen. 18 | * `Recipes/` contain all of the one-screen demos. 19 | * `Resources/`, `Samples`, and `Sounds` contain shared audio and MIDI content. 20 | * `Reusable Components/` contains the code widgets that are shared between recipes. 21 | 22 | ## Recipes 23 | 24 | Each recipe is one file that contains a few related objects: 25 | 26 | * `Conductor` sets up all the AudioKit signal processing. 27 | * `Data` is a structure that holds the state of the demo. It is used by both the view and the conductor. 28 | * `View` creates the SwiftUI user interface for the recipe. 29 | 30 | ## On-going development 31 | 32 | Since this is the primary example for AudioKit, it will continue to evolve as AudioKit does. There are plenty of opportunities to help out. 33 | Check out [Github Issues](https://github.com/AudioKit/Cookbook/issues) for some specific requests. 34 | 35 | Cookbook 36 | Cookbook 37 | -------------------------------------------------------------------------------- /Xcode-config/DEVELOPMENT_TEAM.template.xcconfig: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_TEAM = AB1234C5DE 2 | -------------------------------------------------------------------------------- /Xcode-config/Shared.xcconfig: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_TEAM = 9W69ZP8S5F 2 | 3 | #include? "DEVELOPMENT_TEAM.xcconfig" 4 | 5 | // Create the file DEVELOPMENT_TEAM.xcconfig 6 | // in the "Xcode-config" directory within the project directory 7 | // with the following build setting: 8 | // DEVELOPMENT_TEAM = [Your TeamID] 9 | 10 | // One way to find your Team ID is to set the “Development Team” 11 | // build setting (Xcode key: "DEVELOPMENT_TEAM") and have a look 12 | // at the “Source Control” changes (menu item “Commit…”/⌥⌘C). 13 | // Just make sure that this change doesn’t actually get committed. 14 | 15 | // You can also find your Team ID by logging into your Apple Developer account 16 | // and going to 17 | // https://developer.apple.com/account/#/membership 18 | // It should be listed under “Team ID”. 19 | 20 | // To set this system up for your own project, 21 | // copy the "Xcode-config" directory there, 22 | // add it to your Xcode project, 23 | // navigate to your project settings 24 | // (root icon in the Xcode Project Navigator) 25 | // click on the project icon there, 26 | // click on the “Info” tab 27 | // under “Configurations” 28 | // open the “Debug”, “Release”, 29 | // and any other build configurations you might have. 30 | // There you can set the pull-down menus in the 31 | // “Based on Configuration File” column to “Shared”. 32 | // Your work in Xcode is done. 33 | 34 | // Don’t forget to add the DEVELOPMENT_TEAM.xcconfig file to your .gitignore: 35 | // # User-specific xcconfig files 36 | // Xcode-config/DEVELOPMENT_TEAM.xcconfig 37 | // The two lines above are an example. 38 | // Please don’t copy the comment slashes over though. 39 | 40 | // You can and should now replace the “DevelopmentTeam = AB1234C5DE;” entries in 41 | // .xcodeproj/project.pbxproj 42 | // with “DevelopmentTeam = "";” 43 | // They would otherwise override the Team ID set via this config file and its include. 44 | // Please note that .pbxproj files use straight quotes. 45 | 46 | // When you commit changes to the Xcode project file 47 | // after changing settings in “Signing & Capabilities” 48 | // Entries like these may appear in your file changes/commit diff preview: 49 | // CODE_SIGN_IDENTITY = …; 50 | // CODE_SIGN_STYLE = Manual/Automatic; 51 | // Please make sure not to commit them to source control. 52 | -------------------------------------------------------------------------------- /images/Cookbook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/images/Cookbook.png -------------------------------------------------------------------------------- /images/Cookbook2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/images/Cookbook2.png -------------------------------------------------------------------------------- /images/CookbookMac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AudioKit/Cookbook/86f829c14f3bd8b672513b86a612128dd4154e8a/images/CookbookMac.png --------------------------------------------------------------------------------