Skip to content

Instantly share code, notes, and snippets.

@chrisjz
Last active January 3, 2022 14:03
Show Gist options
  • Save chrisjz/efb6d3aa53fd65fb2364 to your computer and use it in GitHub Desktop.
Save chrisjz/efb6d3aa53fd65fb2364 to your computer and use it in GitHub Desktop.
EMG Raw Data access for Myo Unity package in SDK for Windows version 0.8.0
Patch for Myo Unity package for SDK Windows 0.8.0 to access EMG raw data.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Thalmic.Myo
{
public class MyoEventArgs : EventArgs
{
public MyoEventArgs(Myo myo, DateTime timestamp)
{
this.Myo = myo;
this.Timestamp = timestamp;
}
public Myo Myo { get; private set; }
public DateTime Timestamp { get; private set; }
}
public class ArmSyncedEventArgs : MyoEventArgs
{
public ArmSyncedEventArgs(Myo myo, DateTime timestamp, Arm arm, XDirection xDirection)
: base(myo, timestamp)
{
this.Arm = arm;
this.XDirection = xDirection;
}
public Arm Arm { get; private set; }
public XDirection XDirection { get; private set; }
}
public class AccelerometerDataEventArgs : MyoEventArgs
{
public AccelerometerDataEventArgs(Myo myo, DateTime timestamp, Vector3 accelerometer)
: base(myo, timestamp)
{
this.Accelerometer = accelerometer;
}
public Vector3 Accelerometer { get; private set; }
}
public class GyroscopeDataEventArgs : MyoEventArgs
{
public GyroscopeDataEventArgs(Myo myo, DateTime timestamp, Vector3 gyroscope)
: base(myo, timestamp)
{
this.Gyroscope = gyroscope;
}
public Vector3 Gyroscope { get; private set; }
}
public class OrientationDataEventArgs : MyoEventArgs
{
public OrientationDataEventArgs(Myo myo, DateTime timestamp, Quaternion orientation)
: base(myo, timestamp)
{
this.Orientation = orientation;
}
public Quaternion Orientation { get; private set; }
}
public class PoseEventArgs : MyoEventArgs
{
public PoseEventArgs(Myo myo, DateTime timestamp, Pose pose)
: base(myo, timestamp)
{
this.Pose = pose;
}
public Pose Pose { get; private set; }
}
public class RssiEventArgs : MyoEventArgs
{
public RssiEventArgs(Myo myo, DateTime timestamp, sbyte rssi)
: base(myo, timestamp)
{
this.Rssi = rssi;
}
public sbyte Rssi { get; private set; }
}
public class EmgDataEventArgs : MyoEventArgs
{
public EmgDataEventArgs(Myo myo, DateTime timestamp, int[] emg)
: base(myo, timestamp)
{
this.Emg = emg;
}
public int[] Emg { get; private set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Thalmic.Myo
{
internal static class libmyo
{
#if UNITY_STANDALONE || UNITY_EDITOR
private const string MYO_DLL = "myo";
#elif UNITY_ANDROID
private const string MYO_DLL = "myo-android";
#elif WIN64
private const string MYO_DLL = "myo64.dll";
#elif WIN32
private const string MYO_DLL = "myo32.dll";
#endif
public enum Result
{
Success,
Error,
ErrorInvalidArgument,
ErrorRuntime
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_error_cstring",
CallingConvention = CallingConvention.Cdecl)]
public static extern string error_cstring(IntPtr error);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_error_kind",
CallingConvention = CallingConvention.Cdecl)]
public static extern Result error_kind(IntPtr error);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_free_error_details",
CallingConvention = CallingConvention.Cdecl)]
public static extern void free_error_details(IntPtr error);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_init_hub",
CallingConvention = CallingConvention.Cdecl)]
public static extern Result init_hub(out IntPtr hub, string applicationIdentifier, IntPtr error);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_shutdown_hub",
CallingConvention = CallingConvention.Cdecl)]
public static extern Result shutdown_hub(IntPtr hub, IntPtr error);
public enum LockingPolicy
{
None,
Standard
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_set_locking_policy",
CallingConvention = CallingConvention.Cdecl)]
public static extern Result set_locking_policy(IntPtr hub, LockingPolicy lockingPolicy, IntPtr error);
public enum VibrationType
{
Short,
Medium,
Long
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_vibrate",
CallingConvention = CallingConvention.Cdecl)]
public static extern void vibrate(IntPtr myo, VibrationType type, IntPtr error);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_request_rssi",
CallingConvention = CallingConvention.Cdecl)]
public static extern void request_rssi(IntPtr myo, IntPtr error);
public enum StreamEmg
{
Disabled,
Enabled
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_set_stream_emg",
CallingConvention = CallingConvention.Cdecl)]
public static extern Result set_stream_emg(IntPtr myo, StreamEmg type, IntPtr error);
public enum PoseType
{
Rest = 0,
Fist = 1,
WaveIn = 2,
WaveOut = 3,
FingersSpread = 4,
DoubleTap = 5,
Unknown = 0xffff
}
public enum UnlockType
{
Timed = 0,
Hold = 1
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_myo_unlock",
CallingConvention = CallingConvention.Cdecl)]
public static extern void myo_unlock(IntPtr myo, UnlockType unlockType, IntPtr error);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_myo_lock",
CallingConvention = CallingConvention.Cdecl)]
public static extern void myo_lock(IntPtr myo, IntPtr error);
public enum UserActionType
{
Single = 0
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_myo_notify_user_action",
CallingConvention = CallingConvention.Cdecl)]
public static extern void myo_notify_user_action(IntPtr myo, UserActionType type, IntPtr error);
public enum EventType
{
Paired,
Unpaired,
Connected,
Disconnected,
ArmSynced,
ArmUnsynced,
Orientation,
Pose,
Rssi,
Emg,
Unlocked,
Locked
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_type",
CallingConvention = CallingConvention.Cdecl)]
public static extern EventType event_get_type(IntPtr evt);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_timestamp",
CallingConvention = CallingConvention.Cdecl)]
public static extern UInt64 event_get_timestamp(IntPtr evt);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_myo",
CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr event_get_myo(IntPtr evt);
public enum VersionComponent
{
Major,
Minor,
Patch
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_firmware_version",
CallingConvention = CallingConvention.Cdecl)]
public static extern uint event_get_firmware_version(IntPtr evt, VersionComponent component);
public enum Arm {
Right,
Left,
Unknown
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_arm",
CallingConvention = CallingConvention.Cdecl)]
public static extern Arm event_get_arm(IntPtr evt);
public enum XDirection {
TowardWrist,
TowardElbow,
Unknown
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_x_direction",
CallingConvention = CallingConvention.Cdecl)]
public static extern XDirection event_get_x_direction(IntPtr evt);
public enum OrientationIndex
{
X = 0,
Y = 1,
Z = 2,
W = 3
}
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_orientation",
CallingConvention = CallingConvention.Cdecl)]
public static extern float event_get_orientation(IntPtr evt, OrientationIndex index);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_accelerometer",
CallingConvention = CallingConvention.Cdecl)]
public static extern float event_get_accelerometer(IntPtr evt, uint index);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_gyroscope",
CallingConvention = CallingConvention.Cdecl)]
public static extern float event_get_gyroscope(IntPtr evt, uint index);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_pose",
CallingConvention = CallingConvention.Cdecl)]
public static extern PoseType event_get_pose(IntPtr evt);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_rssi",
CallingConvention = CallingConvention.Cdecl)]
public static extern sbyte event_get_rssi(IntPtr evt);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_event_get_emg",
CallingConvention = CallingConvention.Cdecl)]
public static extern sbyte event_get_emg(IntPtr evt, uint sensor);
public enum HandlerResult
{
Continue,
Stop
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate HandlerResult Handler(IntPtr userData, IntPtr evt);
[DllImport(MYO_DLL,
EntryPoint = "libmyo_run",
CallingConvention = CallingConvention.Cdecl)]
public static extern Result run(IntPtr hub, uint durationMs, Handler handler, IntPtr userData, IntPtr error);
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Thalmic.Myo
{
public class Myo
{
private readonly Hub _hub;
private IntPtr _handle;
private bool streamEmg;
internal Myo(Hub hub, IntPtr handle)
{
Debug.Assert(handle != IntPtr.Zero, "Cannot construct Myo instance with null pointer.");
_hub = hub;
_handle = handle;
}
public event EventHandler<MyoEventArgs> Connected;
public event EventHandler<MyoEventArgs> Disconnected;
public event EventHandler<ArmSyncedEventArgs> ArmSynced;
public event EventHandler<MyoEventArgs> ArmUnsynced;
public event EventHandler<PoseEventArgs> PoseChange;
public event EventHandler<OrientationDataEventArgs> OrientationData;
public event EventHandler<AccelerometerDataEventArgs> AccelerometerData;
public event EventHandler<GyroscopeDataEventArgs> GyroscopeData;
public event EventHandler<RssiEventArgs> Rssi;
public event EventHandler<EmgDataEventArgs> EmgData;
public event EventHandler<MyoEventArgs> Unlocked;
public event EventHandler<MyoEventArgs> Locked;
public int[] emgData = new int[7];
internal Hub Hub
{
get { return _hub; }
}
internal IntPtr Handle
{
get { return _handle; }
}
public void Vibrate(VibrationType type)
{
libmyo.vibrate(_handle, (libmyo.VibrationType)type, IntPtr.Zero);
}
public void RequestRssi()
{
libmyo.request_rssi(_handle, IntPtr.Zero);
}
public Result SetStreamEmg(StreamEmg type)
{
streamEmg = true;
return (Result) libmyo.set_stream_emg(_handle, (libmyo.StreamEmg)type, IntPtr.Zero);
}
public void Unlock(UnlockType type)
{
libmyo.myo_unlock(_handle, (libmyo.UnlockType)type, IntPtr.Zero);
}
public void Lock()
{
libmyo.myo_lock(_handle, IntPtr.Zero);
}
public void NotifyUserAction()
{
libmyo.myo_notify_user_action(_handle, libmyo.UserActionType.Single, IntPtr.Zero);
}
internal void HandleEvent(libmyo.EventType type, DateTime timestamp, IntPtr evt)
{
bool outputEmgData = false;
switch (type)
{
case libmyo.EventType.Connected:
if (Connected != null)
{
Connected(this, new MyoEventArgs(this, timestamp));
}
break;
case libmyo.EventType.Disconnected:
if (Disconnected != null)
{
Disconnected(this, new MyoEventArgs(this, timestamp));
}
break;
case libmyo.EventType.ArmSynced:
if (ArmSynced != null)
{
Arm arm = (Arm)libmyo.event_get_arm(evt);
XDirection xDirection = (XDirection)libmyo.event_get_x_direction(evt);
ArmSynced(this, new ArmSyncedEventArgs(this, timestamp, arm, xDirection));
}
break;
case libmyo.EventType.ArmUnsynced:
if (ArmUnsynced != null)
{
ArmUnsynced(this, new MyoEventArgs(this, timestamp));
}
break;
case libmyo.EventType.Orientation:
if (AccelerometerData != null)
{
float x = libmyo.event_get_accelerometer(evt, 0);
float y = libmyo.event_get_accelerometer(evt, 1);
float z = libmyo.event_get_accelerometer(evt, 2);
var accelerometer = new Vector3(x, y, z);
AccelerometerData(this, new AccelerometerDataEventArgs(this, timestamp, accelerometer));
}
if (GyroscopeData != null)
{
float x = libmyo.event_get_gyroscope(evt, 0);
float y = libmyo.event_get_gyroscope(evt, 1);
float z = libmyo.event_get_gyroscope(evt, 2);
var gyroscope = new Vector3(x, y, z);
GyroscopeData(this, new GyroscopeDataEventArgs(this, timestamp, gyroscope));
}
if (OrientationData != null)
{
float x = libmyo.event_get_orientation(evt, libmyo.OrientationIndex.X);
float y = libmyo.event_get_orientation(evt, libmyo.OrientationIndex.Y);
float z = libmyo.event_get_orientation(evt, libmyo.OrientationIndex.Z);
float w = libmyo.event_get_orientation(evt, libmyo.OrientationIndex.W);
var orientation = new Quaternion(x, y, z, w);
OrientationData(this, new OrientationDataEventArgs(this, timestamp, orientation));
}
break;
case libmyo.EventType.Pose:
if (PoseChange != null)
{
var pose = (Pose)libmyo.event_get_pose(evt);
PoseChange(this, new PoseEventArgs(this, timestamp, pose));
}
break;
case libmyo.EventType.Rssi:
if (Rssi != null)
{
var rssi = libmyo.event_get_rssi(evt);
Rssi(this, new RssiEventArgs(this, timestamp, rssi));
}
break;
case libmyo.EventType.Emg:
outputEmgData = true;
SetEmgData(evt, timestamp);
break;
case libmyo.EventType.Unlocked:
if (Unlocked != null)
{
Unlocked(this, new MyoEventArgs(this, timestamp));
}
break;
case libmyo.EventType.Locked:
if (Locked != null)
{
Locked(this, new MyoEventArgs(this, timestamp));
}
break;
}
if (!outputEmgData && streamEmg) {
SetEmgData(evt, timestamp);
}
}
protected void SetEmgData(IntPtr evt, DateTime timestamp)
{
int[] emg = {
libmyo.event_get_emg(evt, 0),
libmyo.event_get_emg(evt, 1),
libmyo.event_get_emg(evt, 2),
libmyo.event_get_emg(evt, 3),
libmyo.event_get_emg(evt, 4),
libmyo.event_get_emg(evt, 5),
libmyo.event_get_emg(evt, 6),
libmyo.event_get_emg(evt, 7)
};
emgData = emg;
}
}
public enum Result
{
Success,
Error,
ErrorInvalidArgument,
ErrorRuntime
}
public enum Arm
{
Right,
Left,
Unknown
}
public enum XDirection
{
TowardWrist,
TowardElbow,
Unknown
}
public enum VibrationType
{
Short,
Medium,
Long
}
public enum StreamEmg
{
Disabled,
Enabled
}
public enum UnlockType
{
Timed = 0, ///< Unlock for a fixed period of time.
Hold = 1 ///< Unlock until explicitly told to re-lock.
}
}
using UnityEngine;
using System.Collections;
using Arm = Thalmic.Myo.Arm;
using XDirection = Thalmic.Myo.XDirection;
using VibrationType = Thalmic.Myo.VibrationType;
using Pose = Thalmic.Myo.Pose;
using UnlockType = Thalmic.Myo.UnlockType;
using StreamEmg = Thalmic.Myo.StreamEmg;
// Represents a Myo armband. Myo's orientation is made available through transform.localRotation, and other properties
// like the current pose are provided explicitly below. All spatial data about Myo is provided following Unity
// coordinate system conventions (the y axis is up, the z axis is forward, and the coordinate system is left-handed).
public class ThalmicMyo : MonoBehaviour {
// True if and only if Myo has detected that it is on an arm.
public bool armSynced;
// Returns true if and only if Myo is unlocked.
public bool unlocked;
// The current arm that Myo is being worn on. An arm of Unknown means that Myo is unable to detect the arm
// (e.g. because it's not currently being worn).
public Arm arm;
// The current direction of Myo's +x axis relative to the user's arm. A xDirection of Unknown means that Myo is
// unable to detect the direction (e.g. because it's not currently being worn).
public XDirection xDirection;
// The current pose detected by Myo. A pose of Unknown means that Myo is unable to detect the pose (e.g. because
// it's not currently being worn).
public Pose pose = Pose.Unknown;
// Myo's current accelerometer reading, representing the acceleration due to force on the Myo armband in units of
// g (roughly 9.8 m/s^2) and following Unity coordinate system conventions.
public Vector3 accelerometer;
// Myo's current gyroscope reading, representing the angular velocity about each of Myo's axes in degrees/second
// following Unity coordinate system conventions.
public Vector3 gyroscope;
public Thalmic.Myo.Result streamEmg;
public int[] emg;
// True if and only if this Myo armband has paired successfully, at which point it will provide data and a
// connection with it will be maintained when possible.
public bool isPaired {
get { return _myo != null; }
}
// Vibrate the Myo with the provided type of vibration, e.g. VibrationType.Short or VibrationType.Medium.
public void Vibrate (VibrationType type) {
_myo.Vibrate (type);
}
// Cause the Myo to unlock with the provided type of unlock. e.g. UnlockType.Timed or UnlockType.Hold.
public void Unlock (UnlockType type) {
_myo.Unlock (type);
}
// Cause the Myo to re-lock immediately.
public void Lock () {
_myo.Lock ();
}
/// Notify the Myo that a user action was recognized.
public void NotifyUserAction () {
_myo.NotifyUserAction ();
}
void Start() {
if (isPaired) {
streamEmg = _myo.SetStreamEmg (_myoStreamEmg);
}
}
void Update() {
lock (_lock) {
armSynced = _myoArmSynced;
arm = _myoArm;
xDirection = _myoXDirection;
if (_myoQuaternion != null) {
transform.localRotation = new Quaternion(_myoQuaternion.Y, _myoQuaternion.Z, -_myoQuaternion.X, -_myoQuaternion.W);
}
if (_myoAccelerometer != null) {
accelerometer = new Vector3(_myoAccelerometer.Y, _myoAccelerometer.Z, -_myoAccelerometer.X);
}
if (_myoGyroscope != null) {
gyroscope = new Vector3(_myoGyroscope.Y, _myoGyroscope.Z, -_myoGyroscope.X);
}
if (isPaired && streamEmg == Thalmic.Myo.Result.Success) {
emg = _myo.emgData;
}
pose = _myoPose;
unlocked = _myoUnlocked;
}
}
void myo_OnArmSync(object sender, Thalmic.Myo.ArmSyncedEventArgs e) {
lock (_lock) {
_myoArmSynced = true;
_myoArm = e.Arm;
_myoXDirection = e.XDirection;
}
}
void myo_OnArmUnsync(object sender, Thalmic.Myo.MyoEventArgs e) {
lock (_lock) {
_myoArmSynced = false;
_myoArm = Arm.Unknown;
_myoXDirection = XDirection.Unknown;
}
}
void myo_OnOrientationData(object sender, Thalmic.Myo.OrientationDataEventArgs e) {
lock (_lock) {
_myoQuaternion = e.Orientation;
}
}
void myo_OnAccelerometerData(object sender, Thalmic.Myo.AccelerometerDataEventArgs e) {
lock (_lock) {
_myoAccelerometer = e.Accelerometer;
}
}
void myo_OnGyroscopeData(object sender, Thalmic.Myo.GyroscopeDataEventArgs e) {
lock (_lock) {
_myoGyroscope = e.Gyroscope;
}
}
void myo_OnEmgData(object sender, Thalmic.Myo.EmgDataEventArgs e) {
lock (_lock) {
_myoEmg = e.Emg;
}
}
void myo_OnPoseChange(object sender, Thalmic.Myo.PoseEventArgs e) {
lock (_lock) {
_myoPose = e.Pose;
}
}
void myo_OnUnlock(object sender, Thalmic.Myo.MyoEventArgs e) {
lock (_lock) {
_myoUnlocked = true;
}
}
void myo_OnLock(object sender, Thalmic.Myo.MyoEventArgs e) {
lock (_lock) {
_myoUnlocked = false;
}
}
public Thalmic.Myo.Myo internalMyo {
get { return _myo; }
set {
if (_myo != null) {
_myo.ArmSynced -= myo_OnArmSync;
_myo.ArmUnsynced -= myo_OnArmUnsync;
_myo.OrientationData -= myo_OnOrientationData;
_myo.AccelerometerData -= myo_OnAccelerometerData;
_myo.GyroscopeData -= myo_OnGyroscopeData;
_myo.PoseChange -= myo_OnPoseChange;
_myo.Unlocked -= myo_OnUnlock;
_myo.Locked -= myo_OnLock;
}
_myo = value;
if (value != null) {
value.ArmSynced += myo_OnArmSync;
value.ArmUnsynced += myo_OnArmUnsync;
value.OrientationData += myo_OnOrientationData;
value.AccelerometerData += myo_OnAccelerometerData;
value.GyroscopeData += myo_OnGyroscopeData;
value.PoseChange += myo_OnPoseChange;
value.Unlocked += myo_OnUnlock;
value.Locked += myo_OnLock;
}
}
}
private Object _lock = new Object();
private bool _myoArmSynced = false;
private Arm _myoArm = Arm.Unknown;
private XDirection _myoXDirection = XDirection.Unknown;
private Thalmic.Myo.Quaternion _myoQuaternion = null;
private Thalmic.Myo.Vector3 _myoAccelerometer = null;
private Thalmic.Myo.Vector3 _myoGyroscope = null;
private int[] _myoEmg = new int[7];
private Pose _myoPose = Pose.Unknown;
private bool _myoUnlocked = false;
private StreamEmg _myoStreamEmg = StreamEmg.Enabled;
private Thalmic.Myo.Myo _myo;
}
@waane
Copy link

waane commented Feb 19, 2016

Is this patch works at Mac unity too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment