├── .gitignore ├── AudioUnitRenderingExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── AudioUnitRenderingExample.xcscheme ├── AudioUnitRenderingExample ├── AVAudioSinkNode │ ├── InputMeter.swift │ ├── InputMeterKernel.cpp │ ├── InputMeterKernel.hpp │ ├── InputMeterRenderer.h │ ├── InputMeterRenderer.mm │ ├── InputMeterUtils.h │ └── InputMeterViewController.swift ├── AVAudioSourceNodeCommon │ ├── SineKernel.h │ ├── SinePlayer.swift │ ├── SineRenderer.h │ ├── SineRenderer.mm │ └── SineViewController.swift ├── AVAudioSourceNodeDeferred │ ├── DeferredSineKernel.cpp │ ├── DeferredSineKernel.hpp │ ├── RingBuffer.cpp │ └── RingBuffer.hpp ├── AVAudioSourceNodeDirect │ ├── DirectSineKernel.cpp │ └── DirectSineKernel.hpp ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── AudioUnitRenderingExample-Bridging-Header.h ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── Menu │ └── MenuViewController.swift ├── MeterData.swift ├── SceneDelegate.swift ├── Utils │ ├── AudioSession.swift │ ├── SineUtils.cpp │ └── SineUtils.hpp └── ViewController.swift ├── AudioUnitRenderingExampleTests ├── AudioUnitRenderingExampleTests-Bridging-Header.h ├── Info.plist ├── InputMeterKernelTests.mm ├── InputMeterUtilsTests.swift ├── RingBufferTests.mm ├── SineDataTests.mm └── SineUtilsTests.mm ├── LICENSE └── README.md /.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 | # Addition 93 | 94 | .DS_Store 95 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B600BEEC24E13180002111F8 /* SineDataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = B600BEEB24E13180002111F8 /* SineDataTests.mm */; }; 11 | B600BEF324E78E4F002111F8 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B600BEF224E78E4F002111F8 /* MenuViewController.swift */; }; 12 | B600BEFB24E790F8002111F8 /* DeferredSineKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B600BEF924E790F8002111F8 /* DeferredSineKernel.cpp */; }; 13 | B600BEFF24E7CC51002111F8 /* SineUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B600BEFD24E7CC51002111F8 /* SineUtils.cpp */; }; 14 | B600BF0124E7DDAB002111F8 /* SineUtilsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = B600BF0024E7DDAB002111F8 /* SineUtilsTests.mm */; }; 15 | B610B5C224D669EB00B97F76 /* InputMeterUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B610B5C124D669EB00B97F76 /* InputMeterUtilsTests.swift */; }; 16 | B6300EE224E7FFA000447F8C /* RingBufferTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6300EE124E7FFA000447F8C /* RingBufferTests.mm */; }; 17 | B66AED0D24CB18FC00BB8BE8 /* InputMeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66AED0C24CB18FC00BB8BE8 /* InputMeter.swift */; }; 18 | B66AED1324CBB13300BB8BE8 /* InputMeterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66AED1224CBB13300BB8BE8 /* InputMeterViewController.swift */; }; 19 | B674181424DC23A8002C0CA9 /* DirectSineKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B674181324DC23A8002C0CA9 /* DirectSineKernel.cpp */; }; 20 | B69FEC3524E7E6E2001A4F6C /* RingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B69FEC3324E7E6E2001A4F6C /* RingBuffer.cpp */; }; 21 | B6C9765124CA992A0069B2D6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C9765024CA992A0069B2D6 /* AppDelegate.swift */; }; 22 | B6C9765324CA992A0069B2D6 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C9765224CA992A0069B2D6 /* SceneDelegate.swift */; }; 23 | B6C9765524CA992A0069B2D6 /* SineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C9765424CA992A0069B2D6 /* SineViewController.swift */; }; 24 | B6C9765824CA992A0069B2D6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B6C9765624CA992A0069B2D6 /* Main.storyboard */; }; 25 | B6C9765A24CA992E0069B2D6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B6C9765924CA992E0069B2D6 /* Assets.xcassets */; }; 26 | B6C9765D24CA992E0069B2D6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B6C9765B24CA992E0069B2D6 /* LaunchScreen.storyboard */; }; 27 | B6C9767324CAA0F90069B2D6 /* SinePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C9767224CAA0F90069B2D6 /* SinePlayer.swift */; }; 28 | B6C9767A24CD40950069B2D6 /* AudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C9767924CD40940069B2D6 /* AudioSession.swift */; }; 29 | B6F758AA24DAD3A400CD5987 /* SineRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6F758A924DAD3A400CD5987 /* SineRenderer.mm */; }; 30 | B6F758AD24DAE02E00CD5987 /* InputMeterKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6F758AB24DAE02E00CD5987 /* InputMeterKernel.cpp */; }; 31 | B6F758B124DAE6B200CD5987 /* InputMeterRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6F758B024DAE6B200CD5987 /* InputMeterRenderer.mm */; }; 32 | B6F758B724DAECA900CD5987 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6F758B624DAECA900CD5987 /* Accelerate.framework */; }; 33 | B6F758B924DAF23200CD5987 /* InputMeterKernelTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6F758B824DAF23200CD5987 /* InputMeterKernelTests.mm */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | B6C9766424CA992E0069B2D6 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = B6C9764524CA992A0069B2D6 /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = B6C9764C24CA992A0069B2D6; 42 | remoteInfo = AudioUnitRenderingExample; 43 | }; 44 | /* End PBXContainerItemProxy section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | B600BEEB24E13180002111F8 /* SineDataTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SineDataTests.mm; sourceTree = ""; }; 48 | B600BEF224E78E4F002111F8 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; 49 | B600BEF924E790F8002111F8 /* DeferredSineKernel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DeferredSineKernel.cpp; sourceTree = ""; }; 50 | B600BEFA24E790F8002111F8 /* DeferredSineKernel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredSineKernel.hpp; sourceTree = ""; }; 51 | B600BEFC24E79EE5002111F8 /* SineKernel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SineKernel.h; sourceTree = ""; }; 52 | B600BEFD24E7CC51002111F8 /* SineUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SineUtils.cpp; sourceTree = ""; }; 53 | B600BEFE24E7CC51002111F8 /* SineUtils.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SineUtils.hpp; sourceTree = ""; }; 54 | B600BF0024E7DDAB002111F8 /* SineUtilsTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SineUtilsTests.mm; sourceTree = ""; }; 55 | B610B5C124D669EB00B97F76 /* InputMeterUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMeterUtilsTests.swift; sourceTree = ""; }; 56 | B6300EE124E7FFA000447F8C /* RingBufferTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RingBufferTests.mm; sourceTree = ""; }; 57 | B66AED0C24CB18FC00BB8BE8 /* InputMeter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMeter.swift; sourceTree = ""; }; 58 | B66AED1224CBB13300BB8BE8 /* InputMeterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMeterViewController.swift; sourceTree = ""; }; 59 | B674181324DC23A8002C0CA9 /* DirectSineKernel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DirectSineKernel.cpp; sourceTree = ""; }; 60 | B69FEC3324E7E6E2001A4F6C /* RingBuffer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RingBuffer.cpp; sourceTree = ""; }; 61 | B69FEC3424E7E6E2001A4F6C /* RingBuffer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RingBuffer.hpp; sourceTree = ""; }; 62 | B6C9764D24CA992A0069B2D6 /* AudioUnitRenderingExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioUnitRenderingExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | B6C9765024CA992A0069B2D6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 64 | B6C9765224CA992A0069B2D6 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 65 | B6C9765424CA992A0069B2D6 /* SineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SineViewController.swift; sourceTree = ""; }; 66 | B6C9765724CA992A0069B2D6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 67 | B6C9765924CA992E0069B2D6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 68 | B6C9765C24CA992E0069B2D6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 69 | B6C9765E24CA992E0069B2D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | B6C9766324CA992E0069B2D6 /* AudioUnitRenderingExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AudioUnitRenderingExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | B6C9766924CA992E0069B2D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 72 | B6C9767224CAA0F90069B2D6 /* SinePlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinePlayer.swift; sourceTree = ""; }; 73 | B6C9767624CAA9410069B2D6 /* AudioUnitRenderingExampleTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AudioUnitRenderingExampleTests-Bridging-Header.h"; sourceTree = ""; }; 74 | B6C9767924CD40940069B2D6 /* AudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSession.swift; sourceTree = ""; }; 75 | B6F758A424DAD0F300CD5987 /* AudioUnitRenderingExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AudioUnitRenderingExample-Bridging-Header.h"; sourceTree = ""; }; 76 | B6F758A624DAD0F400CD5987 /* DirectSineKernel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DirectSineKernel.hpp; sourceTree = ""; }; 77 | B6F758A824DAD3A400CD5987 /* SineRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SineRenderer.h; sourceTree = ""; }; 78 | B6F758A924DAD3A400CD5987 /* SineRenderer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SineRenderer.mm; sourceTree = ""; }; 79 | B6F758AB24DAE02E00CD5987 /* InputMeterKernel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InputMeterKernel.cpp; sourceTree = ""; }; 80 | B6F758AC24DAE02E00CD5987 /* InputMeterKernel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InputMeterKernel.hpp; sourceTree = ""; }; 81 | B6F758AE24DAE44C00CD5987 /* InputMeterUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InputMeterUtils.h; sourceTree = ""; }; 82 | B6F758AF24DAE6B200CD5987 /* InputMeterRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InputMeterRenderer.h; sourceTree = ""; }; 83 | B6F758B024DAE6B200CD5987 /* InputMeterRenderer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InputMeterRenderer.mm; sourceTree = ""; }; 84 | B6F758B624DAECA900CD5987 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 85 | B6F758B824DAF23200CD5987 /* InputMeterKernelTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InputMeterKernelTests.mm; sourceTree = ""; }; 86 | /* End PBXFileReference section */ 87 | 88 | /* Begin PBXFrameworksBuildPhase section */ 89 | B6C9764A24CA992A0069B2D6 /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | B6F758B724DAECA900CD5987 /* Accelerate.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | B6C9766024CA992E0069B2D6 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | /* End PBXFrameworksBuildPhase section */ 105 | 106 | /* Begin PBXGroup section */ 107 | B600BEED24E7891E002111F8 /* AVAudioSourceNodeCommon */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | B6C9765424CA992A0069B2D6 /* SineViewController.swift */, 111 | B6C9767224CAA0F90069B2D6 /* SinePlayer.swift */, 112 | B6F758A824DAD3A400CD5987 /* SineRenderer.h */, 113 | B6F758A924DAD3A400CD5987 /* SineRenderer.mm */, 114 | B600BEFC24E79EE5002111F8 /* SineKernel.h */, 115 | ); 116 | path = AVAudioSourceNodeCommon; 117 | sourceTree = ""; 118 | }; 119 | B600BEF024E78DAD002111F8 /* AVAudioSourceNodeDeferred */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | B600BEF924E790F8002111F8 /* DeferredSineKernel.cpp */, 123 | B600BEFA24E790F8002111F8 /* DeferredSineKernel.hpp */, 124 | B69FEC3324E7E6E2001A4F6C /* RingBuffer.cpp */, 125 | B69FEC3424E7E6E2001A4F6C /* RingBuffer.hpp */, 126 | ); 127 | path = AVAudioSourceNodeDeferred; 128 | sourceTree = ""; 129 | }; 130 | B600BEF124E78E3D002111F8 /* Menu */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | B600BEF224E78E4F002111F8 /* MenuViewController.swift */, 134 | ); 135 | path = Menu; 136 | sourceTree = ""; 137 | }; 138 | B6C9764424CA992A0069B2D6 = { 139 | isa = PBXGroup; 140 | children = ( 141 | B6C9764F24CA992A0069B2D6 /* AudioUnitRenderingExample */, 142 | B6C9766624CA992E0069B2D6 /* AudioUnitRenderingExampleTests */, 143 | B6C9764E24CA992A0069B2D6 /* Products */, 144 | B6F758B524DAECA900CD5987 /* Frameworks */, 145 | ); 146 | sourceTree = ""; 147 | }; 148 | B6C9764E24CA992A0069B2D6 /* Products */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | B6C9764D24CA992A0069B2D6 /* AudioUnitRenderingExample.app */, 152 | B6C9766324CA992E0069B2D6 /* AudioUnitRenderingExampleTests.xctest */, 153 | ); 154 | name = Products; 155 | sourceTree = ""; 156 | }; 157 | B6C9764F24CA992A0069B2D6 /* AudioUnitRenderingExample */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | B6C9765024CA992A0069B2D6 /* AppDelegate.swift */, 161 | B6C9765224CA992A0069B2D6 /* SceneDelegate.swift */, 162 | B600BEF124E78E3D002111F8 /* Menu */, 163 | B6C9767B24CD42790069B2D6 /* Utils */, 164 | B600BEED24E7891E002111F8 /* AVAudioSourceNodeCommon */, 165 | B6C9767C24CD42830069B2D6 /* AVAudioSourceNodeDirect */, 166 | B600BEF024E78DAD002111F8 /* AVAudioSourceNodeDeferred */, 167 | B6C9767D24CD428F0069B2D6 /* AVAudioSinkNode */, 168 | B6F758A424DAD0F300CD5987 /* AudioUnitRenderingExample-Bridging-Header.h */, 169 | B6C9765624CA992A0069B2D6 /* Main.storyboard */, 170 | B6C9765924CA992E0069B2D6 /* Assets.xcassets */, 171 | B6C9765B24CA992E0069B2D6 /* LaunchScreen.storyboard */, 172 | B6C9765E24CA992E0069B2D6 /* Info.plist */, 173 | ); 174 | path = AudioUnitRenderingExample; 175 | sourceTree = ""; 176 | }; 177 | B6C9766624CA992E0069B2D6 /* AudioUnitRenderingExampleTests */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | B6C9766924CA992E0069B2D6 /* Info.plist */, 181 | B610B5C124D669EB00B97F76 /* InputMeterUtilsTests.swift */, 182 | B6F758B824DAF23200CD5987 /* InputMeterKernelTests.mm */, 183 | B600BEEB24E13180002111F8 /* SineDataTests.mm */, 184 | B600BF0024E7DDAB002111F8 /* SineUtilsTests.mm */, 185 | B6300EE124E7FFA000447F8C /* RingBufferTests.mm */, 186 | B6C9767624CAA9410069B2D6 /* AudioUnitRenderingExampleTests-Bridging-Header.h */, 187 | ); 188 | path = AudioUnitRenderingExampleTests; 189 | sourceTree = ""; 190 | }; 191 | B6C9767B24CD42790069B2D6 /* Utils */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | B6C9767924CD40940069B2D6 /* AudioSession.swift */, 195 | B600BEFD24E7CC51002111F8 /* SineUtils.cpp */, 196 | B600BEFE24E7CC51002111F8 /* SineUtils.hpp */, 197 | ); 198 | path = Utils; 199 | sourceTree = ""; 200 | }; 201 | B6C9767C24CD42830069B2D6 /* AVAudioSourceNodeDirect */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | B674181324DC23A8002C0CA9 /* DirectSineKernel.cpp */, 205 | B6F758A624DAD0F400CD5987 /* DirectSineKernel.hpp */, 206 | ); 207 | path = AVAudioSourceNodeDirect; 208 | sourceTree = ""; 209 | }; 210 | B6C9767D24CD428F0069B2D6 /* AVAudioSinkNode */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | B66AED1224CBB13300BB8BE8 /* InputMeterViewController.swift */, 214 | B66AED0C24CB18FC00BB8BE8 /* InputMeter.swift */, 215 | B6F758AF24DAE6B200CD5987 /* InputMeterRenderer.h */, 216 | B6F758B024DAE6B200CD5987 /* InputMeterRenderer.mm */, 217 | B6F758AB24DAE02E00CD5987 /* InputMeterKernel.cpp */, 218 | B6F758AC24DAE02E00CD5987 /* InputMeterKernel.hpp */, 219 | B6F758AE24DAE44C00CD5987 /* InputMeterUtils.h */, 220 | ); 221 | path = AVAudioSinkNode; 222 | sourceTree = ""; 223 | }; 224 | B6F758B524DAECA900CD5987 /* Frameworks */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | B6F758B624DAECA900CD5987 /* Accelerate.framework */, 228 | ); 229 | name = Frameworks; 230 | sourceTree = ""; 231 | }; 232 | /* End PBXGroup section */ 233 | 234 | /* Begin PBXNativeTarget section */ 235 | B6C9764C24CA992A0069B2D6 /* AudioUnitRenderingExample */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = B6C9766C24CA992E0069B2D6 /* Build configuration list for PBXNativeTarget "AudioUnitRenderingExample" */; 238 | buildPhases = ( 239 | B6C9764924CA992A0069B2D6 /* Sources */, 240 | B6C9764A24CA992A0069B2D6 /* Frameworks */, 241 | B6C9764B24CA992A0069B2D6 /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | ); 247 | name = AudioUnitRenderingExample; 248 | productName = AudioUnitRenderingExample; 249 | productReference = B6C9764D24CA992A0069B2D6 /* AudioUnitRenderingExample.app */; 250 | productType = "com.apple.product-type.application"; 251 | }; 252 | B6C9766224CA992E0069B2D6 /* AudioUnitRenderingExampleTests */ = { 253 | isa = PBXNativeTarget; 254 | buildConfigurationList = B6C9766F24CA992E0069B2D6 /* Build configuration list for PBXNativeTarget "AudioUnitRenderingExampleTests" */; 255 | buildPhases = ( 256 | B6C9765F24CA992E0069B2D6 /* Sources */, 257 | B6C9766024CA992E0069B2D6 /* Frameworks */, 258 | B6C9766124CA992E0069B2D6 /* Resources */, 259 | ); 260 | buildRules = ( 261 | ); 262 | dependencies = ( 263 | B6C9766524CA992E0069B2D6 /* PBXTargetDependency */, 264 | ); 265 | name = AudioUnitRenderingExampleTests; 266 | productName = AudioUnitRenderingExampleTests; 267 | productReference = B6C9766324CA992E0069B2D6 /* AudioUnitRenderingExampleTests.xctest */; 268 | productType = "com.apple.product-type.bundle.unit-test"; 269 | }; 270 | /* End PBXNativeTarget section */ 271 | 272 | /* Begin PBXProject section */ 273 | B6C9764524CA992A0069B2D6 /* Project object */ = { 274 | isa = PBXProject; 275 | attributes = { 276 | LastSwiftUpdateCheck = 1160; 277 | LastUpgradeCheck = 1160; 278 | ORGANIZATIONNAME = "Yuki Yasoshima"; 279 | TargetAttributes = { 280 | B6C9764C24CA992A0069B2D6 = { 281 | CreatedOnToolsVersion = 11.6; 282 | LastSwiftMigration = 1160; 283 | }; 284 | B6C9766224CA992E0069B2D6 = { 285 | CreatedOnToolsVersion = 11.6; 286 | LastSwiftMigration = 1160; 287 | TestTargetID = B6C9764C24CA992A0069B2D6; 288 | }; 289 | }; 290 | }; 291 | buildConfigurationList = B6C9764824CA992A0069B2D6 /* Build configuration list for PBXProject "AudioUnitRenderingExample" */; 292 | compatibilityVersion = "Xcode 9.3"; 293 | developmentRegion = en; 294 | hasScannedForEncodings = 0; 295 | knownRegions = ( 296 | en, 297 | Base, 298 | ); 299 | mainGroup = B6C9764424CA992A0069B2D6; 300 | productRefGroup = B6C9764E24CA992A0069B2D6 /* Products */; 301 | projectDirPath = ""; 302 | projectRoot = ""; 303 | targets = ( 304 | B6C9764C24CA992A0069B2D6 /* AudioUnitRenderingExample */, 305 | B6C9766224CA992E0069B2D6 /* AudioUnitRenderingExampleTests */, 306 | ); 307 | }; 308 | /* End PBXProject section */ 309 | 310 | /* Begin PBXResourcesBuildPhase section */ 311 | B6C9764B24CA992A0069B2D6 /* Resources */ = { 312 | isa = PBXResourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | B6C9765D24CA992E0069B2D6 /* LaunchScreen.storyboard in Resources */, 316 | B6C9765A24CA992E0069B2D6 /* Assets.xcassets in Resources */, 317 | B6C9765824CA992A0069B2D6 /* Main.storyboard in Resources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | B6C9766124CA992E0069B2D6 /* Resources */ = { 322 | isa = PBXResourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | /* End PBXResourcesBuildPhase section */ 329 | 330 | /* Begin PBXSourcesBuildPhase section */ 331 | B6C9764924CA992A0069B2D6 /* Sources */ = { 332 | isa = PBXSourcesBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | B600BEFF24E7CC51002111F8 /* SineUtils.cpp in Sources */, 336 | B6F758AA24DAD3A400CD5987 /* SineRenderer.mm in Sources */, 337 | B66AED1324CBB13300BB8BE8 /* InputMeterViewController.swift in Sources */, 338 | B6C9767A24CD40950069B2D6 /* AudioSession.swift in Sources */, 339 | B66AED0D24CB18FC00BB8BE8 /* InputMeter.swift in Sources */, 340 | B6C9765524CA992A0069B2D6 /* SineViewController.swift in Sources */, 341 | B6C9765124CA992A0069B2D6 /* AppDelegate.swift in Sources */, 342 | B600BEF324E78E4F002111F8 /* MenuViewController.swift in Sources */, 343 | B69FEC3524E7E6E2001A4F6C /* RingBuffer.cpp in Sources */, 344 | B6F758B124DAE6B200CD5987 /* InputMeterRenderer.mm in Sources */, 345 | B6C9765324CA992A0069B2D6 /* SceneDelegate.swift in Sources */, 346 | B6C9767324CAA0F90069B2D6 /* SinePlayer.swift in Sources */, 347 | B674181424DC23A8002C0CA9 /* DirectSineKernel.cpp in Sources */, 348 | B6F758AD24DAE02E00CD5987 /* InputMeterKernel.cpp in Sources */, 349 | B600BEFB24E790F8002111F8 /* DeferredSineKernel.cpp in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | B6C9765F24CA992E0069B2D6 /* Sources */ = { 354 | isa = PBXSourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | B6F758B924DAF23200CD5987 /* InputMeterKernelTests.mm in Sources */, 358 | B6300EE224E7FFA000447F8C /* RingBufferTests.mm in Sources */, 359 | B610B5C224D669EB00B97F76 /* InputMeterUtilsTests.swift in Sources */, 360 | B600BF0124E7DDAB002111F8 /* SineUtilsTests.mm in Sources */, 361 | B600BEEC24E13180002111F8 /* SineDataTests.mm in Sources */, 362 | ); 363 | runOnlyForDeploymentPostprocessing = 0; 364 | }; 365 | /* End PBXSourcesBuildPhase section */ 366 | 367 | /* Begin PBXTargetDependency section */ 368 | B6C9766524CA992E0069B2D6 /* PBXTargetDependency */ = { 369 | isa = PBXTargetDependency; 370 | target = B6C9764C24CA992A0069B2D6 /* AudioUnitRenderingExample */; 371 | targetProxy = B6C9766424CA992E0069B2D6 /* PBXContainerItemProxy */; 372 | }; 373 | /* End PBXTargetDependency section */ 374 | 375 | /* Begin PBXVariantGroup section */ 376 | B6C9765624CA992A0069B2D6 /* Main.storyboard */ = { 377 | isa = PBXVariantGroup; 378 | children = ( 379 | B6C9765724CA992A0069B2D6 /* Base */, 380 | ); 381 | name = Main.storyboard; 382 | sourceTree = ""; 383 | }; 384 | B6C9765B24CA992E0069B2D6 /* LaunchScreen.storyboard */ = { 385 | isa = PBXVariantGroup; 386 | children = ( 387 | B6C9765C24CA992E0069B2D6 /* Base */, 388 | ); 389 | name = LaunchScreen.storyboard; 390 | sourceTree = ""; 391 | }; 392 | /* End PBXVariantGroup section */ 393 | 394 | /* Begin XCBuildConfiguration section */ 395 | B6C9766A24CA992E0069B2D6 /* Debug */ = { 396 | isa = XCBuildConfiguration; 397 | buildSettings = { 398 | ALWAYS_SEARCH_USER_PATHS = NO; 399 | CLANG_ANALYZER_NONNULL = YES; 400 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 401 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 402 | CLANG_CXX_LIBRARY = "libc++"; 403 | CLANG_ENABLE_MODULES = YES; 404 | CLANG_ENABLE_OBJC_ARC = YES; 405 | CLANG_ENABLE_OBJC_WEAK = YES; 406 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 407 | CLANG_WARN_BOOL_CONVERSION = YES; 408 | CLANG_WARN_COMMA = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 411 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 412 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 413 | CLANG_WARN_EMPTY_BODY = YES; 414 | CLANG_WARN_ENUM_CONVERSION = YES; 415 | CLANG_WARN_INFINITE_RECURSION = YES; 416 | CLANG_WARN_INT_CONVERSION = YES; 417 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 418 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 419 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 421 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 422 | CLANG_WARN_STRICT_PROTOTYPES = YES; 423 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 424 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | COPY_PHASE_STRIP = NO; 428 | DEBUG_INFORMATION_FORMAT = dwarf; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | ENABLE_TESTABILITY = YES; 431 | GCC_C_LANGUAGE_STANDARD = gnu11; 432 | GCC_DYNAMIC_NO_PIC = NO; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_OPTIMIZATION_LEVEL = 0; 435 | GCC_PREPROCESSOR_DEFINITIONS = ( 436 | "DEBUG=1", 437 | "$(inherited)", 438 | ); 439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 441 | GCC_WARN_UNDECLARED_SELECTOR = YES; 442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 443 | GCC_WARN_UNUSED_FUNCTION = YES; 444 | GCC_WARN_UNUSED_VARIABLE = YES; 445 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 446 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 447 | MTL_FAST_MATH = YES; 448 | ONLY_ACTIVE_ARCH = YES; 449 | SDKROOT = iphoneos; 450 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 451 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 452 | }; 453 | name = Debug; 454 | }; 455 | B6C9766B24CA992E0069B2D6 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ANALYZER_NONNULL = YES; 460 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 461 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 462 | CLANG_CXX_LIBRARY = "libc++"; 463 | CLANG_ENABLE_MODULES = YES; 464 | CLANG_ENABLE_OBJC_ARC = YES; 465 | CLANG_ENABLE_OBJC_WEAK = YES; 466 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 467 | CLANG_WARN_BOOL_CONVERSION = YES; 468 | CLANG_WARN_COMMA = YES; 469 | CLANG_WARN_CONSTANT_CONVERSION = YES; 470 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 471 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 472 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 473 | CLANG_WARN_EMPTY_BODY = YES; 474 | CLANG_WARN_ENUM_CONVERSION = YES; 475 | CLANG_WARN_INFINITE_RECURSION = YES; 476 | CLANG_WARN_INT_CONVERSION = YES; 477 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 478 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 479 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 480 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 481 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 482 | CLANG_WARN_STRICT_PROTOTYPES = YES; 483 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 484 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 485 | CLANG_WARN_UNREACHABLE_CODE = YES; 486 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 487 | COPY_PHASE_STRIP = NO; 488 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 489 | ENABLE_NS_ASSERTIONS = NO; 490 | ENABLE_STRICT_OBJC_MSGSEND = YES; 491 | GCC_C_LANGUAGE_STANDARD = gnu11; 492 | GCC_NO_COMMON_BLOCKS = YES; 493 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 494 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 495 | GCC_WARN_UNDECLARED_SELECTOR = YES; 496 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 497 | GCC_WARN_UNUSED_FUNCTION = YES; 498 | GCC_WARN_UNUSED_VARIABLE = YES; 499 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 500 | MTL_ENABLE_DEBUG_INFO = NO; 501 | MTL_FAST_MATH = YES; 502 | SDKROOT = iphoneos; 503 | SWIFT_COMPILATION_MODE = wholemodule; 504 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 505 | VALIDATE_PRODUCT = YES; 506 | }; 507 | name = Release; 508 | }; 509 | B6C9766D24CA992E0069B2D6 /* Debug */ = { 510 | isa = XCBuildConfiguration; 511 | buildSettings = { 512 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 513 | CLANG_ENABLE_MODULES = YES; 514 | CODE_SIGN_STYLE = Automatic; 515 | DEVELOPMENT_TEAM = WVVR5YE53F; 516 | INFOPLIST_FILE = AudioUnitRenderingExample/Info.plist; 517 | LD_RUNPATH_SEARCH_PATHS = ( 518 | "$(inherited)", 519 | "@executable_path/Frameworks", 520 | ); 521 | PRODUCT_BUNDLE_IDENTIFIER = "objective-audio.AudioUnitRenderingExample"; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | SWIFT_OBJC_BRIDGING_HEADER = "AudioUnitRenderingExample/AudioUnitRenderingExample-Bridging-Header.h"; 524 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 525 | SWIFT_VERSION = 5.0; 526 | TARGETED_DEVICE_FAMILY = "1,2"; 527 | }; 528 | name = Debug; 529 | }; 530 | B6C9766E24CA992E0069B2D6 /* Release */ = { 531 | isa = XCBuildConfiguration; 532 | buildSettings = { 533 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 534 | CLANG_ENABLE_MODULES = YES; 535 | CODE_SIGN_STYLE = Automatic; 536 | DEVELOPMENT_TEAM = WVVR5YE53F; 537 | INFOPLIST_FILE = AudioUnitRenderingExample/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "@executable_path/Frameworks", 541 | ); 542 | PRODUCT_BUNDLE_IDENTIFIER = "objective-audio.AudioUnitRenderingExample"; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | SWIFT_OBJC_BRIDGING_HEADER = "AudioUnitRenderingExample/AudioUnitRenderingExample-Bridging-Header.h"; 545 | SWIFT_VERSION = 5.0; 546 | TARGETED_DEVICE_FAMILY = "1,2"; 547 | }; 548 | name = Release; 549 | }; 550 | B6C9767024CA992E0069B2D6 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 554 | BUNDLE_LOADER = "$(TEST_HOST)"; 555 | CLANG_ENABLE_MODULES = YES; 556 | CODE_SIGN_STYLE = Automatic; 557 | DEVELOPMENT_TEAM = WVVR5YE53F; 558 | INFOPLIST_FILE = AudioUnitRenderingExampleTests/Info.plist; 559 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 560 | LD_RUNPATH_SEARCH_PATHS = ( 561 | "$(inherited)", 562 | "@executable_path/Frameworks", 563 | "@loader_path/Frameworks", 564 | ); 565 | PRODUCT_BUNDLE_IDENTIFIER = "objective-audio.AudioUnitRenderingExampleTests"; 566 | PRODUCT_NAME = "$(TARGET_NAME)"; 567 | SWIFT_OBJC_BRIDGING_HEADER = "AudioUnitRenderingExampleTests/AudioUnitRenderingExampleTests-Bridging-Header.h"; 568 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 569 | SWIFT_VERSION = 5.0; 570 | TARGETED_DEVICE_FAMILY = "1,2"; 571 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AudioUnitRenderingExample.app/AudioUnitRenderingExample"; 572 | }; 573 | name = Debug; 574 | }; 575 | B6C9767124CA992E0069B2D6 /* Release */ = { 576 | isa = XCBuildConfiguration; 577 | buildSettings = { 578 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 579 | BUNDLE_LOADER = "$(TEST_HOST)"; 580 | CLANG_ENABLE_MODULES = YES; 581 | CODE_SIGN_STYLE = Automatic; 582 | DEVELOPMENT_TEAM = WVVR5YE53F; 583 | INFOPLIST_FILE = AudioUnitRenderingExampleTests/Info.plist; 584 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 585 | LD_RUNPATH_SEARCH_PATHS = ( 586 | "$(inherited)", 587 | "@executable_path/Frameworks", 588 | "@loader_path/Frameworks", 589 | ); 590 | PRODUCT_BUNDLE_IDENTIFIER = "objective-audio.AudioUnitRenderingExampleTests"; 591 | PRODUCT_NAME = "$(TARGET_NAME)"; 592 | SWIFT_OBJC_BRIDGING_HEADER = "AudioUnitRenderingExampleTests/AudioUnitRenderingExampleTests-Bridging-Header.h"; 593 | SWIFT_VERSION = 5.0; 594 | TARGETED_DEVICE_FAMILY = "1,2"; 595 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AudioUnitRenderingExample.app/AudioUnitRenderingExample"; 596 | }; 597 | name = Release; 598 | }; 599 | /* End XCBuildConfiguration section */ 600 | 601 | /* Begin XCConfigurationList section */ 602 | B6C9764824CA992A0069B2D6 /* Build configuration list for PBXProject "AudioUnitRenderingExample" */ = { 603 | isa = XCConfigurationList; 604 | buildConfigurations = ( 605 | B6C9766A24CA992E0069B2D6 /* Debug */, 606 | B6C9766B24CA992E0069B2D6 /* Release */, 607 | ); 608 | defaultConfigurationIsVisible = 0; 609 | defaultConfigurationName = Release; 610 | }; 611 | B6C9766C24CA992E0069B2D6 /* Build configuration list for PBXNativeTarget "AudioUnitRenderingExample" */ = { 612 | isa = XCConfigurationList; 613 | buildConfigurations = ( 614 | B6C9766D24CA992E0069B2D6 /* Debug */, 615 | B6C9766E24CA992E0069B2D6 /* Release */, 616 | ); 617 | defaultConfigurationIsVisible = 0; 618 | defaultConfigurationName = Release; 619 | }; 620 | B6C9766F24CA992E0069B2D6 /* Build configuration list for PBXNativeTarget "AudioUnitRenderingExampleTests" */ = { 621 | isa = XCConfigurationList; 622 | buildConfigurations = ( 623 | B6C9767024CA992E0069B2D6 /* Debug */, 624 | B6C9767124CA992E0069B2D6 /* Release */, 625 | ); 626 | defaultConfigurationIsVisible = 0; 627 | defaultConfigurationName = Release; 628 | }; 629 | /* End XCConfigurationList section */ 630 | }; 631 | rootObject = B6C9764524CA992A0069B2D6 /* Project object */; 632 | } 633 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample.xcodeproj/xcshareddata/xcschemes/AudioUnitRenderingExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeter.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | import AVFoundation 12 | 13 | class InputMeter { 14 | var value: InputMeterRendererValue { renderer.value } 15 | 16 | var lostPublisher: AnyPublisher { lostSubject.eraseToAnyPublisher() } 17 | 18 | private let session: AudioSession 19 | private var engine: AVAudioEngine? 20 | private let renderer = InputMeterRenderer() 21 | private let lostSubject = PassthroughSubject() 22 | private var cancellables = Set() 23 | 24 | enum StartError: Error { 25 | case setupAudioSession 26 | case inputUnavailable 27 | case startEngine 28 | } 29 | 30 | init(session: AudioSession = .shared) { 31 | self.session = session 32 | } 33 | 34 | deinit { 35 | stop() 36 | } 37 | 38 | func start() throws { 39 | do { 40 | try session.activate(category: .record) 41 | } catch { 42 | throw StartError.setupAudioSession 43 | } 44 | 45 | guard session.hasInput else { 46 | throw StartError.inputUnavailable 47 | } 48 | 49 | AudioSession.shared.rerouteSubject.sink { [weak self] in 50 | self?.reroute() 51 | }.store(in: &cancellables) 52 | 53 | AudioSession.shared.lostSubject.subscribe(lostSubject).store(in: &cancellables) 54 | 55 | do { 56 | try setupAndStartEngine() 57 | } catch { 58 | throw StartError.startEngine 59 | } 60 | } 61 | 62 | func stop() { 63 | disposeEngine() 64 | 65 | cancellables.removeAll() 66 | 67 | session.deactivate() 68 | } 69 | } 70 | 71 | private extension InputMeter { 72 | func setupAndStartEngine() throws { 73 | let engine = AVAudioEngine() 74 | 75 | let inputFormat = engine.inputNode.inputFormat(forBus: 0) 76 | let sinkNode = renderer.makeSineNode(sampleRate: inputFormat.sampleRate); 77 | 78 | engine.attach(sinkNode) 79 | engine.connect(engine.inputNode, to: sinkNode, format: nil) 80 | 81 | let outputFormat = engine.inputNode.outputFormat(forBus: 0) 82 | print("InputMeter Engine - format : \(outputFormat)") 83 | 84 | try engine.start() 85 | 86 | self.engine = engine 87 | } 88 | 89 | func disposeEngine() { 90 | engine?.stop() 91 | engine = nil 92 | } 93 | 94 | func reroute() { 95 | disposeEngine() 96 | 97 | do { 98 | try setupAndStartEngine() 99 | } catch { 100 | self.lostSubject.send() 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeterKernel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterKernel.cpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #include "InputMeterKernel.hpp" 10 | #include "InputMeterUtils.h" 11 | #include 12 | #include 13 | 14 | InputMeterKernel::Value InputMeterKernel::value() const { 15 | return InputMeterKernel::Value{.v = atomicValue}; 16 | } 17 | 18 | void InputMeterKernel::updateLevel(float const * const * const buffers, 19 | int const bufferCount, 20 | int const frameCount, 21 | double const sampleRate) { 22 | float currentLinear = 0.0f; 23 | 24 | int bufferIndex = 0; 25 | while (bufferIndex < bufferCount) { 26 | float max = 0.0f; 27 | vDSP_maxmgv(buffers[bufferIndex], 1, &max, frameCount); 28 | currentLinear = std::fmax(max, currentLinear); 29 | ++bufferIndex; 30 | } 31 | 32 | currentLinear = std::fmin(currentLinear, 1.0f); 33 | 34 | peakDuration += (double)frameCount / sampleRate; 35 | 36 | auto const prevValue = value(); 37 | auto const prevDecibel = decibelFromLinear(prevValue.level) - (float)frameCount / (float)sampleRate * 30.0f; 38 | auto const prevLinear = linearFromDecibel(prevDecibel); 39 | auto const nextLinear = std::fmax(prevLinear, currentLinear); 40 | auto const updatePeak = (prevValue.peak < nextLinear) || (peakDuration > 1.0); 41 | 42 | if (updatePeak) { 43 | peakDuration = 0.0; 44 | } 45 | 46 | Value const value{ 47 | .level = nextLinear, 48 | .peak = updatePeak ? nextLinear : prevValue.peak 49 | }; 50 | 51 | atomicValue = value.v; 52 | } 53 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeterKernel.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterKernel.hpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | struct InputMeterKernel { 14 | /// メーターに表示する値2つをアトミックに扱うための共用体 15 | /// そんなに厳密にアトミックにするほどのものではないがサンプルとして使っている 16 | union Value { 17 | void *v; 18 | struct { 19 | float const level; 20 | float const peak; 21 | }; 22 | }; 23 | /// Valueのサイズがvoid *のサイズを超えていないことをコンパイル時にチェック 24 | static_assert(sizeof(Value) == sizeof(void *), ""); 25 | 26 | /// メーターに表示する値をメインスレッドから取得する。アトミック 27 | Value value() const; 28 | 29 | /// 入力されたデータを渡しメーターの値をオーディオIOスレッドから更新する 30 | void updateLevel(float const * const * const buffers, 31 | int const bufferCount, 32 | int const frameCount, 33 | double const sampleRate); 34 | 35 | private: 36 | std::atomic atomicValue; 37 | double peakDuration = 0.0; 38 | }; 39 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeterRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterRenderer.h 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | struct InputMeterRendererValue { 15 | float const level; 16 | float const peak; 17 | }; 18 | 19 | @interface InputMeterRenderer : NSObject 20 | 21 | @property (nonatomic, readonly) struct InputMeterRendererValue value; 22 | 23 | - (AVAudioSinkNode *)makeSinkNodeWithSampleRate:(double)sampleRate NS_SWIFT_NAME(makeSineNode(sampleRate:)); 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeterRenderer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterRenderer.mm 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import "InputMeterRenderer.h" 10 | #include 11 | #include "InputMeterKernel.hpp" 12 | 13 | @implementation InputMeterRenderer { 14 | std::shared_ptr _kernel; 15 | } 16 | 17 | - (instancetype)init { 18 | self = [super init]; 19 | if (self) { 20 | _kernel = std::make_shared(); 21 | } 22 | return self; 23 | } 24 | 25 | - (InputMeterRendererValue)value { 26 | auto const dataValue = _kernel->value(); 27 | return {.level = dataValue.level, .peak = dataValue.peak}; 28 | } 29 | 30 | - (AVAudioSinkNode *)makeSinkNodeWithSampleRate:(double)sampleRate { 31 | return [[AVAudioSinkNode alloc] initWithReceiverBlock:[kernel = _kernel, sampleRate] (const AudioTimeStamp * _Nonnull timestamp, 32 | AVAudioFrameCount frameCount, 33 | const AudioBufferList * _Nonnull inputData) { 34 | auto const bufferCount = inputData->mNumberBuffers; 35 | 36 | float *buffers[bufferCount]; 37 | 38 | for (uint32_t i = 0; i < bufferCount; ++i) { 39 | buffers[i] = static_cast(inputData->mBuffers[i].mData); 40 | } 41 | 42 | kernel->updateLevel(buffers, inputData->mNumberBuffers, frameCount, sampleRate); 43 | 44 | return (OSStatus)noErr; 45 | }]; 46 | } 47 | @end 48 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeterUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterUtils.h 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | float linearFromDecibel(float const decibel) { 14 | return powf(10.0f, decibel / 20.0f); 15 | } 16 | 17 | float decibelFromLinear(float const linear) { 18 | return 20.0f * log10f(linear); 19 | } 20 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSinkNode/InputMeterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterViewController.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/25. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Combine 11 | 12 | class InputMeterViewController: UIViewController { 13 | @IBOutlet private weak var baseView: UIView! 14 | @IBOutlet private weak var levelConstraint: NSLayoutConstraint! 15 | @IBOutlet private weak var peakConstraint: NSLayoutConstraint! 16 | @IBOutlet private weak var meterLabel: UILabel! 17 | 18 | private let meter = InputMeter() 19 | private var displayLink: CADisplayLink? 20 | 21 | private var cancellables = Set() 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | meter.lostPublisher.sink { [weak self] _ in 27 | self?.showAlert(title: "Lost", message: nil) 28 | }.store(in: &cancellables) 29 | 30 | levelConstraint.constant = 0.0 31 | peakConstraint.constant = 0.0 32 | } 33 | 34 | override func viewDidAppear(_ animated: Bool) { 35 | super.viewDidAppear(animated) 36 | 37 | do { 38 | try meter.start() 39 | } catch { 40 | let message = (error as? InputMeter.StartError).flatMap { "\($0)" } 41 | showAlert(title: "Error", message: message) 42 | } 43 | 44 | displayLink = CADisplayLink(target: self, selector: #selector(updateMeter)) 45 | displayLink?.add(to: .main, forMode: .default) 46 | } 47 | 48 | override func viewWillDisappear(_ animated: Bool) { 49 | super.viewWillDisappear(animated) 50 | 51 | displayLink?.invalidate() 52 | displayLink = nil 53 | 54 | meter.stop() 55 | } 56 | } 57 | 58 | private extension InputMeterViewController { 59 | func showAlert(title: String, message: String?) { 60 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 61 | alert.addAction(.init(title: "OK", style: .default) { [weak self] _ in self?.popViewController() }) 62 | present(alert, animated: true, completion: nil) 63 | } 64 | 65 | func popViewController() { 66 | navigationController?.popToRootViewController(animated: true) 67 | } 68 | 69 | @objc func updateMeter() { 70 | let value = meter.value 71 | 72 | let maxWidth = baseView.frame.width 73 | levelConstraint.constant = CGFloat(value.level) * maxWidth 74 | peakConstraint.constant = CGFloat(value.peak) * maxWidth 75 | 76 | let dbValue = String(format: "%.1f", decibelFromLinear(value.peak)) 77 | meterLabel.text = "\(dbValue) dB" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeCommon/SineKernel.h: -------------------------------------------------------------------------------- 1 | // 2 | // SineKernel.h 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | /// サイン波再生用の共有データのためのインターフェース 14 | struct SineKernel { 15 | virtual ~SineKernel() = default; 16 | 17 | /// サイン波の周波数をメインスレッドからセットする 18 | virtual void setFrequency(double const) = 0; 19 | 20 | /// 出力するデータをオーディオIOスレッドから取得する 21 | virtual void render(float * const * const buffers, 22 | int const bufferCount, 23 | int const frameCount) = 0; 24 | }; 25 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeCommon/SinePlayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SinePlayer.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AVFoundation 11 | import Combine 12 | 13 | enum SinePlayerStartError: Error { 14 | case setupAudioSession 15 | case outputUnavailable 16 | case startEngine 17 | } 18 | 19 | class SinePlayer { 20 | @Published var frequency: Double 21 | 22 | private let session: AudioSession 23 | private var engine: AVAudioEngine? 24 | private let renderer: SineRenderer 25 | private let lostSubject = PassthroughSubject() 26 | private var cancellables = Set() 27 | 28 | init(session: AudioSession = .shared, renderer: SineRenderer) { 29 | self.session = session 30 | self.renderer = renderer 31 | self.frequency = renderer.frequency 32 | } 33 | 34 | deinit { 35 | stop() 36 | } 37 | 38 | var frequencyPublisher: AnyPublisher { $frequency.eraseToAnyPublisher() } 39 | var lostPublisher: AnyPublisher { lostSubject.eraseToAnyPublisher() } 40 | 41 | func start() throws { 42 | do { 43 | try session.activate(category: .playback) 44 | } catch { 45 | throw SinePlayerStartError.setupAudioSession 46 | } 47 | 48 | guard session.hasOutput else { 49 | throw SinePlayerStartError.outputUnavailable 50 | } 51 | 52 | AudioSession.shared.rerouteSubject.sink { [weak self] in 53 | self?.reroute() 54 | }.store(in: &cancellables) 55 | 56 | AudioSession.shared.lostSubject.subscribe(lostSubject).store(in: &cancellables) 57 | 58 | do { 59 | try setupAndStartEngine() 60 | } catch { 61 | throw SinePlayerStartError.startEngine 62 | } 63 | } 64 | 65 | func stop() { 66 | disposeEngine() 67 | 68 | cancellables.removeAll() 69 | 70 | session.deactivate() 71 | } 72 | } 73 | 74 | private extension SinePlayer { 75 | func setupAndStartEngine() throws { 76 | let engine = AVAudioEngine() 77 | 78 | let outputFormat = engine.outputNode.outputFormat(forBus: 0); 79 | let sourceNode = renderer.makeSourceNode(format: outputFormat); 80 | 81 | engine.attach(sourceNode) 82 | engine.connect(sourceNode, to: engine.outputNode, format: outputFormat) 83 | 84 | $frequency.sink { [weak self] value in 85 | self?.renderer.frequency = value 86 | }.store(in: &cancellables) 87 | 88 | let inputFormat = engine.outputNode.inputFormat(forBus: 0) 89 | print("SinePlayer Engine - format : \(inputFormat)") 90 | 91 | try engine.start() 92 | 93 | self.engine = engine 94 | } 95 | 96 | func disposeEngine() { 97 | engine?.stop() 98 | engine = nil 99 | } 100 | 101 | func reroute() { 102 | do { 103 | disposeEngine() 104 | try setupAndStartEngine() 105 | } catch { 106 | self.lostSubject.send() 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeCommon/SineRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // DirectSineRenderer.h 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | typedef NS_ENUM(NSUInteger, SineRendererType) { 15 | SineRendererTypeDirect, 16 | SineRendererTypeDeferred, 17 | }; 18 | 19 | @interface SineRenderer : NSObject 20 | 21 | @property (nonatomic) double frequency; 22 | 23 | - (instancetype)init NS_UNAVAILABLE; 24 | - (instancetype)initWithType:(SineRendererType)type; 25 | 26 | - (AVAudioSourceNode *)makeSourceNodeWithFormat:(AVAudioFormat *)format NS_SWIFT_NAME(makeSourceNode(format:)); 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeCommon/SineRenderer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // DirectSineRenderer.mm 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import "SineRenderer.h" 10 | #include "DirectSineKernel.hpp" 11 | #include "DeferredSineKernel.hpp" 12 | #include 13 | 14 | @implementation SineRenderer { 15 | SineRendererType _type; 16 | double _frequency; 17 | std::shared_ptr _kernel; 18 | } 19 | 20 | - (instancetype)initWithType:(SineRendererType)type { 21 | self = [super init]; 22 | if (self) { 23 | _type = type; 24 | _frequency = 1000.0; 25 | } 26 | return self; 27 | } 28 | 29 | - (double)frequency { 30 | return _frequency; 31 | } 32 | 33 | - (void)setFrequency:(double)frequency { 34 | _frequency = frequency; 35 | 36 | if (_kernel) { 37 | _kernel->setFrequency(frequency); 38 | } 39 | } 40 | 41 | - (AVAudioSourceNode *)makeSourceNodeWithFormat:(AVAudioFormat *)format { 42 | switch (_type) { 43 | case SineRendererTypeDirect: 44 | _kernel = std::make_shared(format.sampleRate); 45 | break; 46 | case SineRendererTypeDeferred: 47 | _kernel = std::make_shared(format.sampleRate); 48 | break; 49 | } 50 | 51 | return [[AVAudioSourceNode alloc] initWithFormat:format 52 | renderBlock:[kernel = _kernel] (BOOL * _Nonnull isSilence, 53 | const AudioTimeStamp * _Nonnull timestamp, 54 | AVAudioFrameCount frameCount, 55 | AudioBufferList * _Nonnull outputData) { 56 | auto const bufferCount = outputData->mNumberBuffers; 57 | 58 | float *buffers[bufferCount]; 59 | 60 | for (uint32_t i = 0; i < bufferCount; ++i) { 61 | buffers[i] = static_cast(outputData->mBuffers[i].mData); 62 | } 63 | 64 | kernel->render(buffers, bufferCount, frameCount); 65 | 66 | return (OSStatus)noErr; 67 | }]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeCommon/SineViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Combine 11 | 12 | class SineViewController: UIViewController { 13 | @IBOutlet private weak var frequencySlider: UISlider! 14 | 15 | private var sinePlayer: SinePlayer! 16 | 17 | private var cancellables = Set() 18 | 19 | func setup(sinePlayer: SinePlayer) { 20 | self.sinePlayer = sinePlayer 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | sinePlayer.frequencyPublisher.map { Float($0) }.sink { [weak self] value in 27 | self?.frequencySlider.value = value 28 | }.store(in: &cancellables) 29 | 30 | sinePlayer.lostPublisher.sink { [weak self] _ in 31 | self?.showAlert(title: "Lost", message: nil) 32 | }.store(in: &cancellables) 33 | } 34 | 35 | override func viewDidAppear(_ animated: Bool) { 36 | super.viewDidAppear(animated) 37 | 38 | do { 39 | try sinePlayer.start() 40 | } catch { 41 | let message = (error as? SinePlayerStartError).flatMap { "\($0)" } 42 | showAlert(title: "Error", message: message) 43 | } 44 | } 45 | 46 | override func viewWillDisappear(_ animated: Bool) { 47 | super.viewWillDisappear(animated) 48 | 49 | sinePlayer.stop() 50 | } 51 | 52 | @IBAction func sliderValueChanged(_ sender: UISlider) { 53 | sinePlayer.frequency = Double(sender.value) 54 | } 55 | } 56 | 57 | private extension SineViewController { 58 | func showAlert(title: String, message: String?) { 59 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 60 | alert.addAction(.init(title: "OK", style: .default) { [weak self] _ in self?.popViewController() }) 61 | present(alert, animated: true, completion: nil) 62 | } 63 | 64 | func popViewController() { 65 | navigationController?.popToRootViewController(animated: true) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeDeferred/DeferredSineKernel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DeferredSineKernel.cpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #include "DeferredSineKernel.hpp" 10 | #include 11 | #include "RingBuffer.hpp" 12 | 13 | DeferredSineKernel::~DeferredSineKernel() { 14 | ringBuffer->disposed = true; 15 | } 16 | 17 | DeferredSineKernel::DeferredSineKernel(double const sampleRate) : ringBuffer(std::make_shared(sampleRate)) { 18 | std::thread thread{[ringBuffer = ringBuffer]{ 19 | while (!ringBuffer->disposed) { 20 | ringBuffer->fillSineIfNeeded(); 21 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 22 | } 23 | }}; 24 | 25 | thread.detach(); 26 | } 27 | 28 | void DeferredSineKernel::setFrequency(double const frequency) { 29 | ringBuffer->frequency = frequency; 30 | } 31 | 32 | void DeferredSineKernel::render(float * const * const buffers, 33 | int const bufferCount, 34 | int const frameCount) { 35 | if (bufferCount < 1 || frameCount < 1) { 36 | return; 37 | } 38 | 39 | ringBuffer->read(buffers, bufferCount, frameCount); 40 | } 41 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeDeferred/DeferredSineKernel.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // DeferredSineKernel.hpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "SineKernel.h" 12 | #include 13 | 14 | class RingBuffer; 15 | 16 | struct DeferredSineKernel: SineKernel { 17 | DeferredSineKernel(double const sampleRate); 18 | ~DeferredSineKernel(); 19 | 20 | void setFrequency(double const) override; 21 | 22 | void render(float * const * const buffers, 23 | int const bufferCount, 24 | int const frameCount) override; 25 | 26 | private: 27 | std::shared_ptr ringBuffer; 28 | }; 29 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeDeferred/RingBuffer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RingBuffer.cpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #include "RingBuffer.hpp" 10 | #include "SineUtils.hpp" 11 | #include 12 | #include 13 | 14 | #pragma mark - 15 | 16 | RingBufferElement::RingBufferElement(std::size_t const length) : rawData(length, 0.0f) {} 17 | 18 | RingBufferElement::State RingBufferElement::state() const { 19 | return static_cast(rawState.load()); 20 | } 21 | 22 | void RingBufferElement::setState(State const state) { 23 | rawState.store(state); 24 | } 25 | 26 | int RingBufferElement::frameCount() const { 27 | return static_cast(rawData.size()); 28 | } 29 | 30 | float *RingBufferElement::writableData() { 31 | return rawData.data(); 32 | } 33 | 34 | float const *RingBufferElement::readableData() const { 35 | return rawData.data(); 36 | } 37 | 38 | #pragma mark - 39 | 40 | RingBuffer::RingBuffer(double const sampleRate) : sampleRate(sampleRate) { 41 | auto const length = static_cast(sampleRate); 42 | for (int i = 0; i < 2; ++i) { 43 | elements.emplace_back(std::make_unique(length)); 44 | } 45 | } 46 | 47 | void RingBuffer::fillSineIfNeeded() { 48 | for (auto &element : elements) { 49 | if (element->state() == RingBufferElement::State::writing) { 50 | theta = SineUtils::fillSine(element->writableData(), element->frameCount(), frequency, sampleRate, theta); 51 | element->setState(RingBufferElement::State::reading); 52 | std::this_thread::yield(); 53 | } 54 | } 55 | } 56 | 57 | void RingBuffer::read(float * const * const outBuffers, 58 | int const outBufferCount, 59 | int const outFrameCount) { 60 | int outFrame = 0; 61 | 62 | while (outFrame < outFrameCount) { 63 | auto const &element = elements[readElementIndex]; 64 | 65 | if (element->state() == RingBufferElement::State::reading) { 66 | int const outRemainFrameCount = outFrameCount - outFrame; 67 | int const elementFrameCount = element->frameCount(); 68 | int const elementRemainFrameCount = elementFrameCount - readElementFrame; 69 | int const readFrameCount = std::min(outRemainFrameCount, elementRemainFrameCount); 70 | 71 | for (int bufferIndex = 0; bufferIndex < outBufferCount; ++bufferIndex) { 72 | cblas_scopy(readFrameCount, 73 | &element->readableData()[readElementFrame], 74 | 1, 75 | &outBuffers[bufferIndex][outFrame], 76 | 1); 77 | } 78 | 79 | readElementFrame += readFrameCount; 80 | outFrame += readFrameCount; 81 | 82 | if (elementFrameCount <= readElementFrame) { 83 | elements[readElementIndex]->setState(RingBufferElement::State::writing); 84 | readElementIndex = (readElementIndex + 1) % elements.size(); 85 | readElementFrame = 0; 86 | } 87 | } else { 88 | // Elementが準備されていないので打ち切る 89 | break; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeDeferred/RingBuffer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // RingBuffer.hpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | /// リングバッファの1要素。1秒分のオーディオデータを持つ 16 | struct RingBufferElement { 17 | /// バッファにアクセスする状態 18 | /// writingなら書き込みようスレッドから書き込める 19 | /// readingならオーディオIOスレッドから読み込める 20 | enum State: int { 21 | writing, 22 | reading, 23 | }; 24 | 25 | RingBufferElement(std::size_t const length); 26 | 27 | /// 状態を取得する。アトミック 28 | State state() const; 29 | 30 | /// 状態をセットする。アトミック 31 | void setState(State const); 32 | 33 | /// バッファの長さ 34 | int frameCount() const; 35 | 36 | /// 書き込み用にデータの先頭ポインタを取得する 37 | float *writableData(); 38 | 39 | /// 読み込み用にデータの先頭ポインタを取得する 40 | float const *readableData() const; 41 | 42 | private: 43 | std::vector rawData; 44 | std::atomic rawState{State::writing}; 45 | }; 46 | 47 | /// オーディオデータの要素を2つ持つリングバッファ 48 | struct RingBuffer { 49 | /// リングバッファを破棄して良いかのフラグ。trueなら書き込み用スレッドを終了する 50 | std::atomic disposed{false}; 51 | 52 | /// 書き込むサイン波の周波数 53 | std::atomic frequency{1000.0}; 54 | 55 | RingBuffer(double const sampleRate); 56 | 57 | /// 書き込み可能ならサイン波を書き込む 58 | void fillSineIfNeeded(); 59 | 60 | /// 読み込み可能ならデータを読み込む 61 | void read(float * const * const outBuffers, 62 | int const outBufferCount, 63 | int const outFrameCount); 64 | 65 | private: 66 | std::vector> elements; 67 | double const sampleRate; 68 | int readElementIndex = 0; 69 | int readElementFrame = 0; 70 | double theta = 0.0; 71 | }; 72 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeDirect/DirectSineKernel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DirectSineKernel.cpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/06. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #include "DirectSineKernel.hpp" 10 | #include 11 | #include "SineUtils.hpp" 12 | 13 | DirectSineKernel::DirectSineKernel(double const sampleRate) : sampleRate(sampleRate) { 14 | } 15 | 16 | void DirectSineKernel::setFrequency(double const frequency) { 17 | atomicFrequency = frequency; 18 | } 19 | 20 | void DirectSineKernel::render(float * const * const buffers, 21 | int const bufferCount, 22 | int const frameCount) { 23 | if (bufferCount < 1 || frameCount < 1) { 24 | return; 25 | } 26 | 27 | theta = SineUtils::fillSine(buffers[0], frameCount, atomicFrequency, sampleRate, theta); 28 | 29 | for (int bufferIndex = 1; bufferIndex < bufferCount; ++bufferIndex) { 30 | cblas_scopy(frameCount, buffers[0], 1, buffers[bufferIndex], 1); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AVAudioSourceNodeDirect/DirectSineKernel.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // DirectSineKernel.hpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "SineKernel.h" 12 | 13 | struct DirectSineKernel: SineKernel { 14 | DirectSineKernel(double const sampleRate); 15 | 16 | void setFrequency(double const) override; 17 | 18 | void render(float * const * const buffers, 19 | int const bufferCount, 20 | int const frameCount) override; 21 | 22 | private: 23 | double const sampleRate; 24 | double theta = 0.0; 25 | std::atomic atomicFrequency = 1000.0; 26 | }; 27 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/AudioUnitRenderingExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "SineRenderer.h" 6 | #import "InputMeterRenderer.h" 7 | #import "InputMeterUtils.h" 8 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSMicrophoneUsageDescription 24 | 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | UIWindowSceneSessionRoleApplication 32 | 33 | 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | $(PRODUCT_MODULE_NAME).SceneDelegate 38 | UISceneStoryboardFile 39 | Main 40 | 41 | 42 | 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Menu/MenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuViewController.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MenuViewController: UITableViewController { 12 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 13 | if let viewController = segue.destination as? SineViewController { 14 | switch segue.identifier { 15 | case "SourceDirect": 16 | viewController.setup(sinePlayer: .init(renderer: .init(type: .direct))) 17 | case "SourceDeferred": 18 | viewController.setup(sinePlayer: .init(renderer: .init(type: .deferred))) 19 | default: 20 | fatalError() 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/MeterData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MeterData.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class InputMeterData { 12 | @Atomic var level: Float = 0.0 13 | } 14 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Utils/AudioSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AudioSession.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/26. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AVFoundation 11 | import Combine 12 | 13 | private func printAudioSession(message: String) { 14 | let session = AVAudioSession.sharedInstance() 15 | print("AVAudioSession \(message) sampleRate : \(session.sampleRate) - outputChannelCount : \(session.outputNumberOfChannels) - inputChannelCount : \(session.inputNumberOfChannels)") 16 | } 17 | 18 | class AudioSession { 19 | static let shared = AudioSession() 20 | 21 | let rerouteSubject = PassthroughSubject() 22 | let lostSubject = PassthroughSubject() 23 | 24 | var hasOutput: Bool { session.outputNumberOfChannels > 0 } 25 | var hasInput: Bool { session.isInputAvailable && session.inputNumberOfChannels > 0 } 26 | 27 | private var cancellables = Set() 28 | 29 | private init() { 30 | let session = AVAudioSession.sharedInstance() 31 | 32 | let routePublisher = NotificationCenter.default.publisher(for: AVAudioSession.routeChangeNotification, object: session) 33 | 34 | let routeShared = routePublisher.map { _ in Void() }.receive(on: OperationQueue.main).share() 35 | routeShared.sink { _ in printAudioSession(message: "routeChanged") }.store(in: &cancellables) 36 | routeShared.subscribe(rerouteSubject).store(in: &cancellables) 37 | 38 | let interruptionPublisher = NotificationCenter.default.publisher(for: AVAudioSession.interruptionNotification, object: session) 39 | let lostPublisher = NotificationCenter.default.publisher(for: AVAudioSession.mediaServicesWereLostNotification, object: session) 40 | let resetPublisher = NotificationCenter.default.publisher(for: AVAudioSession.mediaServicesWereResetNotification, object: session) 41 | 42 | interruptionPublisher 43 | .merge(with: lostPublisher, resetPublisher) 44 | .map { _ in Void() } 45 | .receive(on: OperationQueue.main) 46 | .subscribe(lostSubject) 47 | .store(in: &cancellables) 48 | } 49 | 50 | func activate(category: AVAudioSession.Category) throws { 51 | let session = AVAudioSession.sharedInstance() 52 | 53 | try session.setCategory(category) 54 | try session.setActive(true, options: []) 55 | 56 | printAudioSession(message: "activated") 57 | } 58 | 59 | func deactivate() { 60 | try? session.setActive(false, options: []) 61 | } 62 | } 63 | 64 | private extension AudioSession { 65 | var session: AVAudioSession { AVAudioSession.sharedInstance() } 66 | } 67 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Utils/SineUtils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SineUtils.cpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #include "SineUtils.hpp" 10 | #include 11 | #include 12 | 13 | double SineUtils::fillSine(float *buffer, 14 | int const frameCount, 15 | double const frequency, 16 | double const sampleRate, 17 | double const inTheta) { 18 | auto theta = inTheta; 19 | auto const twoPi = 2.0 * M_PI; 20 | auto const thetaDiff = frequency / sampleRate * twoPi; 21 | 22 | int frame = 0; 23 | while (frame < frameCount) { 24 | buffer[frame] = theta; 25 | theta = std::fmod(theta + thetaDiff, twoPi); 26 | ++frame; 27 | } 28 | 29 | vvsinf(buffer, buffer, &frameCount); 30 | 31 | float const volume = 0.1f; 32 | vDSP_vsmul(buffer, 1, &volume, buffer, 1, frameCount); 33 | 34 | return theta; 35 | } 36 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/Utils/SineUtils.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SineUtils.hpp 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #pragma once 10 | 11 | namespace SineUtils { 12 | /// バッファにサイン波を書き込む 13 | /// 返り値は次に書き込む際のinThetaに渡す値を返す 14 | double fillSine(float *buffer, 15 | int const frameCount, 16 | double const frequency, 17 | double const sampleRate, 18 | double const inTheta); 19 | }; 20 | -------------------------------------------------------------------------------- /AudioUnitRenderingExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // AudioUnitRenderingExample 4 | // 5 | // Created by Yuki Yasoshima on 2020/07/24. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SineViewController: UIViewController { 12 | var sinePlayer: SinePlayer! 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | // Do any additional setup after loading the view. 17 | 18 | sinePlayer = SinePlayer() 19 | 20 | do { 21 | try sinePlayer.start() 22 | } catch { 23 | print("start sine player failed.") 24 | } 25 | } 26 | 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/AudioUnitRenderingExampleTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "InputMeterUtils.h" 6 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/InputMeterKernelTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterKernelTests.mm 3 | // AudioUnitRenderingExampleTests 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/05. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "InputMeterKernel.hpp" 11 | #import 12 | 13 | static int constexpr bufferCount = 2; 14 | static int constexpr length = 2; 15 | static double constexpr sampleRate = length; 16 | 17 | @interface InputMeterKernelTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation InputMeterKernelTests { 22 | std::shared_ptr data; 23 | float *buffers[bufferCount]; 24 | float rawBuffers[bufferCount][length]; 25 | } 26 | 27 | - (void)setUp { 28 | data = std::make_shared(); 29 | 30 | XCTAssertEqual(data->value().level, 0.0); 31 | XCTAssertEqual(data->value().peak, 0.0); 32 | 33 | for (int i = 0; i < bufferCount; ++i) { 34 | buffers[i] = rawBuffers[i]; 35 | } 36 | 37 | [self resetPtrs]; 38 | } 39 | 40 | - (void)resetPtrs { 41 | for (int i = 0; i < bufferCount; ++i) { 42 | for (int j = 0; j < length; ++j) { 43 | buffers[i][j] = 0.0f; 44 | } 45 | } 46 | } 47 | 48 | - (void)test_レベルを更新するたびに少しずつレベルが下がる { 49 | buffers[0][0] = 1.0f; 50 | 51 | data->updateLevel(buffers, bufferCount, length, sampleRate); 52 | 53 | XCTAssertEqual(data->value().level, 1.0); 54 | 55 | buffers[0][0] = 0.0f; 56 | 57 | data->updateLevel(buffers, bufferCount, length, sampleRate); 58 | 59 | XCTAssertEqualWithAccuracy(data->value().level, 0.0316, 0.0001); 60 | 61 | data->updateLevel(buffers, bufferCount, length, sampleRate); 62 | 63 | XCTAssertEqualWithAccuracy(data->value().level, 0.001, 0.0001); 64 | 65 | data->updateLevel(buffers, bufferCount, length, sampleRate); 66 | 67 | XCTAssertEqualWithAccuracy(data->value().level, 0.0, 0.0001); 68 | } 69 | 70 | - (void)test_更新時のデータの最大値が自動で下がった値よりも低ければ無視される { 71 | buffers[0][0] = 1.0; 72 | 73 | data->updateLevel(buffers, bufferCount, length, sampleRate); 74 | 75 | XCTAssertEqual(data->value().level, 1.0); 76 | 77 | buffers[0][0] = 0.03; 78 | 79 | data->updateLevel(buffers, bufferCount, length, sampleRate); 80 | 81 | XCTAssertEqualWithAccuracy(data->value().level, 0.0316, 0.0001); 82 | } 83 | 84 | - (void)test_更新時のデータの最大値が自動で下がった値よりも高ければ上書きされる { 85 | buffers[0][0] = 1.0; 86 | 87 | data->updateLevel(buffers, bufferCount, length, sampleRate); 88 | 89 | XCTAssertEqual(data->value().level, 1.0); 90 | 91 | buffers[0][0] = 0.04; 92 | 93 | data->updateLevel(buffers, bufferCount, length, sampleRate); 94 | 95 | XCTAssertEqualWithAccuracy(data->value().level, 0.04, 0.0001); 96 | } 97 | 98 | - (void)test_データのどの位置の値でも最大値として扱われる { 99 | [XCTContext runActivityNamed:@"0番目のポインタの0番目の値でレベルを更新する" block:^(id _Nonnull activity) { 100 | buffers[0][0] = 1.0; 101 | 102 | data->updateLevel(buffers, bufferCount, length, sampleRate); 103 | 104 | XCTAssertEqual(data->value().level, 1.0); 105 | 106 | [self resetPtrs]; 107 | data->updateLevel(buffers, bufferCount, length, sampleRate); 108 | data->updateLevel(buffers, bufferCount, length, sampleRate); 109 | data->updateLevel(buffers, bufferCount, length, sampleRate); 110 | 111 | XCTAssertEqualWithAccuracy(data->value().level, 0.0, 0.0001); 112 | }]; 113 | 114 | [XCTContext runActivityNamed:@"0番目のポインタの1番目の値でレベルを更新する" block:^(id _Nonnull activity) { 115 | buffers[0][1] = 1.0; 116 | 117 | data->updateLevel(buffers, bufferCount, length, sampleRate); 118 | 119 | XCTAssertEqual(data->value().level, 1.0); 120 | 121 | [self resetPtrs]; 122 | data->updateLevel(buffers, bufferCount, length, sampleRate); 123 | data->updateLevel(buffers, bufferCount, length, sampleRate); 124 | data->updateLevel(buffers, bufferCount, length, sampleRate); 125 | 126 | XCTAssertEqualWithAccuracy(data->value().level, 0.0, 0.0001); 127 | }]; 128 | 129 | [XCTContext runActivityNamed:@"1番目のポインタの0番目の値でレベルを更新する" block:^(id _Nonnull activity) { 130 | buffers[1][0] = 1.0; 131 | 132 | data->updateLevel(buffers, bufferCount, length, sampleRate); 133 | 134 | XCTAssertEqual(data->value().level, 1.0); 135 | 136 | [self resetPtrs]; 137 | data->updateLevel(buffers, bufferCount, length, sampleRate); 138 | data->updateLevel(buffers, bufferCount, length, sampleRate); 139 | data->updateLevel(buffers, bufferCount, length, sampleRate); 140 | 141 | XCTAssertEqualWithAccuracy(data->value().level, 0.0, 0.0001); 142 | }]; 143 | 144 | [XCTContext runActivityNamed:@"1番目のポインタの1番目の値でレベルを更新する" block:^(id _Nonnull activity) { 145 | buffers[1][1] = 1.0; 146 | 147 | data->updateLevel(buffers, bufferCount, length, sampleRate); 148 | 149 | XCTAssertEqual(data->value().level, 1.0); 150 | 151 | [self resetPtrs]; 152 | data->updateLevel(buffers, bufferCount, length, sampleRate); 153 | data->updateLevel(buffers, bufferCount, length, sampleRate); 154 | data->updateLevel(buffers, bufferCount, length, sampleRate); 155 | 156 | XCTAssertEqualWithAccuracy(data->value().level, 0.0, 0.0001); 157 | }]; 158 | } 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/InputMeterUtilsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputMeterUtilsTests.swift 3 | // AudioUnitRenderingExampleTests 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/02. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class InputMeterUtilsTests: XCTestCase { 12 | func testLinearFromDecibel() throws { 13 | XCTAssertEqual(linearFromDecibel(Float("-inf")!), 0.0) 14 | XCTAssertEqual(linearFromDecibel(0.0), 1.0) 15 | XCTAssertEqual(linearFromDecibel(-20.0), 0.1) 16 | } 17 | 18 | func testDecibelFromLinear() throws { 19 | XCTAssertEqual(decibelFromLinear(0.0), Float("-inf")) 20 | XCTAssertEqual(decibelFromLinear(1.0), 0.0) 21 | XCTAssertEqual(decibelFromLinear(0.1), -20.0) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/RingBufferTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // RingBufferTests.m 3 | // AudioUnitRenderingExampleTests 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import 10 | #include "RingBuffer.hpp" 11 | 12 | @interface RingBufferTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation RingBufferTests 17 | 18 | - (void)test_element { 19 | RingBufferElement element{2}; 20 | 21 | XCTAssertEqual(element.frameCount(), 2); 22 | XCTAssertEqual(element.readableData()[0], 0.0f); 23 | XCTAssertEqual(element.readableData()[1], 0.0f); 24 | XCTAssertEqual(element.writableData()[0], 0.0f); 25 | XCTAssertEqual(element.writableData()[1], 0.0f); 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/SineDataTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // SineKernelTests.mm 3 | // AudioUnitRenderingExampleTests 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/10. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DirectSineKernel.hpp" 11 | #import 12 | 13 | static int constexpr bufferCount = 2; 14 | static int constexpr bufferLength = 4; 15 | 16 | @interface SineKernelTests : XCTestCase 17 | 18 | @end 19 | 20 | @implementation SineKernelTests { 21 | float *buffers[bufferCount]; 22 | float rawBuffers[bufferCount][bufferLength]; 23 | } 24 | 25 | - (void)setUp { 26 | for (int i = 0; i < bufferCount; ++i) { 27 | buffers[i] = rawBuffers[i]; 28 | } 29 | 30 | [self resetPtrs]; 31 | } 32 | 33 | - (void)resetPtrs { 34 | for (int i = 0; i < bufferCount; ++i) { 35 | for (int j = 0; j < bufferLength; ++j) { 36 | buffers[i][j] = 0.0f; 37 | } 38 | } 39 | } 40 | 41 | - (void)test_サイン波1Hz_サンプリング周波数4Hz { 42 | auto const data = std::make_shared(4); 43 | 44 | data->setFrequency(1.0); 45 | 46 | data->render(buffers, bufferCount, bufferLength); 47 | 48 | for (int bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { 49 | XCTAssertEqual(buffers[bufferIndex][0], 0.0f); 50 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][1], 0.1f, 0.0001f); 51 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][2], 0.0f, 0.0001f); 52 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][3], -0.1f, 0.0001f); 53 | } 54 | 55 | [self resetPtrs]; 56 | 57 | data->render(buffers, bufferCount, bufferLength); 58 | 59 | for (int bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { 60 | XCTAssertEqual(buffers[bufferIndex][0], 0.0f); 61 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][1], 0.1f, 0.0001f); 62 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][2], 0.0f, 0.0001f); 63 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][3], -0.1f, 0.0001f); 64 | } 65 | } 66 | 67 | - (void)test_サイン波1Hz_サンプリング周波数8Hz { 68 | auto const data = std::make_shared(8); 69 | 70 | data->setFrequency(1.0); 71 | 72 | data->render(buffers, bufferCount, bufferLength); 73 | 74 | for (int bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { 75 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][0], 0.0f, 0.0001f); 76 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][1], 0.0707f, 0.0001f); 77 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][2], 0.1f, 0.0001f); 78 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][3], 0.0707f, 0.0001f); 79 | } 80 | 81 | [self resetPtrs]; 82 | 83 | data->render(buffers, bufferCount, bufferLength); 84 | 85 | for (int bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { 86 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][0], 0.0f, 0.0001f); 87 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][1], -0.0707f, 0.0001f); 88 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][2], -0.1f, 0.0001f); 89 | XCTAssertEqualWithAccuracy(buffers[bufferIndex][3], -0.0707f, 0.0001f); 90 | } 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /AudioUnitRenderingExampleTests/SineUtilsTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // SineUtilsTests.m 3 | // AudioUnitRenderingExampleTests 4 | // 5 | // Created by Yuki Yasoshima on 2020/08/15. 6 | // Copyright © 2020 Yuki Yasoshima. All rights reserved. 7 | // 8 | 9 | #import 10 | #include "SineUtils.hpp" 11 | #include 12 | 13 | @interface SineUtilsTests : XCTestCase 14 | 15 | @end 16 | 17 | @implementation SineUtilsTests 18 | 19 | - (void)test_fillSine { 20 | std::vector buffer(10, 2.0f); 21 | 22 | XCTAssertEqual(buffer.size(), 10); 23 | 24 | auto const outTheta = SineUtils::fillSine(&buffer.data()[1], 8, 1.0, 8, 0.0); 25 | 26 | XCTAssertEqualWithAccuracy(outTheta, 0.0f, 0.000f); 27 | 28 | XCTAssertEqual(buffer[0], 2.0f); 29 | XCTAssertEqualWithAccuracy(buffer[1], 0.0f, 0.0001f); 30 | XCTAssertEqualWithAccuracy(buffer[2], 0.0707f, 0.0001f); 31 | XCTAssertEqualWithAccuracy(buffer[3], 0.1f, 0.0001f); 32 | XCTAssertEqualWithAccuracy(buffer[4], 0.0707f, 0.0001f); 33 | XCTAssertEqualWithAccuracy(buffer[5], 0.0f, 0.0001f); 34 | XCTAssertEqualWithAccuracy(buffer[6], -0.0707f, 0.0001f); 35 | XCTAssertEqualWithAccuracy(buffer[7], -0.1f, 0.0001f); 36 | XCTAssertEqualWithAccuracy(buffer[8], -0.0707f, 0.0001f); 37 | XCTAssertEqual(buffer[9], 2.0f); 38 | } 39 | 40 | - (void)test_fillSine_thetaをずらす { 41 | std::vector buffer(10, 2.0f); 42 | 43 | XCTAssertEqual(buffer.size(), 10); 44 | 45 | auto const outTheta = SineUtils::fillSine(&buffer.data()[1], 8, 1.0, 8, M_PI); 46 | 47 | XCTAssertEqualWithAccuracy(outTheta, M_PI, 0.000f); 48 | 49 | XCTAssertEqual(buffer[0], 2.0f); 50 | XCTAssertEqualWithAccuracy(buffer[1], 0.0f, 0.0001f); 51 | XCTAssertEqualWithAccuracy(buffer[2], -0.0707f, 0.0001f); 52 | XCTAssertEqualWithAccuracy(buffer[3], -0.1f, 0.0001f); 53 | XCTAssertEqualWithAccuracy(buffer[4], -0.0707f, 0.0001f); 54 | XCTAssertEqualWithAccuracy(buffer[5], 0.0f, 0.0001f); 55 | XCTAssertEqualWithAccuracy(buffer[6], 0.0707f, 0.0001f); 56 | XCTAssertEqualWithAccuracy(buffer[7], 0.1f, 0.0001f); 57 | XCTAssertEqualWithAccuracy(buffer[8], 0.0707f, 0.0001f); 58 | XCTAssertEqual(buffer[9], 2.0f); 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yuki Yasoshima 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 | # AudioUnitRenderingExample 2 | iOSDC JAPAN 2020 9/20 Track A 14:00〜 3 | 「AVAudioEngineでリアルタイムレンダリング」のサンプルコードです 4 | --------------------------------------------------------------------------------