From 43d89ef2846fa84275fe19999b1a474acbe39e1a Mon Sep 17 00:00:00 2001 From: Matthijs Hollemans Date: Mon, 6 Mar 2017 15:23:14 +0100 Subject: [PATCH] Add Metal version --- .../VoiceMetal.xcodeproj/project.pbxproj | 326 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + VoiceMetal/VoiceMetal/AppDelegate.swift | 12 + .../AppIcon.appiconset/Contents.json | 68 ++++ .../Base.lproj/LaunchScreen.storyboard | 27 ++ .../VoiceMetal/Base.lproj/Main.storyboard | 26 ++ VoiceMetal/VoiceMetal/Float16.swift | 39 +++ VoiceMetal/VoiceMetal/Info.plist | 45 +++ VoiceMetal/VoiceMetal/MPSImage+Floats.swift | 87 +++++ VoiceMetal/VoiceMetal/ViewController.swift | 118 +++++++ VoiceMetal/VoiceMetal/W.bin | 1 + VoiceMetal/VoiceMetal/b.bin | 1 + 12 files changed, 757 insertions(+) create mode 100644 VoiceMetal/VoiceMetal.xcodeproj/project.pbxproj create mode 100644 VoiceMetal/VoiceMetal.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 VoiceMetal/VoiceMetal/AppDelegate.swift create mode 100644 VoiceMetal/VoiceMetal/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 VoiceMetal/VoiceMetal/Base.lproj/LaunchScreen.storyboard create mode 100644 VoiceMetal/VoiceMetal/Base.lproj/Main.storyboard create mode 100644 VoiceMetal/VoiceMetal/Float16.swift create mode 100644 VoiceMetal/VoiceMetal/Info.plist create mode 100644 VoiceMetal/VoiceMetal/MPSImage+Floats.swift create mode 100644 VoiceMetal/VoiceMetal/ViewController.swift create mode 100644 VoiceMetal/VoiceMetal/W.bin create mode 100644 VoiceMetal/VoiceMetal/b.bin diff --git a/VoiceMetal/VoiceMetal.xcodeproj/project.pbxproj b/VoiceMetal/VoiceMetal.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f5f9d59 --- /dev/null +++ b/VoiceMetal/VoiceMetal.xcodeproj/project.pbxproj @@ -0,0 +1,326 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 7B0EA89D1E6C977A00D77F1C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EA89C1E6C977A00D77F1C /* AppDelegate.swift */; }; + 7B0EA89F1E6C977A00D77F1C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EA89E1E6C977A00D77F1C /* ViewController.swift */; }; + 7B0EA8A21E6C977A00D77F1C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B0EA8A01E6C977A00D77F1C /* Main.storyboard */; }; + 7B0EA8A41E6C977A00D77F1C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7B0EA8A31E6C977A00D77F1C /* Assets.xcassets */; }; + 7B0EA8A71E6C977A00D77F1C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B0EA8A51E6C977A00D77F1C /* LaunchScreen.storyboard */; }; + 7B0EA8B01E6C97C600D77F1C /* b.bin in Resources */ = {isa = PBXBuildFile; fileRef = 7B0EA8AE1E6C97C600D77F1C /* b.bin */; }; + 7B0EA8B11E6C97C600D77F1C /* W.bin in Resources */ = {isa = PBXBuildFile; fileRef = 7B0EA8AF1E6C97C600D77F1C /* W.bin */; }; + 7B0EA8B41E6C9A9C00D77F1C /* Float16.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EA8B21E6C9A9C00D77F1C /* Float16.swift */; }; + 7B0EA8B51E6C9A9C00D77F1C /* MPSImage+Floats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EA8B31E6C9A9C00D77F1C /* MPSImage+Floats.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 7B0EA8991E6C977A00D77F1C /* VoiceMetal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoiceMetal.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7B0EA89C1E6C977A00D77F1C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7B0EA89E1E6C977A00D77F1C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 7B0EA8A11E6C977A00D77F1C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 7B0EA8A31E6C977A00D77F1C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7B0EA8A61E6C977A00D77F1C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 7B0EA8A81E6C977A00D77F1C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7B0EA8AE1E6C97C600D77F1C /* b.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = b.bin; sourceTree = ""; }; + 7B0EA8AF1E6C97C600D77F1C /* W.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = W.bin; sourceTree = ""; }; + 7B0EA8B21E6C9A9C00D77F1C /* Float16.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Float16.swift; sourceTree = ""; }; + 7B0EA8B31E6C9A9C00D77F1C /* MPSImage+Floats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MPSImage+Floats.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7B0EA8961E6C977A00D77F1C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7B0EA8901E6C977A00D77F1C = { + isa = PBXGroup; + children = ( + 7B0EA89B1E6C977A00D77F1C /* VoiceMetal */, + 7B0EA89A1E6C977A00D77F1C /* Products */, + ); + sourceTree = ""; + }; + 7B0EA89A1E6C977A00D77F1C /* Products */ = { + isa = PBXGroup; + children = ( + 7B0EA8991E6C977A00D77F1C /* VoiceMetal.app */, + ); + name = Products; + sourceTree = ""; + }; + 7B0EA89B1E6C977A00D77F1C /* VoiceMetal */ = { + isa = PBXGroup; + children = ( + 7B0EA89C1E6C977A00D77F1C /* AppDelegate.swift */, + 7B0EA89E1E6C977A00D77F1C /* ViewController.swift */, + 7B0EA8B31E6C9A9C00D77F1C /* MPSImage+Floats.swift */, + 7B0EA8B21E6C9A9C00D77F1C /* Float16.swift */, + 7B0EA8AF1E6C97C600D77F1C /* W.bin */, + 7B0EA8AE1E6C97C600D77F1C /* b.bin */, + 7B0EA8A01E6C977A00D77F1C /* Main.storyboard */, + 7B0EA8A31E6C977A00D77F1C /* Assets.xcassets */, + 7B0EA8A51E6C977A00D77F1C /* LaunchScreen.storyboard */, + 7B0EA8A81E6C977A00D77F1C /* Info.plist */, + ); + path = VoiceMetal; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7B0EA8981E6C977A00D77F1C /* VoiceMetal */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7B0EA8AB1E6C977A00D77F1C /* Build configuration list for PBXNativeTarget "VoiceMetal" */; + buildPhases = ( + 7B0EA8951E6C977A00D77F1C /* Sources */, + 7B0EA8961E6C977A00D77F1C /* Frameworks */, + 7B0EA8971E6C977A00D77F1C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoiceMetal; + productName = VoiceMetal; + productReference = 7B0EA8991E6C977A00D77F1C /* VoiceMetal.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7B0EA8911E6C977A00D77F1C /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = Hollance; + TargetAttributes = { + 7B0EA8981E6C977A00D77F1C = { + CreatedOnToolsVersion = 8.2.1; + DevelopmentTeam = MGCEKGP4Y4; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 7B0EA8941E6C977A00D77F1C /* Build configuration list for PBXProject "VoiceMetal" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7B0EA8901E6C977A00D77F1C; + productRefGroup = 7B0EA89A1E6C977A00D77F1C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7B0EA8981E6C977A00D77F1C /* VoiceMetal */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7B0EA8971E6C977A00D77F1C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7B0EA8A71E6C977A00D77F1C /* LaunchScreen.storyboard in Resources */, + 7B0EA8B11E6C97C600D77F1C /* W.bin in Resources */, + 7B0EA8A41E6C977A00D77F1C /* Assets.xcassets in Resources */, + 7B0EA8B01E6C97C600D77F1C /* b.bin in Resources */, + 7B0EA8A21E6C977A00D77F1C /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7B0EA8951E6C977A00D77F1C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7B0EA89F1E6C977A00D77F1C /* ViewController.swift in Sources */, + 7B0EA8B41E6C9A9C00D77F1C /* Float16.swift in Sources */, + 7B0EA89D1E6C977A00D77F1C /* AppDelegate.swift in Sources */, + 7B0EA8B51E6C9A9C00D77F1C /* MPSImage+Floats.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 7B0EA8A01E6C977A00D77F1C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 7B0EA8A11E6C977A00D77F1C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 7B0EA8A51E6C977A00D77F1C /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 7B0EA8A61E6C977A00D77F1C /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 7B0EA8A91E6C977A00D77F1C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7B0EA8AA1E6C977A00D77F1C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7B0EA8AC1E6C977A00D77F1C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = MGCEKGP4Y4; + INFOPLIST_FILE = VoiceMetal/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.hollance.VoiceMetal; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 7B0EA8AD1E6C977A00D77F1C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = MGCEKGP4Y4; + INFOPLIST_FILE = VoiceMetal/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.hollance.VoiceMetal; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7B0EA8941E6C977A00D77F1C /* Build configuration list for PBXProject "VoiceMetal" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7B0EA8A91E6C977A00D77F1C /* Debug */, + 7B0EA8AA1E6C977A00D77F1C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7B0EA8AB1E6C977A00D77F1C /* Build configuration list for PBXNativeTarget "VoiceMetal" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7B0EA8AC1E6C977A00D77F1C /* Debug */, + 7B0EA8AD1E6C977A00D77F1C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7B0EA8911E6C977A00D77F1C /* Project object */; +} diff --git a/VoiceMetal/VoiceMetal.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/VoiceMetal/VoiceMetal.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ff0d297 --- /dev/null +++ b/VoiceMetal/VoiceMetal.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/VoiceMetal/VoiceMetal/AppDelegate.swift b/VoiceMetal/VoiceMetal/AppDelegate.swift new file mode 100644 index 0000000..f581723 --- /dev/null +++ b/VoiceMetal/VoiceMetal/AppDelegate.swift @@ -0,0 +1,12 @@ +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } +} diff --git a/VoiceMetal/VoiceMetal/Assets.xcassets/AppIcon.appiconset/Contents.json b/VoiceMetal/VoiceMetal/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/VoiceMetal/VoiceMetal/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/VoiceMetal/VoiceMetal/Base.lproj/LaunchScreen.storyboard b/VoiceMetal/VoiceMetal/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..fdf3f97 --- /dev/null +++ b/VoiceMetal/VoiceMetal/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoiceMetal/VoiceMetal/Base.lproj/Main.storyboard b/VoiceMetal/VoiceMetal/Base.lproj/Main.storyboard new file mode 100644 index 0000000..273375f --- /dev/null +++ b/VoiceMetal/VoiceMetal/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoiceMetal/VoiceMetal/Float16.swift b/VoiceMetal/VoiceMetal/Float16.swift new file mode 100644 index 0000000..0ccb627 --- /dev/null +++ b/VoiceMetal/VoiceMetal/Float16.swift @@ -0,0 +1,39 @@ +import Foundation +import Accelerate + +/* Utility functions for dealing with 16-bit floating point values in Swift. */ + +/** + Since Swift has no datatype for a 16-bit float we use UInt16s instead, which + take up the same amount of memory. (Note: The simd framework does have "half" + types but only for 2, 3, or 4-element vectors, not scalars.) +*/ +public typealias Float16 = UInt16 + +/** + Uses vImage to convert a buffer of float16 values to regular Swift Floats. +*/ +public func float16to32(_ input: UnsafeMutableRawPointer, count: Int) -> [Float] { + var output = [Float](repeating: 0, count: count) + var bufferFloat16 = vImage_Buffer(data: input, height: 1, width: UInt(count), rowBytes: count * 2) + var bufferFloat32 = vImage_Buffer(data: &output, height: 1, width: UInt(count), rowBytes: count * 4) + + if vImageConvert_Planar16FtoPlanarF(&bufferFloat16, &bufferFloat32, 0) != kvImageNoError { + print("Error converting float16 to float32") + } + return output +} + +/** + Uses vImage to convert an array of Swift floats into a buffer of float16s. +*/ +public func float32to16(_ input: UnsafeMutablePointer, count: Int) -> [Float16] { + var output = [Float16](repeating: 0, count: count) + var bufferFloat32 = vImage_Buffer(data: input, height: 1, width: UInt(count), rowBytes: count * 4) + var bufferFloat16 = vImage_Buffer(data: &output, height: 1, width: UInt(count), rowBytes: count * 2) + + if vImageConvert_PlanarFtoPlanar16F(&bufferFloat32, &bufferFloat16, 0) != kvImageNoError { + print("Error converting float32 to float16") + } + return output +} diff --git a/VoiceMetal/VoiceMetal/Info.plist b/VoiceMetal/VoiceMetal/Info.plist new file mode 100644 index 0000000..d052473 --- /dev/null +++ b/VoiceMetal/VoiceMetal/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/VoiceMetal/VoiceMetal/MPSImage+Floats.swift b/VoiceMetal/VoiceMetal/MPSImage+Floats.swift new file mode 100644 index 0000000..cfd58fd --- /dev/null +++ b/VoiceMetal/VoiceMetal/MPSImage+Floats.swift @@ -0,0 +1,87 @@ +import MetalPerformanceShaders + +extension MPSImage { + /* + We receive the predicted output as an MPSImage. We need to convert this + to an array of Floats that we can use from Swift. + + Because Metal is a graphics API, MPSImage stores the data in MTLTexture + objects. Each pixel from the texture stores 4 channels: R contains the + first channel, G is the second channel, B is the third, A is the fourth. + + In addition, these individual R,G,B,A pixel components can be stored as + float16, in which case we also have to convert the data type. + + ---WARNING--- + + If there are > 4 channels in the MPSImage, then the channels are organized + in the output as follows: + + [ 1,2,3,4,1,2,3,4,...,1,2,3,4,5,6,7,8,5,6,7,8,...,5,6,7,8 ] + + and not as you'd expect: + + [ 1,2,3,4,5,6,7,8,...,1,2,3,4,5,6,7,8,...,1,2,3,4,5,6,7,8 ] + + So first are channels 1 - 4 for the entire image, followed by channels + 5 - 8 for the entire image. That happens because we copy the data out of + the texture by slice, and we can't interleave slices. + + If the number of channels is not a multiple of 4, then the output will + have padding bytes in it: + + [ 1,2,3,4,1,2,3,4,...,1,2,3,4,5,6,-,-,5,6,-,-,...,5,6,-,- ] + + The only case where you get the kind of array you'd actually expect is + when the number of channels is 1, 2, or 4 (i.e. there is only one slice): + + [ 1,1,1,...,1] or [ 1,2,1,2,1,2,...,1,2 ] or [ 1,2,3,4,...,1,2,3,4 ] + */ + public func toFloatArray() -> [Float] { + switch pixelFormat { + case .r16Float, .rg16Float, .rgba16Float: return fromFloat16() + case .r32Float, .rg32Float, .rgba32Float: return fromFloat32() + default: fatalError("Pixel format \(pixelFormat) not supported") + } + } + + private func fromFloat16() -> [Float] { + var outputFloat16 = convert(initial: Float16(0)) + return float16to32(&outputFloat16, count: outputFloat16.count) + } + + private func fromFloat32() -> [Float] { + return convert(initial: Float(0)) + } + + private func convert(initial: T) -> [T] { + let numSlices = (featureChannels + 3)/4 + + // If the number of channels is not a multiple of 4, we may need to add + // padding. For 1 and 2 channels we don't need padding. + let channelsPlusPadding = (featureChannels < 3) ? featureChannels : numSlices * 4 + + // How many elements we need to copy over from each pixel in a slice. + // For 1 channel it's just 1 element (R); for 2 channels it is 2 elements + // (R+G), and for any other number of channels it is 4 elements (RGBA). + let numComponents = (featureChannels < 3) ? featureChannels : 4 + + // Allocate the memory for the array. If batching is used, then we need to + // copy numSlices slices for each image in the batch. + let count = width * height * channelsPlusPadding * numberOfImages + var output = [T](repeating: initial, count: count) + + let region = MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), + size: MTLSize(width: width, height: height, depth: 1)) + + for i in 0...size, + bytesPerImage: 0, + from: region, + mipmapLevel: 0, + slice: i) + } + return output + } +} diff --git a/VoiceMetal/VoiceMetal/ViewController.swift b/VoiceMetal/VoiceMetal/ViewController.swift new file mode 100644 index 0000000..7d46d37 --- /dev/null +++ b/VoiceMetal/VoiceMetal/ViewController.swift @@ -0,0 +1,118 @@ +import UIKit +import Metal +import MetalPerformanceShaders + +// I took these examples from the test set. To make this an app that is useful +// in practice, you would need to add code that records audio and then extracts +// the acoustic properties from the audio. + +let maleExample: [Float] = [ + 0.174272105181833,0.0694110453235828,0.190874106652007,0.115601979109401, + 0.228279274326553,0.112677295217152,4.48503835015822,61.7649083141473, + 0.950972024663856,0.635199178449614,0.0500274876305662,0.174272105181833, + 0.102045991451092,0.0183276059564719,0.246153846153846,1.62129934210526, + 0.0078125,7,6.9921875,0.209310986964618, +] + +let femaleExample: [Float] = [ + 0.19753980448103,0.0347746034366121,0.198683834048641,0.182660944206009, + 0.218712446351931,0.0360515021459227,2.45939730314823,9.56744874023233, + 0.839523285188244,0.226976814502006,0.185064377682403,0.19753980448103, + 0.173636160583901,0.0470127326150832,0.271186440677966,1.61474609375, + 0.2109375,15.234375,15.0234375,0.0389615584623385, +] + +class ViewController: UIViewController { + + var device: MTLDevice! + var commandQueue: MTLCommandQueue! + + var layer: MPSCNNFullyConnected! + var inputImage: MPSImage! + var outputImage: MPSImage! + + override func viewDidLoad() { + super.viewDidLoad() + + createGraph() + predict(example: maleExample) + predict(example: femaleExample) + } + + func createGraph() { + device = MTLCreateSystemDefaultDevice() + guard device != nil else { + fatalError("Error: This device does not support Metal") + } + + guard MPSSupportsMTLDevice(device) else { + fatalError("Error: This device does not support Metal Performance Shaders") + } + + commandQueue = device.makeCommandQueue() + + // Load the weights and bias value. + let W_url = Bundle.main.url(forResource: "W", withExtension: "bin") + let b_url = Bundle.main.url(forResource: "b", withExtension: "bin") + let W_data = try! Data(contentsOf: W_url!) + let b_data = try! Data(contentsOf: b_url!) + + // The logistic regression is computed by the formula sigmoid((W * x) + b). + // That's exactly the same computation that a fully-connected layer performs. + + let sigmoid = MPSCNNNeuronSigmoid(device: device) + + let layerDesc = MPSCNNConvolutionDescriptor(kernelWidth: 1, kernelHeight: 1, inputFeatureChannels: 20, outputFeatureChannels: 1, neuronFilter: sigmoid) + + // Create the fully-connected layer. + W_data.withUnsafeBytes { W in + b_data.withUnsafeBytes { b in + layer = MPSCNNFullyConnected(device: device, convolutionDescriptor: layerDesc, kernelWeights: W, biasTerms: b, flags: .none) + } + } + + let inputImgDesc = MPSImageDescriptor(channelFormat: .float16, width: 1, height: 1, featureChannels: 20) + let outputImgDesc = MPSImageDescriptor(channelFormat: .float16, width: 1, height: 1, featureChannels: 1) + + // The input and output are placed in MPSImage objects. + inputImage = MPSImage(device: device, imageDescriptor: inputImgDesc) + outputImage = MPSImage(device: device, imageDescriptor: outputImgDesc) + } + + func convert(example: [Float], to image: MPSImage) { + // The input data must be placed into the MTLTexture from the MPSImage. + // First we convert it to 16-bit floating point, then copy these floats + // into the texture slices. (Note: this will fail if the number of channels + // is not a multiple of 4 -- in that case you should add padding bytes to + // the example array.) + var example = example + let input16 = float32to16(&example, count: example.count) + input16.withUnsafeBufferPointer { ptr in + for i in 0...stride * 4, bytesPerImage: 0) + } + } + } + + func predict(example: [Float]) { + // Load the example data into an MPSImage so we can use it on the GPU. + convert(example: example, to: inputImage) + + // Perform the computations on the GPU. + let commandBuffer = commandQueue.makeCommandBuffer() + layer.encode(commandBuffer: commandBuffer, sourceImage: inputImage, destinationImage: outputImage) + commandBuffer.commit() + commandBuffer.waitUntilCompleted() + + // Print out the result of the prediction. + let y_pred = outputImage.toFloatArray() + print("Probability spoken by a male: \(y_pred[0])%") + + if y_pred[0] > 0.5 { + print("Prediction: male") + } else { + print("Prediction: female") + } + } +} diff --git a/VoiceMetal/VoiceMetal/W.bin b/VoiceMetal/VoiceMetal/W.bin new file mode 100644 index 0000000..20db497 --- /dev/null +++ b/VoiceMetal/VoiceMetal/W.bin @@ -0,0 +1 @@ +?l@D D;OgAGA"A<ż@?QA)0^P=ІU} s? ~ \ No newline at end of file diff --git a/VoiceMetal/VoiceMetal/b.bin b/VoiceMetal/VoiceMetal/b.bin new file mode 100644 index 0000000..3e6f7db --- /dev/null +++ b/VoiceMetal/VoiceMetal/b.bin @@ -0,0 +1 @@ + \ No newline at end of file