├── Editor └── MidiInputEditor.cs ├── MidiInput.cs ├── Plugins ├── UnityMIDIReceiver.bundle │ └── Contents │ │ ├── Info.plist │ │ ├── MacOS │ │ └── UnityMIDIReceiver │ │ └── Resources │ │ └── en.lproj │ │ └── InfoPlist.strings ├── x86 │ └── UnityMidiReceiver.dll └── x86_64 │ └── UnityMidiReceiver.dll └── README.md /Editor/MidiInputEditor.cs: -------------------------------------------------------------------------------- 1 | // Unity MIDI Input plug-in / Inspector 2 | // By Keijiro Takahashi, 2013 3 | using UnityEngine; 4 | using UnityEditor; 5 | using System.Collections.Generic; 6 | 7 | [CustomEditor(typeof(MidiInput))] 8 | class MidiInputEditor : Editor 9 | { 10 | public override void OnInspectorGUI () 11 | { 12 | var midi = target as MidiInput; 13 | 14 | // Filter sensibility settings. 15 | midi.sensibilitySlow = EditorGUILayout.Slider ("CC Sensibility (slow)", midi.sensibilitySlow, 1.0f, 25.0f); 16 | midi.sensibilityFast = EditorGUILayout.Slider ("CC Sensibility (fast)", midi.sensibilityFast, 1.0f, 25.0f); 17 | 18 | // Only shows the details on Play Mode. 19 | if (EditorApplication.isPlaying) { 20 | var endpointCount = MidiInput.CountEndpoints (); 21 | 22 | // Endpoints. 23 | var temp = "Detected MIDI endpoints:"; 24 | for (var i = 0; i < endpointCount; i++) { 25 | var id = MidiInput.GetEndpointIdAtIndex (i); 26 | var name = MidiInput.GetEndpointName (id); 27 | temp += "\n" + id.ToString ("X8") + ": " + name; 28 | } 29 | EditorGUILayout.HelpBox (temp, MessageType.None); 30 | 31 | // Incomming messages. 32 | temp = "Incoming MIDI messages:"; 33 | foreach (var message in (target as MidiInput).History) { 34 | temp += "\n" + message.ToString (); 35 | } 36 | EditorGUILayout.HelpBox (temp, MessageType.None); 37 | 38 | // Make itself dirty to update on every time. 39 | EditorUtility.SetDirty (target); 40 | } else { 41 | EditorGUILayout.HelpBox ("You can view the sutatus on Play Mode.", MessageType.Info); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /MidiInput.cs: -------------------------------------------------------------------------------- 1 | // Unity MIDI Input plug-in / C# interface 2 | // By Keijiro Takahashi, 2013 3 | using UnityEngine; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | 8 | public class MidiInput : MonoBehaviour 9 | { 10 | #region Static interface 11 | // Common instance. 12 | static MidiInput instance; 13 | 14 | // Filter mode IDs. 15 | public enum Filter 16 | { 17 | Realtime, 18 | Fast, 19 | Slow 20 | } 21 | 22 | // Returns the key state (on: velocity, off: zero). 23 | public static float GetKey (int noteNumber) 24 | { 25 | var v = instance.notes [noteNumber]; 26 | if (v > 1.0f) { 27 | return v - 1.0f; 28 | } else if (v > 0.0) { 29 | return v; 30 | } else { 31 | return 0.0f; 32 | } 33 | } 34 | 35 | // Returns true if the key was pressed down in this frame. 36 | public static bool GetKeyDown (int noteNumber) 37 | { 38 | return instance.notes [noteNumber] > 1.0f; 39 | } 40 | 41 | // Returns true if the key was released in this frame. 42 | public static bool GetKeyUp (int noteNumber) 43 | { 44 | return instance.notes [noteNumber] < 0.0f; 45 | } 46 | 47 | // Provides the channel list. 48 | public static int[] KnobChannels { 49 | get { 50 | int[] channels = new int[instance.knobs.Count]; 51 | instance.knobs.Keys.CopyTo (channels, 0); 52 | return channels; 53 | } 54 | } 55 | 56 | // Get the CC value. 57 | public static float GetKnob (int channel, Filter filter = Filter.Realtime) 58 | { 59 | if (instance.knobs.ContainsKey (channel)) { 60 | return instance.knobs [channel].filteredValues [(int)filter]; 61 | } else { 62 | return 0.0f; 63 | } 64 | } 65 | #endregion 66 | 67 | #region MIDI message sturcture 68 | public struct MidiMessage 69 | { 70 | // MIDI source (endpoint) ID. 71 | public uint source; 72 | 73 | // MIDI status byte. 74 | public byte status; 75 | 76 | // MIDI data bytes. 77 | public byte data1; 78 | public byte data2; 79 | 80 | public MidiMessage (ulong data) 81 | { 82 | source = (uint)(data & 0xffffffffUL); 83 | status = (byte)((data >> 32) & 0xff); 84 | data1 = (byte)((data >> 40) & 0xff); 85 | data2 = (byte)((data >> 48) & 0xff); 86 | } 87 | 88 | public override string ToString () 89 | { 90 | return string.Format ("s({0:X2}) d({1:X2},{2:X2}) from {3:X8}", status, data1, data2, source); 91 | } 92 | } 93 | #endregion 94 | 95 | #region Internal data structure 96 | // CC channel (knob) information. 97 | class Knob 98 | { 99 | public float[] filteredValues; 100 | 101 | public Knob (float initial) 102 | { 103 | filteredValues = new float[3]; 104 | filteredValues [0] = filteredValues [1] = filteredValues [2] = initial; 105 | } 106 | 107 | public void Update (float value) 108 | { 109 | filteredValues [0] = value; 110 | } 111 | 112 | public void UpdateFilter (float fastFilterCoeff, float slowFilterCoeff) 113 | { 114 | filteredValues [1] = filteredValues [0] - (filteredValues [0] - filteredValues [1]) * fastFilterCoeff; 115 | filteredValues [2] = filteredValues [0] - (filteredValues [0] - filteredValues [2]) * slowFilterCoeff; 116 | } 117 | } 118 | 119 | // Note state array. 120 | // X<0 : Released on this frame. 121 | // X=0 : Off. 122 | // 0 knobs; 128 | #endregion 129 | 130 | #region Editor supports 131 | #if UNITY_EDITOR 132 | // Incoming message history. 133 | Queue messageHistory; 134 | public Queue History { 135 | get { return messageHistory; } 136 | } 137 | #endif 138 | #endregion 139 | 140 | #region Public properties 141 | public float sensibilityFast = 20.0f; 142 | public float sensibilitySlow = 8.0f; 143 | #endregion 144 | 145 | #region Monobehaviour functions 146 | void Awake () 147 | { 148 | instance = this; 149 | notes = new float[128]; 150 | knobs = new Dictionary (); 151 | #if UNITY_EDITOR 152 | messageHistory = new Queue (); 153 | #endif 154 | } 155 | 156 | void Update () 157 | { 158 | // Update the note state array. 159 | for (var i = 0; i < 128; i++) { 160 | var x = notes [i]; 161 | if (x > 1.0f) { 162 | // Key down -> Hold. 163 | notes [i] = x - 1.0f; 164 | } else if (x < 0) { 165 | // Key up -> Off. 166 | notes [i] = 0.0f; 167 | } 168 | } 169 | 170 | // Calculate the filter coefficients. 171 | var fastFilterCoeff = Mathf.Exp (-sensibilityFast * Time.deltaTime); 172 | var slowFilterCoeff = Mathf.Exp (-sensibilitySlow * Time.deltaTime); 173 | 174 | // Update the filtered value. 175 | foreach (var k in knobs.Values) { 176 | k.UpdateFilter (fastFilterCoeff, slowFilterCoeff); 177 | } 178 | 179 | // Process the message queue. 180 | while (true) { 181 | // Pop from the queue. 182 | var data = DequeueIncomingData (); 183 | if (data == 0) { 184 | break; 185 | } 186 | 187 | // Parse the message. 188 | var message = new MidiMessage (data); 189 | 190 | // Note on message? 191 | if (message.status == 0x90) { 192 | notes [message.data1] = 1.0f / 127 * message.data2 + 1.0f; 193 | } 194 | 195 | // Note off message? 196 | if (message.status == 0x80 || (message.status == 0x90 && message.data2 == 0)) { 197 | notes [message.data1] = -1.0f; 198 | } 199 | 200 | // CC message? 201 | if (message.status == 0xb0) { 202 | // Normalize the value. 203 | var value = 1.0f / 127 * message.data2; 204 | 205 | // Update the channel if it already exists, or add a new channel. 206 | if (knobs.ContainsKey (message.data1)) { 207 | knobs [message.data1].Update (value); 208 | } else { 209 | knobs [message.data1] = new Knob (value); 210 | } 211 | } 212 | 213 | #if UNITY_EDITOR 214 | // Record the message history. 215 | messageHistory.Enqueue (message); 216 | #endif 217 | } 218 | 219 | #if UNITY_EDITOR 220 | // Truncate the history. 221 | while (messageHistory.Count > 8) { 222 | messageHistory.Dequeue (); 223 | } 224 | #endif 225 | } 226 | #endregion 227 | 228 | #region Native module interface 229 | [DllImport ("UnityMIDIReceiver", EntryPoint="UnityMIDIReceiver_CountEndpoints")] 230 | public static extern int CountEndpoints (); 231 | 232 | [DllImport ("UnityMIDIReceiver", EntryPoint="UnityMIDIReceiver_GetEndpointIDAtIndex")] 233 | public static extern uint GetEndpointIdAtIndex (int index); 234 | 235 | [DllImport ("UnityMIDIReceiver", EntryPoint="UnityMIDIReceiver_DequeueIncomingData")] 236 | public static extern ulong DequeueIncomingData (); 237 | 238 | [DllImport ("UnityMIDIReceiver")] 239 | private static extern System.IntPtr UnityMIDIReceiver_GetEndpointName (uint id); 240 | 241 | public static string GetEndpointName (uint id) 242 | { 243 | return Marshal.PtrToStringAnsi (UnityMIDIReceiver_GetEndpointName (id)); 244 | } 245 | #endregion 246 | } 247 | -------------------------------------------------------------------------------- /Plugins/UnityMIDIReceiver.bundle/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 13A538g 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | UnityMIDIReceiver 11 | CFBundleIdentifier 12 | jp.radiumsoftware.UnityMIDIReceiver 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | UnityMIDIReceiver 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | CFPlugInDynamicRegisterFunction 26 | 27 | CFPlugInDynamicRegistration 28 | NO 29 | CFPlugInFactories 30 | 31 | 00000000-0000-0000-0000-000000000000 32 | MyFactoryFunction 33 | 34 | CFPlugInTypes 35 | 36 | 00000000-0000-0000-0000-000000000000 37 | 38 | 00000000-0000-0000-0000-000000000000 39 | 40 | 41 | CFPlugInUnloadFunction 42 | 43 | DTCompiler 44 | com.apple.compilers.llvm.clang.1_0 45 | DTPlatformBuild 46 | 5A11365x 47 | DTPlatformVersion 48 | GM 49 | DTSDKBuild 50 | 13A538c 51 | DTSDKName 52 | macosx10.9 53 | DTXcode 54 | 0500 55 | DTXcodeBuild 56 | 5A11365x 57 | NSHumanReadableCopyright 58 | Copyright © 2013 Radium Software. All rights reserved. 59 | 60 | 61 | -------------------------------------------------------------------------------- /Plugins/UnityMIDIReceiver.bundle/Contents/MacOS/UnityMIDIReceiver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keijiro/unity-midi-input/6516b7e24f22736320c161a07859e51d8349155a/Plugins/UnityMIDIReceiver.bundle/Contents/MacOS/UnityMIDIReceiver -------------------------------------------------------------------------------- /Plugins/UnityMIDIReceiver.bundle/Contents/Resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keijiro/unity-midi-input/6516b7e24f22736320c161a07859e51d8349155a/Plugins/UnityMIDIReceiver.bundle/Contents/Resources/en.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Plugins/x86/UnityMidiReceiver.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keijiro/unity-midi-input/6516b7e24f22736320c161a07859e51d8349155a/Plugins/x86/UnityMidiReceiver.dll -------------------------------------------------------------------------------- /Plugins/x86_64/UnityMidiReceiver.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keijiro/unity-midi-input/6516b7e24f22736320c161a07859e51d8349155a/Plugins/x86_64/UnityMidiReceiver.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MidiInput for Unity 2 | =================== 3 | 4 | **MidiInput** is a native code plug-in for Unity. It allows Unity to communicate with 5 | external MIDI controllers. 6 | 7 | Demo 8 | ---- 9 | 10 | You can find two demo projects in the [test branch] 11 | (https://github.com/keijiro/unity-midi-input/tree/test). 12 | 13 | #### Note 14 | 15 | The first demo "Note" shows how to get key presses and releases from MidiInput. 16 | The size of the cubes are relative to velocity of a key press. 17 | 18 | ![Screenshot](http://keijiro.github.io/unity-midi-input/screenshot1.png) 19 | 20 | #### CC 21 | 22 | The second demo "CC" shows how to get CC (control change) data from MidiInput. 23 | 24 | ![Screenshot](http://keijiro.github.io/unity-midi-input/screenshot2.png) 25 | 26 | System requirement 27 | ------------------ 28 | 29 | - Currently supports only the desktop platforms (Windows and Mac OS X). 30 | - Requires Unity Pro to enable native plug-in feature. 31 | 32 | Setting up 33 | ---------- 34 | 35 | 1. Drag and drop the contents of this repository into the Project view. 36 | 2. Add the **MidiInput** script component to a game object. 37 | 3. Open Script Execution Order Settings (Edit -> Project Settings -> Script Execution 38 | Order) and set the **MidiInput** script to the highest priority. 39 | 40 | Function reference 41 | ------------------ 42 | 43 | #### static float GetKey ( noteNumber ) 44 | 45 | If the key specified with noteNumber has been pressed down, returns its velocity 46 | (greater than 0.0f, and less or equal to 1.0f). If the key isn't pressed, 47 | returns 0.0f. 48 | 49 | #### static bool GetKeyDown ( noteNumber ) 50 | 51 | Returns true during the frame the key was pressed down. 52 | 53 | #### static bool GetKeyUp ( noteNumber ) 54 | 55 | Returns true during the frame the key was released. 56 | 57 | #### static float GetKnob ( channel, filter ) 58 | 59 | Returns the current value of the specified CC channel. 60 | 61 | #### static int [] KonbChannels 62 | 63 | Provides the CC channel list. It contains the channel numbers which has alraedy 64 | sent data to the host. 65 | 66 | Filters for CC input 67 | -------------------- 68 | 69 | You can specify one of the three filter types below when retrieving a CC value with 70 | the GetKnob function. 71 | 72 | - Filter.Realtime - no filter. It returns the last received value. 73 | - Filter.Fast - light low-pass filter. It suits fast-moving objects. 74 | - Filter.Slow - heavy low-pass filter. Very smooth but not responsive. 75 | 76 | You can adjust the sensibility of each filter in the inspector. 77 | 78 | See also 79 | -------- 80 | 81 | This project was derived from [unity-midi-receiver] 82 | (https://github.com/keijiro/unity-midi-receiver). 83 | You can see the details of the implementation there. 84 | 85 | License 86 | ------- 87 | 88 | Copyright (C) 2013 Keijiro Takahashi 89 | 90 | Permission is hereby granted, free of charge, to any person obtaining a copy of 91 | this software and associated documentation files (the "Software"), to deal in 92 | the Software without restriction, including without limitation the rights to 93 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 94 | the Software, and to permit persons to whom the Software is furnished to do so, 95 | subject to the following conditions: 96 | 97 | The above copyright notice and this permission notice shall be included in all 98 | copies or substantial portions of the Software. 99 | 100 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 101 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 102 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 103 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 104 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 105 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 106 | --------------------------------------------------------------------------------