@@ -0,0 +1,15 @@
//
// TLMLockEvent.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <Foundation/Foundation.h>

@class TLMMyo;

@interface TLMLockEvent : NSObject <NSCopying>

@end
@@ -0,0 +1,78 @@
//
// TLMMath.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <Foundation/Foundation.h>

/**
Represents a 3 dimensional vector.
*/
struct _TLMVector3
{
float x, y, z;
};
typedef struct _TLMVector3 TLMVector3;

/**
Convenience constructor for a TLMVector3.
*/
TLMVector3 TLMVector3Make(float x, float y, float z);

/**
Returns the length of \a vector (the square root of the sum of the components).
*/
float TLMVector3Length(TLMVector3 vector);

/**
Returns true if \a vectorLeft equals \a vectorRight, otherwise false.
*/
BOOL TLMVector3Equal(TLMVector3 vectorLeft, TLMVector3 vectorRight);

/**
Represents a quaternion.
*/
struct _TLMQuaternion
{
float x, y, z, w;
};
typedef struct _TLMQuaternion TLMQuaternion;

/**
Constant for the identity quaternion.
*/
extern const TLMQuaternion TLMQuaternionIdentity;

/**
Convenience constructor for a TLMQuaternion.
*/
TLMQuaternion TLMQuaternionMake(float x, float y, float z, float w);

/**
Returns the length of \a quaternion (the square of the sum of the components).
*/
float TLMQuaternionLength(TLMQuaternion quaternion);

/**
Returns true if \a quaternionLeft equals \a quaternionRight, otherwise false.
*/
BOOL TLMQuaternionEqual(TLMQuaternion quaternionLeft, TLMQuaternion quaternionRight);

/**
Returns the normalized form of \a quaternion, whose length equals 1.
*/
TLMQuaternion TLMQuaternionNormalize(TLMQuaternion quaternion);

/**
Returns the product of multiplying \a quaternionLeft by \a quaternionRight. Quaternion multiplication is
noncommutative, so order matters.
*/
TLMQuaternion TLMQuaternionMultiply(TLMQuaternion quaternionLeft, TLMQuaternion quaternionRight);

/**
Returns the inverse of \a quaternion.
*/
TLMQuaternion TLMQuaternionInvert(TLMQuaternion quaternion);
@@ -0,0 +1,248 @@
//
// TLMMyo.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <CoreBluetooth/CoreBluetooth.h>
#import <UIKit/UIKit.h>
#import "TLMOrientationEvent.h"
#import "TLMAccelerometerEvent.h"
#import "TLMPose.h"
#import "TLMArmSyncEvent.h"

//---------------
// Notifications
//---------------

/**
@defgroup deviceNotifications NSNotificationCenter Device Constants
These are notifications posted by the device when new data is available.
They are posted by the default center on the main dispatch queue.
Data associated with the notification can be found in the notification's userInfo dictionary.
@see deviceNotificationDataKeys.
@{
*/

/**
Posted when a new orientation event is available from a TLMMyo. Notifications are posted at a rate of 50 Hz.
kTLMKeyOrientationEvent -> TLMOrientationEvent
*/
extern NSString *const TLMMyoDidReceiveOrientationEventNotification;

/**
Posted when a new accelerometer event is available from a TLMMyo. Notifications are posted at a rate of 50 Hz.
kTLMKeyAccelerometerEvent -> TLMAccelerometerEvent
*/
extern NSString *const TLMMyoDidReceiveAccelerometerEventNotification;

/**
Posted when a new gyroscope event is available from a TLMMyo. Notifications are posted at a rate of 50 Hz.
kTLMKeyGyroscopeEvent -> TLMGyroscopeEvent
*/
extern NSString *const TLMMyoDidReceiveGyroscopeEventNotification;

/**
Posted when a new pose is available from a TLMMyo.
kTLMKeyPose -> TLMPose
*/
extern NSString *const TLMMyoDidReceivePoseChangedNotification;

/**
Posted when a Myo armband is synced with an arm.
kTLMKeyArmSyncEvent -> TLMArmSyncEvent
*/
extern NSString *const TLMMyoDidReceiveArmSyncEventNotification;

/**
Posted when a Myo armband is moved or removed from the arm.
kTLMKeyArmUnsyncEvent -> TLMArmUnsyncEvent
*/
extern NSString *const TLMMyoDidReceiveArmUnsyncEventNotification;

/**
Posted when a Myo is unlocked.
kTLMKeyUnlockEvent -> TLMUnlockEvent
*/
extern NSString *const TLMMyoDidReceiveUnlockEventNotification;

/**
Posted when a Myo is locked.
kTLMKeyLockEvent -> TLMLockEvent
*/
extern NSString *const TLMMyoDidReceiveLockEventNotification;

/** @} */

/**
@defgroup deviceNotificationDataKeys NSNotificationCenter Device Data Key Constants
These keys correspond to data stored in a notifications userInfo dictionary.
@see deviceNotifications
@{
*/

/**
NSNotification userInfo key for a TLMOrientationEvent object.
*/
extern NSString *const kTLMKeyOrientationEvent;

/**
NSNotification userInfo key for a TLMAccelerometerEvent object.
*/
extern NSString *const kTLMKeyAccelerometerEvent;

/**
NSNotification userInfo key for a TLMGyroscopeEvent object.
*/
extern NSString *const kTLMKeyGyroscopeEvent;

/**
NSNotification userInfo key for a TLMPose object.
*/
extern NSString *const kTLMKeyPose;

/**
NSNotification userInfo key for a TLMArmSyncEvent object.
*/
extern NSString *const kTLMKeyArmSyncEvent;

/**
NSNotification userInfo key for a TLMArmUnsyncEvent object.
*/
extern NSString *const kTLMKeyArmUnsyncEvent;

/**
NSNotification userInfo key for a TLMUnlockEvent object.
*/
extern NSString *const kTLMKeyUnlockEvent;

/**
NSNotification userInfo key for a TLMLockEvent object.
*/
extern NSString *const kTLMKeyLockEvent;

/** @} */

//--------
// TLMMyo
//--------

/**
Represents a Myo.
Do not implement NSCopying. You should not be able to copy TLMMyo. Do not subclass. Do not maintain strong
references to instances of this class, as this can cause unexpected behaviour. TLMHub will keep track of TLMMyos.
*/
@interface TLMMyo : NSObject

/**
Represents the connection state of a TLMMyo.
*/
typedef NS_ENUM (NSInteger, TLMMyoConnectionState) {
TLMMyoConnectionStateConnected, /**< TLMMyo is connected */
TLMMyoConnectionStateConnecting, /**< TLMMyo is in the process of connecting */
TLMMyoConnectionStateDisconnected /**< TLMMyo is not connected */
};

/**
Represents the different types of vibrations a TLMMyo can make.
*/
typedef NS_ENUM (NSInteger, TLMVibrationLength) {
TLMVibrationLengthShort, /**< A vibration lasting a small amount of time */
TLMVibrationLengthMedium, /**< A vibration lasting a moderate amount of time */
TLMVibrationLengthLong /**< A vibration lasting a long amount of time */
};

/**
Represents the different ways to unlock a TLMMyo.
*/
typedef NS_ENUM (NSInteger, TLMUnlockType) {
TLMUnlockTypeTimed, /**< Unlock now and re-lock after a fixed time. */
TLMUnlockTypeHold /**< Unlock now and remain unlocked until a lock command is received. */
};

/**
The name of the TLMMyo.
*/
@property (nonatomic, strong, readonly) NSString *name;

/**
The identifier for the TLMMyo.
*/
@property (nonatomic, strong, readonly) NSUUID *identifier;

/**
A snapshot of the current state of the TLMMyo.
*/
@property (nonatomic, readonly) TLMMyoConnectionState state;

/**
The current lock state of the Myo.
*/
@property (nonatomic, readonly) BOOL isLocked;

/**
The current pose being recognized by the TLMMyo.
*/
@property (nonatomic, strong, readonly) TLMPose *pose;

/**
The current orientation of the Myo.
*/
@property (nonatomic, strong, readonly) TLMOrientationEvent *orientation;

/**
The arm that the TLMMyo is currently being worn on.
*/
@property (nonatomic, readonly) TLMArm arm;

/**
The direction of the +x axis of the TLMMyo.
*/
@property (nonatomic, readonly) TLMArmXDirection xDirection;

- (instancetype)init __attribute__((unavailable("init not available")));

/**
Performs an asynchronous read of the signal strength, passing the resulting value in the result block. The resultBlock
is executed on the main thread.
@param resultBlock The resulting signal strength is passed into this block.
*/
- (void)readSignalStrengthWithResult:(void(^)(NSNumber * signalStrength))resultBlock;

/**
Engage the TLMMyo's built in vibration motor.
@param length The amount of time the vibration motor will be active.
*/
- (void)vibrateWithLength:(TLMVibrationLength)length;

/**
Inform the Myo to react to a user action.
*/
- (void)indicateUserAction;

/**
Force the TLMMyo to unlock with the specified type. If the TLMMyo is already unlocked, calling this method will extend
it.
@see TLMUnlockType
*/
- (void)unlockWithType:(TLMUnlockType)type;

/**
Force the TLMMyo to lock again. Has no effect if the TLMMyo is already locked.
*/
- (void)lock;

@end
@@ -0,0 +1,35 @@
//
// TLMOrientationEvent.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <Foundation/Foundation.h>
#import "TLMMath.h"
#import "TLMEulerAngles.h"

@class TLMMyo;

/**
Represents the orientation of a TLMMyo. The orientation is represented via a quaternion.
*/
@interface TLMOrientationEvent : NSObject <NSCopying>

/**
The TLMMyo whose orientation changed.
*/
@property (nonatomic, weak, readonly) TLMMyo *myo;

/**
Orientation representation as a normalized quaternion.
*/
@property (nonatomic, readonly) TLMQuaternion quaternion;

/**
The timestamp associated with the orientation.
*/
@property (nonatomic, strong, readonly) NSDate *timestamp;

@end
@@ -0,0 +1,42 @@
//
// TLMPose.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <Foundation/Foundation.h>

@class TLMMyo;

//---------
// TLMPose
//---------

/** Represents a hand pose detected by a TLMMyo. */
@interface TLMPose : NSObject <NSCopying>

/**
Represents different hand poses.
*/
typedef NS_ENUM (NSInteger, TLMPoseType) {
TLMPoseTypeRest, /**< Rest pose.*/
TLMPoseTypeFist, /**< User is making a fist.*/
TLMPoseTypeWaveIn, /**< User has an open palm rotated towards the posterior of their wrist.*/
TLMPoseTypeWaveOut, /**< User has an open palm rotated towards the anterior of their wrist.*/
TLMPoseTypeFingersSpread, /**< User has an open palm with their fingers spread away from each other.*/
TLMPoseTypeDoubleTap, /**< User taps their thumb to their middle finger twice.*/
TLMPoseTypeUnknown = 0xffff /**< Unknown pose.*/
};

/** The TLMMyo posting the pose. */
@property (nonatomic, weak, readonly) TLMMyo *myo;

/** The pose being recognized. */
@property (nonatomic, readonly) TLMPoseType type;

/** The time the pose was recognized. */
@property (nonatomic, strong, readonly) NSDate *timestamp;

@end
@@ -0,0 +1,40 @@
//
// TLMSettingsViewController.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <UIKit/UIKit.h>

/**
A View Controller which scans and connects to TLMMyos in the vicinity. TLMMyos that are connected through this
view controller will be paired as well. Needs to be embedded in a UINavigationController. To connect, tap a TLMMyo
in the list. To disconnect, tap a connected TLMMyo. To remove the device from the list, slide the cell to the left.
Once removed, the device can be discovered again by clicking scan. If a Myo's firmware verison is out of date, a
yellow circle will appear beside it. This Myo cannot be connected until the firmware version is updated.
*/
@interface TLMSettingsViewController : UITableViewController

/**
Returns a UINavigationController that contains the TLMSettingsViewController. You can present this modally.
*/
+ (UINavigationController *)settingsInNavigationController;

/**
Returns a UIPopoverController that contains a TLMSettingsViewController. You must maintain a strong reference to the
popover once it is presented.
*/
+ (UIPopoverController *)settingsInPopoverController;

- (instancetype)initWithCoder:(NSCoder *)aDecoder
__attribute__((unavailable("initWithCoder not available. Use init.")));

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
__attribute__((unavailable("initWithNibName not available. Use init.")));

- (instancetype)initWithStyle:(UITableViewStyle)style
__attribute__((unavailable("initWithStyle not available. Use init.")));

@end
@@ -0,0 +1,15 @@
//
// TLMUnlockEvent.h
// MyoKit
//
// Copyright (C) 2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt.
//

#import <Foundation/Foundation.h>

@class TLMMyo;

@interface TLMUnlockEvent : NSObject <NSCopying>

@end
Binary file not shown.

Large diffs are not rendered by default.

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>D2C8C0B9-A728-432B-B58A-BF2E1FCC93E4</string>
<key>IDESourceControlProjectName</key>
<string>Runners Beat</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>97178DEF9892538B8DC29BA20251536D0DB83597</key>
<string>https://github.com/Danappelxx/Runners-Beat.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>Runners Beat/Runners Beat.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>97178DEF9892538B8DC29BA20251536D0DB83597</key>
<string>../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/Danappelxx/Runners-Beat.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>97178DEF9892538B8DC29BA20251536D0DB83597</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>97178DEF9892538B8DC29BA20251536D0DB83597</string>
<key>IDESourceControlWCCName</key>
<string>Runners-Beat</string>
</dict>
</array>
</dict>
</plist>
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C7751ABE311C007DAB8C"
BuildableName = "Runners BeatTests.xctest"
BlueprintName = "Runners BeatTests"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C7751ABE311C007DAB8C"
BuildableName = "Runners BeatTests.xctest"
BlueprintName = "Runners BeatTests"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Runners Beat.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>C671C75C1ABE311B007DAB8C</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C671C7751ABE311C007DAB8C</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C7751ABE311C007DAB8C"
BuildableName = "Runners BeatTests.xctest"
BlueprintName = "Runners BeatTests"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C7751ABE311C007DAB8C"
BuildableName = "Runners BeatTests.xctest"
BlueprintName = "Runners BeatTests"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Runners Beat.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>C671C75C1ABE311B007DAB8C</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C671C7751ABE311C007DAB8C</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Runners Beat/MyoViewController.m"
timestampString = "448685823.792821"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "121"
endingLineNumber = "121"
landmarkName = "-selectNextSong"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Runners Beat/MyoViewController.m"
timestampString = "448685823.792821"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "122"
endingLineNumber = "122"
landmarkName = "-viewDidAppear:"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Runners Beat/MyoViewController.m"
timestampString = "448685782.077524"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "49"
endingLineNumber = "49"
landmarkName = "-viewDidLoad"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Runners Beat/MyoViewController.m"
timestampString = "448691255.303274"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "14"
endingLineNumber = "14"
landmarkName = "@interface MyoViewController()"
landmarkType = "2">
<Locations>
<Location
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "-[MyoViewController didConnectToMyo]"
moduleName = "Runners Beat"
urlString = "file:///Users/kasandell/Desktop/Runners-Beat/Runners%20Beat/Runners%20Beat/MyoViewController.m"
timestampString = "448691255.349307"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "16"
endingLineNumber = "16"
offsetFromSymbolStart = "16">
</Location>
<Location
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "-[MyoViewController setDidConnectToMyo:]"
moduleName = "Runners Beat"
urlString = "file:///Users/kasandell/Desktop/Runners-Beat/Runners%20Beat/Runners%20Beat/MyoViewController.m"
timestampString = "448691255.349721"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "16"
endingLineNumber = "16"
offsetFromSymbolStart = "10">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C7751ABE311C007DAB8C"
BuildableName = "Runners BeatTests.xctest"
BlueprintName = "Runners BeatTests"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C7751ABE311C007DAB8C"
BuildableName = "Runners BeatTests.xctest"
BlueprintName = "Runners BeatTests"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C671C75C1ABE311B007DAB8C"
BuildableName = "Runners Beat.app"
BlueprintName = "Runners Beat"
ReferencedContainer = "container:Runners Beat.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Runners Beat.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>C671C75C1ABE311B007DAB8C</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C671C7751ABE311C007DAB8C</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,17 @@
//
// AppDelegate.h
// Runners Beat
//
// Created by Kyle Sandell on 3/21/15.
// Copyright (c) 2015 Kyle Sandell. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;


@end

@@ -0,0 +1,132 @@
////
//// AppDelegate.m
//// Run Beat
////
//// Created by Kyle Sandell on 3/21/15.
//// Copyright (c) 2015 Kyle Sandell. All rights reserved.
//
//
//#import "AppDelegate.h"
//
//@interface AppDelegate ()
//
//@end
//
//@implementation AppDelegate
//
//
//- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// // Override point for customization after application launch.
// [[TLMHub sharedHub] setLockingPolicy:TLMLockingPolicyNone];
//
//// [self modalPresentMyoSettings];
// return YES;
//}
//
//- (void)applicationWillResignActive:(UIApplication *)application {
// // 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.
// // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
//}
//
//- (void)applicationDidEnterBackground:(UIApplication *)application {
// // 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.
// // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
//}
//
//- (void)applicationWillEnterForeground:(UIApplication *)application {
// // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
//}
//
//- (void)applicationDidBecomeActive:(UIApplication *)application {
// // 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.
//}
//
//- (void)applicationWillTerminate:(UIApplication *)application {
// // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
//}
//
//
//@end


// TutorialApp
// Created by Spotify on 04/09/14.
// Copyright (c) 2014 Spotify. All rights reserved.

#import <Spotify/Spotify.h>
#import "AppDelegate.h"
#import <MyoKit/MyoKit.h>

static NSString * const kClientId = @"b25dc953e6ce49ef8c36fb32813177d8";
static NSString * const kCallbackURL = @"runners-beat-login://callback";
static NSString * const kTokenSwapServiceURL = @"http://localhost:1234/swap";

@interface AppDelegate ()

@property (nonatomic, readwrite) SPTAudioStreamingController *player;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

SPTAuth *auth = [SPTAuth defaultInstance];
NSURL *loginURL = [auth loginURLForClientId:kClientId declaredRedirectURL:[NSURL URLWithString:kCallbackURL] scopes:@[SPTAuthStreamingScope]];

[application performSelector:@selector(openURL:) withObject:loginURL afterDelay:0.1];

// Override point for customization after application launch.
// [[TLMHub sharedHub] setLockingPolicy:TLMLockingPolicyNone];

// [self modalPresentMyoSettings];
return YES;

return YES;
}

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {

if([[SPTAuth defaultInstance]canHandleURL:url withDeclaredRedirectURL:[NSURL URLWithString:kCallbackURL]]) {
[[SPTAuth defaultInstance] handleAuthCallbackWithTriggeredAuthURL:url tokenSwapServiceEndpointAtURL:[NSURL URLWithString:kTokenSwapServiceURL] callback:^(NSError *error, SPTSession *session) {

if (error != nil) {
NSLog(@"** Auth error: %@", error);
return;
}

[self playUsingSession:session];
}];
return YES;
}

return NO;
}

-(void)playUsingSession:(SPTSession *)session {

if (self.player == nil) {
self.player = [[SPTAudioStreamingController alloc] initWithClientId:kClientId];
}

[self.player loginWithSession:session callback:^(NSError *error) {

[self.player loginWithSession:session callback:^(NSError *error) {

if (error != nil) {
NSLog(@"*** Enabling playback got error: %@", error);
return;
}

[SPTRequest requestItemAtURI:[NSURL URLWithString:@"spotify:album:4L1HDyfdGIkACuygkt07T7"] withSession:nil callback:^(NSError *error, SPTAlbum *album){
if (error != nil) {
NSLog(@"*** Album lookup got error %@", error);
return;
}
[self.player playTrackProvider:album callback:nil];
}];
}];
}];
}

@end
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 Kyle Sandell. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Runners Beat" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1514" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
</dependencies>
<scenes>
<!--Myo View Controller-->
<scene sceneID="ufC-wZ-h7g">
<objects>
<viewController id="vXZ-lx-hvc" customClass="MyoViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="jyV-Pf-zRb"/>
<viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9z0-5L-jIA">
<rect key="frame" x="277" y="285" width="46" height="30"/>
<state key="normal" title="Button">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<variation key="heightClass=regular-widthClass=compact" fixedFrame="YES">
<rect key="frame" x="177" y="385" width="46" height="30"/>
</variation>
<connections>
<action selector="sendGetRequest:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="f3d-L1-Ybp"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="599" y="291"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,38 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>Kyle-Sandell.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Spotify Auth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>runners-beat-login</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1,14 @@
// ViewController.h
// Runners Beat
//
// Created by Kyle Sandell on 3/21/15.
// Copyright (c) 2015 Kyle Sandell. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface MyoViewController : UIViewController


@end

@@ -0,0 +1,358 @@
//
// ViewController.m
// Run Beat
//
// Created by Kyle Sandell on 3/21/15.
// Copyright (c) 2015 Kyle Sandell. All rights reserved.
//

#import <MyoKit/MyoKit.h>
//#import <Spotify/Spotify.h>
#import "MyoViewController.h"
#import "AppDelegate.h"
#import <AVFoundation/AVFoundation.h>
#import <Spotify/Spotify.h>

@interface MyoViewController ()

@property int stepsAverage; //step averagignggggg
@property int averageCount;
@property int lastStep;
@property int overflowCheck;
@property BOOL didConnectToMyo;//check if connected to myo
@property (nonatomic, retain) TLMMyo *myo;//myo instance
@property TLMVector3 initialVector;//dont matter
@property TLMVector3 secondVector;//domt matter
@property int numVectorsTaken;//dont matter
@property UILabel *BPM;//this is the label where

@property (nonatomic, retain) NSDate *firstStep;//dont matter
@property (nonatomic, retain) NSDate *thirdStep;//dont matter
@property int steps;//dont matter
@property float timeBetween;//dont matter
//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS
@property int stepsPerMinute;//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS//THIS IS YOUR BPM YOU IDIOTS
//THIS IS YOUR BPM YOU IDIOTS//THIS IS YOUR BPM YOU IDIOTS
@property NSString *clientID;//doesnt need any thing
@property NSString *clientSecret;//doesnt do anything
@property BOOL musicIsPaused;//check if music is paused or not
@property (nonatomic, retain) UITextField *calibrationField;//text field
@property (nonatomic, retain) NSUserDefaults *defaults;//where calibration val is stored
@property (nonatomic, retain) UILabel *calibLabel;//text field
@property (nonatomic, retain) UIImageView *albumArtwork;//imageview to show artwork
@property (nonatomic, retain) UIImage *albumWorkImage;//image to load into imageview with artwork
@property (nonatomic, retain) SPTSession *session;//session
@property (nonatomic, retain) SPTAudioStreamingController *streamer;//stream music

//@property (nonatomic, retain) AVAudio
@property float calibrationValue;//calibration value for miyo

@end
//go to line 216, 95, 107
@implementation MyoViewController


- (IBAction)sendGetRequest:(UIButton *)sender {

NSInteger tempminsteps = self.stepsPerMinute - 10;
NSInteger tempmaxsteps = self.stepsPerMinute + 10;

if(tempminsteps < 30) {
tempminsteps = 30;
}

if(tempmaxsteps < 50) {
tempmaxsteps = 50;
}

if(tempmaxsteps > 250)
{
tempmaxsteps = 250;
tempminsteps = 230;
}

NSString *minsteps = [NSString stringWithFormat: @"%ld", (long)tempminsteps];
NSString *maxsteps = [NSString stringWithFormat: @"%ld", (long)tempmaxsteps];

NSLog(@"%@",minsteps);
NSLog(@"%@",maxsteps);

NSString *baseUrl = @"http://developer.echonest.com/api/v4/song/search?api_key=";
NSString *apikey = @"8C5RHDLARNPQQW7FZ";
NSString *urlQueries = @"&format=json&results=1&";
NSString *minTempo = (@"min_tempo=%@", minsteps);
NSString *inBetween = (@"&");
NSString *maxTempo = (@"max_tempo=%@", maxsteps);
NSString *buckets = @"&bucket=audio_summary&bucket=id:spotify";
// allows for customization
NSString *serverAddress=[NSString stringWithFormat:@"%@%@%@%@%@%@%@", baseUrl, apikey, urlQueries, minTempo, inBetween, maxTempo, buckets];

NSMutableURLRequest *request =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverAddress]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10
];

[request setHTTPMethod: @"GET"];

NSError *requestError;
NSURLResponse *urlResponse = nil;


NSData *responseDataJSON = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];

// NSString *response = [[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
//
// NSLog(@"%@", response);

NSError *error;
NSMutableDictionary *responseData = [NSJSONSerialization
JSONObjectWithData:responseDataJSON
options:NSJSONReadingMutableContainers
error:&error];
NSLog(@"%@", responseData);


// NSLog(@"%@", responseData[@"response"][@"artists"][0][@"@%", @"foreign_ids"][0][@"catalog"]);
NSString *foreign_ids = [NSString stringWithFormat:@"%@", responseData[@"response"][@"songs"][0]];
NSLog(@"%@", foreign_ids);


}

- (void)viewDidLoad {
[super viewDidLoad];
self.defaults=[NSUserDefaults standardUserDefaults];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(dismissKeyboard)];

[self.view addGestureRecognizer:tap];
bool havePrintBoxesOnTop=true;//THIS LINE IS SO IMPORTANT, make this true to show calibration and bpm, false to not show anything
self.musicIsPaused=0;
if(havePrintBoxesOnTop){
self.calibrationField=[[UITextField alloc] initWithFrame:CGRectMake((12+140), 24, 100, 24)];
[self.view addSubview:self.calibrationField];
if ([self.defaults objectForKey:@"Calibration"]!=NULL) {
self.calibrationValue=[[self.defaults objectForKey:@"Calibration"] floatValue];
[self.calibrationField setText:[NSString stringWithFormat:@"%f", self.calibrationValue]];
}
else{
self.calibrationValue=0.09;
[self.defaults setObject:[NSNumber numberWithFloat:self.calibrationValue] forKey:@"Calibration"];
[self.calibrationField setText:[NSString stringWithFormat:@"%f", self.calibrationValue]];
}
//[self.BPM initWithFrame:CGRectMake((self.view.frame.size.width/2), (self.view.frame.size.height/2), 150, 50)];
self.BPM=[[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width-(self.view.frame.size.width*.25), 24, 150, 24)];
[self.BPM setText:@"BPM: 0"];
//self.BPM.frame=CGRectMake();
[self.view addSubview:self.BPM];

self.calibLabel=[[UILabel alloc] initWithFrame:CGRectMake(12, 24, 140, 24)];
[self.calibLabel setText:@"Calibration Level: "];
[self.view addSubview:self.calibLabel];
}

self.albumWorkImage=[[UIImage alloc] init];
self.albumArtwork=[[UIImageView alloc] initWithFrame:CGRectMake(self.view.center.x-150, self.view.center.y-150, 300, 300)];
[self.view addSubview:self.albumArtwork];


self.clientID=@"b25dc953e6ce49ef8c36fb32813177d8";
self.clientSecret=@"ff3e24c8633c4176ab30f5c748e23db8";
// Do any additional setup after loading the view, typically from a nib.
self.steps=0;
self.timeBetween=0;

[self holdUnlockForMyo:self.myo];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceivePoseChange:)
name:TLMMyoDidReceivePoseChangedNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didRecieveAccelerometerChange:)
name:TLMMyoDidReceiveAccelerometerEventNotification
object:nil];
[self.defaults synchronize];

}

-(void)dismissKeyboard {
[self.calibrationField resignFirstResponder];
self.calibrationValue=[self.calibrationField.text floatValue];
[self.defaults setObject:[NSNumber numberWithFloat:self.calibrationValue] forKey:@"Calibration"];
}


-(void)playPause{//pause/play
//insert code to play or pause music

if(self.musicIsPaused)
{
//play music
}
else{
//pause music
}

}
-(void)skip{//skip music
//insert code to skip music
//skip
}

-(void)selectNextSong{//pcik the next song
//pick song here
//set album artwork here
//start playing song here
}

-(void)viewDidAppear: (BOOL)animated{

if (!self.didConnectToMyo) {
[self modalPresentMyoSettings];
self.didConnectToMyo=TRUE;
}
}

- (void)holdUnlockForMyo:(TLMMyo *)myo {
[myo unlockWithType:TLMUnlockTypeHold];
}

- (void)endHoldUnlockForMyo:(TLMMyo *)myo immediately:(BOOL)immediately {
if (immediately) {
[myo lock];
} else {
[myo unlockWithType:TLMUnlockTypeTimed];
}
}

- (void)didReceivePoseChange:(NSNotification*)notification {
TLMPose *pose = notification.userInfo[kTLMKeyPose];
if(pose.type==TLMPoseTypeFist)
{
NSLog(@"fist");
[self playPause];
self.musicIsPaused= !self.musicIsPaused;
}
if(pose.type==TLMPoseTypeWaveIn || pose.type==TLMPoseTypeWaveOut)
{
NSLog(@"Wave in/ wave out");
[self skip];
}

//TODO: do something with the pose object.
// NSLog(@"HELLO FROM POSE CHANGE");
}

- (void)didRecieveAccelerometerChange:(NSNotification*)notification {
TLMAccelerometerEvent *accel = notification.userInfo[kTLMKeyAccelerometerEvent];
TLMVector3 accelVector=accel.vector;
self.secondVector=self.initialVector;
self.initialVector=accelVector;
// NSMutableArray *averageDataList = [NSMutableArray arrayWithCapacity: 50];
NSInteger averageDataList[50];
int summation;
int overflowCheck = 1;
for (int i = 0; i < 50; i ++)
{
averageDataList[i] = 0;
}
int stepCount;
if([self dotProduct:self.initialVector secondVector:self.secondVector]<self.calibrationValue)
{
self.steps++;
if (self.steps==1) {
self.firstStep=[NSDate date];
self.timeBetween=0;
}
else if (self.steps==3){
self.thirdStep=[NSDate date];
self.steps=0;
self.timeBetween=[self getIntervalBetweenTimes:self.firstStep date2:self.thirdStep];
int stepsMin=[self getApproxStepsPerMin:self.timeBetween];
self.steps=1;
self.firstStep=self.thirdStep;

NSLog(@"Steps per minute: %d", self.stepsPerMinute); // REMOVE LATER!!!! (DONT MESS WITH KYLE)
if (stepsMin<300) {
self.stepsPerMinute=stepsMin;
stepCount = stepsMin;
if (stepCount != self.lastStep){
averageDataList[self.averageCount] = stepCount;
if (self.averageCount < 50){
self.averageCount++;
overflowCheck = 0;
} else {
self.averageCount = 0;
}
for (int i = 0; i < 50; i++){
summation += averageDataList[i];
}
self.averageCount = summation/(50-((self.averageCount)*self.overflowCheck));
self.lastStep = stepCount;
}
NSLog(@"Steps min (variable): %i",stepsMin);
NSLog(@"%f",self.timeBetween/2);
self.BPM.text=[NSString stringWithFormat:@"BPM: %i ",stepsMin];
}
//NSLog(@"%i",stepsMin);
}
}

//TODO: do something with the pose object.
// NSLog(@"HELLO FROM ACCELEROMETER CHANGE");
}
-(float)getIntervalBetweenTimes:(NSDate *)date1 date2:(NSDate *)date2
{
NSTimeInterval secondsBetween = [date2 timeIntervalSinceDate:date1];
float ret=secondsBetween;
return ret;
}

-(int)getApproxStepsPerMin:(float)interval{
float secondsPerStep=(interval);//how long one step takes
int stepsMin=60/secondsPerStep;
return stepsMin;
}

-(float)dotProduct:(TLMVector3)vector secondVector:(TLMVector3)secondVector
{
float product=((vector.x*secondVector.x)+(vector.y*secondVector.y)+(vector.z*secondVector.z));
return product;
}


- (void)modalPresentMyoSettings {
UINavigationController *settings = [TLMSettingsViewController settingsInNavigationController];

[self presentViewController:settings animated:YES completion:nil];
}
- (void)pushMyoSettings {
TLMSettingsViewController *settings = [[TLMSettingsViewController alloc] init];

[self.navigationController pushViewController:settings animated:YES];
[self presentViewController:self.navigationController animated:true completion:nil];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}



-(void)viewDidUnload{
[super viewDidUnload];
[self endHoldUnlockForMyo:self.myo immediately:YES];
}

@end
@@ -0,0 +1,3 @@
play pause is to play or pause the music. make sure to see if the music is playing or paused already

.
@@ -0,0 +1,16 @@
//
// main.m
// Runners Beat
//
// Created by Kyle Sandell on 3/21/15.
// Copyright (c) 2015 Kyle Sandell. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>Kyle-Sandell.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,40 @@
//
// Runners_BeatTests.m
// Runners BeatTests
//
// Created by Kyle Sandell on 3/21/15.
// Copyright (c) 2015 Kyle Sandell. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>

@interface Runners_BeatTests : XCTestCase

@end

@implementation Runners_BeatTests

- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}

- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}

- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, @"Pass");
}

- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}

@end
@@ -0,0 +1,92 @@
//
// SPTAlbum.h
// Basic Auth
//
// Created by Daniel Kennett on 19/11/2013.
/*
Copyright 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import <Foundation/Foundation.h>
#import "SPTJSONDecoding.h"
#import "SPTRequest.h"
#import "SPTTypes.h"
#import "SPTPartialAlbum.h"

@class SPTImage;
@class SPTPartialArtist;
@class SPTListPage;

/** This class represents an album on the Spotify service. */
@interface SPTAlbum : SPTPartialAlbum <SPTJSONObject, SPTTrackProvider>

///----------------------------
/// @name Requesting Albums
///----------------------------

/** Request the album at the given Spotify URI.
@note This method takes Spotify URIs in the form `spotify:*`, NOT HTTP URLs.
@param uri The Spotify URI of the album to request.
@param session An authenticated session. Can be `nil`.
@param block The block to be called when the operation is complete. The block will pass a Spotify SDK metadata object on success, otherwise an error.
*/
+(void)albumWithURI:(NSURL *)uri session:(SPTSession *)session callback:(SPTRequestCallback)block;

/** Request multiple albums given an array of Spotify URIs.
@note This method takes Spotify URIs in the form `spotify:*`, NOT HTTP URLs.
@param uris An array of Spotify URIs.
@param session An authenticated session. Can be `nil`.
@param block The block to be called when the operation is complete. The block will pass an array of Spotify SDK metadata object on success, otherwise an error.
*/
+(void)albumsWithURIs:(NSArray *)uris session:(SPTSession *)session callback:(SPTRequestCallback)block;

/** Checks if the Spotify URI is a valid Spotify Album URI.
@note This method takes Spotify URIs in the form `spotify:*`, NOT HTTP URLs.
@param uri The Spotify URI to check.
*/
+(BOOL)isAlbumURI:(NSURL*)uri;

///----------------------------
/// @name Properties
///----------------------------

/** Any external IDs of the album, such as the UPC code. */
@property (nonatomic, readonly, copy) NSDictionary *externalIds;

/** An array of artists for this album, as `SPTPartialArtist` objects. */
@property (nonatomic, readonly) NSArray *artists;

/** The tracks contained by this album, as a page of `SPTPartialTrack` objects. */
@property (nonatomic, readonly) SPTListPage *firstTrackPage;

/** The release year of the album if known, otherwise `0`. */
@property (nonatomic, readonly) NSInteger releaseYear;

/** Day-accurate release date of the track if known, otherwise `nil`. */
@property (nonatomic, readonly) NSDate *releaseDate;

/** Returns a list of genre strings for the album. */
@property (nonatomic, readonly, copy) NSArray *genres;

/** The popularity of the album as a value between 0.0 (least popular) to 100.0 (most popular). */
@property (nonatomic, readonly) double popularity;

@end
@@ -0,0 +1,134 @@
//
// SPTArtist.h
// Basic Auth
//
// Created by Daniel Kennett on 19/11/2013.
/*
Copyright 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import <Foundation/Foundation.h>
#import "SPTJSONDecoding.h"
#import "SPTRequest.h"
#import "SPTPartialArtist.h"
#import "SPTAlbum.h"

@class SPTImage;

/** This class represents an artist on the Spotify service. */
@interface SPTArtist : SPTPartialArtist<SPTJSONObject>

///----------------------------
/// @name Requesting Artists
///----------------------------

/** Request the artist at the given Spotify URI.
@note This method takes Spotify URIs in the form `spotify:*`, NOT HTTP URLs.
@param uri The Spotify URI of the artist to request.
@param session An authenticated session. Can be `nil`.
@param block The block to be called when the operation is complete. The block will pass a Spotify SDK metadata object on success, otherwise an error.
*/
+(void)artistWithURI:(NSURL *)uri session:(SPTSession *)session callback:(SPTRequestCallback)block;

/** Request multiple artists given an array of Spotify URIs.
@note This method takes an array Spotify URIs in the form `spotify:*`, NOT HTTP URLs.
@param uris An array of Spotify URIs.
@param session An authenticated session. Can be `nil`.
@param block The block to be called when the operation is complete. The block will pass an array of `SPTArtist` objects on success, otherwise an error.
*/
+(void)artistsWithURIs:(NSArray *)uris session:(SPTSession *)session callback:(SPTRequestCallback)block;

/** Checks if the Spotify URI is a valid Spotify Artist URI.
@note This method takes Spotify URIs in the form `spotify:*`, NOT HTTP URLs.
@param uri The Spotify URI to check.
*/
+(BOOL)isArtistURI:(NSURL*)uri;

///----------------------------
/// @name Properties
///----------------------------

/** Any external IDs of the track, such as the ISRC code. */
@property (nonatomic, readonly, copy) NSDictionary *externalIds;

/** Returns a list of genre strings for the artist. */
@property (nonatomic, readonly, copy) NSArray *genres;

/** Returns a list of artist images in various sizes, as `SPTImage` objects. */
@property (nonatomic, readonly, copy) NSArray *images;

/** Convenience method that returns the smallest available artist image. */
@property (nonatomic, readonly) SPTImage *smallestImage;

/** Convenience method that returns the largest available artist image. */
@property (nonatomic, readonly) SPTImage *largestImage;

/** The popularity of the artist as a value between 0.0 (least popular) to 100.0 (most popular). */
@property (nonatomic, readonly) double popularity;

/** The number of followers this artist has. */
@property (nonatomic, readonly) long followerCount;

///----------------------------
/// @name Requesting Artist Catalogs
///----------------------------

/** Request the artist's albums.
The territory parameter of this method can be `nil` to specify "any country", but expect a lot of
duplicates as the Spotify catalog often has different albums for each country. Pair this with an
`SPTUser`'s `territory` property for best results.
@param type The type of albums to get.
@param session A valid `SPTSession`.
@param territory An ISO 3166 country code of the territory to get albums for, or `nil`.
@param block The block to be called when the operation is complete. The block will pass an
`SPTListPage` object on success, otherwise an error.
*/
-(void)requestAlbumsOfType:(SPTAlbumType)type
withSession:(SPTSession *)session
availableInTerritory:(NSString *)territory
callback:(SPTRequestCallback)block;

/** Request the artist's top tracks.
The territory parameter of this method is required. Pair this with an
`SPTUser`'s `territory` property for best results.
@param territory An ISO 3166 country code of the territory to get top tracks for.
@param session A valid `SPTSession`.
@param block The block to be called when the operation is complete. The block will pass an
`NSArray` object containing `SPTTrack`s on success, otherwise an error.
*/
-(void)requestTopTracksForTerritory:(NSString *)territory
withSession:(SPTSession *)session
callback:(SPTRequestCallback)block;

/** Request the artist's related artists.
@param session A valid `SPTSession`.
@param block The block to be called when the operation is complete. The block will pass an
`NSArray` object containing `SPTArtist`s on success, otherwise an error.
*/
-(void)requestRelatedArtists:(SPTSession *)session
callback:(SPTRequestCallback)block;

@end

Large diffs are not rendered by default.

@@ -0,0 +1,76 @@
//
// SPTAudioStreamingController.h
// Spotify iOS SDK
//
// Created by Daniel Kennett on 16/10/2013.
/*
Copyright 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import <Foundation/Foundation.h>

/** The operation was successful. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeNoError;

/** The operation failed due to an unspecified issue. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeFailed;

/** Audio streaming could not be initialised. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeInitFailed;

/** Audio streaming could not be initialized because of an incompatible API version. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeWrongAPIVersion;

/** An unexpected NULL pointer was passed as an argument to a function. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeNullArgument;

/** An unexpected argument value was passed to a function. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeInvalidArgument;

/** Audio streaming has not yet been initialised for this application. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeUninitialized;

/** Audio streaming has already been initialised for this application. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeAlreadyInitialized;

/** Login to Spotify failed because of invalid credentials. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeBadCredentials;

/** The operation requires a Spotify Premium account. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeNeedsPremium;

/** The Spotify user is not allowed to log in from this country. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeTravelRestriction;

/** The application has been banned by Spotify. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeApplicationBanned;

/** An unspecified login error occurred. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeGeneralLoginError;

/** The operation is not supported. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeUnsupported;

/** The operation is not supported if the device is not the active playback device. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeNotActiveDevice;

/** An unspecified playback error occurred. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeGeneralPlaybackError;

/** The application is rate-limited if it requests the playback of too many tracks within a given amount of time. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodePlaybackRateLimited;

/** The track you're trying to play is unavailable for the current user, or was unable to start. */
FOUNDATION_EXPORT NSInteger const SPTErrorCodeTrackUnavailable;
@@ -0,0 +1,165 @@
//
// SPTAuth.h
// SPTAuth
//
// Created by Daniel Kennett on 27/08/2013.

#import <Foundation/Foundation.h>

/** Scope that lets you stream music. */
FOUNDATION_EXPORT NSString * const SPTAuthStreamingScope;

/** Scope that lets you read private playlists of the authenticated user. */
FOUNDATION_EXPORT NSString * const SPTAuthPlaylistReadPrivateScope;

/** Scope that lets you modify public playlists of the authenticated user. */
FOUNDATION_EXPORT NSString * const SPTAuthPlaylistModifyPublicScope;

/** Scope that lets you modify private playlists of the authenticated user. */
FOUNDATION_EXPORT NSString * const SPTAuthPlaylistModifyPrivateScope;

/** Scope that lets you read email from the authenticated user. */
FOUNDATION_EXPORT NSString * const SPTAuthUserReadEmailScope;

/** Scope that lets you read the private user information of the authenticated user. */
FOUNDATION_EXPORT NSString * const SPTAuthUserReadPrivateScope;

/** Scope that lets you read user's Your Music library. */
FOUNDATION_EXPORT NSString * const SPTAuthUserLibraryReadScope;

/** Scope that lets you modify user's Your Music library. */
FOUNDATION_EXPORT NSString * const SPTAuthUserLibraryModifyScope;

@class SPTSession;

/**
This class provides helper methods for authenticating users against the Spotify OAuth
authentication service.
*/
@interface SPTAuth : NSObject

typedef void (^SPTAuthCallback)(NSError *error, SPTSession *session);

///----------------------------
/// @name Convenience Getters
///----------------------------

/**
Returns a pre-created `SPTAuth` instance for convenience.
@return A pre-created default `SPTAuth` instance.
*/
+(SPTAuth *)defaultInstance;

///----------------------------
/// @name Starting Authentication
///----------------------------

/**
Returns a URL that, when opened, will begin the Spotify authentication process.
@warning You must open this URL with the system handler to have the auth process
happen in Safari. Displaying this inside your application is against the Spotify ToS.
@param clientId Your client ID as declared in the Spotify Developer Centre.
@param declaredURL Your callback URL as declared in the Spotify Developer Centre.
@return The URL to pass to `UIApplication`'s `-openURL:` method.
*/
-(NSURL *)loginURLForClientId:(NSString *)clientId declaredRedirectURL:(NSURL *)declaredURL;

/**
Returns a URL that, when opened, will begin the Spotify authentication process.
@warning You must open this URL with the system handler to have the auth process
happen in Safari. Displaying this inside your application is against the Spotify ToS.
@param clientId Your client ID as declared in the Spotify Developer Centre.
@param declaredURL Your callback URL as declared in the Spotify Developer Centre.
@param scopes The custom scopes to request from the auth API.
@return The URL to pass to `UIApplication`'s `-openURL:` method.
*/
-(NSURL *)loginURLForClientId:(NSString *)clientId declaredRedirectURL:(NSURL *)declaredURL scopes:(NSArray *)scopes;

/**
Returns a URL that, when opened, will begin the Spotify authentication process.
@warning You must open this URL with the system handler to have the auth process
happen in Safari. Displaying this inside your application is against the Spotify ToS.
@param clientId Your client ID as declared in the Spotify Developer Centre.
@param declaredURL Your callback URL as declared in the Spotify Developer Centre.
@param scopes The custom scopes to request from the auth API.
@param responseType Authentication response code type, defaults to "code", use "token" if you want to bounce directly to the app without refresh tokens.
@return The URL to pass to `UIApplication`'s `-openURL:` method.
*/
-(NSURL *)loginURLForClientId:(NSString *)clientId declaredRedirectURL:(NSURL *)declaredURL scopes:(NSArray *)scopes withResponseType:(NSString *)responseType;

///----------------------------
/// @name Handling Authentication Callback URLs
///----------------------------

/**
Find out if the given URL appears to be a Spotify authentication URL.
This method is useful if your application handles multiple URL types. You can pass every URL
you receive through here to filter them.
@param callbackURL The complete callback URL as triggered in your application.
@param declaredURL Your pre-defined callback URL as declared in the Spotify Developer Centre.
@return Returns `YES` if the callback URL appears to be a Spotify auth callback, otherwise `NO`.
*/
-(BOOL)canHandleURL:(NSURL *)callbackURL withDeclaredRedirectURL:(NSURL *)declaredURL;

/**
Check if "flip-flop" application authentication is supported.
@return YES if supported, NO otherwise.
*/
-(BOOL)supportsApplicationAuthentication;

/**
Check if Spotify application is installed.
@return YES if installed, NO otherwise.
*/
-(BOOL)spotifyApplicationIsInstalled;

/**
Handle a Spotify authentication callback URL, returning a Spotify username and OAuth credential.
This URL is obtained when your application delegate's `application:openURL:sourceApplication:annotation:`
method is triggered. Use `-[SPTAuth canHandleURL:withDeclaredRedirectURL:]` to easily filter out other URLs that may be
triggered.
@param url The complete callback URL as triggered in your application.
@param block The callback block to be triggered when authentication succeeds or fails.
*/
-(void)handleAuthCallbackWithTriggeredAuthURL:(NSURL *)url callback:(SPTAuthCallback)block;

/**
Handle a Spotify authentication callback URL, returning a Spotify username and OAuth credential.
This URL is obtained when your application delegate's `application:openURL:sourceApplication:annotation:`
method is triggered. Use `-[SPTAuth canHandleURL:withDeclaredRedirectURL:]` to easily filter out other URLs that may be
triggered.
@note This method requires that you have a Spotify token swap service running and available.
@param url The complete callback URL as triggered in your application.
@param tokenSwapURL The URL of your token swap service endpoint.
@param block The callback block to be triggered when authentication succeeds or fails.
*/
-(void)handleAuthCallbackWithTriggeredAuthURL:(NSURL *)url tokenSwapServiceEndpointAtURL:(NSURL *)tokenSwapURL callback:(SPTAuthCallback)block;

///----------------------------
/// @name Renewing Sessions
///----------------------------

/**
Request a new access token using an existing SPTSession object containing a refresh token.
@param session An SPTSession object with a valid refresh token.
@param endpointURL The URL of the service that requests an access token using the refresh token.
@param block The callback block that will be invoked when the request has been performed.
*/
-(void)renewSession:(SPTSession *)session withServiceEndpointAtURL:(NSURL *)endpointURL callback:(SPTAuthCallback)block;

@end
@@ -0,0 +1,90 @@
//
// SPTCircularBuffer.h
// Viva
//
// Created by Daniel Kennett on 4/9/11.
/*
Copyright 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/** This class is a simple implementation of a circular buffer, designed to match the behaviour of the iOS SDK.
This class gets around the problem of filling the buffer too far ahead by having a maximum size. Once that
size is reached, you cannot add more data without reading some out or clearing it and starting again. When
used with the iOS SDK, this isn't a problem as we can ask the library to re-deliver audio data at a later time.
*/

#import <Foundation/Foundation.h>

@interface SPTCircularBuffer : NSObject

/** Initialize a new buffer.
Initial size will be zero, with a maximum size as provided.
@param size The maximum size of the buffer, in bytes.
@return Returns the newly created SPTCircularBuffer.
*/
-(id)initWithMaximumLength:(NSUInteger)size;

/** Clears all data from the buffer. */
-(void)clear;

/** Attempt to copy new data into the buffer.
Data is copied using the following heuristic:
- If dataLength <= (maximumLength - length), copy all data.
- Otherwise, copy (maximumLength - length) bytes.
@param data A buffer containing the data to be copied in.
@param dataLength The length of the data, in bytes.
@return Returns the amount of data copied into the buffer, in bytes. If this number is
smaller than dataLength, only this number of bytes was copied in from the start of the given buffer.
*/
-(NSUInteger)attemptAppendData:(const void *)data ofLength:(NSUInteger)dataLength;

/** Attempt to copy new data into the buffer.
Data is copied using the following heuristic:
- If dataLength <= (maximumLength - length), copy all data.
- Otherwise, copy (maximumLength - length) bytes.
- Number of bytes copied will be rounded to the largest number less than dataLength that can be
integrally be divided by chunkSize.
@param data A buffer containing the data to be copied in.
@param dataLength The length of the data, in bytes.
@param chunkSize Ensures the number of bytes copies in is a multiple of this number.
@return Returns the amount of data copied into the buffer, in bytes. If this number is
smaller than dataLength, only this number of bytes was copied in from the start of the given buffer.
*/
-(NSUInteger)attemptAppendData:(const void *)data ofLength:(NSUInteger)dataLength chunkSize:(NSUInteger)chunkSize;

/** Read data out of the buffer into a pre-allocated buffer.
@param desiredLength The desired number of bytes to copy out.
@param outBuffer A pointer to a buffer, which must be malloc'ed with at least `desiredLength` bytes.
@return Returns the amount of data copied into the given buffer, in bytes.
*/
-(NSUInteger)readDataOfLength:(NSUInteger)desiredLength intoAllocatedBuffer:(void **)outBuffer;

/** Returns the amount of data currently in the buffer, in bytes. */
@property (readonly) NSUInteger length;

/** Returns the maximum amount of data that the buffer can hold, in bytes. */
@property (readonly, nonatomic) NSUInteger maximumLength;

@end
@@ -0,0 +1,10 @@
//
// SPTConnectButton.h
// Spotify iOS SDK
//
// Created by Daniel Kennett on 2013-09-10.

#import <UIKit/UIKit.h>

@interface SPTConnectButton : UIControl
@end
@@ -0,0 +1,155 @@
//
// SPTCoreAudioController.h
// Viva
//
// Created by Daniel Kennett on 04/02/2012.
/*
Copyright 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// This class encapsulates a Core Audio graph that includes
// an audio format converter, a mixer for iOS volume control and a standard output.
// Clients just need to set the various properties and not worry about the details.

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>

#if TARGET_OS_IPHONE
#import <UIKit/UIApplication.h>
#endif

@class SPTCoreAudioController;
@class SPTCoreAudioDevice;

/** Provides delegate callbacks for SPTCoreAudioController. */

@protocol SPTCoreAudioControllerDelegate <NSObject>

@optional

/** Called repeatedly during audio playback when audio is pushed to the system's audio output.
This can be used to keep track of how much audio has been played back for progress indicators and so on.
@param controller The SPTCoreAudioController that pushed audio.
@param audioDuration The duration of the audio that was pushed to the output device.
*/
-(void)coreAudioController:(SPTCoreAudioController *)controller didOutputAudioOfDuration:(NSTimeInterval)audioDuration;

@end

/** Provides an audio pipeline from SPTAudioStreamingController to the system's audio output. */

@interface SPTCoreAudioController : NSObject

///----------------------------
/// @name Control
///----------------------------

/**
Completely empties all audio that's buffered for playback.
This should be called when you need cancel all pending audio in order to,
for example, play a new track.
*/
-(void)clearAudioBuffers;

/**
Attempts to deliver the passed audio frames passed to the audio output pipeline.
@param audioFrames A buffer containing the audio frames.
@param frameCount The number of frames included in the buffer.
@param audioDescription A description of the audio contained in `audioFrames`.
@return Returns the number of frames actually delievered to the audio pipeline. If this is less than `frameCount`,
you need to retry delivery again later as the internal buffers are full.
*/
-(NSInteger)attemptToDeliverAudioFrames:(const void *)audioFrames ofCount:(NSInteger)frameCount streamDescription:(AudioStreamBasicDescription)audioDescription;

/** Returns the number of bytes in the audio buffer. */
-(uint32_t)bytesInAudioBuffer;

///----------------------------
/// @name Customizing the audio pipeline
///----------------------------

/**
Connects the given `AUNode` instances together to complete the audio pipeline for playback.
If you wish to customise the audio pipeline, you can do so by overriding this method and inserting your
own `AUNode` instances between `sourceNode` and `destinationNode`.
This method will be called whenever the audio pipeline needs to be (re)built.
@warning If you override this method and connect the nodes yourself, do not call the `super`
implementation. You can, however, conditionally decide whether to customise the queue and call `super`
if you want the default behaviour.
@param sourceOutputBusNumber The bus on which the source node will be providing audio data.
@param sourceNode The `AUNode` which will provide audio data for the graph.
@param destinationInputBusNumber The bus on which the destination node expects to receive audio data.
@param destinationNode The `AUNode` which will carry the audio data to the system's audio output.
@param graph The `AUGraph` containing the given nodes.
@param error A pointer to an NSError instance to be filled with an `NSError` should a problem occur.
@return `YES` if the connection was made successfully, otherwise `NO`.
*/
-(BOOL)connectOutputBus:(UInt32)sourceOutputBusNumber ofNode:(AUNode)sourceNode toInputBus:(UInt32)destinationInputBusNumber ofNode:(AUNode)destinationNode inGraph:(AUGraph)graph error:(NSError **)error;

/**
Called when custom nodes in the pipeline should be disposed.
If you inserted your own `AUNode` instances into the audio pipeline, override this method to
perform any cleanup needed.
This method will be called whenever the audio pipeline is being torn down.
@param graph The `AUGraph` that is being disposed.
*/
-(void)disposeOfCustomNodesInGraph:(AUGraph)graph;

///----------------------------
/// @name Properties
///----------------------------

/**
Returns the volume of audio playback, between `0.0` and `1.0`.
This property only applies to audio played back through this class, not the system audio volume.
*/
@property (readwrite, nonatomic) double volume;

/** Whether audio output is enabled. */
@property (readwrite, nonatomic) BOOL audioOutputEnabled;

/** Returns the receiver's delegate. */
@property (readwrite, nonatomic, weak) id <SPTCoreAudioControllerDelegate> delegate;

#if !TARGET_OS_IPHONE

/** Returns the available audio output devices. Mac only. */
@property (readonly, nonatomic, copy) NSArray *availableOutputDevices;

/** Returns the current output device. Set to `nil` to use the system default. Mac only. */
@property (readwrite, nonatomic, strong) SPTCoreAudioDevice *currentOutputDevice;

#endif

#if TARGET_OS_IPHONE

/** Current background playback task reference. */
@property (readwrite, nonatomic) UIBackgroundTaskIdentifier backgroundPlaybackTask;

#endif

@end
@@ -0,0 +1,32 @@
//
// SPTFeaturedPlaylistList.h
// Spotify iOS SDK
//
// Created by Per-Olov Jernberg on 06/11/14.
// Copyright (c) 2014 Spotify AB. All rights reserved.
//
/*
Copyright 2014 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import <Foundation/Foundation.h>
#import "SPTListPage.h"

@interface SPTFeaturedPlaylistList : SPTListPage

/** If there's a message associated with the paginated list. */
@property (nonatomic, readonly) NSString *message;

@end
@@ -0,0 +1,44 @@
//
// SPTAlbumCover.h
// Spotify iOS SDK
//
// Created by Daniel Kennett on 2014-04-04.
/*
Copyright 2014 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

/** This class represents an image from the Spotify service. It could be an
album's cover art or a user image, for example. */
@interface SPTImage : NSObject

///----------------------------
/// @name Properties
///----------------------------

/** The image's size as reported from the backed.
@warning This property may be `CGSizeZero` if the size of the image is unknown
by the backend. This is particularly the case with images not owned by Spotify, for
example if a user's image is taken from their Facebook account.
*/
@property (nonatomic, readonly) CGSize size;

/** The HTTP URL to the image. */
@property (nonatomic, readonly, copy) NSURL *imageURL;

@end
@@ -0,0 +1,89 @@
//
// SPTJSONDecoding.h
// Basic Auth
//
// Created by Daniel Kennett on 14/11/2013.
/*
Copyright 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#import <Foundation/Foundation.h>

@protocol SPTJSONObject <NSObject>

/** Initialise the object with the given decoded JSON response from the web API
(typically an `NSDictionary`, but not always).
@param decodedObject The decoded representation of the object.
@param error An error pointer that will contain an error if a problem occurred.
@return Returns the initalised object, or `nil` if a problem occurred.
*/
-(id)initWithDecodedJSONObject:(id)decodedObject error:(NSError **)error;

/** Returns the original decoded object (typically an `NSDictionary`, but not always)
that was used to create the object. Useful for serialising. */
@property (nonatomic, readonly, copy) id decodedJSONObject;

@end

/** Helper class for decoding JSON from the Spotify web API. You shouldn't need to use this
in your application — use `SPTRequest` instead. */
@interface SPTJSONDecoding : NSObject

///----------------------------
/// @name JSON Decoding
///----------------------------

/** Convert an object decoded from JSON into a Spotify SDK metadata object.
@param decodedJson The object decoded from JSON.
@param error A pointer to an error object that will be filled if an error occurs.
@return The generated object, or `nil` if an error occurs.
*/
+(id)SPObjectFromDecodedJSON:(id)decodedJson error:(NSError **)error;

/** Convert an object from the given JSON data into a Spotify SDK metadata object.
@param json The JSON data.
@param error A pointer to an error object that will be filled if an error occurs.
@return The generated object, or `nil` if an error occurs.
*/
+(id)SPObjectFromEncodedJSON:(NSData *)json error:(NSError **)error;


/** Convert an object decoded from JSON into a partial Spotify SDK metadata object.
@param decodedJson The object decoded from JSON.
@param error A pointer to an error object that will be filled if an error occurs.
@return The generated object, or `nil` if an error occurs.
*/
+(id)partialSPObjectFromDecodedJSON:(id)decodedJson error:(NSError **)error;

/** Convert an object from the given JSON data into a partial Spotify SDK metadata object.
@param json The JSON data.
@param error A pointer to an error object that will be filled if an error occurs.
@return The generated object, or `nil` if an error occurs.
*/
+(id)partialSPObjectFromEncodedJSON:(NSData *)json error:(NSError **)error;

@end

@interface SPTJSONObjectBase : NSObject<SPTJSONObject>

@property (nonatomic, readwrite, copy) id decodedJSONObject;

@end