[
  {
    "path": ".gitignore",
    "content": "## OS X Finder\n.DS_Store\n\n## Build generated\nbuild/\nDerivedData\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata/\n\n## Other\n*.moved-aside\n*.xcuserstate\n*.xcscmblueprint\n\n## Obj-C/Swift specific\n*.hmap\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n# Swift Package Manager\n.build/\n"
  },
  {
    "path": ".swift-version",
    "content": "4.2\n"
  },
  {
    "path": "Example/PianoExample/AppDelegate.swift",
    "content": "//\n//  AppDelegate.swift\n//  PianoExample\n//\n//  Created by Saoud Rizwan on 9/11/17.\n//  Copyright © 2017 Saoud Rizwan. All rights reserved.\n//\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var window: UIWindow?\n\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {\n        // Override point for customization after application launch.\n        return true\n    }\n\n    func applicationWillResignActive(_ application: UIApplication) {\n        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n    }\n\n    func applicationDidEnterBackground(_ application: UIApplication) {\n        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n    }\n\n    func applicationWillEnterForeground(_ application: UIApplication) {\n        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n    }\n\n    func applicationDidBecomeActive(_ application: UIApplication) {\n        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n    }\n\n    func applicationWillTerminate(_ application: UIApplication) {\n        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n    }\n\n\n}\n\n"
  },
  {
    "path": "Example/PianoExample/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"57x57\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-57x57@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"57x57\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-57x57@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"50x50\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-Small-50x50@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"50x50\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-Small-50x50@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"72x72\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-72x72@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"72x72\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-72x72@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"PianoAppIcon.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Example/PianoExample/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Example/PianoExample/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" systemVersion=\"17A277\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"YES\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "Example/PianoExample/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14313.18\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"RZT-lr-wh0\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14283.14\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Navigation Controller-->\n        <scene sceneID=\"pSI-QR-7D9\">\n            <objects>\n                <navigationController id=\"RZT-lr-wh0\" sceneMemberID=\"viewController\">\n                    <navigationBar key=\"navigationBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"XPs-ic-HN1\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"20\" width=\"375\" height=\"44\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </navigationBar>\n                    <connections>\n                        <segue destination=\"BYZ-38-t0r\" kind=\"relationship\" relationship=\"rootViewController\" id=\"TYX-4b-NQp\"/>\n                    </connections>\n                </navigationController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"ucx-A2-xqN\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-196\" y=\"132\"/>\n        </scene>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"PianoExample\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" showsHorizontalScrollIndicator=\"NO\" showsVerticalScrollIndicator=\"NO\" dataMode=\"prototypes\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"HIm-Ye-lBa\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"338\" width=\"375\" height=\"329\"/>\n                                <color key=\"backgroundColor\" cocoaTouchSystemColor=\"groupTableViewBackgroundColor\"/>\n                                <color key=\"sectionIndexBackgroundColor\" cocoaTouchSystemColor=\"groupTableViewBackgroundColor\"/>\n                                <sections/>\n                            </tableView>\n                            <toolbar opaque=\"NO\" clearsContextBeforeDrawing=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Z3c-N2-ow6\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"294\" width=\"375\" height=\"44\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"44\" id=\"x3O-VQ-9IA\"/>\n                                </constraints>\n                                <items>\n                                    <barButtonItem title=\"Item\" id=\"efa-Dd-BH3\"/>\n                                </items>\n                            </toolbar>\n                            <label opaque=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" minimumFontSize=\"5\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hyq-If-A8R\" customClass=\"ResponsiveLabel\" customModule=\"PianoExample\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"20\" y=\"84\" width=\"335\" height=\"190\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"190\" id=\"zm0-Op-tO3\"/>\n                                </constraints>\n                                <fontDescription key=\"fontDescription\" name=\"Menlo-Regular\" family=\"Menlo\" pointSize=\"17\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"hyq-If-A8R\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" constant=\"20\" id=\"09c-X9-aCL\"/>\n                            <constraint firstItem=\"HIm-Ye-lBa\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"4Vg-e3-JdB\"/>\n                            <constraint firstItem=\"Z3c-N2-ow6\" firstAttribute=\"top\" secondItem=\"hyq-If-A8R\" secondAttribute=\"bottom\" constant=\"20\" id=\"Ijo-Sj-1Fl\"/>\n                            <constraint firstItem=\"Z3c-N2-ow6\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"NKi-uA-UVv\"/>\n                            <constraint firstItem=\"HIm-Ye-lBa\" firstAttribute=\"top\" secondItem=\"Z3c-N2-ow6\" secondAttribute=\"bottom\" id=\"WCL-8e-YzH\"/>\n                            <constraint firstItem=\"hyq-If-A8R\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" constant=\"20\" id=\"ZR4-L6-kg2\"/>\n                            <constraint firstItem=\"Z3c-N2-ow6\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"fAd-0q-ad9\"/>\n                            <constraint firstItem=\"HIm-Ye-lBa\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"iz5-dd-kzS\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"hyq-If-A8R\" secondAttribute=\"trailing\" constant=\"20\" id=\"rFo-AP-YBA\"/>\n                            <constraint firstItem=\"HIm-Ye-lBa\" firstAttribute=\"bottom\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"bottom\" id=\"yfL-rw-1eo\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                    <navigationItem key=\"navigationItem\" id=\"ru4-i9-IcL\"/>\n                    <connections>\n                        <outlet property=\"label\" destination=\"hyq-If-A8R\" id=\"cGk-B4-Hsk\"/>\n                        <outlet property=\"tableView\" destination=\"HIm-Ye-lBa\" id=\"lUh-el-D4J\"/>\n                        <outlet property=\"toolBar\" destination=\"Z3c-N2-ow6\" id=\"iYy-Fh-iJZ\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"501.60000000000002\" y=\"131.78410794602701\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "Example/PianoExample/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>Piano</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "Example/PianoExample/ResponsiveLabel.swift",
    "content": "//\n//  ResponsiveLabel.swift\n//  PianoExample\n//\n//  Created by Saoud Rizwan on 10/1/18.\n//  Copyright © 2018 Saoud Rizwan. All rights reserved.\n//\n\nimport UIKit\n\nclass ResponsiveLabel: UILabel {\n    override var canBecomeFirstResponder: Bool {\n        return true\n    }\n}\n"
  },
  {
    "path": "Example/PianoExample/Sounds.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Example/PianoExample/Sounds.xcassets/heart.dataset/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"data\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"Heavy Black Heart.wav\"\n    }\n  ]\n}"
  },
  {
    "path": "Example/PianoExample/Sounds.xcassets/kiss.dataset/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"data\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"Kiss Mark.wav\"\n    }\n  ]\n}"
  },
  {
    "path": "Example/PianoExample/Sounds.xcassets/wink.dataset/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"data\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"Winking Face.wav\"\n    }\n  ]\n}"
  },
  {
    "path": "Example/PianoExample/ViewController.swift",
    "content": "//\n//  ViewController.swift\n//  PianoExample\n//\n//  Created by Saoud Rizwan on 9/11/17.\n//  Copyright © 2017 Saoud Rizwan. All rights reserved.\n//\n\nimport UIKit\nimport Piano\n\nclass ViewController: UIViewController {\n\n    @IBOutlet weak var label: ResponsiveLabel!\n    @IBOutlet weak var toolBar: UIToolbar!\n    @IBOutlet weak var tableView: UITableView!\n    \n    let cellData: [(title: String, rows: [(title: String, note: 🎹.Note)])] = {\n        let sections = [\"\", \"\", \"Vibration\", \"Taptic Engine\", \"Haptic Feedback - Notification\", \"Haptic Feedback - Impact\", \"Haptic Feedback - Selection\", \"Sound - Assets Example\", \"Sound - File Example\", \"Sound - URL Example\", \"Sound - System Predefined\"]\n        \n        var rows = [[(title: String, note: 🎹.Note)]]()\n        for i in 0..<sections.count {\n            switch i {\n            case 0:\n                // Wait\n                rows.append([\n                    (\".wait(text goes here)\", .wait(0))\n                    ])\n            case 1:\n                // Wait Until Finished\n                rows.append([\n                    (\".waitUntilFinished\", .waitUntilFinished)\n                    ])\n            case 2:\n                // Vibration\n                rows.append([\n                    (\".vibration(.default)\", .vibration(.default)),\n                    (\".vibration(.alert)\", .vibration(.alert))\n                    ])\n            case 3:\n                // Taptic Engine\n                rows.append([\n                    (\".tapticEngine(.peek)\", .tapticEngine(.peek)),\n                    (\".tapticEngine(.pop)\", .tapticEngine(.pop)),\n                    (\".tapticEngine(.cancelled)\", .tapticEngine(.cancelled)),\n                    (\".tapticEngine(.tryAgain)\", .tapticEngine(.tryAgain)),\n                    (\".tapticEngine(.failed)\", .tapticEngine(.failed))\n                    ])\n            case 4:\n                // Haptic Feedback - Notification\n                rows.append([\n                    (\".hapticFeedback(.notification(.success))\", .hapticFeedback(.notification(.success))),\n                    (\".hapticFeedback(.notification(.warning))\", .hapticFeedback(.notification(.warning))),\n                    (\".hapticFeedback(.notification(.failure))\", .hapticFeedback(.notification(.failure)))\n                    ])\n            case 5:\n                // Haptic Feedback - Impact\n                rows.append([\n                    (\".hapticFeedback(.impact(.light))\", .hapticFeedback(.impact(.light))),\n                    (\".hapticFeedback(.impact(.medium))\", .hapticFeedback(.impact(.medium))),\n                    (\".hapticFeedback(.impact(.heavy))\", .hapticFeedback(.impact(.heavy)))\n                    ])\n            case 6:\n                // Haptic Feedback - Selection\n                rows.append([\n                    (\".hapticFeedback(.selection)\", .hapticFeedback(.selection))\n                    ])\n            case 7:\n                // Sound - Assets Example\n                rows.append([\n                    (\".sound(.asset(name: \\\"heart\\\"))\", .sound(.asset(name: \"heart\"))),\n                    (\".sound(.asset(name: \\\"kiss\\\"))\", .sound(.asset(name: \"kiss\"))),\n                    (\".sound(.asset(name: \\\"wink\\\"))\", .sound(.asset(name: \"wink\")))\n                    // MARK:-\n                    // MARK: Add your own sound assets here...\n                    // MARK:-\n                    \n                    ])\n            case 8:\n                // Sound - File Example\n                rows.append([\n                    (\".sound(.asset(name: \\\"heart\\\"))\", .sound(.file(name: \"harp\", extension: \"wav\")))\n                    // MARK:-\n                    // MARK: Add your own sound files here...\n                    // MARK:-\n                    \n                    ])\n            case 9:\n                // Sound - URL Example\n                let joyFileUrl = Bundle.main.url(forResource: \"joy\", withExtension: \"wav\")!\n                rows.append([\n                    (\".sound(.url(joyFileUrl))\", .sound(.url(joyFileUrl)))\n                    ])\n            case 10:\n                // Sound - System Predefined\n                rows.append([\n                    (\".sound(.system(.newMail))\", .sound(.system(.newMail))),\n                    (\".sound(.system(.mailSent))\", .sound(.system(.mailSent))),\n                    (\".sound(.system(.voicemail))\", .sound(.system(.voicemail)))\n                    ])\n                // There's too many to manually code here, so let's use some Swift black magic\n                Piano.SystemSound.allCases.forEach {\n                    rows[10].append((title: \".sound(.system(.\\($0))\", note: .sound(.system($0))))\n                }\n                /*\n                var z = 0\n                let sounds = AnyIterator {\n                    let next = withUnsafeBytes(of: &z) { $0.load(as: 🎹.SystemSound.self) }\n                    if next.hashValue != z { return nil }\n                    z += 1\n                    return next\n                } as AnyIterator<🎹.SystemSound>\n                for sound in sounds {\n                    rows[10].append((title: \".sound(.system(.\\(sound))\", note: .sound(.system(sound))))\n                }\n                 */\n                rows[10].removeSubrange(0..<3) // remove the first three we created as an example\n            default: break\n            }\n        }\n        var data = [(title: String, rows: [(title: String, note: 🎹.Note)])]()\n        for i in 0..<sections.count {\n            let section = sections[i]\n            let rows = rows[i]\n            data.append((title: section, rows: rows))\n        }\n        return data\n    }()\n    \n    lazy var waitTextField: UITextField = {\n        let textField = UITextField()\n        textField.translatesAutoresizingMaskIntoConstraints = false\n        textField.keyboardType = .decimalPad\n        textField.delegate = self\n        textField.borderStyle = .none\n        return textField\n    }()\n    \n    var waitValue: TimeInterval? = nil\n    \n    var pianoString: String = \"\" {\n        didSet {\n            if pianoString.count == 0 {\n                label.textAlignment = .center\n                label.textColor = UIColor.gray\n                label.text = \"Add some notes to your symphony\"\n            } else {\n                label.textAlignment = .left\n                label.textColor = UIColor.black\n                label.text = pianoString\n            }\n        }\n    }\n    \n    var notesToPlay = [🎹.Note]()\n    var notesToPlayAsStrings = [String]()\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        title = \"🎹 Piano\"\n        \n        let refreshButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshButtonTapped))\n        navigationItem.setLeftBarButton(refreshButton, animated: false)\n        \n        let undoButton = UIBarButtonItem(barButtonSystemItem: .undo, target: self, action: #selector(undoButtonTapped))\n        navigationItem.setRightBarButton(undoButton, animated: false)\n        \n        pianoString = \"\"\n        \n        let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)\n        let playButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(playButtonTapped))\n        toolBar.setItems([space, playButton, space], animated: false)\n        let shadow = UIView()\n        shadow.translatesAutoresizingMaskIntoConstraints = false\n        shadow.backgroundColor = UIColor.gray.withAlphaComponent(0.275)\n        toolBar.addSubview(shadow)\n        NSLayoutConstraint.activate([\n            shadow.leadingAnchor.constraint(equalTo: toolBar.leadingAnchor),\n            shadow.heightAnchor.constraint(equalToConstant: 0.75),\n            shadow.trailingAnchor.constraint(equalTo: toolBar.trailingAnchor),\n            shadow.bottomAnchor.constraint(equalTo: toolBar.bottomAnchor)\n            ])\n        let toolBarTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(toolBarTapped))\n        toolBarTapGestureRecognizer.delegate = self\n        toolBar.addGestureRecognizer(toolBarTapGestureRecognizer)\n        \n        let labelLongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(labelLongPressed))\n        labelLongPressGestureRecognizer.minimumPressDuration = 0.3\n        labelLongPressGestureRecognizer.delegate = self\n        label.addGestureRecognizer(labelLongPressGestureRecognizer)\n        \n        tableView.register(UITableViewCell.self, forCellReuseIdentifier: \"cellId\")\n        tableView.dataSource = self\n        tableView.delegate = self\n        tableView.keyboardDismissMode = .onDrag\n        \n        label.lineBreakMode = .byTruncatingMiddle\n        label.adjustsFontSizeToFitWidth = true\n        \n        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)\n    }\n    \n    @objc func refreshButtonTapped() {\n        🎹.cancel()\n        waitTextField.resignFirstResponder()\n        notesToPlay.removeAll()\n        notesToPlayAsStrings.removeAll()\n        pianoString = \"\"\n    }\n    \n    @objc func undoButtonTapped() {\n        waitTextField.resignFirstResponder()\n        if !notesToPlay.isEmpty {\n            notesToPlay.removeLast()\n        }\n        if !notesToPlayAsStrings.isEmpty {\n            notesToPlayAsStrings.removeLast()\n        }\n        setPianoStringToNotes()\n    }\n    \n    @objc func toolBarTapped(sender: UITapGestureRecognizer) {\n        tableView.setContentOffset(.zero, animated: true)\n        waitTextField.resignFirstResponder()\n    }\n    \n    @objc func labelLongPressed(sender: UILongPressGestureRecognizer) {\n        guard sender.state == .began && !notesToPlay.isEmpty, let senderView = sender.view, let superView = sender.view?.superview else { return }\n        senderView.becomeFirstResponder()\n        let copyItem = UIMenuItem(title: \"Copy\", action: #selector(labelMenuCopyTapped))\n        UIMenuController.shared.menuItems = [copyItem]\n        UIMenuController.shared.arrowDirection = .up\n        UIMenuController.shared.setTargetRect(senderView.frame, in: superView)\n        UIMenuController.shared.setMenuVisible(true, animated: true)\n    }\n    \n    @objc func labelMenuCopyTapped() {\n        let text = label.text\n        UIPasteboard.general.string = text\n        label.resignFirstResponder()\n    }\n    \n    @objc func playButtonTapped() {\n        waitTextField.resignFirstResponder()\n        if notesToPlay.count > 0 {\n            label.textColor = UIColor(red: 69.0/255.0, green: 241.0/255.0, blue: 126.0/255.0, alpha: 1.0)\n            🎹.play(notesToPlay) {\n                self.label.textColor = UIColor.black\n            }\n        }\n        \n    }\n    \n    func cellTapped(indexPath: IndexPath) {\n        let data = cellData[indexPath.section].rows[indexPath.row]\n        switch data.note {\n        case .wait, .waitUntilFinished: break\n        default:\n            🎹.play([data.note])\n        }\n    }\n    \n    @objc func cellAddButtonTapped(sender: UIButton) {\n        waitTextField.resignFirstResponder()\n        guard let cell = sender.superview as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else { return }\n        let data = cellData[indexPath.section].rows[indexPath.row]\n        switch data.note {\n        case .wait:\n            let waitNote = 🎹.Note.wait(waitValue ?? 0)\n            let waitString = \".wait(\\(waitValue ?? 0.0))\"\n            notesToPlay.append(waitNote)\n            notesToPlayAsStrings.append(waitString)\n        default:\n            notesToPlay.append(data.note)\n            notesToPlayAsStrings.append(data.title)\n        }\n        setPianoStringToNotes()\n    }\n    \n    func setPianoStringToNotes() {\n        var entireCommandAsString = \"\"\n        var numberOfLines = 0\n        for i in 0..<notesToPlayAsStrings.count {\n            if i == 0 {\n                entireCommandAsString.append(\"Piano.play([\\n\")\n                numberOfLines += 1\n            }\n            if i != notesToPlayAsStrings.count - 1 {\n                let noteToPlayAsString = notesToPlayAsStrings[i]\n                entireCommandAsString.append(\"    \" + noteToPlayAsString + \",\\n\")\n                numberOfLines += 1\n            } else {\n                let noteToPlayAsString = notesToPlayAsStrings[i]\n                entireCommandAsString.append(\"    \" + noteToPlayAsString + \"\\n\")\n                numberOfLines += 1\n            }\n            if i == notesToPlayAsStrings.count - 1 {\n                entireCommandAsString.append(\"    ])\")\n                numberOfLines += 1\n            }\n        }\n        pianoString = entireCommandAsString\n        label.numberOfLines = numberOfLines\n    }\n}\n\nextension ViewController: UITableViewDataSource, UITableViewDelegate {\n    func numberOfSections(in tableView: UITableView) -> Int {\n        return cellData.count\n    }\n    \n    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {\n        var title = cellData[section].title\n        \n        let unsupported = \" (UNSUPPORTED)\"\n        switch section {\n        case 3:\n            // Taptic Engine\n            if !UIDevice.current.hasTapticEngine {\n                title.append(unsupported)\n            }\n        case 4, 5, 6:\n            // Haptic Feedback\n            if !UIDevice.current.hasHapticFeedback {\n                title.append(unsupported)\n            }\n        default:\n            break\n        }\n        \n        return title\n    }\n    \n    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n        return cellData[section].rows.count\n    }\n    \n    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n        let cell = tableView.dequeueReusableCell(withIdentifier: \"cellId\") ?? UITableViewCell(style: .value1, reuseIdentifier: \"cellId\")\n        let data = cellData[indexPath.section].rows[indexPath.row]\n        cell.textLabel?.text = data.title\n        \n        let addButton = UIButton(type: .contactAdd)\n        addButton.addTarget(self, action: #selector(cellAddButtonTapped), for: .touchUpInside)\n        cell.accessoryView = addButton\n        \n        if indexPath.section == 0 { // Wait\n            cell.textLabel?.text = \"\"\n            cell.contentView.addSubview(waitTextField)\n            NSLayoutConstraint.activate([\n                waitTextField.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 20),\n                waitTextField.topAnchor.constraint(equalTo: cell.contentView.topAnchor),\n                waitTextField.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -20),\n                waitTextField.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor)\n                ])\n            waitTextField.setContentHuggingPriority(UILayoutPriority.defaultLow, for: .vertical)\n            \n            let one = \".wait(\"\n            let two: String = (waitValue == nil) ? \"tap to input seconds\" : \"\\(waitValue!)\"\n            let three = \")\"\n            let attributedString = NSMutableAttributedString(string: one + two + three)\n            attributedString.addAttributes([.foregroundColor: UIColor.black], range: NSRange(location: 0, length: one.count))\n            attributedString.addAttributes([.foregroundColor: UIColor.lightGray], range: NSRange(location: one.count, length: two.count))\n            attributedString.addAttributes([.foregroundColor: UIColor.black], range: NSRange(location: one.count + two.count, length: three.count))\n            waitTextField.attributedText = attributedString\n        } else {\n            if cell.contentView.subviews.contains(waitTextField) {\n                waitTextField.removeFromSuperview()\n            }\n        }\n        \n        cell.selectionStyle = (indexPath.section == 0 || indexPath.section == 1) ? .none : .default\n        \n        return cell\n    }\n    \n    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {\n        return 45\n    }\n    \n    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n        cellTapped(indexPath: indexPath)\n        tableView.deselectRow(at: indexPath, animated: true)\n    }\n}\n\nextension ViewController: UITextFieldDelegate {\n    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {\n        if textField == waitTextField {\n            let text = (textField.text ?? \"\") as NSString\n            var newString = text.replacingCharacters(in: range, with: string)\n            \n            newString = newString.replacingOccurrences(of: \".wait(\", with: \"\")\n            newString = newString.replacingOccurrences(of: \".wait\", with: \"\")\n            newString = newString.replacingOccurrences(of: \")\", with: \"\")\n            newString = newString.replacingOccurrences(of: \"tap to input seconds\", with: \"\")\n            newString = newString.replacingOccurrences(of: \" \", with: \"\")\n            \n            waitValue = TimeInterval(newString)\n            \n            newString = \".wait(\" + newString + \")\"\n            let attributedString = NSMutableAttributedString(string: newString)\n            attributedString.addAttributes([.foregroundColor: UIColor.black], range: NSRange(location: 0, length: newString.count))\n            waitTextField.attributedText = attributedString\n            \n            if let newPosition = textField.position(from: textField.endOfDocument, offset: -1) {\n                textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)\n            }\n            return false\n        } else {\n            return true\n        }\n    }\n    \n    func textFieldDidBeginEditing(_ textField: UITextField) {\n        if textField == waitTextField {\n            let string = \".wait(\" + ((waitValue == nil) ? \"\" : \"\\(waitValue!)\") + \")\"\n            let attributedString = NSMutableAttributedString(string: string)\n            attributedString.addAttributes([.foregroundColor: UIColor.black], range: NSRange(location: 0, length: string.count))\n            waitTextField.attributedText = attributedString\n            \n            if let newPosition = textField.position(from: textField.endOfDocument, offset: -1) {\n                textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)\n            }\n        }\n    }\n    \n    func textFieldDidEndEditing(_ textField: UITextField) {\n        if textField == waitTextField {\n            let one = \".wait(\"\n            let two: String = (waitValue == nil) ? \"tap to input seconds\" : \"\\(waitValue!)\"\n            let three = \")\"\n            let attributedString = NSMutableAttributedString(string: one + two + three)\n            attributedString.addAttributes([.foregroundColor: UIColor.black], range: NSRange(location: 0, length: one.count))\n            attributedString.addAttributes([.foregroundColor: UIColor.gray], range: NSRange(location: one.count, length: two.count))\n            attributedString.addAttributes([.foregroundColor: UIColor.black], range: NSRange(location: one.count + two.count, length: three.count))\n            waitTextField.attributedText = attributedString\n        }\n    }\n}\n\nextension ViewController: UIGestureRecognizerDelegate {\n    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {\n        if let touchedView = touch.view, touchedView.isKind(of: UIControl.self) {\n            return false\n        } else {\n            return true\n        }\n    }\n}\n\nextension ViewController {\n    @objc private func keyboardWillShow(notification: NSNotification) {\n        let waitCellIndexPath = IndexPath(row: 0, section: 0)\n        if let visibleIndexPaths = tableView.indexPathsForVisibleRows, visibleIndexPaths.contains(waitCellIndexPath) {\n            tableView.scrollToRow(at: waitCellIndexPath, at: UITableViewScrollPosition.middle, animated: true)\n        }\n    }\n}\n"
  },
  {
    "path": "Example/PianoExample.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t105A40E11F675C690078BAA6 /* harp.wav in Resources */ = {isa = PBXBuildFile; fileRef = 10CCC0E51F6738010085294A /* harp.wav */; };\n\t\t105A40E21F675C690078BAA6 /* joy.wav in Resources */ = {isa = PBXBuildFile; fileRef = 105A40E01F6756970078BAA6 /* joy.wav */; };\n\t\t106A373C1F66EE0200BF5BD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106A373B1F66EE0200BF5BD1 /* AppDelegate.swift */; };\n\t\t106A373E1F66EE0200BF5BD1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106A373D1F66EE0200BF5BD1 /* ViewController.swift */; };\n\t\t106A37411F66EE0200BF5BD1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 106A373F1F66EE0200BF5BD1 /* Main.storyboard */; };\n\t\t106A37431F66EE0200BF5BD1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 106A37421F66EE0200BF5BD1 /* Assets.xcassets */; };\n\t\t106A37461F66EE0200BF5BD1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 106A37441F66EE0200BF5BD1 /* LaunchScreen.storyboard */; };\n\t\t10CCC0E01F6733CD0085294A /* Piano.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 10CCC0DF1F6733C70085294A /* Piano.framework */; };\n\t\t10CCC0E11F6733CD0085294A /* Piano.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 10CCC0DF1F6733C70085294A /* Piano.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\t10CCC0E41F6737730085294A /* Sounds.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 10CCC0E31F6737730085294A /* Sounds.xcassets */; };\n\t\tDA4BC02E2162AB77006C5ADF /* ResponsiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BC02D2162AB77006C5ADF /* ResponsiveLabel.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t10CCC0D01F6728FA0085294A /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\t10CCC0E11F6733CD0085294A /* Piano.framework in Embed Frameworks */,\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t105A40E01F6756970078BAA6 /* joy.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = joy.wav; sourceTree = \"<group>\"; };\n\t\t106A37381F66EE0200BF5BD1 /* PianoExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PianoExample.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t106A373B1F66EE0200BF5BD1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t106A373D1F66EE0200BF5BD1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t106A37401F66EE0200BF5BD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t106A37421F66EE0200BF5BD1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t106A37451F66EE0200BF5BD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t106A37471F66EE0200BF5BD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t10CCC0DF1F6733C70085294A /* Piano.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Piano.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t10CCC0E31F6737730085294A /* Sounds.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Sounds.xcassets; sourceTree = \"<group>\"; };\n\t\t10CCC0E51F6738010085294A /* harp.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = harp.wav; sourceTree = \"<group>\"; };\n\t\tDA4BC02D2162AB77006C5ADF /* ResponsiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponsiveLabel.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t106A37351F66EE0200BF5BD1 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t10CCC0E01F6733CD0085294A /* Piano.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t106A372F1F66EE0200BF5BD1 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A373A1F66EE0200BF5BD1 /* PianoExample */,\n\t\t\t\t106A37391F66EE0200BF5BD1 /* Products */,\n\t\t\t\t10CCC0C61F6712630085294A /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t106A37391F66EE0200BF5BD1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A37381F66EE0200BF5BD1 /* PianoExample.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t106A373A1F66EE0200BF5BD1 /* PianoExample */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t10CCC0E21F67350C0085294A /* IGNORE */,\n\t\t\t\t10CCC0E31F6737730085294A /* Sounds.xcassets */,\n\t\t\t\t10CCC0E51F6738010085294A /* harp.wav */,\n\t\t\t\t105A40E01F6756970078BAA6 /* joy.wav */,\n\t\t\t\t106A373D1F66EE0200BF5BD1 /* ViewController.swift */,\n\t\t\t);\n\t\t\tpath = PianoExample;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t10CCC0C61F6712630085294A /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t10CCC0DF1F6733C70085294A /* Piano.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t10CCC0E21F67350C0085294A /* IGNORE */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A37421F66EE0200BF5BD1 /* Assets.xcassets */,\n\t\t\t\t106A373B1F66EE0200BF5BD1 /* AppDelegate.swift */,\n\t\t\t\t106A373F1F66EE0200BF5BD1 /* Main.storyboard */,\n\t\t\t\t106A37441F66EE0200BF5BD1 /* LaunchScreen.storyboard */,\n\t\t\t\t106A37471F66EE0200BF5BD1 /* Info.plist */,\n\t\t\t\tDA4BC02D2162AB77006C5ADF /* ResponsiveLabel.swift */,\n\t\t\t);\n\t\t\tname = IGNORE;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t106A37371F66EE0200BF5BD1 /* PianoExample */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 106A374A1F66EE0200BF5BD1 /* Build configuration list for PBXNativeTarget \"PianoExample\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t106A37341F66EE0200BF5BD1 /* Sources */,\n\t\t\t\t106A37351F66EE0200BF5BD1 /* Frameworks */,\n\t\t\t\t106A37361F66EE0200BF5BD1 /* Resources */,\n\t\t\t\t10CCC0D01F6728FA0085294A /* Embed Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = PianoExample;\n\t\t\tproductName = PianoExample;\n\t\t\tproductReference = 106A37381F66EE0200BF5BD1 /* PianoExample.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t106A37301F66EE0200BF5BD1 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0900;\n\t\t\t\tLastUpgradeCheck = 0900;\n\t\t\t\tORGANIZATIONNAME = \"Saoud Rizwan\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t106A37371F66EE0200BF5BD1 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.0;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 106A37331F66EE0200BF5BD1 /* Build configuration list for PBXProject \"PianoExample\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 106A372F1F66EE0200BF5BD1;\n\t\t\tproductRefGroup = 106A37391F66EE0200BF5BD1 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t106A37371F66EE0200BF5BD1 /* PianoExample */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t106A37361F66EE0200BF5BD1 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t105A40E11F675C690078BAA6 /* harp.wav in Resources */,\n\t\t\t\t105A40E21F675C690078BAA6 /* joy.wav in Resources */,\n\t\t\t\t106A37461F66EE0200BF5BD1 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t106A37431F66EE0200BF5BD1 /* Assets.xcassets in Resources */,\n\t\t\t\t106A37411F66EE0200BF5BD1 /* Main.storyboard in Resources */,\n\t\t\t\t10CCC0E41F6737730085294A /* Sounds.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t106A37341F66EE0200BF5BD1 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t106A373E1F66EE0200BF5BD1 /* ViewController.swift in Sources */,\n\t\t\t\t106A373C1F66EE0200BF5BD1 /* AppDelegate.swift in Sources */,\n\t\t\t\tDA4BC02E2162AB77006C5ADF /* ResponsiveLabel.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t106A373F1F66EE0200BF5BD1 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t106A37401F66EE0200BF5BD1 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t106A37441F66EE0200BF5BD1 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t106A37451F66EE0200BF5BD1 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t106A37481F66EE0200BF5BD1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = \"$(SRCROOT)/**\";\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t106A37491F66EE0200BF5BD1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = \"$(SRCROOT)/**\";\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t106A374B1F66EE0200BF5BD1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tDEVELOPMENT_TEAM = LR7NY5NPR9;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = \"$(SRCROOT)/**\";\n\t\t\t\tINFOPLIST_FILE = PianoExample/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = saoudrizwan.PianoExample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 4.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t106A374C1F66EE0200BF5BD1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tDEVELOPMENT_TEAM = LR7NY5NPR9;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = \"$(SRCROOT)/**\";\n\t\t\t\tINFOPLIST_FILE = PianoExample/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = saoudrizwan.PianoExample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 4.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t106A37331F66EE0200BF5BD1 /* Build configuration list for PBXProject \"PianoExample\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t106A37481F66EE0200BF5BD1 /* Debug */,\n\t\t\t\t106A37491F66EE0200BF5BD1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t106A374A1F66EE0200BF5BD1 /* Build configuration list for PBXNativeTarget \"PianoExample\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t106A374B1F66EE0200BF5BD1 /* Debug */,\n\t\t\t\t106A374C1F66EE0200BF5BD1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 106A37301F66EE0200BF5BD1 /* Project object */;\n}\n"
  },
  {
    "path": "Example/PianoExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:PianoExample.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Saoud Rizwan <hello@saoudmr.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Piano.podspec",
    "content": "Pod::Spec.new do |s|\n  s.name         = \"Piano\"\n  s.version      = \"1.8\"\n  s.summary      = \"Compose a symphony of sounds and vibrations with Taptic Engine\"\n  s.description  = <<-DESC\n    Piano is a delightful and easy-to-use wrapper around the AVFoundation and UIHapticFeedback classes, leveraging the full capabilities of the Taptic Engine, while following strict Apple guidelines to preserve battery life. Ultimately, Piano allows you, the composer, to conduct masterful symphonies of sounds and vibrations, and create a more immersive, usable and meaningful user experience in your app or game.\n  DESC\n  s.homepage     = \"https://github.com/saoudrizwan/Piano\"\n  s.license      = { :type => \"MIT\", :file => \"LICENSE\" }\n  s.author             = { \"Saoud Rizwan\" => \"hello@saoudmr.com\" }\n  s.social_media_url   = \"https://twitter.com/sdrzn\"\n  s.platform     = :ios, \"10.0\"\n  s.source       = { :git => \"https://github.com/saoudrizwan/Piano.git\", :tag => \"#{s.version}\" }\n  s.source_files  = \"Sources/**/*.{h,m,swift}\"\nend\n"
  },
  {
    "path": "Piano.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t106A36E81F66ECC300BF5BD1 /* Piano.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 106A36DE1F66ECC300BF5BD1 /* Piano.framework */; };\n\t\t106A36ED1F66ECC300BF5BD1 /* PianoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106A36EC1F66ECC300BF5BD1 /* PianoTests.swift */; };\n\t\t106A36EF1F66ECC300BF5BD1 /* Piano.h in Headers */ = {isa = PBXBuildFile; fileRef = 106A36E11F66ECC300BF5BD1 /* Piano.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t106A36F91F66ECCF00BF5BD1 /* Piano.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106A36F81F66ECCF00BF5BD1 /* Piano.swift */; };\n\t\t10CCC0E71F6747FA0085294A /* SystemSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0E61F6747FA0085294A /* SystemSound.swift */; };\n\t\t10CCC0E91F6748340085294A /* Audio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0E81F6748340085294A /* Audio.swift */; };\n\t\t10CCC0EB1F67484C0085294A /* Vibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0EA1F67484C0085294A /* Vibration.swift */; };\n\t\t10CCC0ED1F6748600085294A /* TapticEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0EC1F6748600085294A /* TapticEngine.swift */; };\n\t\t10CCC0EF1F6748760085294A /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0EE1F6748760085294A /* HapticFeedback.swift */; };\n\t\t10CCC0F11F67488D0085294A /* Note.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0F01F67488D0085294A /* Note.swift */; };\n\t\t10CCC0F31F6748E20085294A /* Piano+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0F21F6748E20085294A /* Piano+Error.swift */; };\n\t\t10CCC0F51F6749FB0085294A /* UIDevice+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCC0F41F6749FB0085294A /* UIDevice+Extension.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t106A36E91F66ECC300BF5BD1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 106A36D51F66ECC300BF5BD1 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 106A36DD1F66ECC300BF5BD1;\n\t\t\tremoteInfo = Piano;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t106A36DE1F66ECC300BF5BD1 /* Piano.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Piano.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t106A36E11F66ECC300BF5BD1 /* Piano.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Piano.h; sourceTree = \"<group>\"; };\n\t\t106A36E21F66ECC300BF5BD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t106A36E71F66ECC300BF5BD1 /* PianoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PianoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t106A36EC1F66ECC300BF5BD1 /* PianoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PianoTests.swift; sourceTree = \"<group>\"; };\n\t\t106A36EE1F66ECC300BF5BD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t106A36F81F66ECCF00BF5BD1 /* Piano.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Piano.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0E61F6747FA0085294A /* SystemSound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemSound.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0E81F6748340085294A /* Audio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Audio.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0EA1F67484C0085294A /* Vibration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vibration.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0EC1F6748600085294A /* TapticEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapticEngine.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0EE1F6748760085294A /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0F01F67488D0085294A /* Note.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Note.swift; sourceTree = \"<group>\"; };\n\t\t10CCC0F21F6748E20085294A /* Piano+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Piano+Error.swift\"; sourceTree = \"<group>\"; };\n\t\t10CCC0F41F6749FB0085294A /* UIDevice+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"UIDevice+Extension.swift\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t106A36DA1F66ECC300BF5BD1 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t106A36E41F66ECC300BF5BD1 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t106A36E81F66ECC300BF5BD1 /* Piano.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t106A36D41F66ECC300BF5BD1 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A36E01F66ECC300BF5BD1 /* Sources */,\n\t\t\t\t106A36EB1F66ECC300BF5BD1 /* Tests */,\n\t\t\t\t106A36DF1F66ECC300BF5BD1 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t106A36DF1F66ECC300BF5BD1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A36DE1F66ECC300BF5BD1 /* Piano.framework */,\n\t\t\t\t106A36E71F66ECC300BF5BD1 /* PianoTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t106A36E01F66ECC300BF5BD1 /* Sources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A36E21F66ECC300BF5BD1 /* Info.plist */,\n\t\t\t\t106A36E11F66ECC300BF5BD1 /* Piano.h */,\n\t\t\t\t106A36F81F66ECCF00BF5BD1 /* Piano.swift */,\n\t\t\t\t10CCC0F21F6748E20085294A /* Piano+Error.swift */,\n\t\t\t\t10CCC0F01F67488D0085294A /* Note.swift */,\n\t\t\t\t10CCC0E61F6747FA0085294A /* SystemSound.swift */,\n\t\t\t\t10CCC0E81F6748340085294A /* Audio.swift */,\n\t\t\t\t10CCC0EA1F67484C0085294A /* Vibration.swift */,\n\t\t\t\t10CCC0EC1F6748600085294A /* TapticEngine.swift */,\n\t\t\t\t10CCC0EE1F6748760085294A /* HapticFeedback.swift */,\n\t\t\t\t10CCC0F41F6749FB0085294A /* UIDevice+Extension.swift */,\n\t\t\t);\n\t\t\tpath = Sources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t106A36EB1F66ECC300BF5BD1 /* Tests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t106A36EC1F66ECC300BF5BD1 /* PianoTests.swift */,\n\t\t\t\t106A36EE1F66ECC300BF5BD1 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = Tests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\t106A36DB1F66ECC300BF5BD1 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t106A36EF1F66ECC300BF5BD1 /* Piano.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\t106A36DD1F66ECC300BF5BD1 /* Piano */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 106A36F21F66ECC300BF5BD1 /* Build configuration list for PBXNativeTarget \"Piano\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t106A36D91F66ECC300BF5BD1 /* Sources */,\n\t\t\t\t106A36DA1F66ECC300BF5BD1 /* Frameworks */,\n\t\t\t\t106A36DB1F66ECC300BF5BD1 /* Headers */,\n\t\t\t\t106A36DC1F66ECC300BF5BD1 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Piano;\n\t\t\tproductName = Piano;\n\t\t\tproductReference = 106A36DE1F66ECC300BF5BD1 /* Piano.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\t106A36E61F66ECC300BF5BD1 /* PianoTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 106A36F51F66ECC300BF5BD1 /* Build configuration list for PBXNativeTarget \"PianoTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t106A36E31F66ECC300BF5BD1 /* Sources */,\n\t\t\t\t106A36E41F66ECC300BF5BD1 /* Frameworks */,\n\t\t\t\t106A36E51F66ECC300BF5BD1 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t106A36EA1F66ECC300BF5BD1 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = PianoTests;\n\t\t\tproductName = PianoTests;\n\t\t\tproductReference = 106A36E71F66ECC300BF5BD1 /* PianoTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t106A36D51F66ECC300BF5BD1 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0900;\n\t\t\t\tLastUpgradeCheck = 0900;\n\t\t\t\tORGANIZATIONNAME = \"Saoud Rizwan\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t106A36DD1F66ECC300BF5BD1 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.0;\n\t\t\t\t\t\tLastSwiftMigration = 0900;\n\t\t\t\t\t};\n\t\t\t\t\t106A36E61F66ECC300BF5BD1 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.0;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 106A36D81F66ECC300BF5BD1 /* Build configuration list for PBXProject \"Piano\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t);\n\t\t\tmainGroup = 106A36D41F66ECC300BF5BD1;\n\t\t\tproductRefGroup = 106A36DF1F66ECC300BF5BD1 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t106A36DD1F66ECC300BF5BD1 /* Piano */,\n\t\t\t\t106A36E61F66ECC300BF5BD1 /* PianoTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t106A36DC1F66ECC300BF5BD1 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t106A36E51F66ECC300BF5BD1 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t106A36D91F66ECC300BF5BD1 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t10CCC0F31F6748E20085294A /* Piano+Error.swift in Sources */,\n\t\t\t\t10CCC0EB1F67484C0085294A /* Vibration.swift in Sources */,\n\t\t\t\t10CCC0E91F6748340085294A /* Audio.swift in Sources */,\n\t\t\t\t10CCC0F11F67488D0085294A /* Note.swift in Sources */,\n\t\t\t\t10CCC0F51F6749FB0085294A /* UIDevice+Extension.swift in Sources */,\n\t\t\t\t10CCC0ED1F6748600085294A /* TapticEngine.swift in Sources */,\n\t\t\t\t10CCC0E71F6747FA0085294A /* SystemSound.swift in Sources */,\n\t\t\t\t106A36F91F66ECCF00BF5BD1 /* Piano.swift in Sources */,\n\t\t\t\t10CCC0EF1F6748760085294A /* HapticFeedback.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t106A36E31F66ECC300BF5BD1 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t106A36ED1F66ECC300BF5BD1 /* PianoTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t106A36EA1F66ECC300BF5BD1 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 106A36DD1F66ECC300BF5BD1 /* Piano */;\n\t\t\ttargetProxy = 106A36E91F66ECC300BF5BD1 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin XCBuildConfiguration section */\n\t\t106A36F01F66ECC300BF5BD1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t106A36F11F66ECC300BF5BD1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t106A36F31F66ECC300BF5BD1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/Sources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = saoudrizwan.Piano;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t106A36F41F66ECC300BF5BD1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/Sources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = saoudrizwan.Piano;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t106A36F61F66ECC300BF5BD1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = 5M795QY47C;\n\t\t\t\tINFOPLIST_FILE = Tests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = saoudrizwan.PianoTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 4.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t106A36F71F66ECC300BF5BD1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = 5M795QY47C;\n\t\t\t\tINFOPLIST_FILE = Tests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = saoudrizwan.PianoTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 4.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t106A36D81F66ECC300BF5BD1 /* Build configuration list for PBXProject \"Piano\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t106A36F01F66ECC300BF5BD1 /* Debug */,\n\t\t\t\t106A36F11F66ECC300BF5BD1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t106A36F21F66ECC300BF5BD1 /* Build configuration list for PBXNativeTarget \"Piano\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t106A36F31F66ECC300BF5BD1 /* Debug */,\n\t\t\t\t106A36F41F66ECC300BF5BD1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t106A36F51F66ECC300BF5BD1 /* Build configuration list for PBXNativeTarget \"PianoTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t106A36F61F66ECC300BF5BD1 /* Debug */,\n\t\t\t\t106A36F71F66ECC300BF5BD1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 106A36D51F66ECC300BF5BD1 /* Project object */;\n}\n"
  },
  {
    "path": "Piano.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:Piano.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Piano.xcodeproj/xcshareddata/xcschemes/Piano.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0900\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"106A36DD1F66ECC300BF5BD1\"\n               BuildableName = \"Piano.framework\"\n               BlueprintName = \"Piano\"\n               ReferencedContainer = \"container:Piano.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      language = \"\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      language = \"\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"106A36DD1F66ECC300BF5BD1\"\n            BuildableName = \"Piano.framework\"\n            BlueprintName = \"Piano\"\n            ReferencedContainer = \"container:Piano.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"106A36DD1F66ECC300BF5BD1\"\n            BuildableName = \"Piano.framework\"\n            BlueprintName = \"Piano\"\n            ReferencedContainer = \"container:Piano.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Piano.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Example/PianoExample.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Piano.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:README.md\">\n   </FileRef>\n   <FileRef\n      location = \"group:LICENSE\">\n   </FileRef>\n   <FileRef\n      location = \"group:Piano.podspec\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <img src=\"https://user-images.githubusercontent.com/7799382/30356431-dbba9920-97ed-11e7-8f2b-a5b5ba0e7682.png\" alt=\"Piano\" />\n</p>\n\n<p align=\"center\">\n    <img src=\"https://user-images.githubusercontent.com/7799382/30309920-bcdb85ec-9742-11e7-96fc-af8155f4712d.png\" alt=\"Platform: iOS 10.0+\" />\n    <a href=\"https://developer.apple.com/swift\" target=\"_blank\"><img src=\"https://user-images.githubusercontent.com/7799382/30309908-ace5d886-9742-11e7-85ea-8d4e5f2af2ac.png\" alt=\"Language: Swift 4\" /></a>\n    <a href=\"https://cocoapods.org/pods/Piano\" target=\"_blank\"><img src=\"https://user-images.githubusercontent.com/7799382/33073452-cd78293e-ce77-11e7-8b39-8a1565616814.png\" alt=\"CocoaPods compatible\" /></a>\n    <a href=\"https://github.com/Carthage/Carthage\" target=\"_blank\"><img src=\"https://user-images.githubusercontent.com/7799382/30309900-9fc15d2e-9742-11e7-91fd-31bb1226db90.png\" alt=\"Carthage compatible\" /></a>\n    <img src=\"https://user-images.githubusercontent.com/7799382/30309910-adef2b38-9742-11e7-8140-d05534dd92a5.png\" alt=\"License: MIT\" />\n</p>\n\n<p align=\"center\">\n    <a href=\"#installation\">Installation</a>\n  • <a href=\"#usage\">Usage</a>\n  • <a href=\"#documentation\">Documentation</a>\n  • <a href=\"#why-i-built-piano\">Why I Built Piano</a>\n  • <a href=\"#license\">License</a>\n  • <a href=\"#contribute\">Contribute</a>\n  • <a href=\"#questions\">Questions?</a>\n  • <a href=\"#credits\">Credits</a>\n</p>\n\nPiano is a **convenient** and **easy-to-use** wrapper around the `AVFoundation` and `UIHapticFeedback` frameworks, leveraging the full capabilities of the **Taptic Engine**, while following strict Apple guidelines to **preserve battery life**. Ultimately, Piano allows you, the composer, to conduct masterful symphonies of sounds and vibrations, and create a more immersive, usable and meaningful user experience in your app or game.\n\n\n## Compatibility\n\nPiano requires **iOS 10+** and is compatible with **Swift 4.2** projects.\n\n## Installation\n\n* Installation for <a href=\"https://guides.cocoapods.org/using/using-cocoapods.html\" target=\"_blank\">CocoaPods</a>:\n\n```ruby\nplatform :ios, '10.0'\ntarget 'ProjectName' do\nuse_frameworks!\n\n    pod 'Piano', '~> 1.8'\n\nend\n```\n*(if you run into problems, `pod repo update` and try again)*\n\n* Installation for <a href=\"https://github.com/Carthage/Carthage\" target=\"_blank\">Carthage</a>:\n\n ```ruby\n github \"saoudrizwan/Piano\"\n ```\n *(make sure Xcode 10 is [set as your system's default Xcode](https://stackoverflow.com/a/28901378/3502608) before using CocoaPods or Carthage with Swift 4 frameworks)*\n\n* Or embed the Piano framework into your project\n\nAnd `import Piano` in the files you'd like to use it.\n\n## Usage\n\nUsing Piano is simple.\n```swift\nlet symphony: [Piano.Note] = [\n    .sound(.asset(name: \"acapella\")),\n    .hapticFeedback(.impact(.light)),\n    .waitUntilFinished,\n    .hapticFeedback(.impact(.heavy)),\n    .wait(0.2),\n    .sound(.system(.chooChoo))\n]\n\nPiano.play(symphony)\n```\n... or better yet:\n```swift\n🎹.play([\n    .sound(.asset(name: \"acapella\"))\n    ])\n```\nOptionally add a completion block to be called when all the notes are finished playing:\n```swift\n🎹.play([\n    .sound(.asset(name: \"acapella\"))\n]) {\n    // ...\n}\n```\nOr cancel the currently playing symphony:\n```swift\n🎹.cancel()\n```\n\nIn the background, each note has an internal completion block, so you can add a `.waitUntilFinished` note that tells Piano to not play the next note until the previous note is done playing. This is useful for creating patterns of custom haptic feedback, besides the ones Apple predefined. This is also great for creating complex combinations of sound effects and vibrations.\n\n### Notes\n\n#### `.sound(Audio)`\nPlays an audio file.\n\n|Audio | |\n|------------ | ------------- |\n|`.asset(name: String)` | Name of asset in any .xcassets catalogs. It's recommended to add your sound files to Asset Catalogs instead of as standalone files to your main bundle.|\n|`.file(name: String, extension: String)` | Retrieves a file from the main bundle. For example a file named `Beep.wav` would be accessed with `.file(name: \"Beep\", extension: \"wav\")`.|\n|`.url(URL)` | This only works for file URLs, not network URLs.|\n|`.system(SystemSound)` | Predefined system sounds in every iPhone. [See all available options here](https://github.com/saoudrizwan/Piano/blob/master/Sources/SystemSound.swift). |\n\n#### `.vibration(Vibration)`\nPlays standard vibrations available on all models of the iPhone.\n\n|Vibration | |\n|------------ | -------------|\n|`.default`  | Basic 1-second vibration |\n|`.alert`  | Two short consecutive vibrations |\n\n#### `.tapticEngine(TapticEngine)`\nPlays Taptic Engine vibrations available on the iPhone 6S and above.\n\n|TapticEngine | |\n| ------------ | ------------- |\n|`.peek` | One weak boom |\n|`.pop` | One strong boom |\n|`.cancelled` | Three sequential weak booms |\n|`.tryAgain` | One weak boom then one strong boom |\n|`.failed` | Three sequential strong booms |\n\n#### `.hapticFeedback(HapticFeedback)`\nPlays Taptic Engine Haptic Feedback available on the iPhone 7 and above.\n\n|HapticFeedback | | |\n|------------ | ------------- |------------- |\n|`.notification(Notification)` | **Notification** | Communicate that a task or action has succeeded, failed, or produced a warning of some kind. |\n| | `.success` | Indicates that a task or action has completed successfully. |\n| | `.warning` | Indicates that a task or action has produced a warning. |\n| | `.failure` | Indicates that a task or action has failed. |\n|`.impact(Impact)`  | **Impact** | Indicates that an impact has occurred. For example, you might trigger impact feedback when a user interface object collides with something or snaps into place. |\n| | `.light` | Provides a physical metaphor representing a collision between small, light user interface elements.|\n| | `.medium` | Provides a physical metaphor representing a collision between moderately sized user interface elements.|\n| | `.heavy` | Provides a physical metaphor representing a collision between large, heavy user interface elements.|\n|`.selection` | | Indicates that the selection is actively changing. For example, the user feels light taps while scrolling a picker wheel.|\n\n<sub>See: [Apple's Guidelines for using Haptic Feedback](https://developer.apple.com/ios/human-interface-guidelines/user-interaction/feedback/)</sub>\n\n#### `.waitUntilFinished`\nTells Piano to wait until the previous note is done playing before playing the next note.\n\n#### `.wait(TimeInterval)`\nTells Piano to wait a given duration before playing the next note.\n\n### Device Capabilities\n\n* The iPhone 6S and 6S Plus carry the first generation of Taptic Engine which has a few \"haptic\" vibration patterns, which you can play with Piano using the `.tapticEngine()` notes.\n\n* The iPhone 7 and above carry the latest version of the Taptic Engine which supports the iOS 10 Haptic Feedback frameworks, allowing you to select from many more vibration types. You can play these vibrations using the `.hapticFeedback()` notes.\n\n* All versions of the iPhone can play the `.vibration()` notes.\n\nPiano also includes a useful extension for `UIDevice` to check if the user's device has a Taptic Engine and if it supports Haptic Feedback. This extension is especially useful for creating symphonies for all devices:\n```swift\nif UIDevice.current.hasHapticFeedback {\n    // use .hapticFeedback(HapticFeedback) notes\n} else if UIDevice.current.hasTapticEngine {\n    // use .tapticEngine(TapticEngine) notes\n} else {\n    // use .vibration(Vibration) notes\n}\n```\n**Note:** This extension does not work on simulators, it will always return false.\n\n### Taptic Engine Guide\n\nApple's [guide over the Haptic Feedback framework](https://developer.apple.com/documentation/uikit/uifeedbackgenerator) is very clear about using the Taptic Engine appropriately in order to prevent draining the user's device's battery life. Piano was built with this in mind, and handles most cases as efficiently as possible. But you can help preserve battery life and reduce latency further by calling these helper methods based on your specific needs.\n\n#### 1. Wake up the Taptic Engine\n```swift\nPiano.wakeTapticEngine()\n```\nThis initializes and allocates the Haptic Feedback framework and essentially \"wakes up\" the Taptic Engine, as it is normally in an idle state. A good place to put this is at the begin state of a gesture or action, in anticipation of playing a `.hapticFeedback()` note.\n\n#### 2. Prepare the Taptic Engine\n\n```swift\nPiano.prepareTapticEngine()\n```\nThis tells the Taptic Engine to prepare itself before creating any feedback to reduce latency when triggering feedback.\n\nFrom Apple's [documentation](https://developer.apple.com/documentation/uikit/uifeedbackgenerator):\n> This is particularly important when trying to match feedback to sound or visual cues. To preserve power, the Taptic Engine stays in this prepared state for only a short period of time (on the order of seconds), or until you next trigger feedback. Think about when and where you can best prepare your generators. If you call prepare and then immediately trigger feedback, the system won’t have enough time to get the Taptic Engine into the prepared state, and you may not see a reduction in latency. On the other hand, if you call prepare too early, the Taptic Engine may become idle again before you trigger feedback.\n\ntl;dr A good place to put this is right after calling `.wakeTapticEngine()`, usually at the beginning of a gesture or action, in anticipation of playing a `.hapticFeedback()` note.\n\n#### 3. Put the Taptic Engine back to Sleep\n```swift\nPiano.putTapticEngineToSleep()\n```\nOnce we know we're done using the Taptic Engine, we can deallocate the Haptic Feedback framework, returning the Taptic Engine to its idle state. A good place to put this is at the end of a finished, cancelled, or failed gesture or action.\n\n#### But you don't have to.\nPiano automatically wakes and prepares the Taptic Engine when you call `.play([ ... ])` if it includes a `.hapticFeedback()` note, and returns the Taptic Engine back to sleep when the notes are done playing.\n\n### The Example App\n\nThe [example app](https://github.com/saoudrizwan/Piano/tree/master/Example) is a great place to get started. It's designed as a playground for you to compose and test out your own symphonies of sounds and vibrations.\n\n<p align=\"center\">\n<img src=\"https://user-images.githubusercontent.com/7799382/30370416-613f985a-982c-11e7-8646-33f1efb55d90.png\" alt=\"Piano\" width=\"300\" height=\"500\" />\n</p>\n\nYou can even drag and drop your own sound files into the project and tweak the code a bit to see how your own sounds can work alongside the Taptic Engine. To add your own sound file, simply drag it into `Sounds.xcassets`, name it accordingly, then edit the `cellData` property in `ViewController.swift` (Scroll down to `case 7` in `cellData`, or look for \"Add your own sound assets here...\" in the Jump Bar using `Ctrl + 6`).\n\n## Documentation\nOption + click on any of Piano's methods or notes for detailed documentation.\n<img src=\"https://user-images.githubusercontent.com/7799382/30358465-97784ee0-97f9-11e7-9f12-75fa041cf556.png\" alt=\"documentation\">\n\n\n## Why I Built Piano\n\nWith the new iPhone 8 and iPhone X, we are going to see many new Augmented Reality apps, and one of the keypoints in the [Human Interface Guidelines for AR](https://developer.apple.com/ios/human-interface-guidelines/technologies/augmented-reality/) is to not clutter the AR view, allowing as much content from the augmented reality to be displayed as possible. Besides AR, Apple has spent tremendous time and manpower giving the iPhone an interface beyond our vision with the Taptic Engine and Siri. Apple even had a [session during WWDC 2017](https://developer.apple.com/videos/play/wwdc2017/803/) talking about the importance of sound design and the impact it can have on a user experience. It's obvious that the future of technology is not visual interfaces, but augmenting our connection with the real world. By using our physical, auditory, and most importantly visual senses, we can see the world in a whole new light. That's why I built Piano and [ARLogger](https://github.com/saoudrizwan/ARLogger), frameworks I hope will help developers create immersive and uncluttered interfaces, while keeping the user aware of the technology's state and purpose. If you'd like my help on an AR project, or just want to chat about the future of technology, don't hesitate to reach out to me on Twitter [@sdrzn](http://twitter.com/sdrzn).\n\n## License\n\nPiano uses the MIT license. Please file an issue if you have any questions or if you'd like to share how you're using Piano.\n\n## Contribute\n\nPlease feel free to create issues for feature requests or send pull requests of any additions you think would complement Piano and its philosophy.\n\n## Questions?\n\nContact me by email <a href=\"mailto:hello@saoudmr.com\">hello@saoudmr.com</a>, or by twitter <a href=\"https://twitter.com/sdrzn\" target=\"_blank\">@sdrzn</a>. Please create an <a href=\"https://github.com/saoudrizwan/Piano/issues\">issue</a> if you come across a bug or would like a feature to be added.\n\n## Credits\n\n* Example app sound files from [Icons 8 UI Sounds](https://icons8.com/sounds)\n* Music notes in README header image from [LSE Design on the Noun Project](https://thenounproject.com/LSEdesigns/collection/music-notes/)\n"
  },
  {
    "path": "Sources/Audio.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// Audio file to play\n    public enum Audio {\n        /// Name of asset in any .xcassets catalogs\n        case asset(name: String)\n        \n        /// Searches main bundle for file with given name and extension\n        case file(name: String, extension: String)\n        \n        /// URL of audio file\n        case url(URL)\n        \n        /// Predefined system sound included in all iPhones\n        case system(SystemSound)\n    }\n}\n"
  },
  {
    "path": "Sources/HapticFeedback.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// Second Generation Taptic Engine vibration options\n    public enum HapticFeedback {\n        /// Use notification feedback to communicate that a task or action has succeeded, failed, or produced a warning of some kind.\n        case notification(Notification)\n        public enum Notification {\n            /// Indicates that a task or action, such as depositing a check or unlocking a vehicle, has completed.\n            case success\n            \n            /// Indicates that a task or action, such as depositing a check or unlocking a vehicle, has produced a warning of some kind.\n            case warning\n            \n            /// Indicates that a task or action, such as depositing a check or unlocking a vehicle, has failed.\n            case failure\n        }\n        \n        /// Use impact feedback generators to indicate that an impact has occurred. For example, you might trigger impact feedback when a user interface object collides with something or snaps into place.\n        case impact(Impact)\n        public enum Impact {\n            /// Provides a physical metaphor representing a collision between small, light user interface elements. For example, the user might feel a thud when a view slides into place or two objects collide.\n            case light\n            \n            /// Provides a physical metaphor representing a collision between moderately sized user interface elements. For example, the user might feel a thud when a view slides into place or two objects collide.\n            case medium\n            \n            /// Provides a physical metaphor representing a collision between large, heavy user interface elements. For example, the user might feel a thud when a view slides into place or two objects collide.\n            case heavy\n        }\n        \n        /// Indicates that the selection is actively changing. For example, the user feels light taps while scrolling a picker wheel. This feedback is intended for communicating movement through a series of discrete values, not making or confirming a selection.\n        case selection\n    }\n}\n\n"
  },
  {
    "path": "Sources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.8</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>NSPrincipalClass</key>\n\t<string></string>\n</dict>\n</plist>\n"
  },
  {
    "path": "Sources/Note.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// Sound, feedback, vibration, or pause for Piano to play\n    public enum Note {\n        /// Audio file to play\n        case sound(Audio)\n        \n        /// Standard vibrations available on all models of the iPhone\n        case vibration(Vibration)\n        \n        /// First generation Taptic Engine vibrations\n        case tapticEngine(TapticEngine)\n        \n        /// Second Generation Taptic Engine vibrations\n        case hapticFeedback(HapticFeedback)\n        \n        /// Tells Piano to wait until the previous note is done playing before playing the next note\n        case waitUntilFinished\n        \n        /// Tells Piano to wait a given duration before playing the next note\n        case wait(TimeInterval)\n    }\n}\n"
  },
  {
    "path": "Sources/Piano+Error.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// Possible errors when trying to play notes\n    public enum PianoError: Error {\n        case notFound(String)\n        case couldNotPlay(String)\n    }\n    \n    /// Currently, printing the errors in console is the most friendly way to handle them\n    func handle(error: Error) {\n        if let error = error as? PianoError {\n            switch error {\n            case .notFound(let name):\n                print(\"🎹 Piano could not find \\(name)!\")\n            case .couldNotPlay(let name):\n                print(\"🎹 Piano could not play \\(name)!\")\n            }\n        } else {\n            let error = error as NSError\n            print(\"\"\"\n                🎹 Piano encountered an error!\n                Domain: \\(error.domain)\n                Code: \\(error.code)\n                Description: \\(error.localizedDescription)\n                Failure Reason: \\(error.localizedFailureReason ?? \"\")\n                Suggestions: \\(error.localizedRecoverySuggestion ?? \"\")\n                \"\"\")\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Piano.h",
    "content": "//\n//  Piano.h\n//  Piano\n//\n//  Created by Saoud Rizwan on 9/11/17.\n//  Copyright © 2017 Saoud Rizwan. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n//! Project version number for Piano.\nFOUNDATION_EXPORT double PianoVersionNumber;\n\n//! Project version string for Piano.\nFOUNDATION_EXPORT const unsigned char PianoVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <Piano/PublicHeader.h>\n\n\n"
  },
  {
    "path": "Sources/Piano.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\nimport AudioToolbox.AudioServices\nimport AVFoundation\n\n@available(iOS 10.0, *)\npublic typealias 🎹 = Piano\n\n/// Piano\n///\n/// Compose a symphony of sounds and vibrations using Taptic Engine\n@available(iOS 10.0, *)\npublic class Piano {\n    \n    /// Internal instance of Piano to manage shared feedback generators and symphony trackers\n    private static let `default` = Piano()\n    \n    /// Allocatable/deallocatable tuple of UIFeedbackGenerators (Apple recommended)\n    private var feedbackGenerator: (notification: UINotificationFeedbackGenerator?,\n        impact: (light: UIImpactFeedbackGenerator?,\n        medium: UIImpactFeedbackGenerator?,\n        heavy: UIImpactFeedbackGenerator?),\n        selection: UISelectionFeedbackGenerator?) = (nil, (nil, nil, nil), nil)\n    \n    private var player: AVAudioPlayer?\n    \n    /// Keeps track of multiple symphonies, preventing multiple symphonies from being played at once\n    private var symphonyCounter = 0\n    \n    /// Holds all the scheduled Timers with music\n    private var timers = [Timer]()\n    \n    private init() { }\n    \n    /// Wakes the Taptic Engine up from an idle state\n    public static func wakeTapticEngine() {\n        if Piano.default.feedbackGenerator.notification == nil {\n            Piano.default.feedbackGenerator = (notification: UINotificationFeedbackGenerator(),\n                                               impact: (light: UIImpactFeedbackGenerator(style: .light),\n                                                        medium: UIImpactFeedbackGenerator(style: .medium),\n                                                        heavy: UIImpactFeedbackGenerator(style: .heavy)),\n                                               selection: UISelectionFeedbackGenerator())\n        }\n    }\n    \n    /// This tells the Taptic Engine to prepare itself before creating any feedback to reduce latency when triggering feedback. You can call this as many times as you want, preferrably right before playing a .hapticFeedback note.\n    ///\n    /// Apple docs:\n    /// When you call this method, the generator is placed into a prepared state for a short period of time. While the generator is prepared, you can trigger feedback with lower latency.\n    /// Think about when you can best prepare your generators. Call prepare() before the event that triggers feedback. The system needs time to prepare the Taptic Engine for minimal latency. Calling prepare() and then immediately triggering feedback (without any time in between) does not improve latency.\n    /// To conserve power, the Taptic Engine returns to an idle state after any of the following events:\n    /// - You trigger feedback on the generator.\n    /// - A short period of time passes (typically seconds).\n    /// - The generator is deallocated.\n    ///\n    /// After feedback is triggered, the Taptic Engine returns to its idle state. If you might trigger additional feedback within the next few seconds, immediately call prepare() to keep the Taptic Engine in the prepared state.\n    /// You can also extend the prepared state by repeatedly calling the prepare() method. However, if you continue calling prepare() without ever triggering feedback, the system may eventually place the Taptic Engine back in an idle state and ignore any further prepare() calls until after you trigger feedback at least once.\n    public static func prepareTapticEngine() {\n        if Piano.default.feedbackGenerator.notification == nil {\n            Piano.wakeTapticEngine()\n        }\n        Piano.default.feedbackGenerator.selection?.prepare()\n        Piano.default.feedbackGenerator.notification?.prepare()\n        Piano.default.feedbackGenerator.impact.light?.prepare()\n        Piano.default.feedbackGenerator.impact.medium?.prepare()\n        Piano.default.feedbackGenerator.impact.heavy?.prepare()\n    }\n    \n    /// Returns the Taptic Engine to an idle state\n    public static func putTapticEngineToSleep() {\n        Piano.default.feedbackGenerator = (nil, (nil, nil, nil), nil)\n    }\n    \n    /// Plays the audio asset with the given name\n    ///\n    /// - Parameters:\n    ///   - assetName: name of asset as per in its respective .xcassets catalog\n    ///   - completion: completion handler\n    private func playAudio(from assetName: String, completion: (() -> Void)?) {\n        guard let asset = NSDataAsset(name: assetName) else {\n            handle(error: PianoError.notFound(assetName))\n            completion?()\n            return\n        }\n        do {\n            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)\n            try AVAudioSession.sharedInstance().setActive(true)\n            player = try AVAudioPlayer(data: asset.data, fileTypeHint: nil)\n            if let player = player {\n                player.play()\n                DispatchQueue.main.asyncAfter(deadline: .now() + player.duration, execute: {\n                    completion?()\n                })\n            } else {\n                handle(error: PianoError.couldNotPlay(assetName))\n                completion?()\n            }\n        } catch {\n            handle(error: error)\n            completion?()\n        }\n    }\n    \n    /// Plays the audio file with the given name and extension\n    ///\n    /// - Parameters:\n    ///   - file: name of file (Sound.mp4 -> (\"Sound\", \"mp4\")\n    ///   - completion: completion handler\n    private func playAudio(from file: (name: String, extension: String), completion: (() -> Void)?) {\n        guard let url = Bundle.main.url(forResource: file.name, withExtension: file.extension) else {\n            handle(error: PianoError.notFound(\"\\(file.name + \".\" + file.extension)\"))\n            completion?()\n            return\n        }\n        do {\n            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)\n            try AVAudioSession.sharedInstance().setActive(true)\n            player = try AVAudioPlayer(contentsOf: url)\n            if let player = player {\n                player.play()\n                DispatchQueue.main.asyncAfter(deadline: .now() + player.duration, execute: {\n                    completion?()\n                })\n            } else {\n                handle(error: PianoError.couldNotPlay(\"\\(file.name + \".\" + file.extension)\"))\n                completion?()\n            }\n        } catch {\n            handle(error: error)\n            completion?()\n        }\n    }\n    \n    /// Plays the audio from the specified URL\n    ///\n    /// - Parameters:\n    ///   - url: file URL of audio file\n    ///   - completion: completion handler\n    private func playAudio(from url: URL, completion: (() -> Void)?) {\n        do {\n            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)\n            try AVAudioSession.sharedInstance().setActive(true)\n            player = try AVAudioPlayer(contentsOf: url)\n            if let player = player {\n                player.play()\n                DispatchQueue.main.asyncAfter(deadline: .now() + player.duration, execute: {\n                    completion?()\n                })\n            } else {\n                handle(error: PianoError.couldNotPlay(url.absoluteString))\n                completion?()\n            }\n        } catch {\n            handle(error: error)\n            completion?()\n        }\n    }\n    \n    /// Plays system sound using Audio Services\n    ///\n    /// - Parameters:\n    ///   - soundId: System Sound ID of sound\n    ///   - completion: completion handler\n    private func playSystemSound(with soundId: Int, completion: (() -> Void)?) {\n        AudioServicesPlaySystemSoundWithCompletion(SystemSoundID(soundId)) {\n            DispatchQueue.main.async {\n                completion?()\n            }\n        }\n    }\n    \n    /// Plays the specified haptic feedback, calling the specified completion handler after a time manually calculated from Apple's website\n    ///\n    /// - Parameters:\n    ///   - feedback: type of feedback to generate\n    ///   - completion: completion handler\n    private func playHapticFeedback(_ feedback: HapticFeedback, completion: (() -> Void)?) {\n        let duration: TimeInterval // value is calculated from https://developer.apple.com/ios/human-interface-guidelines/interaction/feedback/\n        switch feedback {\n        case .notification(let notification):\n            switch notification {\n            case .success:\n                Piano.default.feedbackGenerator.notification?.notificationOccurred(.success)\n                duration = 0.2\n            case .warning:\n                Piano.default.feedbackGenerator.notification?.notificationOccurred(.warning)\n                duration = 0.25\n            case .failure:\n                Piano.default.feedbackGenerator.notification?.notificationOccurred(.error)\n                duration = 0.5\n            }\n        case .impact(let impact):\n            switch impact {\n            case .light:\n                Piano.default.feedbackGenerator.impact.light?.impactOccurred()\n            case .medium:\n                Piano.default.feedbackGenerator.impact.medium?.impactOccurred()\n            case .heavy:\n                Piano.default.feedbackGenerator.impact.heavy?.impactOccurred()\n            }\n            duration = 0.1\n        case .selection:\n            Piano.default.feedbackGenerator.selection?.selectionChanged()\n            duration = 0.05\n        }\n        DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: {\n            completion?()\n        })\n    }\n    \n    /// Cancels the currently playing symphony\n    public static func cancel() {\n        for timer in Piano.default.timers {\n            timer.invalidate()\n        }\n        Piano.default.timers.removeAll()\n    }\n    \n    /// Play a symphony of notes\n    ///\n    /// Note: This method automatically cancels any previously playing symphonies\n    public static func play(_ notes: [Note], completion: (() -> Void)? = nil) {\n        cancel()\n        Piano.default.symphonyCounter += 1\n        var pauseDurationBeforeNextNote: TimeInterval = 0\n        let notes = Piano.default.removeUnnecessaryNotes(from: notes)\n        var completion = completion\n        if notes.contains(where: { (note) -> Bool in\n            switch note {\n            case .hapticFeedback: return true\n            default: return false\n            }\n        }) {\n            prepareTapticEngine()\n            if let definedCompletion = completion {\n                let newCompletion: (() -> Void) = {\n                    definedCompletion()\n                    putTapticEngineToSleep()\n                }\n                completion = newCompletion\n            } else {\n                completion = {\n                    putTapticEngineToSleep()\n                }\n            }\n        }\n        notesLoop: for i in 0..<notes.count {\n            let note = notes[i]\n            var music: (() -> Void)? = nil\n            var iterationCompletion: (() -> Void)? = nil\n            if (i < notes.count - 2) {\n                let nextNote = notes[i + 1]\n                switch nextNote {\n                case .waitUntilFinished:\n                    let afterNextNoteIndex = i + 2\n                    let finalNoteIndex = notes.count - 1\n                    let restOfNotes = Array(notes[afterNextNoteIndex...finalNoteIndex])\n                    let capturedCounter = Piano.default.symphonyCounter\n                    iterationCompletion = {\n                        if Piano.default.symphonyCounter == capturedCounter {\n                            play(restOfNotes, completion: completion)\n                        }\n                    }\n                default: break\n                }\n            } else if (i < notes.count - 1) {\n                let nextNote = notes[i + 1]\n                switch nextNote {\n                case .waitUntilFinished:\n                    iterationCompletion = completion\n                default: break\n                }\n            } else if i == notes.count - 1 {\n                iterationCompletion = completion\n            }\n            switch note {\n            case .sound(let audio):\n                switch audio {\n                case .asset(let name):\n                    music = { Piano.default.playAudio(from: name, completion: iterationCompletion) }\n                case .file(let name, let type):\n                    music = { Piano.default.playAudio(from: (name, type), completion: iterationCompletion) }\n                case .url(let url):\n                    music = { Piano.default.playAudio(from: url, completion: iterationCompletion) }\n                case .system(let sound):\n                    music = { Piano.default.playSystemSound(with: sound.rawValue, completion: iterationCompletion) }\n                }\n            case .vibration(let vibration):\n                music = { Piano.default.playSystemSound(with: vibration.rawValue, completion: iterationCompletion) }\n            case .tapticEngine(let engine):\n                music = { Piano.default.playSystemSound(with: engine.rawValue, completion: iterationCompletion) }\n            case .hapticFeedback(let feedback):\n                music = { Piano.default.playHapticFeedback(feedback, completion: iterationCompletion) }\n            case .waitUntilFinished:\n                if i != 0 {\n                    break notesLoop\n                }\n            case .wait(let interval):\n                pauseDurationBeforeNextNote += interval\n                if i == notes.count - 1 {\n                    music = { iterationCompletion?() }\n                }\n            }\n            if let music = music {\n                let timer = Timer(timeInterval: pauseDurationBeforeNextNote, repeats: false) { (_) in\n                    music()\n                }\n                RunLoop.main.add(timer, forMode: .common)\n                Piano.default.timers.append(timer)\n            }\n        }\n        if notes.count == 0 {\n            completion?()\n        }\n    }\n    \n    /// Helper method for .play() to remove unnecessary .waitUntileFinisheds\n    private func removeUnnecessaryNotes(from notes: [Note]) -> [Note] {\n        var results = [Note]()\n        for note in notes {\n            if results.count == 0 {\n                results.append(note)\n            } else if let last = results.last {\n                switch note {\n                case .waitUntilFinished:\n                    switch last {\n                    case .waitUntilFinished: break\n                    default: results.append(note)\n                    }\n                default: results.append(note)\n                }\n            }\n        }\n        if results.count == 1 {\n            let onlyNote = results[0]\n            switch onlyNote {\n            case .waitUntilFinished: return []\n            default: break\n            }\n        } else {\n            var removedFirstWaits = false\n            var removedLastWaits = false\n            while !removedFirstWaits || !removedLastWaits {\n                switch results.first! {\n                case .waitUntilFinished: results.removeFirst()\n                default: removedFirstWaits = true\n                }\n                switch results.last! {\n                case .waitUntilFinished: results.removeLast()\n                default: removedLastWaits = true\n                }\n            }\n        }\n        return results\n    }\n}\n"
  },
  {
    "path": "Sources/SystemSound.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// Default system sounds predefined and available on all iPhones\n    /// Source: http://iphonedevwiki.net/index.php/AudioServices\n    public enum SystemSound: Int, CaseIterable {\n        case newMail = 1000\n        case mailSent = 1001\n        case voicemail = 1002\n        case receivedMessage = 1003\n        case sentMessage = 1004\n        case alarm = 1005\n        case lowPower = 1006\n        case smsReceived1 = 1007\n        case smsReceived2 = 1008\n        case smsReceived3 = 1009\n        case smsReceived4 = 1010\n        case smsReceived7 = 1012\n        case smsReceived5 = 1013\n        case smsReceived6 = 1014\n        case tweetSent = 1016\n        case anticipate = 1020\n        case bloom = 1021\n        case calypso = 1022\n        case chooChoo = 1023\n        case descent = 1024\n        case fanfare = 1025\n        case ladder = 1026\n        case minuet = 1027\n        case newsFlash = 1028\n        case noir = 1029\n        case sherwhoodForest = 1030\n        case spell = 1031\n        case suspense = 1032\n        case telegraph = 1033\n        case tiptoes = 1034\n        case typewriters = 1035\n        case update = 1036\n        case ussd = 1050\n        case simToolkitCallDropped = 1051\n        case simToolkitGeneralBeep = 1052\n        case simToolkitNegativeAck = 1053\n        case simToolkitPositiveAck = 1054\n        case simToolkitSms = 1055\n        case tinkQuiet = 1057\n        case ctBusy = 1070\n        case ctCongestion = 1071\n        case ctPathAck = 1072\n        case ctError = 1073\n        case ctCallWaiting = 1074\n        case ctKeyTone2 = 1075\n        case lock = 1100\n        case unlockFailed = 1102\n        case tink = 1103\n        case tock = 1104\n        case beepBeep = 1106\n        case ringerChanged = 1107\n        case photoShutter = 1108\n        case shake = 1109\n        case jblBegin = 1110\n        case jblConfirm = 1111\n        case jblCancel = 1112\n        case beginRecord = 1113\n        case endRecord = 1114\n        case jblAmbiguous = 1115\n        case jblNoMatch = 1116\n        case beginVideoRecord = 1117\n        case endVideoRecord = 1118\n        case vcInvitationAccepted = 1150\n        case vcRinging = 1151\n        case vcEnded = 1152\n        case ctCallWaiting2 = 1153\n        case vcRingingQuiet = 1154\n        case touchTone0 = 1200\n        case touchTone1 = 1201\n        case touchTone2 = 1202\n        case touchTone3 = 1203\n        case touchTone4 = 1204\n        case touchTone5 = 1205\n        case touchTone6 = 1206\n        case touchTone7 = 1207\n        case touchTone8 = 1208\n        case touchTone9 = 1209\n        case touchToneStar = 1210\n        case touchTonePound = 1211\n        case headsetStartCall = 1254\n        case headsetRedial = 1255\n        case headsetAnswerCall = 1256\n        case headsetEndCall = 1257\n        case headsetWait = 1258\n        case headsetTransitionEnd = 1259\n        case tockQuiet = 1306\n    }\n}\n"
  },
  {
    "path": "Sources/TapticEngine.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// First generation Taptic Engine vibrations\n    public enum TapticEngine: Int {\n        /// Weak boom\n        case peek = 1519\n        \n        /// Strong boom\n        case pop = 1520\n        \n        /// Three sequential weak booms\n        case cancelled = 1521\n        \n        /// Weak boom then strong boom\n        case tryAgain = 1102\n        \n        /// Three sequential strong booms\n        case failed = 1107\n    }\n}\n"
  },
  {
    "path": "Sources/UIDevice+Extension.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n/// Device extension to check whether user's device supports Taptic Engine and/or Haptic Feedback\n/// Be sure to use with UIDevice.current\npublic extension UIDevice {\n    /// In order to check if the iPhone has Taptic Engine and/or Haptic Feedback support, we need to check the device's model version. This function returns the generation and version of the current device.\n    /// Note: Simulators will return a result of (0, 0), resulting in the hasTapticEngine and hasHapticFeedback BOOLs returning false\n    /* Example:\n     \"iPhone7,1\" on iPhone 6 Plus -> (7, 1)\n     \"iPhone7,2\" on iPhone 6 -> (7, 2)\n     \"iPhone8,1\" on iPhone 6S -> (8, 1)\n     \"iPhone8,2\" on iPhone 6S Plus -> (8, 2)\n     \"iPhone8,4\" on iPhone SE -> (8, 4)\n     \"iPhone9,1\" on iPhone 7 (CDMA) -> (9, 1)\n     \"iPhone9,3\" on iPhone 7 (GSM) -> (9, 3)\n     \"iPhone9,2\" on iPhone 7 Plus (CDMA) -> (9, 2)\n     \"iPhone9,4\" on iPhone 7 Plus (GSM) -> (9, 4)\n     iPhone 8, 8S, and X will likely use a generation of 10 or greater, and will support Haptic Feedback, so this extension will work for those devices as well.\n     iPhone X -> iPhone10,6\n     */\n    private func getDeviceGenerationVersion() -> (generation: Int, version: Int) {\n        var sysinfo = utsname()\n        uname(&sysinfo)\n        let platform = String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters)\n        if platform.lowercased().prefix(\"iPhone\".count) != \"iPhone\".lowercased() { // Not an iPhone (probably simulator)\n            return (0, 0)\n        }\n        let numbers = platform.filter { \"0123456789,\".contains($0) }\n        if let commaIndex = numbers.index(of: \",\") {\n            let firstNumber = numbers[numbers.startIndex..<commaIndex]\n            let afterCommaIndex = numbers.index(after: commaIndex)\n            let secondNumber = numbers[afterCommaIndex..<numbers.endIndex] // endIndex is an index after the last index\n            let generation = Int(firstNumber) ?? 0\n            let version = Int(secondNumber) ?? 0\n            return (generation, version)\n        } else {\n            return (0, 0)\n        }\n    }\n    \n    // Returns a BOOL value representing whether the current device has a Taptic Engine or not\n    public var hasTapticEngine: Bool {\n        get {\n            let device = getDeviceGenerationVersion()\n            if device.generation == 8 {\n                if device.version == 4 { // SE\n                    return false\n                } else {\n                    return true\n                }\n            } else if device.generation > 8 {\n                return true\n            } else {\n                return false\n            }\n        }\n    }\n    \n    // Returns a BOOL value representing whether the current device has a Taptic Engine with Haptic Feedback support\n    public var hasHapticFeedback: Bool {\n        get {\n            let device = getDeviceGenerationVersion()\n            if device.generation >= 9 {\n                return true\n            } else {\n                return false\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Vibration.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2018 Saoud Rizwan <hello@saoudmr.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nimport Foundation\n\n@available(iOS 10.0, *)\nextension Piano {\n    /// Standard vibrations available on all models of the iPhone\n    public enum Vibration: Int {\n        /// Basic 1-second vibration\n        case `default` = 4095\n        /// Two short consecutive vibrations\n        case alert = 1011\n    }\n}\n"
  },
  {
    "path": "Tests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "Tests/PianoTests.swift",
    "content": "//\n//  PianoTests.swift\n//  PianoTests\n//\n//  Created by Saoud Rizwan on 9/11/17.\n//  Copyright © 2017 Saoud Rizwan. All rights reserved.\n//\n\nimport XCTest\n@testable import Piano\n\nclass PianoTests: XCTestCase {\n    \n    override func setUp() {\n        super.setUp()\n    }\n    \n    override func tearDown() {\n        super.tearDown()\n    }\n    \n}\n"
  }
]