├── .gitignore ├── FunctionalDSP.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── FunctionalDSP ├── AudioEngine.swift ├── AudioFileIO.swift ├── Blocks.swift ├── FunctionalDSP.h ├── FunctionalDSP.swift ├── Info.plist └── Utilities.swift ├── FunctionalDSPTests ├── FunctionalDSPTests.swift ├── Info.plist └── PlaybackPlayground.playground │ ├── Contents.swift │ ├── Sources │ └── SupportCode.swift │ ├── contents.xcplayground │ └── timeline.xctimeline ├── LICENSE ├── README.md ├── TestPlayback ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist └── ViewController.swift └── TestPlaybackTests ├── Info.plist └── TestPlaybackTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | *.xccheckout 3 | -------------------------------------------------------------------------------- /FunctionalDSP.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 83D1DCE71AACEF84007915C2 /* FunctionalDSP.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D1DCE61AACEF84007915C2 /* FunctionalDSP.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 83D1DCED1AACEF84007915C2 /* FunctionalDSP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D1DCE11AACEF84007915C2 /* FunctionalDSP.framework */; }; 12 | 83D1DCF41AACEF84007915C2 /* FunctionalDSPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D1DCF31AACEF84007915C2 /* FunctionalDSPTests.swift */; }; 13 | 83D1DCFE1AACEFA0007915C2 /* FunctionalDSP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D1DCFD1AACEFA0007915C2 /* FunctionalDSP.swift */; }; 14 | 83D1DD001AACF1AD007915C2 /* AudioFileIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D1DCFF1AACF1AD007915C2 /* AudioFileIO.swift */; }; 15 | 83D1DD021AAD1E54007915C2 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D1DD011AAD1E54007915C2 /* CoreAudio.framework */; }; 16 | 83D1DD051AAD1F30007915C2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D1DD041AAD1F30007915C2 /* AudioToolbox.framework */; }; 17 | 83D1DD061AAD1F3E007915C2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D1DD041AAD1F30007915C2 /* AudioToolbox.framework */; }; 18 | 83D1DD071AAD1FA4007915C2 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D1DD011AAD1E54007915C2 /* CoreAudio.framework */; }; 19 | 83D1DD0E1AAD213A007915C2 /* libswiftCoreAudio.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D1DD0D1AAD213A007915C2 /* libswiftCoreAudio.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 20 | 83E8D5D81ADD455100B3DC9F /* Blocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E8D5D71ADD455100B3DC9F /* Blocks.swift */; }; 21 | 83E8D5DC1ADDB4B100B3DC9F /* AudioEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E8D5DB1ADDB4B100B3DC9F /* AudioEngine.swift */; }; 22 | 83E8D5E71ADDC82A00B3DC9F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E8D5E61ADDC82A00B3DC9F /* AppDelegate.swift */; }; 23 | 83E8D5E91ADDC82A00B3DC9F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E8D5E81ADDC82A00B3DC9F /* ViewController.swift */; }; 24 | 83E8D5EB1ADDC82A00B3DC9F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83E8D5EA1ADDC82A00B3DC9F /* Images.xcassets */; }; 25 | 83E8D5EE1ADDC82A00B3DC9F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83E8D5EC1ADDC82A00B3DC9F /* Main.storyboard */; }; 26 | 83E8D5FA1ADDC82B00B3DC9F /* TestPlaybackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E8D5F91ADDC82B00B3DC9F /* TestPlaybackTests.swift */; }; 27 | 83F476FF1AADD14500001FDC /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F476FE1AADD14500001FDC /* Utilities.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 83D1DCEE1AACEF84007915C2 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 83D1DCD81AACEF83007915C2 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 83D1DCE01AACEF84007915C2; 36 | remoteInfo = FunctionalDSP; 37 | }; 38 | 83E8D5F41ADDC82A00B3DC9F /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 83D1DCD81AACEF83007915C2 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 83E8D5E11ADDC82A00B3DC9F; 43 | remoteInfo = TestPlayback; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXCopyFilesBuildPhase section */ 48 | 83D1DD0C1AAD212B007915C2 /* CopyFiles */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = ""; 52 | dstSubfolderSpec = 10; 53 | files = ( 54 | 83D1DD0E1AAD213A007915C2 /* libswiftCoreAudio.dylib in CopyFiles */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXCopyFilesBuildPhase section */ 59 | 60 | /* Begin PBXFileReference section */ 61 | 83D1DCE11AACEF84007915C2 /* FunctionalDSP.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FunctionalDSP.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 83D1DCE51AACEF84007915C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | 83D1DCE61AACEF84007915C2 /* FunctionalDSP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FunctionalDSP.h; sourceTree = ""; }; 64 | 83D1DCEC1AACEF84007915C2 /* FunctionalDSPTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FunctionalDSPTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 83D1DCF21AACEF84007915C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | 83D1DCF31AACEF84007915C2 /* FunctionalDSPTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionalDSPTests.swift; sourceTree = ""; }; 67 | 83D1DCFD1AACEFA0007915C2 /* FunctionalDSP.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FunctionalDSP.swift; sourceTree = ""; }; 68 | 83D1DCFF1AACF1AD007915C2 /* AudioFileIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioFileIO.swift; sourceTree = ""; }; 69 | 83D1DD011AAD1E54007915C2 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 70 | 83D1DD041AAD1F30007915C2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 71 | 83D1DD0D1AAD213A007915C2 /* libswiftCoreAudio.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libswiftCoreAudio.dylib; path = Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/libswiftCoreAudio.dylib; sourceTree = DEVELOPER_DIR; }; 72 | 83E8D5D71ADD455100B3DC9F /* Blocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blocks.swift; sourceTree = ""; }; 73 | 83E8D5DB1ADDB4B100B3DC9F /* AudioEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioEngine.swift; sourceTree = ""; }; 74 | 83E8D5DD1ADDC65E00B3DC9F /* PlaybackPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = PlaybackPlayground.playground; path = FunctionalDSPTests/PlaybackPlayground.playground; sourceTree = SOURCE_ROOT; }; 75 | 83E8D5E21ADDC82A00B3DC9F /* TestPlayback.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestPlayback.app; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 83E8D5E51ADDC82A00B3DC9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | 83E8D5E61ADDC82A00B3DC9F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 78 | 83E8D5E81ADDC82A00B3DC9F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 79 | 83E8D5EA1ADDC82A00B3DC9F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 80 | 83E8D5ED1ADDC82A00B3DC9F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 81 | 83E8D5F31ADDC82A00B3DC9F /* TestPlaybackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TestPlaybackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 82 | 83E8D5F81ADDC82B00B3DC9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83 | 83E8D5F91ADDC82B00B3DC9F /* TestPlaybackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestPlaybackTests.swift; sourceTree = ""; }; 84 | 83F476FE1AADD14500001FDC /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | 83D1DCDD1AACEF84007915C2 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | 83D1DD051AAD1F30007915C2 /* AudioToolbox.framework in Frameworks */, 93 | 83D1DD021AAD1E54007915C2 /* CoreAudio.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | 83D1DCE91AACEF84007915C2 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | 83D1DD071AAD1FA4007915C2 /* CoreAudio.framework in Frameworks */, 102 | 83D1DD061AAD1F3E007915C2 /* AudioToolbox.framework in Frameworks */, 103 | 83D1DCED1AACEF84007915C2 /* FunctionalDSP.framework in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 83E8D5DF1ADDC82A00B3DC9F /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | 83E8D5F01ADDC82A00B3DC9F /* Frameworks */ = { 115 | isa = PBXFrameworksBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | 83D1DCD71AACEF83007915C2 = { 125 | isa = PBXGroup; 126 | children = ( 127 | 83D1DD0D1AAD213A007915C2 /* libswiftCoreAudio.dylib */, 128 | 83D1DD041AAD1F30007915C2 /* AudioToolbox.framework */, 129 | 83D1DD011AAD1E54007915C2 /* CoreAudio.framework */, 130 | 83D1DCE31AACEF84007915C2 /* FunctionalDSP */, 131 | 83D1DCF01AACEF84007915C2 /* FunctionalDSPTests */, 132 | 83E8D5E31ADDC82A00B3DC9F /* TestPlayback */, 133 | 83E8D5F61ADDC82B00B3DC9F /* TestPlaybackTests */, 134 | 83D1DCE21AACEF84007915C2 /* Products */, 135 | ); 136 | sourceTree = ""; 137 | }; 138 | 83D1DCE21AACEF84007915C2 /* Products */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 83D1DCE11AACEF84007915C2 /* FunctionalDSP.framework */, 142 | 83D1DCEC1AACEF84007915C2 /* FunctionalDSPTests.xctest */, 143 | 83E8D5E21ADDC82A00B3DC9F /* TestPlayback.app */, 144 | 83E8D5F31ADDC82A00B3DC9F /* TestPlaybackTests.xctest */, 145 | ); 146 | name = Products; 147 | sourceTree = ""; 148 | }; 149 | 83D1DCE31AACEF84007915C2 /* FunctionalDSP */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 83E8D5DD1ADDC65E00B3DC9F /* PlaybackPlayground.playground */, 153 | 83D1DCE61AACEF84007915C2 /* FunctionalDSP.h */, 154 | 83D1DCE41AACEF84007915C2 /* Supporting Files */, 155 | 83D1DCFD1AACEFA0007915C2 /* FunctionalDSP.swift */, 156 | 83E8D5D71ADD455100B3DC9F /* Blocks.swift */, 157 | 83D1DCFF1AACF1AD007915C2 /* AudioFileIO.swift */, 158 | 83E8D5DB1ADDB4B100B3DC9F /* AudioEngine.swift */, 159 | 83F476FE1AADD14500001FDC /* Utilities.swift */, 160 | ); 161 | path = FunctionalDSP; 162 | sourceTree = ""; 163 | }; 164 | 83D1DCE41AACEF84007915C2 /* Supporting Files */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 83D1DCE51AACEF84007915C2 /* Info.plist */, 168 | ); 169 | name = "Supporting Files"; 170 | sourceTree = ""; 171 | }; 172 | 83D1DCF01AACEF84007915C2 /* FunctionalDSPTests */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 83D1DCF31AACEF84007915C2 /* FunctionalDSPTests.swift */, 176 | 83D1DCF11AACEF84007915C2 /* Supporting Files */, 177 | ); 178 | path = FunctionalDSPTests; 179 | sourceTree = ""; 180 | }; 181 | 83D1DCF11AACEF84007915C2 /* Supporting Files */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 83D1DCF21AACEF84007915C2 /* Info.plist */, 185 | ); 186 | name = "Supporting Files"; 187 | sourceTree = ""; 188 | }; 189 | 83E8D5E31ADDC82A00B3DC9F /* TestPlayback */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | 83E8D5E61ADDC82A00B3DC9F /* AppDelegate.swift */, 193 | 83E8D5E81ADDC82A00B3DC9F /* ViewController.swift */, 194 | 83E8D5EA1ADDC82A00B3DC9F /* Images.xcassets */, 195 | 83E8D5EC1ADDC82A00B3DC9F /* Main.storyboard */, 196 | 83E8D5E41ADDC82A00B3DC9F /* Supporting Files */, 197 | ); 198 | path = TestPlayback; 199 | sourceTree = ""; 200 | }; 201 | 83E8D5E41ADDC82A00B3DC9F /* Supporting Files */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 83E8D5E51ADDC82A00B3DC9F /* Info.plist */, 205 | ); 206 | name = "Supporting Files"; 207 | sourceTree = ""; 208 | }; 209 | 83E8D5F61ADDC82B00B3DC9F /* TestPlaybackTests */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | 83E8D5F91ADDC82B00B3DC9F /* TestPlaybackTests.swift */, 213 | 83E8D5F71ADDC82B00B3DC9F /* Supporting Files */, 214 | ); 215 | path = TestPlaybackTests; 216 | sourceTree = ""; 217 | }; 218 | 83E8D5F71ADDC82B00B3DC9F /* Supporting Files */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | 83E8D5F81ADDC82B00B3DC9F /* Info.plist */, 222 | ); 223 | name = "Supporting Files"; 224 | sourceTree = ""; 225 | }; 226 | /* End PBXGroup section */ 227 | 228 | /* Begin PBXHeadersBuildPhase section */ 229 | 83D1DCDE1AACEF84007915C2 /* Headers */ = { 230 | isa = PBXHeadersBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 83D1DCE71AACEF84007915C2 /* FunctionalDSP.h in Headers */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXHeadersBuildPhase section */ 238 | 239 | /* Begin PBXNativeTarget section */ 240 | 83D1DCE01AACEF84007915C2 /* FunctionalDSP */ = { 241 | isa = PBXNativeTarget; 242 | buildConfigurationList = 83D1DCF71AACEF84007915C2 /* Build configuration list for PBXNativeTarget "FunctionalDSP" */; 243 | buildPhases = ( 244 | 83D1DCDC1AACEF84007915C2 /* Sources */, 245 | 83D1DCDD1AACEF84007915C2 /* Frameworks */, 246 | 83D1DCDE1AACEF84007915C2 /* Headers */, 247 | 83D1DCDF1AACEF84007915C2 /* Resources */, 248 | ); 249 | buildRules = ( 250 | ); 251 | dependencies = ( 252 | ); 253 | name = FunctionalDSP; 254 | productName = FunctionalDSP; 255 | productReference = 83D1DCE11AACEF84007915C2 /* FunctionalDSP.framework */; 256 | productType = "com.apple.product-type.framework"; 257 | }; 258 | 83D1DCEB1AACEF84007915C2 /* FunctionalDSPTests */ = { 259 | isa = PBXNativeTarget; 260 | buildConfigurationList = 83D1DCFA1AACEF84007915C2 /* Build configuration list for PBXNativeTarget "FunctionalDSPTests" */; 261 | buildPhases = ( 262 | 83D1DCE81AACEF84007915C2 /* Sources */, 263 | 83D1DCE91AACEF84007915C2 /* Frameworks */, 264 | 83D1DCEA1AACEF84007915C2 /* Resources */, 265 | 83D1DD0C1AAD212B007915C2 /* CopyFiles */, 266 | ); 267 | buildRules = ( 268 | ); 269 | dependencies = ( 270 | 83D1DCEF1AACEF84007915C2 /* PBXTargetDependency */, 271 | ); 272 | name = FunctionalDSPTests; 273 | productName = FunctionalDSPTests; 274 | productReference = 83D1DCEC1AACEF84007915C2 /* FunctionalDSPTests.xctest */; 275 | productType = "com.apple.product-type.bundle.unit-test"; 276 | }; 277 | 83E8D5E11ADDC82A00B3DC9F /* TestPlayback */ = { 278 | isa = PBXNativeTarget; 279 | buildConfigurationList = 83E8D5FB1ADDC82B00B3DC9F /* Build configuration list for PBXNativeTarget "TestPlayback" */; 280 | buildPhases = ( 281 | 83E8D5DE1ADDC82A00B3DC9F /* Sources */, 282 | 83E8D5DF1ADDC82A00B3DC9F /* Frameworks */, 283 | 83E8D5E01ADDC82A00B3DC9F /* Resources */, 284 | ); 285 | buildRules = ( 286 | ); 287 | dependencies = ( 288 | ); 289 | name = TestPlayback; 290 | productName = TestPlayback; 291 | productReference = 83E8D5E21ADDC82A00B3DC9F /* TestPlayback.app */; 292 | productType = "com.apple.product-type.application"; 293 | }; 294 | 83E8D5F21ADDC82A00B3DC9F /* TestPlaybackTests */ = { 295 | isa = PBXNativeTarget; 296 | buildConfigurationList = 83E8D5FE1ADDC82B00B3DC9F /* Build configuration list for PBXNativeTarget "TestPlaybackTests" */; 297 | buildPhases = ( 298 | 83E8D5EF1ADDC82A00B3DC9F /* Sources */, 299 | 83E8D5F01ADDC82A00B3DC9F /* Frameworks */, 300 | 83E8D5F11ADDC82A00B3DC9F /* Resources */, 301 | ); 302 | buildRules = ( 303 | ); 304 | dependencies = ( 305 | 83E8D5F51ADDC82A00B3DC9F /* PBXTargetDependency */, 306 | ); 307 | name = TestPlaybackTests; 308 | productName = TestPlaybackTests; 309 | productReference = 83E8D5F31ADDC82A00B3DC9F /* TestPlaybackTests.xctest */; 310 | productType = "com.apple.product-type.bundle.unit-test"; 311 | }; 312 | /* End PBXNativeTarget section */ 313 | 314 | /* Begin PBXProject section */ 315 | 83D1DCD81AACEF83007915C2 /* Project object */ = { 316 | isa = PBXProject; 317 | attributes = { 318 | LastSwiftUpdateCheck = 0700; 319 | LastUpgradeCheck = 0630; 320 | ORGANIZATIONNAME = "SuperMegaUltraGroovy, Inc."; 321 | TargetAttributes = { 322 | 83D1DCE01AACEF84007915C2 = { 323 | CreatedOnToolsVersion = 6.3; 324 | }; 325 | 83D1DCEB1AACEF84007915C2 = { 326 | CreatedOnToolsVersion = 6.3; 327 | }; 328 | 83E8D5E11ADDC82A00B3DC9F = { 329 | CreatedOnToolsVersion = 6.3; 330 | }; 331 | 83E8D5F21ADDC82A00B3DC9F = { 332 | CreatedOnToolsVersion = 6.3; 333 | TestTargetID = 83E8D5E11ADDC82A00B3DC9F; 334 | }; 335 | }; 336 | }; 337 | buildConfigurationList = 83D1DCDB1AACEF83007915C2 /* Build configuration list for PBXProject "FunctionalDSP" */; 338 | compatibilityVersion = "Xcode 3.2"; 339 | developmentRegion = English; 340 | hasScannedForEncodings = 0; 341 | knownRegions = ( 342 | en, 343 | Base, 344 | ); 345 | mainGroup = 83D1DCD71AACEF83007915C2; 346 | productRefGroup = 83D1DCE21AACEF84007915C2 /* Products */; 347 | projectDirPath = ""; 348 | projectRoot = ""; 349 | targets = ( 350 | 83D1DCE01AACEF84007915C2 /* FunctionalDSP */, 351 | 83D1DCEB1AACEF84007915C2 /* FunctionalDSPTests */, 352 | 83E8D5E11ADDC82A00B3DC9F /* TestPlayback */, 353 | 83E8D5F21ADDC82A00B3DC9F /* TestPlaybackTests */, 354 | ); 355 | }; 356 | /* End PBXProject section */ 357 | 358 | /* Begin PBXResourcesBuildPhase section */ 359 | 83D1DCDF1AACEF84007915C2 /* Resources */ = { 360 | isa = PBXResourcesBuildPhase; 361 | buildActionMask = 2147483647; 362 | files = ( 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | 83D1DCEA1AACEF84007915C2 /* Resources */ = { 367 | isa = PBXResourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | 83E8D5E01ADDC82A00B3DC9F /* Resources */ = { 374 | isa = PBXResourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | 83E8D5EB1ADDC82A00B3DC9F /* Images.xcassets in Resources */, 378 | 83E8D5EE1ADDC82A00B3DC9F /* Main.storyboard in Resources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | 83E8D5F11ADDC82A00B3DC9F /* Resources */ = { 383 | isa = PBXResourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | /* End PBXResourcesBuildPhase section */ 390 | 391 | /* Begin PBXSourcesBuildPhase section */ 392 | 83D1DCDC1AACEF84007915C2 /* Sources */ = { 393 | isa = PBXSourcesBuildPhase; 394 | buildActionMask = 2147483647; 395 | files = ( 396 | 83F476FF1AADD14500001FDC /* Utilities.swift in Sources */, 397 | 83E8D5D81ADD455100B3DC9F /* Blocks.swift in Sources */, 398 | 83E8D5DC1ADDB4B100B3DC9F /* AudioEngine.swift in Sources */, 399 | 83D1DD001AACF1AD007915C2 /* AudioFileIO.swift in Sources */, 400 | 83D1DCFE1AACEFA0007915C2 /* FunctionalDSP.swift in Sources */, 401 | ); 402 | runOnlyForDeploymentPostprocessing = 0; 403 | }; 404 | 83D1DCE81AACEF84007915C2 /* Sources */ = { 405 | isa = PBXSourcesBuildPhase; 406 | buildActionMask = 2147483647; 407 | files = ( 408 | 83D1DCF41AACEF84007915C2 /* FunctionalDSPTests.swift in Sources */, 409 | ); 410 | runOnlyForDeploymentPostprocessing = 0; 411 | }; 412 | 83E8D5DE1ADDC82A00B3DC9F /* Sources */ = { 413 | isa = PBXSourcesBuildPhase; 414 | buildActionMask = 2147483647; 415 | files = ( 416 | 83E8D5E91ADDC82A00B3DC9F /* ViewController.swift in Sources */, 417 | 83E8D5E71ADDC82A00B3DC9F /* AppDelegate.swift in Sources */, 418 | ); 419 | runOnlyForDeploymentPostprocessing = 0; 420 | }; 421 | 83E8D5EF1ADDC82A00B3DC9F /* Sources */ = { 422 | isa = PBXSourcesBuildPhase; 423 | buildActionMask = 2147483647; 424 | files = ( 425 | 83E8D5FA1ADDC82B00B3DC9F /* TestPlaybackTests.swift in Sources */, 426 | ); 427 | runOnlyForDeploymentPostprocessing = 0; 428 | }; 429 | /* End PBXSourcesBuildPhase section */ 430 | 431 | /* Begin PBXTargetDependency section */ 432 | 83D1DCEF1AACEF84007915C2 /* PBXTargetDependency */ = { 433 | isa = PBXTargetDependency; 434 | target = 83D1DCE01AACEF84007915C2 /* FunctionalDSP */; 435 | targetProxy = 83D1DCEE1AACEF84007915C2 /* PBXContainerItemProxy */; 436 | }; 437 | 83E8D5F51ADDC82A00B3DC9F /* PBXTargetDependency */ = { 438 | isa = PBXTargetDependency; 439 | target = 83E8D5E11ADDC82A00B3DC9F /* TestPlayback */; 440 | targetProxy = 83E8D5F41ADDC82A00B3DC9F /* PBXContainerItemProxy */; 441 | }; 442 | /* End PBXTargetDependency section */ 443 | 444 | /* Begin PBXVariantGroup section */ 445 | 83E8D5EC1ADDC82A00B3DC9F /* Main.storyboard */ = { 446 | isa = PBXVariantGroup; 447 | children = ( 448 | 83E8D5ED1ADDC82A00B3DC9F /* Base */, 449 | ); 450 | name = Main.storyboard; 451 | sourceTree = ""; 452 | }; 453 | /* End PBXVariantGroup section */ 454 | 455 | /* Begin XCBuildConfiguration section */ 456 | 83D1DCF51AACEF84007915C2 /* Debug */ = { 457 | isa = XCBuildConfiguration; 458 | buildSettings = { 459 | ALWAYS_SEARCH_USER_PATHS = NO; 460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 461 | CLANG_CXX_LIBRARY = "libc++"; 462 | CLANG_ENABLE_MODULES = YES; 463 | CLANG_ENABLE_OBJC_ARC = YES; 464 | CLANG_WARN_BOOL_CONVERSION = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 467 | CLANG_WARN_EMPTY_BODY = YES; 468 | CLANG_WARN_ENUM_CONVERSION = YES; 469 | CLANG_WARN_INT_CONVERSION = YES; 470 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 471 | CLANG_WARN_UNREACHABLE_CODE = YES; 472 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 473 | COPY_PHASE_STRIP = NO; 474 | CURRENT_PROJECT_VERSION = 1; 475 | DEBUG_INFORMATION_FORMAT = dwarf; 476 | ENABLE_STRICT_OBJC_MSGSEND = YES; 477 | GCC_C_LANGUAGE_STANDARD = gnu99; 478 | GCC_DYNAMIC_NO_PIC = NO; 479 | GCC_NO_COMMON_BLOCKS = YES; 480 | GCC_OPTIMIZATION_LEVEL = 0; 481 | GCC_PREPROCESSOR_DEFINITIONS = ( 482 | "DEBUG=1", 483 | "$(inherited)", 484 | ); 485 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 486 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 487 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 488 | GCC_WARN_UNDECLARED_SELECTOR = YES; 489 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 490 | GCC_WARN_UNUSED_FUNCTION = YES; 491 | GCC_WARN_UNUSED_VARIABLE = YES; 492 | MACOSX_DEPLOYMENT_TARGET = 10.10; 493 | MTL_ENABLE_DEBUG_INFO = YES; 494 | ONLY_ACTIVE_ARCH = YES; 495 | SDKROOT = macosx; 496 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 497 | VERSIONING_SYSTEM = "apple-generic"; 498 | VERSION_INFO_PREFIX = ""; 499 | }; 500 | name = Debug; 501 | }; 502 | 83D1DCF61AACEF84007915C2 /* Release */ = { 503 | isa = XCBuildConfiguration; 504 | buildSettings = { 505 | ALWAYS_SEARCH_USER_PATHS = NO; 506 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 507 | CLANG_CXX_LIBRARY = "libc++"; 508 | CLANG_ENABLE_MODULES = YES; 509 | CLANG_ENABLE_OBJC_ARC = YES; 510 | CLANG_WARN_BOOL_CONVERSION = YES; 511 | CLANG_WARN_CONSTANT_CONVERSION = YES; 512 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 513 | CLANG_WARN_EMPTY_BODY = YES; 514 | CLANG_WARN_ENUM_CONVERSION = YES; 515 | CLANG_WARN_INT_CONVERSION = YES; 516 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 517 | CLANG_WARN_UNREACHABLE_CODE = YES; 518 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 519 | COPY_PHASE_STRIP = NO; 520 | CURRENT_PROJECT_VERSION = 1; 521 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 522 | ENABLE_NS_ASSERTIONS = NO; 523 | ENABLE_STRICT_OBJC_MSGSEND = YES; 524 | GCC_C_LANGUAGE_STANDARD = gnu99; 525 | GCC_NO_COMMON_BLOCKS = YES; 526 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 527 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 528 | GCC_WARN_UNDECLARED_SELECTOR = YES; 529 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 530 | GCC_WARN_UNUSED_FUNCTION = YES; 531 | GCC_WARN_UNUSED_VARIABLE = YES; 532 | MACOSX_DEPLOYMENT_TARGET = 10.10; 533 | MTL_ENABLE_DEBUG_INFO = NO; 534 | SDKROOT = macosx; 535 | VERSIONING_SYSTEM = "apple-generic"; 536 | VERSION_INFO_PREFIX = ""; 537 | }; 538 | name = Release; 539 | }; 540 | 83D1DCF81AACEF84007915C2 /* Debug */ = { 541 | isa = XCBuildConfiguration; 542 | buildSettings = { 543 | CLANG_ENABLE_MODULES = YES; 544 | COMBINE_HIDPI_IMAGES = YES; 545 | DEFINES_MODULE = YES; 546 | DYLIB_COMPATIBILITY_VERSION = 1; 547 | DYLIB_CURRENT_VERSION = 1; 548 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 549 | FRAMEWORK_VERSION = A; 550 | INFOPLIST_FILE = FunctionalDSP/Info.plist; 551 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 552 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SKIP_INSTALL = YES; 555 | SWIFT_INSTALL_OBJC_HEADER = NO; 556 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 557 | }; 558 | name = Debug; 559 | }; 560 | 83D1DCF91AACEF84007915C2 /* Release */ = { 561 | isa = XCBuildConfiguration; 562 | buildSettings = { 563 | CLANG_ENABLE_MODULES = YES; 564 | COMBINE_HIDPI_IMAGES = YES; 565 | DEFINES_MODULE = YES; 566 | DYLIB_COMPATIBILITY_VERSION = 1; 567 | DYLIB_CURRENT_VERSION = 1; 568 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 569 | FRAMEWORK_VERSION = A; 570 | INFOPLIST_FILE = FunctionalDSP/Info.plist; 571 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 572 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 573 | PRODUCT_NAME = "$(TARGET_NAME)"; 574 | SKIP_INSTALL = YES; 575 | SWIFT_INSTALL_OBJC_HEADER = NO; 576 | }; 577 | name = Release; 578 | }; 579 | 83D1DCFB1AACEF84007915C2 /* Debug */ = { 580 | isa = XCBuildConfiguration; 581 | buildSettings = { 582 | COMBINE_HIDPI_IMAGES = YES; 583 | FRAMEWORK_SEARCH_PATHS = ( 584 | "$(DEVELOPER_FRAMEWORKS_DIR)", 585 | "$(inherited)", 586 | ); 587 | GCC_PREPROCESSOR_DEFINITIONS = ( 588 | "DEBUG=1", 589 | "$(inherited)", 590 | ); 591 | INFOPLIST_FILE = FunctionalDSPTests/Info.plist; 592 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 593 | PRODUCT_NAME = "$(TARGET_NAME)"; 594 | }; 595 | name = Debug; 596 | }; 597 | 83D1DCFC1AACEF84007915C2 /* Release */ = { 598 | isa = XCBuildConfiguration; 599 | buildSettings = { 600 | COMBINE_HIDPI_IMAGES = YES; 601 | FRAMEWORK_SEARCH_PATHS = ( 602 | "$(DEVELOPER_FRAMEWORKS_DIR)", 603 | "$(inherited)", 604 | ); 605 | INFOPLIST_FILE = FunctionalDSPTests/Info.plist; 606 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 607 | PRODUCT_NAME = "$(TARGET_NAME)"; 608 | }; 609 | name = Release; 610 | }; 611 | 83E8D5FC1ADDC82B00B3DC9F /* Debug */ = { 612 | isa = XCBuildConfiguration; 613 | buildSettings = { 614 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 615 | CODE_SIGN_IDENTITY = "-"; 616 | COMBINE_HIDPI_IMAGES = YES; 617 | GCC_PREPROCESSOR_DEFINITIONS = ( 618 | "DEBUG=1", 619 | "$(inherited)", 620 | ); 621 | INFOPLIST_FILE = TestPlayback/Info.plist; 622 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 623 | PRODUCT_NAME = "$(TARGET_NAME)"; 624 | }; 625 | name = Debug; 626 | }; 627 | 83E8D5FD1ADDC82B00B3DC9F /* Release */ = { 628 | isa = XCBuildConfiguration; 629 | buildSettings = { 630 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 631 | CODE_SIGN_IDENTITY = "-"; 632 | COMBINE_HIDPI_IMAGES = YES; 633 | INFOPLIST_FILE = TestPlayback/Info.plist; 634 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 635 | PRODUCT_NAME = "$(TARGET_NAME)"; 636 | }; 637 | name = Release; 638 | }; 639 | 83E8D5FF1ADDC82B00B3DC9F /* Debug */ = { 640 | isa = XCBuildConfiguration; 641 | buildSettings = { 642 | BUNDLE_LOADER = "$(TEST_HOST)"; 643 | COMBINE_HIDPI_IMAGES = YES; 644 | FRAMEWORK_SEARCH_PATHS = ( 645 | "$(DEVELOPER_FRAMEWORKS_DIR)", 646 | "$(inherited)", 647 | ); 648 | GCC_PREPROCESSOR_DEFINITIONS = ( 649 | "DEBUG=1", 650 | "$(inherited)", 651 | ); 652 | INFOPLIST_FILE = TestPlaybackTests/Info.plist; 653 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 654 | PRODUCT_NAME = "$(TARGET_NAME)"; 655 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestPlayback.app/Contents/MacOS/TestPlayback"; 656 | }; 657 | name = Debug; 658 | }; 659 | 83E8D6001ADDC82B00B3DC9F /* Release */ = { 660 | isa = XCBuildConfiguration; 661 | buildSettings = { 662 | BUNDLE_LOADER = "$(TEST_HOST)"; 663 | COMBINE_HIDPI_IMAGES = YES; 664 | FRAMEWORK_SEARCH_PATHS = ( 665 | "$(DEVELOPER_FRAMEWORKS_DIR)", 666 | "$(inherited)", 667 | ); 668 | INFOPLIST_FILE = TestPlaybackTests/Info.plist; 669 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 670 | PRODUCT_NAME = "$(TARGET_NAME)"; 671 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestPlayback.app/Contents/MacOS/TestPlayback"; 672 | }; 673 | name = Release; 674 | }; 675 | /* End XCBuildConfiguration section */ 676 | 677 | /* Begin XCConfigurationList section */ 678 | 83D1DCDB1AACEF83007915C2 /* Build configuration list for PBXProject "FunctionalDSP" */ = { 679 | isa = XCConfigurationList; 680 | buildConfigurations = ( 681 | 83D1DCF51AACEF84007915C2 /* Debug */, 682 | 83D1DCF61AACEF84007915C2 /* Release */, 683 | ); 684 | defaultConfigurationIsVisible = 0; 685 | defaultConfigurationName = Release; 686 | }; 687 | 83D1DCF71AACEF84007915C2 /* Build configuration list for PBXNativeTarget "FunctionalDSP" */ = { 688 | isa = XCConfigurationList; 689 | buildConfigurations = ( 690 | 83D1DCF81AACEF84007915C2 /* Debug */, 691 | 83D1DCF91AACEF84007915C2 /* Release */, 692 | ); 693 | defaultConfigurationIsVisible = 0; 694 | defaultConfigurationName = Release; 695 | }; 696 | 83D1DCFA1AACEF84007915C2 /* Build configuration list for PBXNativeTarget "FunctionalDSPTests" */ = { 697 | isa = XCConfigurationList; 698 | buildConfigurations = ( 699 | 83D1DCFB1AACEF84007915C2 /* Debug */, 700 | 83D1DCFC1AACEF84007915C2 /* Release */, 701 | ); 702 | defaultConfigurationIsVisible = 0; 703 | defaultConfigurationName = Release; 704 | }; 705 | 83E8D5FB1ADDC82B00B3DC9F /* Build configuration list for PBXNativeTarget "TestPlayback" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | 83E8D5FC1ADDC82B00B3DC9F /* Debug */, 709 | 83E8D5FD1ADDC82B00B3DC9F /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | 83E8D5FE1ADDC82B00B3DC9F /* Build configuration list for PBXNativeTarget "TestPlaybackTests" */ = { 715 | isa = XCConfigurationList; 716 | buildConfigurations = ( 717 | 83E8D5FF1ADDC82B00B3DC9F /* Debug */, 718 | 83E8D6001ADDC82B00B3DC9F /* Release */, 719 | ); 720 | defaultConfigurationIsVisible = 0; 721 | defaultConfigurationName = Release; 722 | }; 723 | /* End XCConfigurationList section */ 724 | }; 725 | rootObject = 83D1DCD81AACEF83007915C2 /* Project object */; 726 | } 727 | -------------------------------------------------------------------------------- /FunctionalDSP.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FunctionalDSP/AudioEngine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AudioEngine.swift 3 | // FunctionalDSP 4 | // 5 | // Created by Christopher Liscio on 4/14/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AVFoundation 11 | 12 | public protocol BufferQueueType { 13 | typealias BufferType 14 | var processor: BufferType -> Bool { get } 15 | 16 | func acquireBuffer() -> BufferType? 17 | func releaseBuffer(_: BufferType) 18 | } 19 | 20 | public final class AVAudioPCMBufferQueue: BufferQueueType { 21 | public typealias BufferType = AVAudioPCMBuffer 22 | private let buffers: [AVAudioPCMBuffer] 23 | private var availableBuffers: [AVAudioPCMBuffer] 24 | private(set) public var processor: AVAudioPCMBuffer -> Bool 25 | private var semaphore: dispatch_semaphore_t 26 | 27 | public init(audioFormat: AVAudioFormat, bufferCount: Int, bufferLength: Int, processor bufferProcessor: AVAudioPCMBuffer -> Bool) { 28 | var allBuffers = [AVAudioPCMBuffer]() 29 | for i in 0.. AVAudioPCMBuffer? { 42 | dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) 43 | 44 | var available: AVAudioPCMBuffer? 45 | dispatch_sync(rq) { 46 | if self.availableBuffers.count > 0 { 47 | available = self.availableBuffers.removeAtIndex(0) 48 | } 49 | } 50 | return available 51 | } 52 | 53 | public func releaseBuffer(buffer: AVAudioPCMBuffer) { 54 | dispatch_async(rq) { 55 | if self.processor(buffer) { 56 | self.availableBuffers.append(buffer) 57 | } 58 | dispatch_semaphore_signal(self.semaphore) 59 | } 60 | } 61 | 62 | public func prime() { 63 | for buffer in buffers { 64 | releaseBuffer(buffer) 65 | } 66 | } 67 | } 68 | 69 | let kActiveBufferCount = 2 70 | let kSamplesPerBuffer = 4096 71 | 72 | public func fillFloats(floats: UnsafeMutablePointer, withSignal signal: Signal, ofLength length: Int, startingAtSample startSample: Int) { 73 | for i in 0.. OSStatus? { 14 | if ( status != noErr ) { 15 | return status 16 | } 17 | return nil 18 | } 19 | 20 | extension SampleType { 21 | static var audioByteSize: UInt32 { 22 | return UInt32(sizeof(self)) 23 | } 24 | } 25 | 26 | public protocol AudioConverter { 27 | var audioConverter: AudioConverterRef { get } 28 | 29 | var inputStreamDescription: AudioStreamBasicDescription { get set } 30 | var outputStreamDescription: AudioStreamBasicDescription { get set } 31 | } 32 | 33 | public class LPCMAudioConverter: AudioConverter { 34 | private(set) public var audioConverter = AudioConverterRef() 35 | 36 | public var inputStreamDescription: AudioStreamBasicDescription 37 | public var outputStreamDescription: AudioStreamBasicDescription 38 | 39 | public init?(inputFormat: AudioStreamBasicDescription, outputFormat: AudioStreamBasicDescription) { 40 | inputStreamDescription = inputFormat 41 | outputStreamDescription = outputFormat 42 | 43 | if let status = audioCallFailed(AudioConverterNew(&inputStreamDescription, &outputStreamDescription, &audioConverter)) { 44 | print( "Failed to create audio converter. \(status)" ) 45 | return nil; 46 | } 47 | } 48 | 49 | deinit { 50 | if let status = audioCallFailed(AudioConverterDispose(audioConverter)) { 51 | print("Failed to dispose audio converter. \(status)") 52 | } 53 | audioConverter = AudioConverterRef() 54 | } 55 | 56 | public func convertSamplesInBuffer(inputBuffer: UnsafePointer, withLength inputLength: Int, toBuffer outputBuffer: UnsafeMutablePointer, inout withLength outputLength: Int) { 57 | var uOutputLength = UInt32(outputLength) 58 | if let status = audioCallFailed(AudioConverterConvertBuffer(audioConverter, UInt32(inputLength), inputBuffer, &uOutputLength, outputBuffer)) { 59 | print( "Failed to convert audio data for writing. \(status)" ) 60 | } 61 | outputLength = Int(uOutputLength) 62 | } 63 | } 64 | 65 | public class AudioFile { 66 | var audioFileID = AudioFileID() 67 | var audioConverter: LPCMAudioConverter! 68 | var fileType: AudioFileTypeID = 0 69 | 70 | public let sampleRate: Int 71 | public let channelCount: Int 72 | public let bitDepth: Int 73 | 74 | var fileOpened: Bool = false 75 | 76 | var nativeStreamDescription: AudioStreamBasicDescription { 77 | return AudioStreamBasicDescription( 78 | mSampleRate: Float64(sampleRate), 79 | mFormatID: UInt32(kAudioFormatLinearPCM), 80 | mFormatFlags: UInt32(kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked), 81 | mBytesPerPacket: SampleType.audioByteSize, 82 | mFramesPerPacket: UInt32(1), 83 | mBytesPerFrame: UInt32(channelCount) * SampleType.audioByteSize, 84 | mChannelsPerFrame: UInt32(channelCount), 85 | mBitsPerChannel: UInt32( 8 * SampleType.audioByteSize ), 86 | mReserved: UInt32(0)) 87 | } 88 | 89 | var fileStreamDescription: AudioStreamBasicDescription { 90 | let endianFlag: Int = (Int(fileType) == Int(kAudioFileAIFFType) ? Int(kAudioFormatFlagIsBigEndian) : 0) 91 | let bytesPerSample: Int = bitDepth / 8 92 | return AudioStreamBasicDescription( 93 | mSampleRate: Float64(sampleRate), 94 | mFormatID: UInt32(kAudioFormatLinearPCM), 95 | mFormatFlags: UInt32(Int(kAudioFormatFlagIsSignedInteger) | Int(kAudioFormatFlagIsPacked) | endianFlag), 96 | mBytesPerPacket: UInt32(channelCount * bytesPerSample), 97 | mFramesPerPacket: UInt32(1), 98 | mBytesPerFrame: UInt32(channelCount * bytesPerSample), 99 | mChannelsPerFrame: UInt32(channelCount), 100 | mBitsPerChannel: UInt32(bytesPerSample * 8), 101 | mReserved: UInt32(0)) 102 | } 103 | 104 | public init?(forWritingToURL url: NSURL, withBitDepth bitDepth: Int, sampleRate: Int, channelCount: Int = 1) { 105 | self.sampleRate = sampleRate 106 | self.channelCount = channelCount 107 | self.bitDepth = bitDepth 108 | 109 | assert(channelCount == 1, "Sorry, cannot (yet) support more than 1 channel output") 110 | 111 | if let fileType = inferTypeFromURL(url) { 112 | self.fileType = fileType 113 | 114 | var destinationFormat = fileStreamDescription 115 | if let status = audioCallFailed(AudioFileCreateWithURL(url as CFURL, self.fileType, &destinationFormat, AudioFileFlags.EraseFile, &audioFileID)) { 116 | print( "Failed to open audio file for writing with status \(status): \(url)" ) 117 | return nil; 118 | } 119 | 120 | self.audioConverter = LPCMAudioConverter(inputFormat: nativeStreamDescription, outputFormat: fileStreamDescription) 121 | if self.audioConverter == nil { 122 | print( "Failed to create audio converter when opening file: \(url)" ) 123 | return nil 124 | } 125 | 126 | fileOpened = true 127 | } else { 128 | print( "Failed to infer audio file type for file: \(url)" ) 129 | return nil 130 | } 131 | } 132 | 133 | func destroyAudioObjects() { 134 | if let status = audioCallFailed(AudioFileClose(audioFileID)) { 135 | print("Failed to close audio file. \(status)") 136 | } 137 | audioFileID = AudioFileID() 138 | fileOpened = false 139 | } 140 | 141 | deinit { 142 | if fileOpened { 143 | destroyAudioObjects() 144 | } 145 | 146 | if writeBuffer != nil { 147 | destroyAudioBuffer() 148 | } 149 | } 150 | 151 | public func close() { 152 | destroyAudioObjects() 153 | } 154 | 155 | var writeBufferSize = 0 156 | var writeBuffer: UnsafeMutablePointer = nil 157 | 158 | func destroyAudioBuffer() { 159 | writeBuffer.dealloc(writeBufferSize) 160 | writeBufferSize = 0 161 | writeBuffer = nil 162 | } 163 | 164 | func allocateAudioBufferWithSize(size: Int) { 165 | writeBufferSize = size 166 | writeBuffer = UnsafeMutablePointer.alloc(writeBufferSize) 167 | } 168 | 169 | var fileWritePosition: Int64 = 0 170 | 171 | public func writeSamples(samples: [SampleType]) -> Bool { 172 | assert(fileOpened) 173 | 174 | let outputByteSize = samples.count * Int(fileStreamDescription.mBytesPerFrame) 175 | 176 | if writeBuffer != nil && writeBufferSize < outputByteSize { 177 | destroyAudioBuffer() 178 | } 179 | if writeBuffer == nil { 180 | allocateAudioBufferWithSize(outputByteSize) 181 | } 182 | 183 | var convertedSize = outputByteSize 184 | audioConverter.convertSamplesInBuffer(samples, withLength: samples.count * sizeof(SampleType), toBuffer: writeBuffer, withLength: &convertedSize) 185 | 186 | var uSize = UInt32(convertedSize) 187 | if let status = audioCallFailed(AudioFileWriteBytes(audioFileID, false, fileWritePosition, &uSize, writeBuffer)) { 188 | print( "Failed to write audio data to file. \(status)" ) 189 | return false 190 | } 191 | 192 | fileWritePosition = fileWritePosition + Int64(convertedSize) 193 | 194 | return true 195 | } 196 | 197 | func inferTypeFromURL(url: NSURL) -> AudioFileTypeID? { 198 | if let fileExtension = url.pathExtension?.lowercaseString { 199 | switch fileExtension.lowercaseString { 200 | case "aif", "aiff": 201 | return AudioFileTypeID(kAudioFileAIFFType) 202 | case "wav", "wave": 203 | return AudioFileTypeID(kAudioFileWAVEType) 204 | default: 205 | return nil 206 | } 207 | } 208 | return nil 209 | } 210 | } -------------------------------------------------------------------------------- /FunctionalDSP/Blocks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Blocks.swift 3 | // FunctionalDSP 4 | // 5 | // Created by Christopher Liscio on 4/14/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // Inspired by Faust: 12 | // http://faust.grame.fr/index.php/documentation/references/12-documentation/reference/48-faust-syntax-reference-art 13 | 14 | /// A block has zero or more inputs, and produces zero or more outputs 15 | public protocol BlockType { 16 | typealias SignalType 17 | var inputCount: Int { get } 18 | var outputCount: Int { get } 19 | var process: [SignalType] -> [SignalType] { get } 20 | 21 | init(inputCount: Int, outputCount: Int, process: [SignalType] -> [SignalType]) 22 | } 23 | 24 | public struct Block: BlockType { 25 | public typealias SignalType = Signal 26 | 27 | public let inputCount: Int 28 | public let outputCount: Int 29 | public let process: [Signal] -> [Signal] 30 | 31 | public init(inputCount: Int, outputCount: Int, process: [Signal] -> [Signal]) { 32 | self.inputCount = inputCount 33 | self.outputCount = outputCount 34 | self.process = process 35 | } 36 | } 37 | 38 | public func identity(inputs: Int) -> Block { 39 | return Block(inputCount: inputs, outputCount: inputs, process: { $0 }) 40 | } 41 | 42 | // 43 | // -block----- 44 | // =|=[A]=[B]=|= 45 | // ----------- 46 | // 47 | 48 | /// Runs two blocks serially 49 | public func serial(lhs: B, rhs: B) -> B { 50 | return B(inputCount: lhs.inputCount, outputCount: rhs.outputCount, process: { inputs in 51 | return rhs.process(lhs.process(inputs)) 52 | }) 53 | } 54 | 55 | // 56 | // -block--- 57 | // =|==[A]==|= 58 | // =|==[B]==|= 59 | // --------- 60 | // 61 | 62 | /// Runs two blocks in parallel 63 | public func parallel(lhs: B, rhs: B) -> B { 64 | let totalInputs = lhs.inputCount + rhs.inputCount 65 | let totalOutputs = lhs.outputCount + rhs.outputCount 66 | 67 | return B(inputCount: totalInputs, outputCount: totalOutputs, process: { inputs in 68 | var outputs: [B.SignalType] = [] 69 | 70 | outputs += lhs.process(Array(inputs[0..(inputs[lhs.inputCount..-[B]-|- 80 | // ------------- 81 | // 82 | 83 | /// Merges the outputs of the block on the left to the inputs of the block on the right 84 | public func merge(lhs: B, rhs: B) -> B { 85 | return B(inputCount: lhs.inputCount, outputCount: rhs.outputCount, process: { inputs in 86 | let leftOutputs = lhs.process(inputs) 87 | var rightInputs: [B.SignalType] = [] 88 | 89 | let k = lhs.outputCount / rhs.inputCount 90 | for i in 0..() 92 | for j in 0..(lhs: B, rhs: B) -> B { 112 | return B(inputCount: lhs.inputCount, outputCount: rhs.outputCount, process: { inputs in 113 | let leftOutputs = lhs.process(inputs) 114 | var rightInputs: [B.SignalType] = [] 115 | 116 | // Replicate the channels from the lhs to each of the inputs 117 | let k = lhs.outputCount 118 | for i in 0..- { associativity left } 132 | 133 | // Parallel 134 | public func |-(lhs: B, rhs: B) -> B { 135 | return parallel(lhs, rhs: rhs) 136 | } 137 | 138 | // Serial 139 | public func --(lhs: B, rhs: B) -> B { 140 | return serial(lhs, rhs: rhs) 141 | } 142 | 143 | // Split 144 | public func -<(lhs: B, rhs: B) -> B { 145 | return split(lhs, rhs: rhs) 146 | } 147 | 148 | // Merge 149 | public func >-(lhs: B, rhs: B) -> B { 150 | return merge(lhs, rhs: rhs) 151 | } -------------------------------------------------------------------------------- /FunctionalDSP/FunctionalDSP.h: -------------------------------------------------------------------------------- 1 | // 2 | // FunctionalDSP.h 3 | // FunctionalDSP 4 | // 5 | // Created by Christopher Liscio on 3/8/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | //! Project version number for FunctionalDSP. 14 | FOUNDATION_EXPORT double FunctionalDSPVersionNumber; 15 | 16 | //! Project version string for FunctionalDSP. 17 | FOUNDATION_EXPORT const unsigned char FunctionalDSPVersionString[]; 18 | 19 | // In this header, you should import all the public headers of your framework using statements like #import 20 | 21 | 22 | -------------------------------------------------------------------------------- /FunctionalDSP/FunctionalDSP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FunctionalDSP.swift 3 | // FunctionalDSP 4 | // 5 | // Created by Christopher Liscio on 3/8/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Accelerate 11 | 12 | // Just to demonstrate, mixing doubles and floats for parameter and sample types, respectively 13 | public typealias ParameterType = Double 14 | public typealias SampleType = Float 15 | 16 | public typealias Signal = Int -> SampleType 17 | 18 | public func NullSignal(_: Int) -> SampleType { 19 | return 0 20 | } 21 | 22 | // MARK: Basic Operations 23 | 24 | /// Scale a signal by a given amplitude 25 | public func scale(s: Signal, amplitude: ParameterType) -> Signal { 26 | return { i in 27 | return SampleType(s(i) * SampleType(amplitude)) 28 | } 29 | } 30 | 31 | // MARK: Mixing 32 | 33 | /// Mix two signals together 34 | public func mix(s1: Signal, s2: Signal) -> Signal { 35 | return { i in 36 | return s1(i) + s2(i) 37 | } 38 | } 39 | 40 | /// Mix an arbitrary number of signals together 41 | public func mix(signals: [Signal]) -> Signal { 42 | return { i in 43 | return signals.reduce(SampleType(0)) { $0 + $1(i) } 44 | } 45 | } 46 | 47 | // MARK: Generators 48 | 49 | /// Generate a sine wave 50 | public func sineWave(sampleRate: Int, frequency: ParameterType) -> Signal { 51 | let phi = frequency / ParameterType(sampleRate) 52 | return { i in 53 | return SampleType(sin(2.0 * ParameterType(i) * phi * ParameterType(M_PI))) 54 | } 55 | } 56 | 57 | /// Simple white noise generator 58 | public func whiteNoise() -> Signal { 59 | return { _ in 60 | return SampleType(-1.0 + 2.0 * (SampleType(arc4random_uniform(UInt32(Int16.max))) / SampleType(Int16.max))) 61 | } 62 | } 63 | 64 | // MARK: Output 65 | 66 | /// Read count samples from the signal starting at the specified index 67 | public func getOutput(signal: Signal, index: Int, count: Int) -> [SampleType] { 68 | return [Int](index.. Signal { 91 | return filt(x, b: gFilt.b, a: gFilt.a, w: &gFilt.w) 92 | } 93 | 94 | public func filt(x: Signal, var b: [FilterType], var a: [FilterType], inout w: [FilterType]!) -> Signal { 95 | let N = a.count 96 | let M = b.count 97 | let MN = max(N, M) 98 | let lw = MN - 1 99 | 100 | if w == nil { 101 | w = [FilterType](count: lw, repeatedValue: 0) 102 | } 103 | assert(w.count == lw) 104 | 105 | if b.count < MN { 106 | b = b + zeros(MN-b.count) 107 | } 108 | if a.count < MN { 109 | a = a + zeros(MN-a.count) 110 | } 111 | 112 | let norm = a[0] 113 | assert(norm > 0, "First element in A must be nonzero") 114 | if fabs(norm - 1.0) > FilterType.Epsilon { 115 | scale(&b, a: 1.0 / norm) 116 | } 117 | 118 | if N > 1 { 119 | // IIR Filter Case 120 | if fabs(norm - 1.0) > FilterType.Epsilon { 121 | scale(&a, a: 1.0 / norm) 122 | } 123 | 124 | return { i in 125 | let xi = FilterType(x(i)) 126 | let y = w[0] + (b[0] * xi) 127 | if ( lw > 1 ) { 128 | for j in 0..<(lw - 1) { 129 | w[j] = w[j+1] + (b[j+1] * xi) - (a[j+1] * y) 130 | } 131 | w[lw-1] = (b[MN-1] * xi) - (a[MN-1] * y) 132 | } else { 133 | w[0] = (b[MN-1] * xi) - (a[MN-1] * y) 134 | } 135 | return SampleType(y) 136 | } 137 | } else { 138 | // FIR Filter Case 139 | if lw > 0 { 140 | return { i in 141 | let xi = FilterType(x(i)) 142 | let y = w[0] + b[0] * xi 143 | if ( lw > 1 ) { 144 | for j in 0..<(lw - 1) { 145 | w[j] = w[j+1] + (b[j+1] * xi) 146 | } 147 | w[lw-1] = b[MN-1] * xi; 148 | } 149 | else { 150 | w[0] = b[1] * xi 151 | } 152 | return Float(y) 153 | } 154 | } else { 155 | // No delay 156 | return { i in Float(Double(x(i)) * b[0]) } 157 | } 158 | } 159 | } 160 | 161 | 162 | -------------------------------------------------------------------------------- /FunctionalDSP/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /FunctionalDSP/Utilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities.swift 3 | // FunctionalDSP 4 | // 5 | // Created by Christopher Liscio on 2015-03-09. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Accelerate 11 | 12 | func scale(inout x: [Float], var a: Float) { 13 | vDSP_vsmul(x, 1, &a, &x, 1, vDSP_Length(x.count)) 14 | } 15 | 16 | func scale(inout x: [Double], var a: Double) { 17 | vDSP_vsmulD(x, 1, &a, &x, 1, vDSP_Length(x.count)) 18 | } 19 | 20 | func zeros(count: Int) -> [Float] { 21 | return [Float](count: count, repeatedValue: 0) 22 | } 23 | 24 | func zeros(count: Int) -> [Double] { 25 | return [Double](count: count, repeatedValue: 0) 26 | } -------------------------------------------------------------------------------- /FunctionalDSPTests/FunctionalDSPTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FunctionalDSPTests.swift 3 | // FunctionalDSPTests 4 | // 5 | // Created by Christopher Liscio on 3/8/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | import FunctionalDSP 12 | 13 | let dtmfFrequencies = [ 14 | ( 941.0, 1336.0 ), 15 | 16 | ( 697.0, 1209.0 ), 17 | ( 697.0, 1336.0 ), 18 | ( 697.0, 1477.0 ), 19 | 20 | ( 770.0, 1209.0 ), 21 | ( 770.0, 1336.0 ), 22 | ( 770.0, 1477.0 ), 23 | 24 | ( 852.0, 1209.0 ), 25 | ( 852.0, 1336.0 ), 26 | ( 852.0, 1477.0 ), 27 | ] 28 | 29 | func dtmfTone(digit: Int, sampleRate: Int) -> Block { 30 | assert( digit < dtmfFrequencies.count ) 31 | let (f1, f2) = dtmfFrequencies[digit] 32 | 33 | let f1Block = Block(inputCount: 0, outputCount: 1, process: { _ in [sineWave(sampleRate, f1)] }) 34 | let f2Block = Block(inputCount: 0, outputCount: 1, process: { _ in [sineWave(sampleRate, f2)] }) 35 | 36 | return ( f1Block |- f2Block ) >- Block(inputCount: 1, outputCount: 1, process: { return $0 }) 37 | } 38 | 39 | class FunctionalDSPTests: XCTestCase { 40 | func testDTMF() { 41 | let sampleRate = 44100 42 | 43 | let phoneNumber = [8, 6, 7, 5, 3, 0, 9] 44 | let signals = phoneNumber.map { dtmfTone($0, sampleRate) } 45 | 46 | let toneDuration = Int(Float(sampleRate) * 0.2) 47 | 48 | // The below concatenates all the signal samples together in a continuous tone 49 | // let samples = signals.map { getOutput($0, 0, toneDuration) }.reduce([], combine: +) 50 | 51 | let silence = [SampleType](count: toneDuration, repeatedValue: 0) 52 | 53 | if let af = AudioFile(forWritingToURL: NSURL(fileURLWithPath: "/Users/chris/testfile.wav")!, withBitDepth: 16, sampleRate: 44100, channelCount: 1) { 54 | 55 | for signal in signals { 56 | af.writeSamples(getOutput(signal.process([])[0], 0, toneDuration)) 57 | af.writeSamples(silence) 58 | } 59 | 60 | af.close() 61 | XCTAssertTrue(true, "yay") 62 | } else { 63 | XCTAssertTrue(false, "oops") 64 | } 65 | } 66 | 67 | func testWhite() { 68 | let sampleRate = 44100 69 | 70 | let whiteBlock = Block(inputCount: 0, outputCount: 1, process: { _ in [whiteNoise()] }) 71 | let filterBlock = Block(inputCount: 0, outputCount: 1, process: { inputs in inputs.map { pinkFilter($0) } } ) 72 | 73 | let pinkNoise = whiteBlock -- filterBlock 74 | 75 | if let af = AudioFile(forWritingToURL: NSURL(fileURLWithPath: "/Users/chris/testwhite.wav")!, withBitDepth: 16, sampleRate: 44100, channelCount: 1) { 76 | af.writeSamples(getOutput(whiteBlock.process([])[0], 0, 88200)) 77 | af.close() 78 | XCTAssertTrue(true, "yay") 79 | } else { 80 | XCTAssertTrue(false, "oops") 81 | } 82 | 83 | if let af = AudioFile(forWritingToURL: NSURL(fileURLWithPath: "/Users/chris/testpink.wav")!, withBitDepth: 16, sampleRate: 44100, channelCount: 1) { 84 | af.writeSamples(getOutput(pinkNoise.process([])[0], 0, 88200)) 85 | af.close() 86 | XCTAssertTrue(true, "yay") 87 | } else { 88 | XCTAssertTrue(false, "oops") 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /FunctionalDSPTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.supermegaultragroovy.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /FunctionalDSPTests/PlaybackPlayground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import FunctionalDSP 4 | import AVFoundation 5 | 6 | let engine = AVAudioEngine() 7 | let playerNode = AVAudioPlayerNode() 8 | 9 | let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100.0, channels: 2) 10 | 11 | engine.attachNode(playerNode) 12 | engine.connect(playerNode, to: engine.mainMixerNode, format: audioFormat) 13 | 14 | do { 15 | try engine.start() 16 | } catch { 17 | NSLog("Error starting audio engine: \(error)") 18 | } 19 | 20 | 21 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { 22 | playTone(playerNode) 23 | } 24 | 25 | playerNode.pan = 1.0 26 | playerNode.play() 27 | -------------------------------------------------------------------------------- /FunctionalDSPTests/PlaybackPlayground.playground/Sources/SupportCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This file (and all other Swift source files in the Sources directory of this playground) will be precompiled into a framework which is automatically made available to PlaybackPlayground.playground. 3 | // 4 | -------------------------------------------------------------------------------- /FunctionalDSPTests/PlaybackPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /FunctionalDSPTests/PlaybackPlayground.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Christopher Liscio 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Functional DSP using Swift 2 | ========================== 3 | 4 | I put this project together to explore how functional programming could help us do cooler things with audio signal processing. It is based on ideas already explored in projects such as [Faust][faust], or [PureData][pd]. 5 | 6 | [pd]: http://puredata.info 7 | [faust]: http://sourceforge.net/projects/faudiostream/ 8 | -------------------------------------------------------------------------------- /TestPlayback/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TestPlayback 4 | // 5 | // Created by Christopher Liscio on 4/14/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import FunctionalDSP 11 | import AVFoundation 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | let engine = AVAudioEngine() 16 | let playerNode = AVAudioPlayerNode() 17 | 18 | func applicationDidFinishLaunching(aNotification: NSNotification) { 19 | engine.attachNode(playerNode) 20 | engine.connect(playerNode, to: engine.mainMixerNode, format: engine.mainMixerNode.inputFormatForBus(0)) 21 | 22 | do { 23 | try engine.start() 24 | } catch { 25 | NSLog("Error starting audio engine: \(error)") 26 | } 27 | 28 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { 29 | playTone(self.playerNode) 30 | } 31 | 32 | playerNode.play() 33 | } 34 | 35 | func applicationWillTerminate(aNotification: NSNotification) { 36 | // Insert code here to tear down your application 37 | } 38 | 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /TestPlayback/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 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 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 | 112 | 113 | 114 | 115 | 116 | 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 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 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 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | Default 510 | 511 | 512 | 513 | 514 | 515 | 516 | Left to Right 517 | 518 | 519 | 520 | 521 | 522 | 523 | Right to Left 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | Default 535 | 536 | 537 | 538 | 539 | 540 | 541 | Left to Right 542 | 543 | 544 | 545 | 546 | 547 | 548 | Right to Left 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | -------------------------------------------------------------------------------- /TestPlayback/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /TestPlayback/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.supermegaultragroovy.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /TestPlayback/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TestPlayback 4 | // 5 | // Created by Christopher Liscio on 4/14/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | override var representedObject: AnyObject? { 20 | didSet { 21 | // Update the view, if already loaded. 22 | } 23 | } 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /TestPlaybackTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.supermegaultragroovy.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TestPlaybackTests/TestPlaybackTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestPlaybackTests.swift 3 | // TestPlaybackTests 4 | // 5 | // Created by Christopher Liscio on 4/14/15. 6 | // Copyright (c) 2015 SuperMegaUltraGroovy, Inc. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | 12 | class TestPlaybackTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------