├── Editor.meta ├── Editor ├── OscCore.Editor.asmdef ├── OscCore.Editor.asmdef.meta ├── Scripts.meta └── Scripts │ ├── EditorHelp.cs │ ├── EditorHelp.cs.meta │ ├── Inspectors.meta │ ├── Inspectors │ ├── Input.meta │ ├── Input │ │ ├── OscBlobMessageHandlerInspector.cs │ │ ├── OscBlobMessageHandlerInspector.cs.meta │ │ ├── OscMessageHandlerInspector.cs │ │ ├── OscMessageHandlerInspector.cs.meta │ │ ├── OscReceiverInspector.cs │ │ └── OscReceiverInspector.cs.meta │ ├── Output.meta │ └── Output │ │ ├── OscSenderInspector.cs │ │ ├── OscSenderInspector.cs.meta │ │ ├── PropertyOutputInspector.cs │ │ └── PropertyOutputInspector.cs.meta │ ├── MonitorWindow.cs │ ├── MonitorWindow.cs.meta │ ├── OscCoreSettingsProvider.cs │ └── OscCoreSettingsProvider.cs.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── Debug.meta ├── Debug │ ├── Runtime Receiver Debug.unity │ ├── Runtime Receiver Debug.unity.meta │ ├── Scripts.meta │ └── Scripts │ │ ├── MonitorToDebugText.cs │ │ └── MonitorToDebugText.cs.meta ├── Dependencies.meta ├── Dependencies │ ├── BlobHandles.meta │ ├── BlobHandles │ │ ├── LICENSE │ │ ├── LICENSE.meta │ │ ├── README.md │ │ ├── README.md.meta │ │ ├── Runtime.meta │ │ ├── Runtime │ │ │ ├── BlobHandles.Runtime.asmdef │ │ │ ├── BlobHandles.Runtime.asmdef.meta │ │ │ ├── Collections.meta │ │ │ ├── Collections │ │ │ │ ├── BlobHandleDictionaryMethods.cs │ │ │ │ ├── BlobHandleDictionaryMethods.cs.meta │ │ │ │ ├── BlobHandleHashSetMethods.cs │ │ │ │ ├── BlobHandleHashSetMethods.cs.meta │ │ │ │ ├── BlobStringDictionary.cs │ │ │ │ └── BlobStringDictionary.cs.meta │ │ │ ├── Compiler.meta │ │ │ ├── Compiler │ │ │ │ ├── Il2CppSetOptionAttribute.cs │ │ │ │ └── Il2CppSetOptionAttribute.cs.meta │ │ │ ├── Structs.meta │ │ │ └── Structs │ │ │ │ ├── BlobHandle.cs │ │ │ │ ├── BlobHandle.cs.meta │ │ │ │ ├── BlobString.cs │ │ │ │ └── BlobString.cs.meta │ │ ├── Tests.meta │ │ └── Tests │ │ │ ├── Editor.meta │ │ │ ├── Editor │ │ │ ├── BlobHandleTests.cs │ │ │ ├── BlobHandleTests.cs.meta │ │ │ ├── BlobHandles.Tests.Editor.asmdef │ │ │ ├── BlobHandles.Tests.Editor.asmdef.meta │ │ │ ├── BlobStringDictionaryTests.cs │ │ │ └── BlobStringDictionaryTests.cs.meta │ │ │ ├── Runtime.meta │ │ │ └── Runtime │ │ │ ├── BlobHandles.Tests.Runtime.asmdef │ │ │ ├── BlobHandles.Tests.Runtime.asmdef.meta │ │ │ ├── PerformanceTestRunner.cs │ │ │ ├── PerformanceTestRunner.cs.meta │ │ │ ├── PerformanceTests.cs │ │ │ ├── PerformanceTests.cs.meta │ │ │ ├── TestData.cs │ │ │ └── TestData.cs.meta │ ├── NtpTimestamp.meta │ └── NtpTimestamp │ │ ├── ExtensionMethods.cs │ │ ├── ExtensionMethods.cs.meta │ │ ├── NtpTimestamp.asmdef │ │ ├── NtpTimestamp.asmdef.meta │ │ ├── NtpTimestamp.cs │ │ ├── NtpTimestamp.cs.meta │ │ ├── README.md │ │ ├── README.md.meta │ │ ├── TimeConstants.cs │ │ └── TimeConstants.cs.meta ├── OscCore.Runtime.asmdef ├── OscCore.Runtime.asmdef.meta ├── Scripts.meta └── Scripts │ ├── Component.meta │ ├── Component │ ├── Message Handlers.meta │ ├── Message Handlers │ │ ├── OscBlobMessageHandler.cs │ │ ├── OscBlobMessageHandler.cs.meta │ │ ├── OscBooleanMessageHandler.cs │ │ ├── OscBooleanMessageHandler.cs.meta │ │ ├── OscColorMessageHandler.cs │ │ ├── OscColorMessageHandler.cs.meta │ │ ├── OscFloat64MessageHandler.cs │ │ ├── OscFloat64MessageHandler.cs.meta │ │ ├── OscFloatMessageHandler.cs │ │ ├── OscFloatMessageHandler.cs.meta │ │ ├── OscInt64MessageHandler.cs │ │ ├── OscInt64MessageHandler.cs.meta │ │ ├── OscIntMessageHandler.cs │ │ ├── OscIntMessageHandler.cs.meta │ │ ├── OscStringMessageHandler.cs │ │ ├── OscStringMessageHandler.cs.meta │ │ ├── OscVector3MessageHandler.cs │ │ └── OscVector3MessageHandler.cs.meta │ ├── OscMessageHandler.cs │ ├── OscMessageHandler.cs.meta │ ├── OscReceiver.cs │ ├── OscReceiver.cs.meta │ ├── Output.meta │ ├── Output │ │ ├── OscSender.cs │ │ ├── OscSender.cs.meta │ │ ├── PropertyOutput.cs │ │ └── PropertyOutput.cs.meta │ ├── UnityEvents.cs │ └── UnityEvents.cs.meta │ ├── Constant.cs │ ├── Constant.cs.meta │ ├── Delegates.cs │ ├── Delegates.cs.meta │ ├── Enums.meta │ ├── Enums │ ├── AddressType.cs │ ├── AddressType.cs.meta │ ├── TypeTag.cs │ ├── TypeTag.cs.meta │ ├── VectorElementFilters.cs │ └── VectorElementFilters.cs.meta │ ├── Message.meta │ ├── Message │ ├── OscMessageValues.AsciiChar.cs │ ├── OscMessageValues.AsciiChar.cs.meta │ ├── OscMessageValues.Blob.cs │ ├── OscMessageValues.Blob.cs.meta │ ├── OscMessageValues.Boolean.cs │ ├── OscMessageValues.Boolean.cs.meta │ ├── OscMessageValues.Color32.cs │ ├── OscMessageValues.Color32.cs.meta │ ├── OscMessageValues.Float.cs │ ├── OscMessageValues.Float.cs.meta │ ├── OscMessageValues.Float64.cs │ ├── OscMessageValues.Float64.cs.meta │ ├── OscMessageValues.Int.cs │ ├── OscMessageValues.Int.cs.meta │ ├── OscMessageValues.Int64.cs │ ├── OscMessageValues.Int64.cs.meta │ ├── OscMessageValues.MIDI.cs │ ├── OscMessageValues.MIDI.cs.meta │ ├── OscMessageValues.Null.cs │ ├── OscMessageValues.Null.cs.meta │ ├── OscMessageValues.String.cs │ ├── OscMessageValues.String.cs.meta │ ├── OscMessageValues.Timestamp.cs │ ├── OscMessageValues.Timestamp.cs.meta │ ├── OscMessageValues.cs │ └── OscMessageValues.cs.meta │ ├── OscActionPair.cs │ ├── OscActionPair.cs.meta │ ├── OscAddressSpace.cs │ ├── OscAddressSpace.cs.meta │ ├── OscClient.cs │ ├── OscClient.cs.meta │ ├── OscParser.cs │ ├── OscParser.cs.meta │ ├── OscServer.cs │ ├── OscServer.cs.meta │ ├── OscSocket.cs │ ├── OscSocket.cs.meta │ ├── OscWriter.cs │ ├── OscWriter.cs.meta │ ├── Structs.meta │ ├── Structs │ ├── MidiMessage.cs │ └── MidiMessage.cs.meta │ ├── Utility.meta │ └── Utility │ ├── ExtensionMethods.cs │ ├── ExtensionMethods.cs.meta │ ├── OscAddressMethods.cs │ ├── OscAddressMethods.cs.meta │ ├── Utils.cs │ └── Utils.cs.meta ├── Samples~ └── OscCoreExamples.unitypackage ├── Tests.meta ├── Tests ├── Editor.meta ├── Editor │ ├── MessageReadPerformanceTests.cs │ ├── MessageReadPerformanceTests.cs.meta │ ├── OscCore.Tests.Editor.asmdef │ ├── OscCore.Tests.Editor.asmdef.meta │ ├── OscSenderTests.cs │ ├── OscSenderTests.cs.meta │ ├── OscWriterTests.cs │ ├── OscWriterTests.cs.meta │ ├── ParseTestData.cs │ ├── ParseTestData.cs.meta │ ├── ParsingTests.cs │ ├── ParsingTests.cs.meta │ ├── TestUtil.cs │ └── TestUtil.cs.meta ├── Runtime.meta └── Runtime │ ├── OscCore.Tests.Runtime.asmdef │ ├── OscCore.Tests.Runtime.asmdef.meta │ ├── StandardTypeMessageSendTest.cs │ └── StandardTypeMessageSendTest.cs.meta ├── package.json └── package.json.meta /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e3ea5c24339fd2459ef421510ad276d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/OscCore.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OscCore.Editor", 3 | "references": [ 4 | "OscCore.Runtime", 5 | "BlobHandles.Runtime", 6 | "NtpTimestamp" 7 | ], 8 | "optionalUnityReferences": [], 9 | "includePlatforms": [ 10 | "Editor" 11 | ], 12 | "excludePlatforms": [], 13 | "allowUnsafeCode": false, 14 | "overrideReferences": false, 15 | "precompiledReferences": [], 16 | "autoReferenced": false, 17 | "defineConstraints": [], 18 | "versionDefines": [] 19 | } -------------------------------------------------------------------------------- /Editor/OscCore.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff330059db6be5641891254400da252d 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c8dc053bded841d5b5bdc84cd85f40ee 3 | timeCreated: 1581244788 -------------------------------------------------------------------------------- /Editor/Scripts/EditorHelp.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace OscCore 4 | { 5 | static class EditorHelp 6 | { 7 | public const string PrefKey = "OscCore_ShowEditorHelp"; 8 | 9 | public static bool Show => EditorPrefs.GetBool(PrefKey, true); 10 | 11 | public static void DrawBox(string text, MessageType type = MessageType.Info) 12 | { 13 | if(EditorPrefs.GetBool(PrefKey, true)) 14 | EditorGUILayout.HelpBox(text, type); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Editor/Scripts/EditorHelp.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba43c8866631444a93e052c744067ea4 3 | timeCreated: 1581252319 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e2bc4af16fb0514abeda103e2595b4b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 32559b2c1ca94a409f775675c5584a39 3 | timeCreated: 1581306944 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input/OscBlobMessageHandlerInspector.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace OscCore 4 | { 5 | [CustomEditor(typeof(OscBlobMessageHandler))] 6 | class OscBlobMessageHandlerInspector : Editor 7 | { 8 | SerializedProperty m_ReceiverProp; 9 | SerializedProperty m_AddressProp; 10 | SerializedProperty m_OnReceivedProp; 11 | 12 | void OnEnable() 13 | { 14 | m_ReceiverProp = serializedObject.FindProperty("m_Receiver"); 15 | m_AddressProp = serializedObject.FindProperty("m_Address"); 16 | m_OnReceivedProp = serializedObject.FindProperty("OnMessageReceived"); 17 | } 18 | 19 | public override void OnInspectorGUI() 20 | { 21 | serializedObject.Update(); 22 | EditorGUILayout.PropertyField(m_ReceiverProp); 23 | EditorGUILayout.PropertyField(m_AddressProp); 24 | EditorGUILayout.Space(); 25 | if (m_OnReceivedProp != null) 26 | EditorGUILayout.PropertyField(m_OnReceivedProp); 27 | 28 | serializedObject.ApplyModifiedProperties(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input/OscBlobMessageHandlerInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3b28edc838b451d849b9ce11cc55740 3 | timeCreated: 1581277396 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input/OscMessageHandlerInspector.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace OscCore 4 | { 5 | [CustomEditor(typeof(OscMessageHandler<,>), true)] 6 | class OscMessageHandlerInspector : Editor 7 | { 8 | SerializedProperty m_ReceiverProp; 9 | SerializedProperty m_AddressProp; 10 | SerializedProperty m_OnReceivedProp; 11 | 12 | void OnEnable() 13 | { 14 | m_ReceiverProp = serializedObject.FindProperty("m_Receiver"); 15 | m_AddressProp = serializedObject.FindProperty("m_Address"); 16 | m_OnReceivedProp = serializedObject.FindProperty("OnMessageReceived"); 17 | } 18 | 19 | public override void OnInspectorGUI() 20 | { 21 | serializedObject.Update(); 22 | EditorGUILayout.PropertyField(m_ReceiverProp); 23 | EditorGUILayout.PropertyField(m_AddressProp); 24 | EditorGUILayout.Space(); 25 | if (m_OnReceivedProp != null) 26 | EditorGUILayout.PropertyField(m_OnReceivedProp); 27 | 28 | serializedObject.ApplyModifiedProperties(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input/OscMessageHandlerInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ddc4eb533dcf49b3a5e779c967779074 3 | timeCreated: 1581242624 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input/OscReceiverInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | using UnityEngine; 4 | 5 | namespace OscCore 6 | { 7 | [CustomEditor(typeof(OscReceiver))] 8 | class OscReceiverInspector : Editor 9 | { 10 | const string k_HelpText = "Handles receiving & parsing OSC messages on the given port.\n" + 11 | "Forwards messages to all event handler components that reference it."; 12 | 13 | static readonly List k_SortedAddresses = new List(); 14 | 15 | OscReceiver m_Target; 16 | SerializedProperty m_PortProp; 17 | 18 | bool m_ShowAddressFoldout; 19 | bool m_PreviouslyRunning; 20 | 21 | void OnEnable() 22 | { 23 | m_Target = (OscReceiver) target; 24 | m_PortProp = serializedObject.FindProperty("m_Port"); 25 | 26 | SortAddresses(); 27 | } 28 | 29 | public override void OnInspectorGUI() 30 | { 31 | var running = m_Target != null && m_Target.Running; 32 | if (running != m_PreviouslyRunning) 33 | SortAddresses(); 34 | 35 | m_PreviouslyRunning = running; 36 | 37 | EditorGUI.BeginDisabledGroup(running && Application.IsPlaying(this)); 38 | EditorGUILayout.PropertyField(m_PortProp); 39 | EditorGUI.EndDisabledGroup(); 40 | 41 | using (new EditorGUI.DisabledScope(!running)) 42 | { 43 | var count = CountHandlers(); 44 | var prefix = m_ShowAddressFoldout ? "Hide" : "Show"; 45 | m_ShowAddressFoldout = EditorGUILayout.Foldout(m_ShowAddressFoldout, $"{prefix} {count} Listening Addresses", true); 46 | 47 | if (m_ShowAddressFoldout) 48 | { 49 | foreach (var addr in k_SortedAddresses) 50 | EditorGUILayout.LabelField(addr, EditorStyles.miniBoldLabel); 51 | } 52 | } 53 | 54 | serializedObject.ApplyModifiedProperties(); 55 | 56 | if (EditorHelp.Show) 57 | { 58 | EditorGUILayout.Space(); 59 | EditorGUILayout.HelpBox(k_HelpText, MessageType.Info); 60 | } 61 | } 62 | 63 | int CountHandlers() 64 | { 65 | return m_Target == null || m_Target.Server == null ? 0 : m_Target.Server.CountHandlers(); 66 | } 67 | 68 | void SortAddresses() 69 | { 70 | if (m_Target == null || m_Target.Server == null) 71 | return; 72 | 73 | k_SortedAddresses.Clear(); 74 | k_SortedAddresses.AddRange(m_Target.Server.AddressSpace.Addresses); 75 | k_SortedAddresses.Sort(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Input/OscReceiverInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d65547287cb648ee975918d894821430 3 | timeCreated: 1581244776 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Output.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 96a0d4209e0840cfbe9385e2ed020507 3 | timeCreated: 1581306953 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Output/OscSenderInspector.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace OscCore 4 | { 5 | [CustomEditor(typeof(OscSender))] 6 | class OscSenderInspector : Editor 7 | { 8 | const string k_HelpText = "Handles serializing & sending OSC messages to the given IP address and port.\n" + 9 | "Forwards messages from all property sender components that reference it."; 10 | 11 | SerializedProperty m_IpAddressProp; 12 | SerializedProperty m_PortProp; 13 | 14 | void OnEnable() 15 | { 16 | m_IpAddressProp = serializedObject.FindProperty("m_IpAddress"); 17 | m_PortProp = serializedObject.FindProperty("m_Port"); 18 | } 19 | 20 | public override void OnInspectorGUI() 21 | { 22 | serializedObject.Update(); 23 | 24 | EditorGUILayout.PropertyField(m_IpAddressProp); 25 | EditorGUILayout.PropertyField(m_PortProp); 26 | 27 | EditorGUILayout.Space(); 28 | EditorHelp.DrawBox(k_HelpText); 29 | 30 | serializedObject.ApplyModifiedProperties(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Output/OscSenderInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 556de9ddd6b546848fc328dccace8f59 3 | timeCreated: 1581302494 -------------------------------------------------------------------------------- /Editor/Scripts/Inspectors/Output/PropertyOutputInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f823a73b06644f9a9d6ecb0372dbcc05 3 | timeCreated: 1581293364 -------------------------------------------------------------------------------- /Editor/Scripts/MonitorWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using BlobHandles; 6 | using UnityEditor; 7 | using UnityEngine; 8 | 9 | namespace OscCore 10 | { 11 | public class MonitorWindow : EditorWindow 12 | { 13 | const int showLinesCount = 32; 14 | 15 | static readonly StringBuilder k_Builder = new StringBuilder(); 16 | 17 | OscServer m_Server; 18 | 19 | readonly Queue m_LogMessages = new Queue(showLinesCount); 20 | readonly List m_ToQueue = new List(16); 21 | readonly List m_ToQueueAlt = new List(16); 22 | bool m_UseAlt; 23 | List m_ActiveQueueBuffer; 24 | 25 | string m_ServerLabel = ""; 26 | 27 | bool m_NeedsRepaint; 28 | bool m_ShowNoServerWarning; 29 | int m_PreviousServerCount; 30 | 31 | bool m_PreviouslyPlaying; 32 | 33 | void OnEnable() 34 | { 35 | m_PreviousServerCount = 0; 36 | m_ActiveQueueBuffer = m_ToQueue; 37 | if (OscServer.PortToServer.Count == 0) 38 | m_ShowNoServerWarning = true; 39 | 40 | HandleServerChanges(); 41 | } 42 | 43 | void OnDisable() 44 | { 45 | m_Server?.RemoveMonitorCallback(Monitor); 46 | m_PreviousServerCount = 0; 47 | } 48 | 49 | void HandleServerChanges() 50 | { 51 | if (OscServer.PortToServer.Count > m_PreviousServerCount) 52 | { 53 | m_Server = OscServer.PortToServer.First().Value; 54 | m_Server.AddMonitorCallback(Monitor); 55 | m_ServerLabel = $"Messages being received on port {m_Server.Port}"; 56 | m_ShowNoServerWarning = false; 57 | 58 | if (!Application.isPlaying) 59 | EditorApplication.update += Update; 60 | } 61 | else if (OscServer.PortToServer.Count < m_PreviousServerCount) 62 | { 63 | m_Server?.RemoveMonitorCallback(Monitor); 64 | m_Server = null; 65 | m_ServerLabel = null; 66 | m_ShowNoServerWarning = true; 67 | m_PreviousServerCount = OscServer.PortToServer.Count; 68 | 69 | if (!Application.isPlaying) 70 | EditorApplication.update -= Update; 71 | } 72 | 73 | m_PreviousServerCount = OscServer.PortToServer.Count; 74 | } 75 | 76 | void HandleModeChange() 77 | { 78 | if (EditorApplication.isPlaying != m_PreviouslyPlaying) 79 | { 80 | m_PreviousServerCount = 0; 81 | lock (m_LogMessages) 82 | { 83 | m_LogMessages.Clear(); 84 | m_ToQueueAlt.Clear(); 85 | m_ToQueue.Clear(); 86 | } 87 | } 88 | 89 | m_PreviouslyPlaying = EditorApplication.isPlaying; 90 | } 91 | 92 | void Update() 93 | { 94 | if (Time.frameCount % 2 != 0) return; 95 | 96 | HandleModeChange(); 97 | HandleServerChanges(); 98 | 99 | lock (m_LogMessages) 100 | { 101 | if (m_LogMessages.Count == 0 && m_ToQueue.Count == 0 && m_ToQueueAlt.Count == 0) 102 | return; 103 | 104 | var useAlt = m_UseAlt; 105 | m_UseAlt = !m_UseAlt; 106 | if (useAlt) 107 | { 108 | try 109 | { 110 | foreach (var msg in m_ToQueueAlt) 111 | { 112 | m_LogMessages.Enqueue(msg); 113 | } 114 | } 115 | catch (InvalidOperationException) { } 116 | 117 | m_ToQueueAlt.Clear(); 118 | } 119 | else 120 | { 121 | try 122 | { 123 | foreach (var msg in m_ToQueue) 124 | { 125 | m_LogMessages.Enqueue(msg); 126 | } 127 | 128 | m_ToQueue.Clear(); 129 | } 130 | catch (InvalidOperationException) { } 131 | } 132 | 133 | while (m_LogMessages.Count > showLinesCount) 134 | { 135 | m_LogMessages.Dequeue(); 136 | } 137 | } 138 | 139 | if(m_NeedsRepaint) Repaint(); 140 | } 141 | 142 | public void OnGUI() 143 | { 144 | const string noServerWarning = "No OSC Servers are currently active, so no messages can be received"; 145 | var label = m_ShowNoServerWarning ? noServerWarning : m_ServerLabel; 146 | EditorGUILayout.HelpBox(label, MessageType.Info); 147 | 148 | lock (m_LogMessages) 149 | { 150 | foreach (var line in m_LogMessages) 151 | { 152 | EditorGUILayout.LabelField(line); 153 | } 154 | } 155 | } 156 | 157 | void Monitor(BlobString address, OscMessageValues values) 158 | { 159 | if(m_UseAlt) 160 | m_ToQueueAlt.Add(MessageToString(address.ToString(), values)); 161 | else 162 | m_ToQueue.Add(MessageToString(address.ToString(), values)); 163 | 164 | m_NeedsRepaint = true; 165 | } 166 | 167 | static string MessageToString(string address, OscMessageValues values) 168 | { 169 | k_Builder.Clear(); 170 | k_Builder.Append(address); 171 | const string divider = " ,"; 172 | k_Builder.Append(divider); 173 | values.ForEachElement((i, type) => { k_Builder.Append((char)type); }); 174 | k_Builder.Append(" "); 175 | 176 | var lastIndex = values.ElementCount - 1; 177 | values.ForEachElement((i, type) => 178 | { 179 | var elementText = values.ReadStringElement(i); 180 | k_Builder.Append(elementText); 181 | if(i != lastIndex) k_Builder.Append(' '); 182 | }); 183 | 184 | return k_Builder.ToString(); 185 | } 186 | 187 | [MenuItem("Window/OscCore/Monitor")] 188 | static void InitWindow() 189 | { 190 | ((MonitorWindow) GetWindow(typeof(MonitorWindow)))?.Show(); 191 | } 192 | } 193 | } 194 | 195 | 196 | -------------------------------------------------------------------------------- /Editor/Scripts/MonitorWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 25c921629c088234487fb97be2dfc0f6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/OscCoreSettingsProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | using UnityEngine; 4 | 5 | namespace OscCore 6 | { 7 | static class OscCoreSettingsIMGUIRegister 8 | { 9 | const string k_HelpTooltip = "If enabled, display tutorial & hint messages in the Editor"; 10 | static readonly GUIContent k_HelpContent = new GUIContent("Show Help", k_HelpTooltip); 11 | 12 | [SettingsProvider] 13 | public static SettingsProvider CreateOscCoreSettingsProvider() 14 | { 15 | var provider = new SettingsProvider("User/Open Sound Control Core", SettingsScope.User) 16 | { 17 | label = "OSC Core", 18 | guiHandler = (searchContext) => 19 | { 20 | if (EditorPrefs.HasKey(EditorHelp.PrefKey)) 21 | { 22 | var setting = EditorPrefs.GetBool(EditorHelp.PrefKey); 23 | var afterSetting = EditorGUILayout.Toggle(k_HelpContent, setting); 24 | if(afterSetting != setting) 25 | EditorPrefs.SetBool(EditorHelp.PrefKey, afterSetting); 26 | } 27 | else 28 | { 29 | EditorPrefs.SetBool(EditorHelp.PrefKey, true); 30 | } 31 | }, 32 | 33 | keywords = new HashSet(new[] { "OSC", "Help" , "Open Sound" }) 34 | }; 35 | 36 | return provider; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Editor/Scripts/OscCoreSettingsProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 75007d9bcc9d4b80a0898ad2e4c69436 3 | timeCreated: 1581246884 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Stella Cannefax 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a777fdec1138e2942961a5f53d7d198a 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f6f62ede97d8394b829d7169fd10c19 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b8691af448bc0647bf1af3ba489bb00 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Debug.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 10fbaaa8bd90c724d90cc133b2689a52 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Debug/Runtime Receiver Debug.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 507b1970f87e44b4997feb513e9d8ab4 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Debug/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 267eeef5b2e99aa47a729fce8dc245e8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Debug/Scripts/MonitorToDebugText.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using BlobHandles; 3 | using UnityEngine; 4 | 5 | namespace OscCore.Demo 6 | { 7 | public class MonitorToDebugText : MonoBehaviour 8 | { 9 | const int k_LineCount = 9; 10 | const int k_LastIndex = k_LineCount - 1; 11 | static readonly StringBuilder k_StringBuilder = new StringBuilder(); 12 | 13 | public OscReceiver Receiver; 14 | 15 | public TextMesh IpAddressText; 16 | public TextMesh RecentValueText; 17 | 18 | int m_ReplaceLineIndex; 19 | bool m_Dirty; 20 | 21 | readonly string[] m_ReceivedAsString = new string[k_LineCount]; 22 | 23 | public void Awake() 24 | { 25 | IpAddressText.text = $"Local IP: {Utils.GetLocalIpAddress()} , Port {Receiver.Port}"; 26 | 27 | Receiver.Server.AddMonitorCallback(Monitor); 28 | } 29 | 30 | void Update() 31 | { 32 | if (m_Dirty) 33 | { 34 | RecentValueText.text = BuildMultiLine(); 35 | m_Dirty = false; 36 | } 37 | } 38 | 39 | void Monitor(BlobString address, OscMessageValues values) 40 | { 41 | m_Dirty = true; 42 | 43 | if (m_ReplaceLineIndex == k_LastIndex) 44 | { 45 | for (int i = 0; i < k_LastIndex; i++) 46 | { 47 | m_ReceivedAsString[i] = m_ReceivedAsString[i + 1]; 48 | } 49 | } 50 | 51 | m_ReceivedAsString[m_ReplaceLineIndex] = Utils.MonitorMessageToString(address, values); 52 | 53 | if (m_ReplaceLineIndex < k_LastIndex) 54 | m_ReplaceLineIndex++; 55 | } 56 | 57 | string BuildMultiLine() 58 | { 59 | k_StringBuilder.Clear(); 60 | for (int i = 0; i <= m_ReplaceLineIndex; i++) 61 | { 62 | k_StringBuilder.AppendLine(m_ReceivedAsString[i]); 63 | k_StringBuilder.AppendLine(); 64 | } 65 | 66 | return k_StringBuilder.ToString(); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Runtime/Debug/Scripts/MonitorToDebugText.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5d1c158853eeaab40899e9cb4f86dbb3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Dependencies.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fcdb7ed57a81f8f41a84efbe80299ac1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b04e234c357df8147a0f841805536ad1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Stella Cannefax 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5db0cf505647ccc43bdf23f5981dddd3 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/README.md: -------------------------------------------------------------------------------- 1 | # BlobHandles 2 | _"Blob Handles"_ are a fast & easy way to hash and compare segments of memory in C# / Unity. 3 | 4 | They allow you to do two main things: 5 | 1) Use a sequence of bytes as a hash key, like in a `Dictionary`. 6 | 2) Quickly compare two handles' slices of memory for equality 7 | 8 | ## Compatibility 9 | 10 | Any release will be tested against these versions, _(italics in parentheses)_ indicates the version when written 11 | - Latest **LTS** - _(2018.4.11f1)_ 12 | - Latest **Official Release** - _(2019.2.9f1)_ 13 | 14 | Primarily tested on windows, but should work on any platform that lets you use pointers. 15 | 16 | ## Blob Strings 17 | This also includes _BlobString_, a wrapper around `BlobHandle` that points to an unmanaged representation of a string. 18 | 19 | _BlobString_ is designed for use cases that involve reading strings from an unmanaged source (network / disk) & comparing them against some sort of hash set. 20 | 21 | Set the `BlobString.Encoding` property to change the encoding used for strings. 22 | It's recommended to pick an encoding on startup and not change it after you've encoded any strings, unless you dispose all of them before changing the encoding. 23 | 24 | ## Dictionaries 25 | 26 | Dictionaries keyed on _BlobHandle_ are one of the main intended uses. 27 | 28 | For dealing with strings, there is `BlobStringDictionary`. You add regular strings and it takes care of conversion to the unmanaged representation for you. 29 | 30 | #### TryGetValueFromBytes() 31 | 32 | `Dictionary` and `BlobStringDictionary` both have a method, `TryGetValueFromBytes`, that allows using a segment of bytes as the key to a dictionary value search, without having to construct a blob handle yourself. 33 | ```csharp 34 | // dictionary is populated elsewhere 35 | Dictionary m_Actions = new Dictionary(); 36 | byte[] m_Buffer = new byte[64]; 37 | Socket m_Socket; 38 | 39 | void ReceiveMethodCall() 40 | { 41 | var receivedByteCount = m_Socket.Receive(m_Buffer); 42 | 43 | // equivalent to 44 | // m_Actions.TryGetValue(new BlobHandle(m_Buffer, receivedByteCount, out Action action) 45 | if (m_Actions.TryGetValueFromBytes(m_Buffer, receivedByteCount, out Action action)) 46 | action(); 47 | } 48 | ``` 49 | 50 | There are a number of other [TryGetValueFromBytes() overloads](Runtime/Dictionary/BlobHandleDictionaryMethods.cs) for using with offsets into arrays and pointers . 51 | 52 | ## HashSets 53 | 54 | HashSets of _BlobHandle_ are another use, and also get their own extension method. 55 | 56 | #### ContainsBlob() 57 | 58 | `HashSet` gets a method, `ContainsBlob()`, that allows using a segment of bytes as the key to a `.Contains()` check, using the same pattern as the dictionary `TryGetValueFromBytes` method. 59 | ```csharp 60 | HashSet m_HandleSet = new HashSet(); 61 | byte[] m_Buffer = new byte[64]; 62 | Socket m_Socket; 63 | 64 | void ReceiveMethodCall() 65 | { 66 | var receivedByteCount = m_Socket.Receive(m_Buffer); 67 | if (m_HandleSet.ContainsBlob(m_Buffer, receivedByteCount)) 68 | { 69 | // do something in response to matching 70 | } 71 | } 72 | ``` 73 | 74 | There are the same [overloads for ContainsBlob()](Runtime/Dictionary/BlobHandleHashSet.ContainsBlob.cs) as the dictionary method. 75 | 76 | ## Performance Details 77 | 78 | ###### Runtimes 79 | 80 | `BlobHandle` is significantly faster under Mono than IL2CPP in my testing on Windows x64, 2019.1.14. 81 | 82 | However, it still performs fine under IL2CPP. 83 | 84 | ###### Memory & Constructors 85 | `BlobHandle` is an immutable struct with only a pointer & a length. This means 86 | - fast to create 87 | - uses little memory, no heap 88 | 89 | `BlobString` uses a `NativeArray` to store a copy of the string. This means 90 | 91 | - Doesn't add objects to the managed heap. 92 | This matters because garbage collection performance [degrades as the managed heap expands](https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html). 93 | 94 | - Takes some extra amount of memory to hold encoded string copies, but an amount easily spared in most cases. 95 | The original strings could even be set to null and garbage collected if you are highly memory constrained. 96 | 97 | ###### Equality Testing 98 | I tested a number of different ways of testing memory equality between handles to find out what method would be faster, and how that changed depending on the number of bytes to compare & the compiler (Mono or IL2CPP) used. 99 | 100 | Consistently the fastest method under both runtimes, easily several times faster than anything else (at least on Windows x64 where i've tested) is a direct wrapper around [memcmp](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/memcmp-wmemcmp?view=vs-2019). Other platforms may call for a different implementation of the equality check if it proves faster there. 101 | 102 | ###### GetHashCode() 103 | The included method for getting a blob handle's hash code uses the length of the blob and the value of the last byte in its contents. 104 | 105 | `Length * 397 ^ Pointer[Length - 1];` 106 | 107 | This was the fastest method I tested on my data, and it should work well on any data that doesn't have a lot of entries of the same length that _also_ end in the same byte. 108 | 109 | You may be able to get better performance with a different method, especially if your data is different. 110 | 111 | ###### IL2CPP Options 112 | An [attribute](https://docs.unity3d.com/Manual/IL2CPP-CompilerOptions.html) is used to disable IL2CPP null checks in some methods of `BlobStringDictionary`. 113 | The only managed objects inside those methods are _readonly_ members of the class, and initialized when the dictionary is, so they should always not be null. 114 | 115 | ###### Tests 116 | Performance tests for BlobHandles include, but are not limited to: 117 | - `.Equals()` performance vs string 118 | - `.GetHashCode()` performance vs string 119 | - `.TryGetValue(str, out T value)` performance, for `Dictionary` vs `Dictionary` 120 | This one is probably the most direct comparison of hash lookup performance 121 | - `Dictionary.TryGetValueFromBytes()` performance, basically the same as the above test but adding the step of reading from unknown bytes like in actual use. 122 | 123 | Performance tests are in a runtime assembly so they can be run in players. 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2d5aba5c39f6a6949ac731e6db18d710 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 26b86955d6ec7de4a8f82fa5caac6758 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/BlobHandles.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BlobHandles.Runtime", 3 | "references": [], 4 | "includePlatforms": [ 5 | "Android", 6 | "Editor", 7 | "iOS", 8 | "LinuxStandalone64", 9 | "macOSStandalone", 10 | "WindowsStandalone32", 11 | "WindowsStandalone64" 12 | ], 13 | "excludePlatforms": [], 14 | "allowUnsafeCode": true, 15 | "overrideReferences": false, 16 | "precompiledReferences": [], 17 | "autoReferenced": true, 18 | "defineConstraints": [], 19 | "versionDefines": [], 20 | "noEngineReferences": false 21 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/BlobHandles.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19c41e5ceaa0e4b4daca3d6a88d9235d 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a1905bdef21352f44985ab8710e0b957 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections/BlobHandleDictionaryMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace BlobHandles 5 | { 6 | public static unsafe class BlobHandleDictionaryMethods 7 | { 8 | /// 9 | /// Try to find the value associated with a given chunk of bytes 10 | /// 11 | /// The dictionary to look in 12 | /// Pointer to the start of the bytes 13 | /// The number of bytes to read 14 | /// The output value 15 | /// The dictionary value type 16 | /// True if the value was found, false otherwise 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static bool TryGetValueFromBytes(this Dictionary self, 19 | byte* ptr, int length, out T value) 20 | { 21 | return self.TryGetValue(new BlobHandle(ptr, length), out value); 22 | } 23 | 24 | /// 25 | /// Try to find the value associated with a given chunk of bytes 26 | /// 27 | /// The dictionary to look in 28 | /// The byte array to read from 29 | /// The output value 30 | /// The dictionary value type 31 | /// True if the value was found, false otherwise 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | public static bool TryGetValueFromBytes(this Dictionary self, 34 | byte[] bytes, out T value) 35 | { 36 | return self.TryGetValue(new BlobHandle(bytes), out value); 37 | } 38 | 39 | /// 40 | /// Try to find the value associated with a given chunk of bytes 41 | /// 42 | /// The dictionary to look in 43 | /// The byte array to read from 44 | /// The number of bytes to read 45 | /// The output value 46 | /// The dictionary value type 47 | /// True if the value was found, false otherwise 48 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 49 | public static bool TryGetValueFromBytes(this Dictionary self, 50 | byte[] bytes, int length, out T value) 51 | { 52 | return self.TryGetValue(new BlobHandle(bytes, length), out value); 53 | } 54 | 55 | /// 56 | /// Try to find the value associated with a given chunk of bytes. 57 | /// No bounds checking is performed, so be certain that offset + length is within the byte array. 58 | /// 59 | /// The dictionary to look in 60 | /// The byte array to read from 61 | /// The number of bytes to read 62 | /// The index in the byte array to start reading at 63 | /// The output value 64 | /// The dictionary value type 65 | /// True if the value was found, false otherwise 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | public static bool TryGetValueFromBytes(this Dictionary self, 68 | byte[] bytes, int length, int offset, out T value) 69 | { 70 | return self.TryGetValue(new BlobHandle(bytes, length, offset), out value); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections/BlobHandleDictionaryMethods.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 92dfe1eaeb03401583879bec889da92b 3 | timeCreated: 1570828913 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections/BlobHandleHashSetMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace BlobHandles 5 | { 6 | public static unsafe class BlobHandleHashSetMethods 7 | { 8 | /// Determines whether the HashSet contains the specified sequence of bytes 9 | /// The HashSet to look in 10 | /// Pointer to the start of the bytes 11 | /// The number of bytes to read 12 | /// True if the key was found, false otherwise 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | public static bool ContainsBlob(this HashSet self, byte* ptr, int length) 15 | { 16 | return self.Contains(new BlobHandle(ptr, length)); 17 | } 18 | 19 | /// Determines whether the HashSet contains the specified sequence of bytes 20 | /// The HashSet to look in 21 | /// The byte array to read from 22 | /// True if the key was found, false otherwise 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public static bool ContainsBlob(this HashSet self, byte[] bytes) 25 | { 26 | return self.Contains(new BlobHandle(bytes)); 27 | } 28 | 29 | /// 30 | /// Determines whether the HashSet contains the specified sequence of bytes 31 | /// No bounds checking is performed, so be certain that length is less than or equal to bytes.Length 32 | /// 33 | /// The HashSet to look in 34 | /// The byte array to read from 35 | /// The number of bytes to read 36 | /// True if the key was found, false otherwise 37 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 38 | public static bool ContainsBlob(this HashSet self, byte[] bytes, int length) 39 | { 40 | return self.Contains(new BlobHandle(bytes, length)); 41 | } 42 | 43 | /// 44 | /// Determines whether the HashSet contains the specified sequence of bytes 45 | /// No bounds checking is performed, so be certain that offset + length is within end of the byte array. 46 | /// 47 | /// The HashSet to look in 48 | /// The byte array to read from 49 | /// The number of bytes to read 50 | /// The index in the byte array to start reading at 51 | /// True if the key was found, false otherwise 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static bool ContainsBlob(this HashSet self, byte[] bytes, int length, int offset) 54 | { 55 | return self.Contains(new BlobHandle(bytes, length, offset)); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections/BlobHandleHashSetMethods.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1e66224890e4e5b87210eb376b0168b 3 | timeCreated: 1571722355 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections/BlobStringDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using Unity.IL2CPP.CompilerServices.BlobHandles; 5 | 6 | namespace BlobHandles 7 | { 8 | /// 9 | /// Designed to allow efficient matching of strings received as bytes (such as from network or disk) to values. 10 | /// 11 | /// The type to associate a string key with 12 | public sealed unsafe class BlobStringDictionary : IDisposable 13 | { 14 | const int defaultSize = 16; 15 | 16 | public readonly Dictionary HandleToValue; 17 | readonly Dictionary SourceToBlob; 18 | 19 | public BlobStringDictionary(int initialCapacity = defaultSize) 20 | { 21 | HandleToValue = new Dictionary(initialCapacity); 22 | SourceToBlob = new Dictionary(initialCapacity); 23 | } 24 | 25 | /// Converts a string into a BlobString and adds it and the value to the dictionary 26 | /// The string to add 27 | /// The value to associate with the key 28 | [Il2CppSetOption(Option.NullChecks, false)] 29 | public void Add(string str, T value) 30 | { 31 | if (str == null || SourceToBlob.ContainsKey(str)) 32 | return; 33 | 34 | var blobStr = new BlobString(str); 35 | HandleToValue.Add(blobStr.Handle, value); 36 | SourceToBlob.Add(str, blobStr); 37 | } 38 | 39 | /// Adds a BlobString and its associated value to the dictionary 40 | /// The blob string to add 41 | /// The value to associate with the key 42 | [Il2CppSetOption(Option.NullChecks, false)] 43 | public void Add(BlobString blobStr, T value) 44 | { 45 | HandleToValue.Add(blobStr.Handle, value); 46 | } 47 | 48 | /// Removes the value with the specified key 49 | /// The string to remove 50 | /// true if the string was found and removed, false otherwise 51 | [Il2CppSetOption(Option.NullChecks, false)] 52 | public bool Remove(string str) 53 | { 54 | if (!SourceToBlob.TryGetValue(str, out var blobStr)) 55 | return false; 56 | 57 | SourceToBlob.Remove(str); 58 | var removed = HandleToValue.Remove(blobStr.Handle); 59 | blobStr.Dispose(); 60 | return removed; 61 | } 62 | 63 | /// Removes the value with the specified key 64 | /// The blob string to remove 65 | /// true if the string was found and removed, false otherwise 66 | [Il2CppSetOption(Option.NullChecks, false)] 67 | public bool Remove(BlobString blobStr) 68 | { 69 | return HandleToValue.Remove(blobStr.Handle); 70 | } 71 | 72 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 73 | [Il2CppSetOption(Option.NullChecks, false)] 74 | public bool TryGetValueFromBytes(byte* ptr, int byteCount, out T value) 75 | { 76 | return HandleToValue.TryGetValue(new BlobHandle(ptr, byteCount), out value); 77 | } 78 | 79 | public void Clear() 80 | { 81 | HandleToValue.Clear(); 82 | SourceToBlob.Clear(); 83 | } 84 | 85 | public void Dispose() 86 | { 87 | foreach (var kvp in SourceToBlob) 88 | kvp.Value.Dispose(); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Collections/BlobStringDictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 89e821b0caea4423bc6d0c8382cf444e 3 | timeCreated: 1570589703 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Compiler.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0de7dd0d26b4c9e49b62883226849bc0 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Compiler/Il2CppSetOptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unity.IL2CPP.CompilerServices.BlobHandles 4 | { 5 | /// 6 | /// The code generation options available for IL to C++ conversion. 7 | /// Enable or disabled these with caution. 8 | /// 9 | public enum Option 10 | { 11 | /// 12 | /// Enable or disable code generation for null checks. 13 | /// 14 | /// Global null check support is enabled by default when il2cpp.exe 15 | /// is launched from the Unity editor. 16 | /// 17 | /// Disabling this will prevent NullReferenceException exceptions from 18 | /// being thrown in generated code. In *most* cases, code that dereferences 19 | /// a null pointer will crash then. Sometimes the point where the crash 20 | /// happens is later than the location where the null reference check would 21 | /// have been emitted though. 22 | /// 23 | NullChecks = 1, 24 | /// 25 | /// Enable or disable code generation for array bounds checks. 26 | /// 27 | /// Global array bounds check support is enabled by default when il2cpp.exe 28 | /// is launched from the Unity editor. 29 | /// 30 | /// Disabling this will prevent IndexOutOfRangeException exceptions from 31 | /// being thrown in generated code. This will allow reading and writing to 32 | /// memory outside of the bounds of an array without any runtime checks. 33 | /// Disable this check with extreme caution. 34 | /// 35 | ArrayBoundsChecks = 2, 36 | /// 37 | /// Enable or disable code generation for divide by zero checks. 38 | /// 39 | /// Global divide by zero check support is disabled by default when il2cpp.exe 40 | /// is launched from the Unity editor. 41 | /// 42 | /// Enabling this will cause DivideByZeroException exceptions to be 43 | /// thrown in generated code. Most code doesn't need to handle this 44 | /// exception, so it is probably safe to leave it disabled. 45 | /// 46 | DivideByZeroChecks = 3, 47 | } 48 | 49 | /// 50 | /// Use this attribute on a class, method, or property to inform the IL2CPP code conversion utility to override the 51 | /// global setting for one of a few different runtime checks. 52 | /// 53 | /// Example: 54 | /// 55 | /// [Il2CppSetOption(Option.NullChecks, false)] 56 | /// public static string MethodWithNullChecksDisabled() 57 | /// { 58 | /// var tmp = new Object(); 59 | /// return tmp.ToString(); 60 | /// } 61 | /// 62 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] 63 | public class Il2CppSetOptionAttribute : Attribute 64 | { 65 | public Option Option { get; private set; } 66 | public object Value { get; private set; } 67 | 68 | public Il2CppSetOptionAttribute(Option option, object value) 69 | { 70 | Option = option; 71 | Value = value; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Compiler/Il2CppSetOptionAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 240472c29f15b604ab098bc4ee446ef8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Structs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 48ac05ef4f73017468c1b452ea891090 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Structs/BlobHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace BlobHandles 6 | { 7 | /// 8 | /// Wraps an arbitrary chunk of bytes in memory, so it can be used as a hash key 9 | /// and compared against other instances of the same set of bytes 10 | /// 11 | public unsafe struct BlobHandle : IEquatable 12 | { 13 | /// A pointer to the start of the blob 14 | public readonly byte* Pointer; 15 | /// The number of bytes in the blob 16 | public readonly int Length; 17 | 18 | public BlobHandle(byte* pointer, int length) 19 | { 20 | Pointer = pointer; 21 | Length = length; 22 | } 23 | 24 | public BlobHandle(IntPtr pointer, int length) 25 | { 26 | Pointer = (byte*) pointer; 27 | Length = length; 28 | } 29 | 30 | /// 31 | /// Get a blob handle for a byte array. The byte array should have its address pinned to work safely! 32 | /// 33 | /// The bytes to get a handle to 34 | public BlobHandle(byte[] bytes) 35 | { 36 | fixed (byte* ptr = bytes) 37 | { 38 | Pointer = ptr; 39 | Length = bytes.Length; 40 | } 41 | } 42 | 43 | /// 44 | /// Get a blob handle for part of a byte array. The byte array should have its address pinned to work safely! 45 | /// 46 | /// The bytes to get a handle to 47 | /// The number of bytes to include. Not bounds checked 48 | public BlobHandle(byte[] bytes, int length) 49 | { 50 | fixed (byte* ptr = bytes) 51 | { 52 | Pointer = ptr; 53 | Length = length; 54 | } 55 | } 56 | 57 | /// 58 | /// Get a blob handle for a slice of a byte array. The byte array should have its address pinned to work safely! 59 | /// 60 | /// The bytes to get a handle to 61 | /// The number of bytes to include. Not bounds checked 62 | /// The byte array index to start the blob at 63 | public BlobHandle(byte[] bytes, int length, int offset) 64 | { 65 | fixed (byte* ptr = &bytes[offset]) 66 | { 67 | Pointer = ptr; 68 | Length = length; 69 | } 70 | } 71 | 72 | public override string ToString() 73 | { 74 | return $"{Length.ToString()} bytes @ {new IntPtr(Pointer).ToString()}"; 75 | } 76 | 77 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 78 | public override int GetHashCode() 79 | { 80 | unchecked 81 | { 82 | return Length * 397 ^ Pointer[Length - 1]; 83 | } 84 | } 85 | 86 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 87 | public bool Equals(BlobHandle other) 88 | { 89 | return Length == other.Length && 90 | MemoryCompare(Pointer, other.Pointer, (UIntPtr) Length) == 0; 91 | } 92 | 93 | public override bool Equals(object obj) 94 | { 95 | return obj is BlobHandle other && Equals(other); 96 | } 97 | 98 | public static bool operator ==(BlobHandle left, BlobHandle right) 99 | { 100 | return left.Length == right.Length && 101 | MemoryCompare(left.Pointer, right.Pointer, (UIntPtr) left.Length) == 0; 102 | } 103 | 104 | public static bool operator !=(BlobHandle left, BlobHandle right) 105 | { 106 | return left.Length != right.Length || 107 | MemoryCompare(left.Pointer, right.Pointer, (UIntPtr) left.Length) != 0; 108 | } 109 | 110 | #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN 111 | // comparing bytes using memcmp has shown to be several times faster than any other method i've found 112 | [DllImport("msvcrt.dll", EntryPoint = "memcmp")] 113 | static extern int MemoryCompare(void* ptr1, void* ptr2, UIntPtr count); 114 | #else 115 | static int MemoryCompare(void* ptr1, void* ptr2, UIntPtr count) 116 | { 117 | var p1 = new Span(ptr1, (int) count); 118 | var p2 = new Span(ptr2, (int) count); 119 | for (int i = 0; i < p1.Length; i++) 120 | { 121 | if (p1[i] != p2[i]) 122 | { 123 | return 1; 124 | } 125 | } 126 | return 0; 127 | } 128 | #endif 129 | 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Structs/BlobHandle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 624d8c76e7624581be90263e6662dc2e 3 | timeCreated: 1570833731 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Structs/BlobString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Text; 4 | using Unity.Collections; 5 | using Unity.Collections.LowLevel.Unsafe; 6 | 7 | namespace BlobHandles 8 | { 9 | /// 10 | /// Represents a string as a fixed blob of bytes 11 | /// 12 | public struct BlobString : IDisposable, IEquatable 13 | { 14 | /// 15 | /// The encoding used to convert to and from strings. 16 | /// WARNING - Changing this after strings have been encoded will probably lead to errors! 17 | /// 18 | public static Encoding Encoding { get; set; } = Encoding.ASCII; 19 | 20 | // Stores all of the bytes that represent this string 21 | readonly NativeArray Bytes; 22 | 23 | public readonly BlobHandle Handle; 24 | 25 | public int Length => Bytes.Length; 26 | 27 | public unsafe BlobString(string source, Allocator allocator = Allocator.Persistent) 28 | { 29 | var byteCount = Encoding.GetByteCount(source); 30 | Bytes = new NativeArray(byteCount, allocator); 31 | var nativeBytesPtr = (byte*) Bytes.GetUnsafePtr(); 32 | 33 | // write encoded string bytes directly to unmanaged memory 34 | fixed (char* strPtr = source) 35 | { 36 | Encoding.GetBytes(strPtr, source.Length, nativeBytesPtr, byteCount); 37 | Handle = new BlobHandle(nativeBytesPtr, byteCount); 38 | } 39 | } 40 | 41 | public unsafe BlobString(byte* sourcePtr, int length) 42 | { 43 | Handle = new BlobHandle(sourcePtr, length); 44 | Bytes = default; 45 | } 46 | 47 | public override unsafe string ToString() 48 | { 49 | return Encoding.GetString(Handle.Pointer, Handle.Length); 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public override int GetHashCode() 54 | { 55 | return Handle.GetHashCode(); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public bool Equals(BlobString other) 60 | { 61 | return Handle.Equals(other.Handle); 62 | } 63 | 64 | public override bool Equals(object obj) 65 | { 66 | return obj is BlobString other && Handle.Equals(other.Handle); 67 | } 68 | 69 | public static bool operator ==(BlobString l, BlobString r) 70 | { 71 | return l.Handle == r.Handle; 72 | } 73 | 74 | public static bool operator !=(BlobString l, BlobString r) 75 | { 76 | return l.Handle != r.Handle; 77 | } 78 | 79 | public void Dispose() 80 | { 81 | if(Bytes.IsCreated) Bytes.Dispose(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Runtime/Structs/BlobString.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9239ae1d5f0442c0b2eae13a5ae98f19 3 | timeCreated: 1570514795 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3fef54f63177dfe438ec2a7fcc072d15 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c9a3918391a53e468d3883a59a3093e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor/BlobHandleTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | 6 | namespace BlobHandles.Tests 7 | { 8 | public class BlobHandleTests 9 | { 10 | [Test] 11 | public unsafe void BlobHandle_PointerConstructor() 12 | { 13 | var b1 = Encoding.ASCII.GetBytes(TestData.RandomString(16, 16)); 14 | fixed (byte* bPtr = b1) 15 | { 16 | var handle = new BlobHandle(bPtr, b1.Length); 17 | Assert.True(handle.Pointer == bPtr); 18 | Assert.True(handle.Length == b1.Length); 19 | } 20 | } 21 | 22 | [Test] 23 | public unsafe void BlobHandle_IntPointerConstructor() 24 | { 25 | var b1 = Encoding.ASCII.GetBytes(TestData.RandomString(16, 16)); 26 | fixed (byte* bPtr = b1) 27 | { 28 | var handle = new BlobHandle((IntPtr) bPtr, b1.Length); 29 | Assert.True(handle.Pointer == bPtr); 30 | Assert.True(handle.Length == b1.Length); 31 | } 32 | } 33 | 34 | [Test] 35 | public unsafe void BlobHandle_DifferentAddressesStillEqual() 36 | { 37 | var str = TestData.RandomString(32, 32); 38 | var b1 = Encoding.ASCII.GetBytes(str); 39 | var b2 = Encoding.ASCII.GetBytes(str); 40 | 41 | BlobHandle h1, h2; 42 | fixed (byte* bPtr = b1) h1 = new BlobHandle(bPtr, b1.Length); 43 | fixed (byte* bPtr = b2) h2 = new BlobHandle(bPtr, b2.Length); 44 | 45 | Assert.AreEqual(h1.GetHashCode(), h2.GetHashCode()); 46 | Assert.AreEqual(h1, h2); 47 | } 48 | 49 | [TestCase(TestData.StringConstants.EatTheRich)] 50 | [TestCase(TestData.StringConstants.M4A)] 51 | [TestCase(TestData.StringConstants.HealthJustice)] 52 | public void BlobString_ToString_OutputIsIdentical(string input) 53 | { 54 | var blobString = new BlobString(input); 55 | Debug.Log($"input - {input}, managed int string output - {blobString}"); 56 | Assert.AreEqual(input, blobString.ToString()); 57 | blobString.Dispose(); 58 | } 59 | 60 | [TestCase(TestData.StringConstants.EatTheRich)] 61 | [TestCase(TestData.StringConstants.M4A)] 62 | [TestCase(TestData.StringConstants.HealthJustice)] 63 | public void BlobString_GetHashCode_OutputSameAcrossInstances(string input) 64 | { 65 | var blobString1 = new BlobString(input); 66 | var blobString2 = new BlobString(input); 67 | 68 | var hashCode1 = blobString1.GetHashCode(); 69 | var hashCode2 = blobString2.GetHashCode(); 70 | blobString1.Dispose(); 71 | blobString2.Dispose(); 72 | 73 | Assert.AreEqual(hashCode1, hashCode2); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor/BlobHandleTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e1afbceddae4870898b5ad2c86ba1d4 3 | timeCreated: 1570677461 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor/BlobHandles.Tests.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BlobHandles.Tests.Editor", 3 | "references": [ 4 | "BlobHandles.Runtime", 5 | "BlobHandles.Tests.Runtime" 6 | ], 7 | "optionalUnityReferences": [ 8 | "TestAssemblies" 9 | ], 10 | "includePlatforms": [ 11 | "Editor" 12 | ], 13 | "excludePlatforms": [], 14 | "allowUnsafeCode": true, 15 | "overrideReferences": false, 16 | "precompiledReferences": [], 17 | "autoReferenced": false, 18 | "defineConstraints": [], 19 | "versionDefines": [] 20 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor/BlobHandles.Tests.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 962a4879a5dd1a046ab8dd6938351a3b 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor/BlobStringDictionaryTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using NUnit.Framework; 4 | 5 | namespace BlobHandles.Tests 6 | { 7 | public class BlobStringDictionaryTests 8 | { 9 | const int k_Value = 303; 10 | 11 | static readonly Dictionary k_Dictionary = new Dictionary(); 12 | 13 | [SetUp] 14 | public void BeforeEach() 15 | { 16 | k_Dictionary.Clear(); 17 | } 18 | 19 | [Test] 20 | public unsafe void Dictionary_TryGetValueFromBytes_Pointer() 21 | { 22 | var b1 = Encoding.ASCII.GetBytes(TestData.RandomString(10, 50)); 23 | var handle = new BlobHandle(b1); 24 | 25 | Assert.False(k_Dictionary.ContainsKey(handle)); 26 | k_Dictionary.Add(handle, k_Value); 27 | 28 | fixed (byte* b1Ptr = b1) 29 | { 30 | Assert.True(k_Dictionary.TryGetValue(handle, out var value)); 31 | Assert.True(k_Dictionary.TryGetValueFromBytes(b1Ptr, b1.Length, out var valueFromBytes)); 32 | Assert.AreEqual(k_Value, value); 33 | Assert.AreEqual(k_Value, valueFromBytes); 34 | } 35 | } 36 | 37 | [Test] 38 | public void Dictionary_TryGetValueFromBytes_Array() 39 | { 40 | var b1 = Encoding.ASCII.GetBytes(TestData.RandomString(10, 50)); 41 | var handle = new BlobHandle(b1); 42 | 43 | Assert.False(k_Dictionary.ContainsKey(handle)); 44 | k_Dictionary.Add(handle, k_Value); 45 | 46 | Assert.True(k_Dictionary.TryGetValue(handle, out var value)); 47 | Assert.True(k_Dictionary.TryGetValueFromBytes(b1, out var valueFromBytes)); 48 | Assert.AreEqual(k_Value, value); 49 | Assert.AreEqual(k_Value, valueFromBytes); 50 | } 51 | 52 | [Test] 53 | public void Dictionary_TryGetValueFromBytes_ArrayWithLength() 54 | { 55 | var b1 = Encoding.ASCII.GetBytes(TestData.RandomString(30, 50)); 56 | var length = b1.Length - 10; 57 | var handle = new BlobHandle(b1, length); 58 | 59 | Assert.False(k_Dictionary.ContainsKey(handle)); 60 | k_Dictionary.Add(handle, k_Value); 61 | 62 | Assert.True(k_Dictionary.TryGetValue(handle, out var value)); 63 | Assert.True(k_Dictionary.TryGetValueFromBytes(b1, length, out var valueFromBytes)); 64 | Assert.AreEqual(k_Value, value); 65 | Assert.AreEqual(k_Value, valueFromBytes); 66 | } 67 | 68 | [Test] 69 | public void Dictionary_TryGetValueFromBytes_ArrayWithLengthAndOffset() 70 | { 71 | var b1 = Encoding.ASCII.GetBytes(TestData.RandomString(40, 50)); 72 | var length = b1.Length - 8; 73 | const int offset = 4; 74 | var handle = new BlobHandle(b1, length, offset); 75 | 76 | Assert.False(k_Dictionary.ContainsKey(handle)); 77 | k_Dictionary.Add(handle, k_Value); 78 | 79 | Assert.True(k_Dictionary.TryGetValue(handle, out var value)); 80 | Assert.True(k_Dictionary.TryGetValueFromBytes(b1, length, offset, out var valueFromBytes)); 81 | Assert.AreEqual(k_Value, value); 82 | Assert.AreEqual(k_Value, valueFromBytes); 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Editor/BlobStringDictionaryTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 57e57218ed78491f9bb8eeff9457630f 3 | timeCreated: 1570850507 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cd78d1196dcb15645be3d8cb5403f688 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/BlobHandles.Tests.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BlobHandles.Tests.Runtime", 3 | "references": [ 4 | "BlobHandles.Runtime" 5 | ], 6 | "optionalUnityReferences": [], 7 | "includePlatforms": [ 8 | "Editor", 9 | "LinuxStandalone64", 10 | "macOSStandalone", 11 | "WindowsStandalone64" 12 | ], 13 | "excludePlatforms": [], 14 | "allowUnsafeCode": true, 15 | "overrideReferences": false, 16 | "precompiledReferences": [], 17 | "autoReferenced": false, 18 | "defineConstraints": [], 19 | "versionDefines": [] 20 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/BlobHandles.Tests.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1cebcd6621d92d34ebcfc65608a48577 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/PerformanceTestRunner.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using BlobHandles.Tests; 3 | using UnityEngine; 4 | 5 | namespace BlobHandles 6 | { 7 | public class PerformanceTestRunner : MonoBehaviour 8 | { 9 | const string logFile = "PerformanceTestLog.txt"; 10 | 11 | PerformanceTests m_Tests; 12 | int m_StartFrame; 13 | 14 | public void Start() 15 | { 16 | m_Tests = new PerformanceTests { RuntimeLog = Path.Combine(Application.dataPath, logFile) }; 17 | m_Tests.BeforeAll(); 18 | m_StartFrame = Time.frameCount; 19 | } 20 | 21 | void Update() 22 | { 23 | var frame = Time.frameCount - m_StartFrame; 24 | switch (frame) 25 | { 26 | case 20: 27 | m_Tests.BlobString_Equals(); 28 | break; 29 | case 40: 30 | m_Tests.ManagedBlobString_GetHashCode(); 31 | break; 32 | case 60: 33 | m_Tests.DictionaryTryGetValue_BlobString(); 34 | break; 35 | case 70: 36 | m_Tests.DictionaryTryGetValue_BlobHandles(); 37 | break; 38 | case 80: 39 | m_Tests.DictionaryExtension_TryGetValueFromBytes(); 40 | break; 41 | case 100: 42 | m_Tests.BlobStringLookup_TryGetValueFromBytes(); 43 | break; 44 | case 120: 45 | m_Tests.GetAsciiStringFromBytes(); 46 | break; 47 | case 125: 48 | m_Tests.AfterAll(); 49 | enabled = false; 50 | break; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/PerformanceTestRunner.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e89e58d63674d34844586abcfb96fa7 3 | timeCreated: 1570693250 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/PerformanceTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 410f82e3a43b47a78642786147b9a308 3 | timeCreated: 1570831180 -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/TestData.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UnityEngine; 3 | 4 | namespace BlobHandles.Tests 5 | { 6 | public static class TestData 7 | { 8 | public static string[] RandomStrings(int count, int stringLengthMin, int stringLengthMax) 9 | { 10 | var strings = new string[count]; 11 | for (int i = 0; i < strings.Length; i++) 12 | strings[i] = RandomString(stringLengthMin, stringLengthMax); 13 | 14 | return strings; 15 | } 16 | 17 | // helps test performance impact of strings that share a common beginning 18 | public static string[] RandomStringsWithPrefix(string prefix, int count, int stringLengthMin, int stringLengthMax) 19 | { 20 | var strings = new string[count]; 21 | for (int i = 0; i < strings.Length; i++) 22 | strings[i] = prefix + RandomString(stringLengthMin, stringLengthMax); 23 | 24 | return strings; 25 | } 26 | 27 | public static string RandomString(int minLength, int maxLength) 28 | { 29 | const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789"; 30 | 31 | var length = Random.Range(minLength, maxLength); 32 | return new string(Enumerable.Repeat(chars, length) 33 | .Select(s => s[Random.Range(0, s.Length)]).ToArray()); 34 | } 35 | 36 | public static class StringConstants 37 | { 38 | public const string EatTheRich = "Eat the rich"; 39 | public const string M4A = "Medicare for all"; 40 | public const string HealthJustice = "Health justice now!"; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/BlobHandles/Tests/Runtime/TestData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: da5b1717928e413429310cc9d3f72ac5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1c3f3297bc4fc948a5dcc43f501df37 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace MiniNtp 4 | { 5 | public static class ExtensionMethods 6 | { 7 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 8 | public static uint ReverseBytes(this uint value) 9 | { 10 | return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 | 11 | (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/ExtensionMethods.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38f32a271ae047ca9859cc4711a6169b 3 | timeCreated: 1571367700 -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/NtpTimestamp.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NtpTimestamp", 3 | "references": [], 4 | "includePlatforms": [], 5 | "excludePlatforms": [], 6 | "allowUnsafeCode": true, 7 | "overrideReferences": false, 8 | "precompiledReferences": [], 9 | "autoReferenced": true, 10 | "defineConstraints": [], 11 | "versionDefines": [], 12 | "noEngineReferences": false 13 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/NtpTimestamp.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b7159db16e428174da6301d7fb076771 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/NtpTimestamp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MiniNtp 5 | { 6 | /// 64-bit NTP timestamp as described in RFC-1305 & 5905 7 | public struct NtpTimestamp : IEquatable, IComparable 8 | { 9 | /// The number of seconds since the last epoch 10 | public readonly uint Seconds; 11 | 12 | /// Number of ~200 picosecond fractions of a second elapsed this second 13 | public readonly uint Fractions; 14 | 15 | public NtpTimestamp(uint seconds, uint fractions) 16 | { 17 | Seconds = seconds; 18 | Fractions = fractions; 19 | } 20 | 21 | public NtpTimestamp(DateTime dt) 22 | { 23 | var epoch = dt < TimeConstants.Epoch2036 ? TimeConstants.Epoch1900 : TimeConstants.Epoch2036; 24 | Seconds = (uint)(dt - epoch).TotalSeconds; 25 | Fractions = (uint)dt.Millisecond * TimeConstants.TimestampFractionsPerMs; 26 | } 27 | 28 | /// Read a new timestamp from big-endian bytes 29 | /// The array to read from 30 | /// The index in the array to start reading from 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public static unsafe NtpTimestamp FromBigEndianBytes(byte[] buffer, int offset) 33 | { 34 | fixed (byte* bPtr = &buffer[offset]) return FromBigEndianBytes((uint*) bPtr); 35 | } 36 | 37 | /// Read a new timestamp from big-endian bytes 38 | /// Pointer to the timestamp to read 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | public static unsafe NtpTimestamp FromBigEndianBytes(byte* tsPtr) 41 | { 42 | return FromBigEndianBytes((uint*) tsPtr); 43 | } 44 | 45 | /// Read a new timestamp from big-endian bytes 46 | /// Pointer to the timestamp to read 47 | public static unsafe NtpTimestamp FromBigEndianBytes(uint* tsPtr) 48 | { 49 | var bSeconds = *tsPtr; 50 | uint seconds = (bSeconds & 0x000000FFU) << 24 | (bSeconds & 0x0000FF00U) << 8 | 51 | (bSeconds & 0x00FF0000U) >> 8 | (bSeconds & 0xFF000000U) >> 24; 52 | 53 | var bFractions = tsPtr[1]; 54 | uint fractions = (bFractions & 0x000000FFU) << 24 | (bFractions & 0x0000FF00U) << 8 | 55 | (bFractions & 0x00FF0000U) >> 8 | (bFractions & 0xFF000000U) >> 24; 56 | 57 | return new NtpTimestamp(seconds, fractions); 58 | } 59 | 60 | public DateTime ToDateTime() 61 | { 62 | // account for the special "now" value 63 | if (Fractions == 1 && Seconds == 0) return DateTime.Now; 64 | var epoch = DateTime.Now < TimeConstants.Epoch2036 ? TimeConstants.Epoch1900 : TimeConstants.Epoch2036; 65 | var fractionMs = Fractions * TimeConstants.FractionMillisecondMultiplier; 66 | var ms = (double) Seconds * 1000 + fractionMs; 67 | return epoch.AddMilliseconds(ms); 68 | } 69 | 70 | /// Convert to big-endian byte order 71 | /// The bytes to copy into 72 | /// The index in the bytes to start writing at 73 | public unsafe void ToBigEndianBytes(byte[] bytes, int offset) 74 | { 75 | fixed (byte* bPtr = &bytes[offset]) 76 | { 77 | var uiPtr = (uint*) bPtr; 78 | uiPtr[0] = Seconds.ReverseBytes(); 79 | uiPtr[1] = Fractions.ReverseBytes(); 80 | } 81 | } 82 | 83 | /// Convert to big-endian byte order 84 | /// The pointer to start writing at 85 | public unsafe void ToBigEndianBytes(uint* writePtr) 86 | { 87 | writePtr[0] = Seconds.ReverseBytes(); 88 | writePtr[1] = Fractions.ReverseBytes(); 89 | } 90 | 91 | public override string ToString() 92 | { 93 | return $"Seconds: {Seconds} , Fractions {Fractions}"; 94 | } 95 | 96 | public bool Equals(NtpTimestamp other) 97 | { 98 | return Seconds == other.Seconds && Fractions == other.Fractions; 99 | } 100 | 101 | public override bool Equals(object obj) 102 | { 103 | return obj is NtpTimestamp other && Equals(other); 104 | } 105 | 106 | public override int GetHashCode() 107 | { 108 | unchecked 109 | { 110 | return ((int) Seconds * 397) ^ (int) Fractions; 111 | } 112 | } 113 | 114 | public static bool operator ==(NtpTimestamp left, NtpTimestamp right) 115 | { 116 | return left.Equals(right); 117 | } 118 | 119 | public static bool operator !=(NtpTimestamp left, NtpTimestamp right) 120 | { 121 | return !left.Equals(right); 122 | } 123 | 124 | public int CompareTo(NtpTimestamp other) 125 | { 126 | var secondsComparison = Seconds.CompareTo(other.Seconds); 127 | return secondsComparison != 0 ? secondsComparison : Fractions.CompareTo(other.Fractions); 128 | } 129 | 130 | public static bool operator <(NtpTimestamp left, NtpTimestamp right) 131 | { 132 | return left.CompareTo(right) < 0; 133 | } 134 | 135 | public static bool operator >(NtpTimestamp left, NtpTimestamp right) 136 | { 137 | return left.CompareTo(right) > 0; 138 | } 139 | 140 | public static bool operator <=(NtpTimestamp left, NtpTimestamp right) 141 | { 142 | return left.CompareTo(right) <= 0; 143 | } 144 | 145 | public static bool operator >=(NtpTimestamp left, NtpTimestamp right) 146 | { 147 | return left.CompareTo(right) >= 0; 148 | } 149 | 150 | // You can directly subtract ntp timestamps, but not add them 151 | public static NtpTimestamp operator -(NtpTimestamp left, NtpTimestamp right) 152 | { 153 | return new NtpTimestamp(left.Seconds - right.Seconds, left.Fractions - right.Fractions); 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/NtpTimestamp.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8d468ac29f8e4600a4d9cb31a4454bb8 3 | timeCreated: 1570260037 -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/README.md: -------------------------------------------------------------------------------- 1 | # NtpTimestamp 2 | The `NtpTimestamp` struct represents a [64-bit NTP timestamp](http://www.beaglesoft.com/Manual/page53.htm). 3 | 4 | This exists by itself to be re-used in other projects. 5 | 6 | -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d680ad74116c0d40845c575ca1a9a4f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/TimeConstants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiniNtp 4 | { 5 | public static class TimeConstants 6 | { 7 | public static readonly DateTime Epoch1900 = DateTime.Parse("1900-01-01 00:00:00.000"); 8 | public static readonly DateTime Epoch2036 = DateTime.Parse("2036-02-07 06:28:15"); 9 | 10 | public const int TicksPerSecond = (int) TimeSpan.TicksPerMillisecond * 1000; 11 | public const uint TimestampFractionsPerMs = 5000000; 12 | public const double FractionMillisecondMultiplier = 200 / (double) 1000000000; 13 | } 14 | } -------------------------------------------------------------------------------- /Runtime/Dependencies/NtpTimestamp/TimeConstants.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 751c22c7054c4bccae152cb14bcd4290 3 | timeCreated: 1571376565 -------------------------------------------------------------------------------- /Runtime/OscCore.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OscCore.Runtime", 3 | "references": [ 4 | "BlobHandles.Runtime", 5 | "NtpTimestamp" 6 | ], 7 | "optionalUnityReferences": [], 8 | "includePlatforms": [], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": true, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [] 16 | } -------------------------------------------------------------------------------- /Runtime/OscCore.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d1f21c757f944f4188b1b799669c761 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6115a8b616aae2f44a632961d9164326 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7dacbd1fa74b49c45b4ee035cf3f8cc5 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7fbb7d44147d4d32a6001baac70e50f6 3 | timeCreated: 1581276020 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscBlobMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [ExecuteInEditMode] 6 | [AddComponentMenu("OSC/Input/Blob Input")] 7 | public class OscBlobMessageHandler : MessageHandlerBase 8 | { 9 | public BlobUnityEvent OnMessageReceived; 10 | 11 | protected byte[] m_Buffer = new byte[128]; 12 | 13 | public byte[] Buffer => m_Buffer; 14 | public int LastReceivedBlobLength { get; private set; } 15 | 16 | protected override void ValueRead(OscMessageValues values) 17 | { 18 | LastReceivedBlobLength = values.ReadBlobElement(0, ref m_Buffer); 19 | } 20 | 21 | protected override void InvokeEvent() 22 | { 23 | OnMessageReceived.Invoke(m_Buffer, LastReceivedBlobLength); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscBlobMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 56ebe4ab0b484e07bf7454c3f6b04942 3 | timeCreated: 1581276569 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscBooleanMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Boolean Input")] 6 | public class OscBooleanMessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadBooleanElement(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscBooleanMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c05a0168890342f48a708c70f4930538 3 | timeCreated: 1581277735 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscColorMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Color Input")] 6 | public class OscColorMessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadColor32Element(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscColorMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 786b31c878784b46bcad78656936b8a6 3 | timeCreated: 1581250210 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscFloat64MessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Double Input")] 6 | public class OscFloat64MessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadFloat64Element(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscFloat64MessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9a92df49c48e471f9719af2c3363ff68 3 | timeCreated: 1573192421 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscFloatMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Float Input")] 6 | public class OscFloatMessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadFloatElement(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscFloatMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a5ae5bb5d1704c35aa20277630960b18 3 | timeCreated: 1573190563 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscInt64MessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Long Input")] 6 | public class OscInt64MessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadInt64Element(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscInt64MessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b65ddafb322547acb658591bfe1118c2 3 | timeCreated: 1573192459 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscIntMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Integer Input")] 6 | public class OscIntMessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadIntElement(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscIntMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f98b8931f42473c86594c4d60823231 3 | timeCreated: 1573190707 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscStringMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/String Input")] 6 | public class OscStringMessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | m_Value = values.ReadStringElement(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscStringMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b5beabcbc17443892c70f197665a627 3 | timeCreated: 1573190744 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscVector3MessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace OscCore 4 | { 5 | [AddComponentMenu("OSC/Input/Vector3 Input")] 6 | public class OscVector3MessageHandler : OscMessageHandler 7 | { 8 | protected override void ValueRead(OscMessageValues values) 9 | { 10 | var x = values.ReadFloatElement(0); 11 | var y = values.ReadFloatElement(1); 12 | var z = values.ReadFloatElement(2); 13 | m_Value = new Vector3(x, y, z); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Message Handlers/OscVector3MessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1b8c75ccb2a40e98a006b8c7070d038 3 | timeCreated: 1581279899 -------------------------------------------------------------------------------- /Runtime/Scripts/Component/OscMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Events; 3 | using UnityEngine.Serialization; 4 | 5 | namespace OscCore 6 | { 7 | [ExecuteInEditMode] 8 | public abstract class MessageHandlerBase : MonoBehaviour 9 | { 10 | [Tooltip("The receiver to handle messages from")] 11 | [FormerlySerializedAs("Receiver")] 12 | [SerializeField] 13 | protected OscReceiver m_Receiver; 14 | public OscReceiver Receiver => m_Receiver; 15 | 16 | [Tooltip("The OSC address to associate with this event. Must start with /")] 17 | [FormerlySerializedAs("Address")] 18 | [SerializeField] 19 | protected string m_Address = "/"; 20 | public string Address => m_Address; 21 | 22 | protected OscActionPair m_ActionPair; 23 | protected bool m_Registered; 24 | 25 | void OnEnable() 26 | { 27 | if (m_Receiver == null) 28 | m_Receiver = GetComponentInParent(); 29 | 30 | if (m_Registered || string.IsNullOrEmpty(Address)) 31 | return; 32 | 33 | if (m_Receiver != null && m_Receiver.Server != null) 34 | { 35 | m_ActionPair = new OscActionPair(ValueRead, InvokeEvent); 36 | Receiver.Server.TryAddMethodPair(Address, m_ActionPair); 37 | m_Registered = true; 38 | } 39 | } 40 | 41 | void OnDisable() 42 | { 43 | m_Registered = false; 44 | if (m_Receiver != null) 45 | m_Receiver.Server?.RemoveMethodPair(Address, m_ActionPair); 46 | } 47 | 48 | void OnValidate() 49 | { 50 | Utils.ValidateAddress(ref m_Address); 51 | } 52 | 53 | protected abstract void InvokeEvent(); 54 | 55 | protected abstract void ValueRead(OscMessageValues values); 56 | 57 | // Empty update method here so the component gets an enable checkbox 58 | protected virtual void Update() { } 59 | } 60 | 61 | [ExecuteInEditMode] 62 | public abstract class OscMessageHandler : MessageHandlerBase 63 | where TUnityEvent : UnityEvent 64 | { 65 | [FormerlySerializedAs("Handler")] 66 | public TUnityEvent OnMessageReceived; 67 | 68 | protected T m_Value; 69 | 70 | protected override void InvokeEvent() 71 | { 72 | OnMessageReceived.Invoke(m_Value); 73 | } 74 | } 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/OscMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 294fc1ca16c87d0489d93fea11cf326f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/OscReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace OscCore 5 | { 6 | /// Wraps an OscServer in a Unity Component 7 | [AddComponentMenu("OSC/OSC Receiver", int.MinValue)] 8 | [ExecuteInEditMode] 9 | public class OscReceiver : MonoBehaviour 10 | { 11 | [Tooltip("The local port to listen for incoming messages on")] 12 | [SerializeField] int m_Port = 9000; 13 | 14 | /// 15 | /// The local port to listen to incoming messages on. 16 | /// Setting this will destroy any existing server and create a new one on the new port 17 | /// 18 | public int Port 19 | { 20 | get => m_Port; 21 | set => SetPort(value); 22 | } 23 | 24 | /// True if this receiver is bound to its port and listening, false otherwise 25 | public bool Running { get; private set; } 26 | 27 | /// The underlying server that handles message receiving. 28 | public OscServer Server { get; private set; } 29 | 30 | void OnEnable() 31 | { 32 | OnStart(); 33 | } 34 | 35 | void Awake() 36 | { 37 | OnStart(); 38 | } 39 | 40 | void OnStart() 41 | { 42 | Server = OscServer.GetOrCreate(m_Port); 43 | Running = true; 44 | } 45 | 46 | void OnValidate() 47 | { 48 | m_Port = m_Port.ClampPort(); 49 | } 50 | 51 | void Update() 52 | { 53 | Server?.Update(); 54 | } 55 | 56 | void OnDestroy() 57 | { 58 | Server?.Dispose(); 59 | Server = null; 60 | } 61 | 62 | void SetPort(int newPort) 63 | { 64 | var clamped = newPort.ClampPort(); 65 | if (clamped != newPort) return; 66 | 67 | var oldValue = m_Port; 68 | var oldServer = Server; 69 | try 70 | { 71 | Server = OscServer.GetOrCreate(newPort); 72 | m_Port = newPort; 73 | oldServer?.Dispose(); 74 | } 75 | // if creating a new server throws for any reason, make sure to keep old settings 76 | catch (Exception e) 77 | { 78 | Debug.LogException(e, this); 79 | m_Port = oldValue; 80 | Server = oldServer; 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/OscReceiver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6291d1cd9c2bea94482c4d7a30f5ae2e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: -150 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Output.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9dfc6cd8cc8172c40a1639a70352526b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Output/OscSender.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Net; 3 | 4 | namespace OscCore 5 | { 6 | [ExecuteInEditMode] 7 | [AddComponentMenu("OSC/OSC Sender", int.MaxValue - 10)] 8 | public class OscSender : MonoBehaviour 9 | { 10 | [Tooltip("The IP address to send to")] 11 | [SerializeField] string m_IpAddress = "127.0.0.1"; 12 | 13 | [Tooltip("The port on the remote IP to send to")] 14 | [SerializeField] int m_Port = 7000; 15 | 16 | /// The IP address to send to 17 | public string IpAddress 18 | { 19 | get { return m_IpAddress; } 20 | set { 21 | if(IPAddress.TryParse(value, out var ip)){ 22 | m_IpAddress = value; 23 | ReInitialize(); 24 | } 25 | } 26 | } 27 | 28 | /// The port on the remote IP to send to 29 | public int Port 30 | { 31 | get { return m_Port; } 32 | set { 33 | m_Port = value; 34 | ReInitialize(); 35 | } 36 | } 37 | 38 | /// 39 | /// Handles serializing and sending messages. Use methods on this to send messages to the endpoint. 40 | /// 41 | public OscClient Client { get; protected set; } 42 | 43 | void OnEnable() 44 | { 45 | Setup(); 46 | } 47 | 48 | void Awake() 49 | { 50 | Setup(); 51 | } 52 | 53 | void OnValidate() 54 | { 55 | m_Port = m_Port.ClampPort(); 56 | } 57 | 58 | void Setup() 59 | { 60 | if(Client == null) 61 | Client = new OscClient(m_IpAddress, m_Port); 62 | } 63 | 64 | void ReInitialize() 65 | { 66 | Client = null; 67 | Setup(); 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Output/OscSender.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c1928c5603a46bc81a0f7aaa53b60bc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: -200 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/Output/PropertyOutput.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 31b433bc4acd2c848a34f71ebc0f3e17 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Component/UnityEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.Events; 4 | 5 | namespace OscCore 6 | { 7 | [Serializable] public class BoolUnityEvent : UnityEvent { } 8 | [Serializable] public class IntUnityEvent : UnityEvent { } 9 | [Serializable] public class LongUnityEvent : UnityEvent { } 10 | [Serializable] public class FloatUnityEvent : UnityEvent { } 11 | [Serializable] public class DoubleUnityEvent : UnityEvent { } 12 | [Serializable] public class StringUnityEvent : UnityEvent { } 13 | [Serializable] public class ColorUnityEvent : UnityEvent { } 14 | [Serializable] public class Vector3UnityEvent : UnityEvent { } 15 | [Serializable] public class BlobUnityEvent : UnityEvent { } 16 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Component/UnityEvents.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c7a914ab6824a8986d7f56979384c9e 3 | timeCreated: 1573191172 -------------------------------------------------------------------------------- /Runtime/Scripts/Constant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using UnityEditor; 4 | 5 | namespace OscCore 6 | { 7 | #if UNITY_EDITOR 8 | [InitializeOnLoad] 9 | #endif 10 | static class Constant 11 | { 12 | public const byte Comma = (byte) ','; 13 | public const byte ForwardSlash = (byte) '/'; 14 | 15 | public static readonly byte[] BundlePrefixBytes; 16 | public static readonly long BundlePrefixLong; 17 | 18 | static Constant() 19 | { 20 | var bundleBytes = Encoding.ASCII.GetBytes("#bundle "); 21 | bundleBytes[7] = 0; 22 | BundlePrefixBytes = bundleBytes; 23 | BundlePrefixLong = BitConverter.ToInt64(bundleBytes, 0); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Constant.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 03cacd7a6227402f96b2a28aa35a0cd3 3 | timeCreated: 1570091611 -------------------------------------------------------------------------------- /Runtime/Scripts/Delegates.cs: -------------------------------------------------------------------------------- 1 | using BlobHandles; 2 | 3 | namespace OscCore 4 | { 5 | public delegate void MonitorCallback(BlobString address, OscMessageValues values); 6 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Delegates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0247e529a51b4d0795e59aa26f607d16 3 | timeCreated: 1570922879 -------------------------------------------------------------------------------- /Runtime/Scripts/Enums.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 24504e8892a245fd984f66b13e932016 3 | timeCreated: 1581923111 -------------------------------------------------------------------------------- /Runtime/Scripts/Enums/AddressType.cs: -------------------------------------------------------------------------------- 1 | namespace OscCore 2 | { 3 | enum AddressType 4 | { 5 | Invalid, 6 | Pattern, 7 | Address 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Runtime/Scripts/Enums/AddressType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5396595a14e84e4d922d3ecef7cb4f1b 3 | timeCreated: 1581975897 -------------------------------------------------------------------------------- /Runtime/Scripts/Enums/TypeTag.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | /// 6 | /// type tags from http://opensoundcontrol.org/spec-1_0 7 | /// 8 | public enum TypeTag : byte 9 | { 10 | False = 70, // F, non-standard 11 | Infinitum = 73, // I, non-standard 12 | Nil = 78, // N, non-standard 13 | AltTypeString = 83, // S, non-standard 14 | True = 84, // T, non-standard 15 | ArrayStart = 91, // [, non-standard 16 | ArrayEnd = 93, // ], non-standard 17 | Blob = 98, // b, STANDARD 18 | AsciiChar32 = 99, // c, non-standard 19 | Float64 = 100, // d, non-standard 20 | Float32 = 102, // f, STANDARD 21 | Int64 = 104, // h, non-standard 22 | Int32 = 105, // i, STANDARD 23 | MIDI = 109, // m, non-standard 24 | Color32 = 114, // r, non-standard 25 | String = 115, // s, STANDARD 26 | TimeTag = 116 // t, non-standard 27 | } 28 | 29 | public static class TypeTagMethods 30 | { 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public static bool IsSupported(this TypeTag tag) 33 | { 34 | switch (tag) 35 | { 36 | case TypeTag.False: 37 | case TypeTag.Infinitum: 38 | case TypeTag.Nil: 39 | case TypeTag.AltTypeString: 40 | case TypeTag.True: 41 | case TypeTag.Blob: 42 | case TypeTag.AsciiChar32: 43 | case TypeTag.Float64: 44 | case TypeTag.Float32: 45 | case TypeTag.Int64: 46 | case TypeTag.Int32: 47 | case TypeTag.MIDI: 48 | case TypeTag.Color32: 49 | case TypeTag.String: 50 | case TypeTag.TimeTag: 51 | case TypeTag.ArrayStart: 52 | case TypeTag.ArrayEnd: 53 | return true; 54 | default: 55 | return false; 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /Runtime/Scripts/Enums/TypeTag.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5351d8ea62474f7f91cf2408222a626d 3 | timeCreated: 1570089029 -------------------------------------------------------------------------------- /Runtime/Scripts/Enums/VectorElementFilters.cs: -------------------------------------------------------------------------------- 1 | namespace OscCore 2 | { 3 | enum Vector3ElementFilter : byte 4 | { 5 | XYZ = 0, 6 | X = 1, 7 | Y = 2, 8 | Z = 3, 9 | XY = 4, 10 | XZ = 5, 11 | YZ = 6 12 | } 13 | 14 | enum Vector2ElementFilter : byte 15 | { 16 | XY = 0, 17 | X = 1, 18 | Y = 2, 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Runtime/Scripts/Enums/VectorElementFilters.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 437463bf322547919ab4075f5aa675cf 3 | timeCreated: 1581923004 -------------------------------------------------------------------------------- /Runtime/Scripts/Message.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7483d66ec5914f4e932374c4ab5e4e94 3 | timeCreated: 1570412369 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.AsciiChar.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | public sealed partial class OscMessageValues 6 | { 7 | /// 8 | /// Read a non-standard ascii char element. 9 | /// Checks the element type before reading & returns default if it does not have the 'c' type tag 10 | /// 11 | /// The element index 12 | /// The character value if the element has the right type tag, default otherwise 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | public char ReadAsciiCharElement(int index) 15 | { 16 | #if OSCCORE_SAFETY_CHECKS 17 | if (OutOfBounds(index)) return default; 18 | #endif 19 | switch (Tags[index]) 20 | { 21 | case TypeTag.AsciiChar32: 22 | // the ascii byte is placed at the end of the 4 bytes given for an element 23 | return (char) m_SharedBuffer[Offsets[index] + 3]; 24 | default: 25 | return default; 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.AsciiChar.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a92535bfc5af44cb92c3356bef579006 3 | timeCreated: 1570422616 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Blob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace OscCore 5 | { 6 | public sealed unsafe partial class OscMessageValues 7 | { 8 | const int k_ResizeByteHeadroom = 1024; 9 | 10 | /// 11 | /// Read a blob element. 12 | /// Checks the element type before reading, and does nothing if the element is not a blob. 13 | /// 14 | /// The element index 15 | /// 16 | /// The array to copy blob contents into. 17 | /// Will be resized if it lacks sufficient capacity 18 | /// 19 | /// The index in the copyTo array to start copying at 20 | /// The size of the blob if valid, 0 otherwise 21 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 22 | public int ReadBlobElement(int index, ref byte[] copyTo, int copyOffset = 0) 23 | { 24 | #if OSCCORE_SAFETY_CHECKS 25 | if (OutOfBounds(index)) return default; 26 | #endif 27 | switch (Tags[index]) 28 | { 29 | case TypeTag.Blob: 30 | var offset = Offsets[index]; 31 | var size = ReadIntIndex(offset); 32 | var dataStart = offset + 4; // skip the size int 33 | if (copyTo.Length - copyOffset <= size) 34 | Array.Resize(ref copyTo, size + copyOffset + k_ResizeByteHeadroom); 35 | 36 | Buffer.BlockCopy(m_SharedBuffer, dataStart, copyTo, copyOffset, size); 37 | return size; 38 | default: 39 | return default; 40 | } 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Blob.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 96eab224d41d4fdcafd46f6d51dd1fc8 3 | timeCreated: 1570423980 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Boolean.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | public sealed partial class OscMessageValues 6 | { 7 | /// 8 | /// Checks the element type before reading & returns default if it's not interpretable as a boolean. 9 | /// 10 | /// The element index 11 | /// The value of the element 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public bool ReadBooleanElement(int index) 14 | { 15 | #if OSCCORE_SAFETY_CHECKS 16 | if (OutOfBounds(index)) return default; 17 | #endif 18 | switch (Tags[index]) 19 | { 20 | case TypeTag.True: return true; 21 | case TypeTag.False: return false; 22 | case TypeTag.Int32: return ReadIntElementUnchecked(index) > 0; 23 | default: return default; 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Boolean.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3ea128be8ce64f6b90dfd93254639313 3 | timeCreated: 1570421615 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Color32.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using UnityEngine; 3 | 4 | namespace OscCore 5 | { 6 | public sealed unsafe partial class OscMessageValues 7 | { 8 | /// 9 | /// Read a single 32-bit RGBA color message element. 10 | /// Checks the element type before reading & returns default if it's not interpretable as a color. 11 | /// 12 | /// The element index 13 | /// The value of the element 14 | public Color32 ReadColor32Element(int index) 15 | { 16 | #if OSCCORE_SAFETY_CHECKS 17 | if (OutOfBounds(index)) return default; 18 | #endif 19 | var offset = Offsets[index]; 20 | switch (Tags[index]) 21 | { 22 | case TypeTag.Color32: 23 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 24 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 25 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 26 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 27 | return *SwapBufferColor32Ptr; 28 | default: 29 | return default; 30 | } 31 | } 32 | 33 | /// 34 | /// Read a single 32-bit RGBA color message element, without checking the type tag of the element. 35 | /// Only call this if you are really sure that the element at the given index is a valid Color32, 36 | /// as the performance difference is small. 37 | /// 38 | /// The element index 39 | /// The value of the element 40 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 41 | public Color32 ReadColor32ElementUnchecked(int index) 42 | { 43 | #if OSCCORE_SAFETY_CHECKS 44 | if (OutOfBounds(index)) return default; 45 | #endif 46 | var offset = Offsets[index]; 47 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 48 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 49 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 50 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 51 | return *SwapBufferColor32Ptr; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Color32.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7664411263334c28852865a0b55c8e4f 3 | timeCreated: 1570419200 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Float.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | public sealed unsafe partial class OscMessageValues 6 | { 7 | /// 8 | /// Read a single 32-bit float message element. 9 | /// Checks the element type before reading & returns 0 if it's not interpretable as a float. 10 | /// 11 | /// The element index 12 | /// The value of the element 13 | public float ReadFloatElement(int index) 14 | { 15 | #if OSCCORE_SAFETY_CHECKS 16 | if (OutOfBounds(index)) return default; 17 | #endif 18 | var offset = Offsets[index]; 19 | switch (Tags[index]) 20 | { 21 | case TypeTag.Float32: 22 | m_SwapBuffer32[0] = SharedBufferPtr[offset + 3]; 23 | m_SwapBuffer32[1] = SharedBufferPtr[offset + 2]; 24 | m_SwapBuffer32[2] = SharedBufferPtr[offset + 1]; 25 | m_SwapBuffer32[3] = SharedBufferPtr[offset]; 26 | return *SwapBuffer32Ptr; 27 | case TypeTag.Int32: 28 | return SharedBufferPtr[index ] << 24 | 29 | SharedBufferPtr[index + 1] << 16 | 30 | SharedBufferPtr[index + 2] << 8 | 31 | SharedBufferPtr[index + 3]; 32 | default: 33 | return default; 34 | } 35 | } 36 | 37 | /// 38 | /// Read a single 32-bit float message element, without checking the type tag of the element. 39 | /// Only call this if you are really sure that the element at the given index is a valid float, 40 | /// as the performance difference is small. 41 | /// 42 | /// The element index 43 | /// The value of the element 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public float ReadFloatElementUnchecked(int index) 46 | { 47 | #if OSCCORE_SAFETY_CHECKS 48 | if (OutOfBounds(index)) return default; 49 | #endif 50 | var offset = Offsets[index]; 51 | m_SwapBuffer32[0] = SharedBufferPtr[offset + 3]; 52 | m_SwapBuffer32[1] = SharedBufferPtr[offset + 2]; 53 | m_SwapBuffer32[2] = SharedBufferPtr[offset + 1]; 54 | m_SwapBuffer32[3] = SharedBufferPtr[offset]; 55 | return *SwapBuffer32Ptr; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Float.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0c28b0f57e3f47d78f6ae94bbba98f00 3 | timeCreated: 1570412435 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Float64.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace OscCore 5 | { 6 | public sealed unsafe partial class OscMessageValues 7 | { 8 | /// 9 | /// Read a single 64-bit float (double) message element. 10 | /// Checks the element type before reading & returns 0 if it's not interpretable as a double. 11 | /// 12 | /// The element index 13 | /// The value of the element 14 | public double ReadFloat64Element(int index) 15 | { 16 | #if OSCCORE_SAFETY_CHECKS 17 | if (OutOfBounds(index)) return default; 18 | #endif 19 | var offset = Offsets[index]; 20 | switch (Tags[index]) 21 | { 22 | case TypeTag.Float64: 23 | m_SwapBuffer64[7] = m_SharedBuffer[offset]; 24 | m_SwapBuffer64[6] = m_SharedBuffer[offset + 1]; 25 | m_SwapBuffer64[5] = m_SharedBuffer[offset + 2]; 26 | m_SwapBuffer64[4] = m_SharedBuffer[offset + 3]; 27 | m_SwapBuffer64[3] = m_SharedBuffer[offset + 4]; 28 | m_SwapBuffer64[2] = m_SharedBuffer[offset + 5]; 29 | m_SwapBuffer64[1] = m_SharedBuffer[offset + 6]; 30 | m_SwapBuffer64[0] = m_SharedBuffer[offset + 7]; 31 | return *SwapBuffer64Ptr; 32 | case TypeTag.Float32: 33 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 34 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 35 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 36 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 37 | return *SwapBuffer32Ptr; 38 | case TypeTag.Int64: 39 | long bigEndian = *(SharedBufferPtr + offset); 40 | return IPAddress.NetworkToHostOrder(bigEndian); 41 | case TypeTag.Int32: 42 | return m_SharedBuffer[index ] << 24 | 43 | m_SharedBuffer[index + 1] << 16 | 44 | m_SharedBuffer[index + 2] << 8 | 45 | m_SharedBuffer[index + 3]; 46 | } 47 | 48 | return default; 49 | } 50 | 51 | /// 52 | /// Read a single 64-bit float (double) message element, without checking the type tag of the element. 53 | /// Only call this if you are really sure that the element at the given index is a valid double, 54 | /// as the performance difference is small. 55 | /// 56 | /// The element index 57 | /// The value of the element 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public double ReadFloat64ElementUnchecked(int index) 60 | { 61 | #if OSCCORE_SAFETY_CHECKS 62 | if (OutOfBounds(index)) return default; 63 | #endif 64 | var offset = Offsets[index]; 65 | m_SwapBuffer64[7] = m_SharedBuffer[offset]; 66 | m_SwapBuffer64[6] = m_SharedBuffer[offset + 1]; 67 | m_SwapBuffer64[5] = m_SharedBuffer[offset + 2]; 68 | m_SwapBuffer64[4] = m_SharedBuffer[offset + 3]; 69 | m_SwapBuffer64[3] = m_SharedBuffer[offset + 4]; 70 | m_SwapBuffer64[2] = m_SharedBuffer[offset + 5]; 71 | m_SwapBuffer64[1] = m_SharedBuffer[offset + 6]; 72 | m_SwapBuffer64[0] = m_SharedBuffer[offset + 7]; 73 | return *SwapBuffer64Ptr; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Float64.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5f3e252aee244137b09fa44cf29f00b8 3 | timeCreated: 1570416275 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Int.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | public sealed unsafe partial class OscMessageValues 6 | { 7 | /// 8 | /// Read a single 32-bit integer message element. 9 | /// Checks the element type before reading & returns 0 if it's not interpretable as a integer. 10 | /// 11 | /// The element index 12 | /// The value of the element 13 | public int ReadIntElement(int index) 14 | { 15 | #if OSCCORE_SAFETY_CHECKS 16 | if (OutOfBounds(index)) return default; 17 | #endif 18 | var offset = Offsets[index]; 19 | switch (Tags[index]) 20 | { 21 | case TypeTag.Int32: 22 | return m_SharedBuffer[offset ] << 24 | 23 | m_SharedBuffer[offset + 1] << 16 | 24 | m_SharedBuffer[offset + 2] << 8 | 25 | m_SharedBuffer[offset + 3]; 26 | case TypeTag.Float32: 27 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 28 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 29 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 30 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 31 | float f = *SwapBuffer32Ptr; 32 | return (int) f; 33 | default: 34 | return default; 35 | } 36 | } 37 | 38 | /// 39 | /// Read a single 32-bit int message element, without checking the type tag of the element. 40 | /// Only call this if you are really sure that the element at the given index is a valid integer, 41 | /// as the performance difference is small. 42 | /// 43 | /// The element index 44 | /// The value of the element 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public int ReadIntElementUnchecked(int index) 47 | { 48 | #if OSCCORE_SAFETY_CHECKS 49 | if (OutOfBounds(index)) return default; 50 | #endif 51 | var offset = Offsets[index]; 52 | return m_SharedBuffer[offset ] << 24 | 53 | m_SharedBuffer[offset + 1] << 16 | 54 | m_SharedBuffer[offset + 2] << 8 | 55 | m_SharedBuffer[offset + 3]; 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | internal uint ReadUIntIndex(int index) 60 | { 61 | m_SwapBuffer32[0] = m_SharedBuffer[index + 3]; 62 | m_SwapBuffer32[1] = m_SharedBuffer[index + 2]; 63 | m_SwapBuffer32[2] = m_SharedBuffer[index + 1]; 64 | m_SwapBuffer32[3] = m_SharedBuffer[index]; 65 | return *SwapBuffer32UintPtr; 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | internal int ReadIntIndex(int index) 70 | { 71 | return m_SharedBuffer[index ] << 24 | 72 | m_SharedBuffer[index + 1] << 16 | 73 | m_SharedBuffer[index + 2] << 8 | 74 | m_SharedBuffer[index + 3]; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Int.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27342c7800824a1f876f2a02dfde12c3 3 | timeCreated: 1570414077 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Int64.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace OscCore 5 | { 6 | public sealed unsafe partial class OscMessageValues 7 | { 8 | /// 9 | /// Read a single 64-bit integer (long) message element. 10 | /// Checks the element type before reading & returns 0 if it's not interpretable as a long. 11 | /// 12 | /// The element index 13 | /// The value of the element 14 | public long ReadInt64Element(int index) 15 | { 16 | #if OSCCORE_SAFETY_CHECKS 17 | if (OutOfBounds(index)) return default; 18 | #endif 19 | var offset = Offsets[index]; 20 | switch (Tags[index]) 21 | { 22 | case TypeTag.Int64: 23 | // TODO - optimize 24 | long bigEndian = *(SharedBufferPtr + offset); 25 | return IPAddress.NetworkToHostOrder(bigEndian); 26 | case TypeTag.Int32: 27 | return m_SharedBuffer[offset ] << 24 | 28 | m_SharedBuffer[offset + 1] << 16 | 29 | m_SharedBuffer[offset + 2] << 8 | 30 | m_SharedBuffer[offset + 3]; 31 | case TypeTag.Float64: 32 | m_SwapBuffer64[7] = m_SharedBuffer[offset]; 33 | m_SwapBuffer64[6] = m_SharedBuffer[offset + 1]; 34 | m_SwapBuffer64[5] = m_SharedBuffer[offset + 2]; 35 | m_SwapBuffer64[4] = m_SharedBuffer[offset + 3]; 36 | m_SwapBuffer64[3] = m_SharedBuffer[offset + 4]; 37 | m_SwapBuffer64[2] = m_SharedBuffer[offset + 5]; 38 | m_SwapBuffer64[1] = m_SharedBuffer[offset + 6]; 39 | m_SwapBuffer64[0] = m_SharedBuffer[offset + 7]; 40 | double d = *SwapBuffer64Ptr; 41 | return (long) d; 42 | case TypeTag.Float32: 43 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 44 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 45 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 46 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 47 | float f = *SwapBuffer32Ptr; 48 | return (long) f; 49 | default: 50 | return default; 51 | } 52 | } 53 | 54 | /// 55 | /// Read a single 64-bit integer (long) message element, without checking the type tag of the element. 56 | /// Only call this if you are really sure that the element at the given index is a valid long, 57 | /// as the performance difference is small. 58 | /// 59 | /// The element index 60 | /// The value of the element 61 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 62 | public long ReadInt64ElementUnchecked(int index) 63 | { 64 | #if OSCCORE_SAFETY_CHECKS 65 | if (OutOfBounds(index)) return default; 66 | #endif 67 | long bigEndian = *(SharedBufferPtr + Offsets[index]); 68 | return IPAddress.NetworkToHostOrder(bigEndian); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Int64.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: da8a46951fbd435ca664d3bcef0fb2ca 3 | timeCreated: 1570418235 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.MIDI.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | public sealed unsafe partial class OscMessageValues 6 | { 7 | /// 8 | /// Read a single MIDI message element. 9 | /// Checks the element type before reading & returns default if it's not interpretable as a MIDI message. 10 | /// 11 | /// The element index 12 | /// The value of the element 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | public MidiMessage ReadMidiElement(int index) 15 | { 16 | #if OSCCORE_SAFETY_CHECKS 17 | if (OutOfBounds(index)) return default; 18 | #endif 19 | switch (Tags[index]) 20 | { 21 | case TypeTag.MIDI: 22 | return *(MidiMessage*) (SharedBufferPtr + Offsets[index]); 23 | default: 24 | return default; 25 | } 26 | } 27 | 28 | /// 29 | /// Read a single MIDI message element, without checking the type tag of the element. 30 | /// Only call this if you are really sure that the element at the given index is a valid MIDI message, 31 | /// as the performance difference is small. 32 | /// 33 | /// The element index 34 | /// The value of the element 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public MidiMessage ReadMidiElementUnchecked(int index) 37 | { 38 | #if OSCCORE_SAFETY_CHECKS 39 | if (OutOfBounds(index)) return default; 40 | #endif 41 | return *(MidiMessage*) (SharedBufferPtr + Offsets[index]); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.MIDI.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d44a910ca144f71a34e9b4b037b9bbb 3 | timeCreated: 1570417202 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Null.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace OscCore 4 | { 5 | public sealed partial class OscMessageValues 6 | { 7 | /// 8 | /// Read a non-standard 'nil' or 'infinitum' tag, which has no value 9 | /// 10 | /// The element index 11 | /// True if the element's type tag is nil or infinitum, false otherwise 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public bool ReadNilOrInfinitumElement(int index) 14 | { 15 | #if OSCCORE_SAFETY_CHECKS 16 | if (OutOfBounds(index)) return default; 17 | #endif 18 | switch (Tags[index]) 19 | { 20 | case TypeTag.Infinitum: 21 | case TypeTag.Nil: return true; 22 | default: return default; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Null.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 09fa10454ed44868975a75e23d2beb1d 3 | timeCreated: 1570421936 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.String.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Net; 3 | using System.Runtime.CompilerServices; 4 | using System.Text; 5 | 6 | namespace OscCore 7 | { 8 | public sealed unsafe partial class OscMessageValues 9 | { 10 | /// 11 | /// Read a single string message element. 12 | /// Checks the element type before reading & returns empty if it's not interpretable as a string. 13 | /// 14 | /// The element index 15 | /// The value of the element 16 | public string ReadStringElement(int index) 17 | { 18 | #if OSCCORE_SAFETY_CHECKS 19 | if (OutOfBounds(index)) return default; 20 | #endif 21 | var offset = Offsets[index]; 22 | switch (Tags[index]) 23 | { 24 | case TypeTag.AltTypeString: 25 | case TypeTag.String: 26 | var length = 0; 27 | while (m_SharedBuffer[offset + length] != byte.MinValue) length++; 28 | return Encoding.UTF8.GetString(m_SharedBuffer, offset, length); 29 | case TypeTag.Float64: 30 | m_SwapBuffer64[7] = m_SharedBuffer[offset]; 31 | m_SwapBuffer64[6] = m_SharedBuffer[offset + 1]; 32 | m_SwapBuffer64[5] = m_SharedBuffer[offset + 2]; 33 | m_SwapBuffer64[4] = m_SharedBuffer[offset + 3]; 34 | m_SwapBuffer64[3] = m_SharedBuffer[offset + 4]; 35 | m_SwapBuffer64[2] = m_SharedBuffer[offset + 5]; 36 | m_SwapBuffer64[1] = m_SharedBuffer[offset + 6]; 37 | m_SwapBuffer64[0] = m_SharedBuffer[offset + 7]; 38 | double f64 = *SwapBuffer64Ptr; 39 | return f64.ToString(CultureInfo.CurrentCulture); 40 | case TypeTag.Float32: 41 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 42 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 43 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 44 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 45 | float f32 = *SwapBuffer32Ptr; 46 | return f32.ToString(CultureInfo.CurrentCulture); 47 | case TypeTag.Int64: 48 | var i64 = IPAddress.NetworkToHostOrder(m_SharedBuffer[offset]); 49 | return i64.ToString(CultureInfo.CurrentCulture); 50 | case TypeTag.Int32: 51 | int i32 = m_SharedBuffer[offset ] << 24 | 52 | m_SharedBuffer[offset + 1] << 16 | 53 | m_SharedBuffer[offset + 2] << 8 | 54 | m_SharedBuffer[offset + 3]; 55 | return i32.ToString(CultureInfo.CurrentCulture); 56 | case TypeTag.False: 57 | return "False"; 58 | case TypeTag.True: 59 | return "True"; 60 | case TypeTag.Nil: 61 | return "Nil"; 62 | case TypeTag.Infinitum: 63 | return "Infinitum"; 64 | case TypeTag.Color32: 65 | m_SwapBuffer32[0] = m_SharedBuffer[offset + 3]; 66 | m_SwapBuffer32[1] = m_SharedBuffer[offset + 2]; 67 | m_SwapBuffer32[2] = m_SharedBuffer[offset + 1]; 68 | m_SwapBuffer32[3] = m_SharedBuffer[offset]; 69 | var color32 = *SwapBufferColor32Ptr; 70 | return color32.ToString(); 71 | case TypeTag.MIDI: 72 | var midiPtr = SharedBufferPtr + offset; 73 | var midi = *(MidiMessage*) midiPtr; 74 | return midi.ToString(); 75 | case TypeTag.AsciiChar32: 76 | // ascii chars are encoded in the last byte of the 4-byte block 77 | return ((char) m_SharedBuffer[offset + 3]).ToString(); 78 | default: 79 | return string.Empty; 80 | } 81 | } 82 | 83 | /// 84 | /// Read a single string message element as bytes. 85 | /// 86 | /// The element index 87 | /// The byte array to copy the string's bytes to 88 | /// The byte length of the string 89 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 90 | public int ReadStringElementBytes(int index, byte[] copyTo) 91 | { 92 | #if OSCCORE_SAFETY_CHECKS 93 | if (OutOfBounds(index)) return default; 94 | #endif 95 | switch (Tags[index]) 96 | { 97 | case TypeTag.AltTypeString: 98 | case TypeTag.String: 99 | int i; 100 | var offset = Offsets[index]; 101 | for (i = offset; i < m_SharedBuffer.Length; i++) 102 | { 103 | byte b = m_SharedBuffer[i]; 104 | if (b == byte.MinValue) break; 105 | copyTo[i - offset] = b; 106 | } 107 | return i - offset; 108 | default: 109 | return default; 110 | } 111 | } 112 | 113 | /// 114 | /// Read a single string message element as bytes. 115 | /// 116 | /// The element index 117 | /// The byte array to copy the string's bytes to 118 | /// The index in the copyTo array to start copying at 119 | /// The byte length of the string 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public int ReadStringElementBytes(int index, byte[] copyTo, int copyOffset) 122 | { 123 | #if OSCCORE_SAFETY_CHECKS 124 | if (OutOfBounds(index)) return default; 125 | #endif 126 | switch (Tags[index]) 127 | { 128 | case TypeTag.AltTypeString: 129 | case TypeTag.String: 130 | int i; 131 | var offset = Offsets[index]; 132 | // when this is subtracted from i, it's the same as i - offset + copyOffset 133 | var copyStartOffset = offset - copyOffset; 134 | for (i = offset; i < m_SharedBuffer.Length; i++) 135 | { 136 | byte b = m_SharedBuffer[i]; 137 | if (b == byte.MinValue) break; 138 | copyTo[i - copyStartOffset] = b; 139 | } 140 | 141 | return i - offset; 142 | default: 143 | return default; 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.String.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a9d90a709f5c429099eb32c8100b9949 3 | timeCreated: 1570419766 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Timestamp.cs: -------------------------------------------------------------------------------- 1 | using MiniNtp; 2 | 3 | namespace OscCore 4 | { 5 | public sealed unsafe partial class OscMessageValues 6 | { 7 | /// 8 | /// Read a single NTP timestamp message element. 9 | /// Checks the element type before reading & returns 0 if it's not interpretable as a integer. 10 | /// 11 | /// The element index 12 | /// The value of the element 13 | public NtpTimestamp ReadTimestampElement(int index) 14 | { 15 | #if OSCCORE_SAFETY_CHECKS 16 | if (OutOfBounds(index)) return default; 17 | #endif 18 | switch (Tags[index]) 19 | { 20 | case TypeTag.TimeTag: 21 | return NtpTimestamp.FromBigEndianBytes(SharedBufferPtr + Offsets[index]); 22 | default: 23 | return default; 24 | } 25 | } 26 | 27 | /// 28 | /// Read a single NTP timestamp message element, without checking the type tag of the element. 29 | /// Only call this if you are really sure that the element at the given index is a valid timestamp, 30 | /// as the performance difference is small. 31 | /// 32 | /// The element index 33 | /// The value of the element 34 | public NtpTimestamp ReadTimestampElementUnchecked(int index) 35 | { 36 | #if OSCCORE_SAFETY_CHECKS 37 | if (OutOfBounds(index)) return default; 38 | #endif 39 | var ptr = SharedBufferPtr + Offsets[index]; 40 | return NtpTimestamp.FromBigEndianBytes(ptr); 41 | } 42 | 43 | internal NtpTimestamp ReadTimestampIndex(int index) 44 | { 45 | var ptr = SharedBufferPtr + index; 46 | 47 | var bSeconds = *(uint*) ptr; 48 | // swap bytes from big to little endian 49 | uint seconds = (bSeconds & 0x000000FFU) << 24 | (bSeconds & 0x0000FF00U) << 8 | 50 | (bSeconds & 0x00FF0000U) >> 8 | (bSeconds & 0xFF000000U) >> 24; 51 | 52 | var bFractions = *(uint*) (ptr + 4); 53 | uint fractions = (bFractions & 0x000000FFU) << 24 | (bFractions & 0x0000FF00U) << 8 | 54 | (bFractions & 0x00FF0000U) >> 8 | (bFractions & 0xFF000000U) >> 24; 55 | 56 | return new NtpTimestamp(seconds, fractions); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.Timestamp.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 984099d57714409bb5cfcde1af5983e6 3 | timeCreated: 1570434433 -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using UnityEngine; 5 | using Debug = UnityEngine.Debug; 6 | 7 | // allow tests to modify things as if in the same assembly 8 | [assembly:InternalsVisibleTo("OscCore.Tests.Editor")] 9 | 10 | namespace OscCore 11 | { 12 | /// 13 | /// Represents the tags and values associated with a received OSC message 14 | /// 15 | public sealed unsafe partial class OscMessageValues 16 | { 17 | // the buffer where we read messages from - usually provided + filled by a socket reader 18 | readonly byte[] m_SharedBuffer; 19 | readonly byte* SharedBufferPtr; 20 | // used to swap bytes for 32-bit numbers when reading 21 | readonly byte[] m_SwapBuffer32 = new byte[4]; 22 | readonly float* SwapBuffer32Ptr; 23 | readonly uint* SwapBuffer32UintPtr; 24 | readonly Color32* SwapBufferColor32Ptr; 25 | readonly GCHandle m_Swap32Handle; 26 | // used to swap bytes for 64-bit numbers when reading 27 | readonly byte[] m_SwapBuffer64 = new byte[8]; 28 | readonly double* SwapBuffer64Ptr; 29 | readonly GCHandle m_Swap64Handle; 30 | 31 | /// 32 | /// All type tags in the message. 33 | /// All values past index >= ElementCount are junk data and should NEVER BE USED! 34 | /// 35 | internal readonly TypeTag[] Tags; 36 | 37 | /// 38 | /// Indexes into the shared buffer associated with each message element 39 | /// All values at index >= ElementCount are junk data and should NEVER BE USED! 40 | /// 41 | internal readonly int[] Offsets; 42 | 43 | /// The number of elements in the OSC Message 44 | public int ElementCount { get; internal set; } 45 | 46 | internal OscMessageValues(byte[] buffer, int elementCapacity = 8) 47 | { 48 | ElementCount = 0; 49 | Tags = new TypeTag[elementCapacity]; 50 | Offsets = new int[elementCapacity]; 51 | m_SharedBuffer = buffer; 52 | 53 | fixed (byte* bufferPtr = buffer) { SharedBufferPtr = bufferPtr; } 54 | 55 | // pin byte swap buffers in place, so that we can count on the pointers never changing 56 | m_Swap32Handle = GCHandle.Alloc(m_SwapBuffer32, GCHandleType.Pinned); 57 | var swap32Ptr = m_Swap32Handle.AddrOfPinnedObject(); 58 | SwapBuffer32Ptr = (float*) swap32Ptr; 59 | SwapBuffer32UintPtr = (uint*) swap32Ptr; 60 | SwapBufferColor32Ptr = (Color32*) (byte*) swap32Ptr; 61 | 62 | SwapBuffer64Ptr = Utils.PinPtr(m_SwapBuffer64, out m_Swap64Handle); 63 | } 64 | 65 | ~OscMessageValues() 66 | { 67 | m_Swap32Handle.Free(); 68 | m_Swap64Handle.Free(); 69 | } 70 | 71 | /// Execute a method for every element in the message 72 | /// A method that takes in the index and type tag for an element 73 | public void ForEachElement(Action elementAction) 74 | { 75 | for (int i = 0; i < ElementCount; i++) 76 | elementAction(i, Tags[i]); 77 | } 78 | 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | bool OutOfBounds(int index) 81 | { 82 | if (index >= ElementCount) 83 | { 84 | Debug.LogError($"Tried to read message element index {index}, but there are only {ElementCount} elements"); 85 | return true; 86 | } 87 | 88 | return false; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Message/OscMessageValues.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79fb4efbcea84ee88e4b7e7631bf35a9 3 | timeCreated: 1570331121 -------------------------------------------------------------------------------- /Runtime/Scripts/OscActionPair.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OscCore 4 | { 5 | /// 6 | /// A pair of methods associated with an OSC address 7 | /// 8 | public class OscActionPair 9 | { 10 | /// 11 | /// A method executed immediately when the a message is received at the associated OSC address, on the server background thread. 12 | /// All message values must be read during this callback, as the data it points to may be overwritten afterwards. 13 | /// 14 | public readonly Action ValueRead; 15 | 16 | /// 17 | /// An optional method, which will be queued for execution on the main thread in the next frame after the message was received. 18 | /// This is useful for UnityEvents and anything that needs a main thread only Unity api. 19 | /// 20 | public readonly Action MainThreadQueued; 21 | 22 | public OscActionPair(Action valueRead, Action mainThreadQueued = null) 23 | { 24 | const string nullWarning = "Value read callbacks required!"; 25 | ValueRead = valueRead ?? throw new ArgumentNullException(nameof(valueRead), nullWarning); 26 | MainThreadQueued = mainThreadQueued; 27 | } 28 | 29 | public static OscActionPair operator + (OscActionPair l, OscActionPair r) 30 | { 31 | var mainThread = l.MainThreadQueued == null ? r.MainThreadQueued : l.MainThreadQueued + r.MainThreadQueued; 32 | var valueRead = l.ValueRead + r.ValueRead; 33 | return new OscActionPair(valueRead, mainThread); 34 | } 35 | 36 | public static OscActionPair operator - (OscActionPair l, OscActionPair r) 37 | { 38 | var mainThread = l.MainThreadQueued == null ? r.MainThreadQueued : l.MainThreadQueued - r.MainThreadQueued; 39 | var valueRead = l.ValueRead - r.ValueRead; 40 | return new OscActionPair(valueRead, mainThread); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Runtime/Scripts/OscActionPair.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e0d23502e4d4e62b91b4f9f4254964c 3 | timeCreated: 1571821994 -------------------------------------------------------------------------------- /Runtime/Scripts/OscAddressSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace OscCore 7 | { 8 | public sealed class OscAddressSpace 9 | { 10 | const int k_DefaultPatternCapacity = 8; 11 | const int k_DefaultCapacity = 16; 12 | 13 | StringBuilder escapedStringBuilder = new StringBuilder(); 14 | HashSet specialRegexCharacters = new HashSet(new char[] { '.', '^', '$', '*', '+', '?', '{', '}', '[', ']', '\\', '|', '(', ')' }); 15 | 16 | internal readonly OscAddressMethods AddressToMethod; 17 | 18 | // Keep a list of registered address patterns and the methods they're associated with just like addresses 19 | internal int PatternCount; 20 | internal Regex[] Patterns = new Regex[k_DefaultPatternCapacity]; 21 | internal OscActionPair[] PatternMethods = new OscActionPair[k_DefaultPatternCapacity]; 22 | 23 | readonly Queue FreedPatternIndices = new Queue(); 24 | readonly Dictionary PatternStringToIndex = new Dictionary(); 25 | 26 | public int HandlerCount => AddressToMethod.HandleToValue.Count; 27 | 28 | public IEnumerable Addresses => AddressToMethod.SourceToBlob.Keys; 29 | 30 | public OscAddressSpace(int startingCapacity = k_DefaultCapacity) 31 | { 32 | AddressToMethod = new OscAddressMethods(startingCapacity); 33 | } 34 | 35 | public bool TryAddMethod(string address, OscActionPair onReceived) 36 | { 37 | if (string.IsNullOrEmpty(address) || onReceived == null) 38 | return false; 39 | 40 | switch (OscParser.GetAddressType(address)) 41 | { 42 | case AddressType.Address: 43 | AddressToMethod.Add(address, onReceived); 44 | return true; 45 | case AddressType.Pattern: 46 | int index; 47 | // if a method has already been registered for this pattern, add the new delegate 48 | if (PatternStringToIndex.TryGetValue(address, out index)) 49 | { 50 | PatternMethods[index] += onReceived; 51 | return true; 52 | } 53 | 54 | if (FreedPatternIndices.Count > 0) 55 | { 56 | index = FreedPatternIndices.Dequeue(); 57 | } 58 | else 59 | { 60 | index = PatternCount; 61 | if (index >= Patterns.Length) 62 | { 63 | var newSize = Patterns.Length * 2; 64 | Array.Resize(ref Patterns, newSize); 65 | Array.Resize(ref PatternMethods, newSize); 66 | } 67 | } 68 | 69 | Regex regex = null; 70 | try 71 | { 72 | regex = new Regex(address); //don't escape the address if we don't need to 73 | } 74 | catch (ArgumentException e) 75 | { 76 | try 77 | { 78 | regex = new Regex(EscapeRegexSpecialCharacters(address)); //if we fail, try to escape the address for the regex 79 | } 80 | catch (Exception) 81 | { 82 | throw e; //throw original error if still can't parse even with escaped RegEx 83 | } 84 | } 85 | 86 | Patterns[index] = regex; 87 | PatternMethods[index] = onReceived; 88 | PatternStringToIndex[address] = index; 89 | PatternCount++; 90 | return true; 91 | default: 92 | return false; 93 | } 94 | } 95 | 96 | public bool RemoveAddressMethod(string address) 97 | { 98 | if (string.IsNullOrEmpty(address)) 99 | return false; 100 | 101 | switch (OscParser.GetAddressType(address)) 102 | { 103 | case AddressType.Address: 104 | return AddressToMethod.RemoveAddress(address); 105 | default: 106 | return false; 107 | } 108 | } 109 | 110 | public bool RemoveMethod(string address, OscActionPair onReceived) 111 | { 112 | if (string.IsNullOrEmpty(address) || onReceived == null) 113 | return false; 114 | 115 | switch (OscParser.GetAddressType(address)) 116 | { 117 | case AddressType.Address: 118 | return AddressToMethod.Remove(address, onReceived); 119 | case AddressType.Pattern: 120 | if (!PatternStringToIndex.TryGetValue(address, out var patternIndex)) 121 | return false; 122 | 123 | var method = PatternMethods[patternIndex].ValueRead; 124 | if (method.GetInvocationList().Length == 1) 125 | { 126 | Patterns[patternIndex] = null; 127 | PatternMethods[patternIndex] = null; 128 | } 129 | else 130 | { 131 | PatternMethods[patternIndex] -= onReceived; 132 | } 133 | 134 | PatternCount--; 135 | FreedPatternIndices.Enqueue(patternIndex); 136 | return PatternStringToIndex.Remove(address); 137 | default: 138 | return false; 139 | } 140 | } 141 | 142 | /// 143 | /// Try to match an address against all known address patterns, 144 | /// and add a handler for the address if a pattern is matched 145 | /// 146 | /// The address to match 147 | /// 148 | /// True if a match was found, false otherwise 149 | public bool TryMatchPatternHandler(string address, List allMatchedMethods) 150 | { 151 | if (!OscParser.AddressIsValid(address)) 152 | return false; 153 | 154 | allMatchedMethods.Clear(); 155 | 156 | bool any = false; 157 | for (var i = 0; i < PatternCount; i++) 158 | { 159 | if (Patterns[i].IsMatch(address)) 160 | { 161 | var handler = PatternMethods[i]; 162 | AddressToMethod.Add(address, handler); 163 | any = true; 164 | } 165 | } 166 | 167 | return any; 168 | } 169 | 170 | string EscapeRegexSpecialCharacters(string input) 171 | { 172 | if (string.IsNullOrEmpty(input)) 173 | { 174 | return input; 175 | } 176 | 177 | // Using StringBuilder for efficiency when dealing with potentially large strings and multiple replacements 178 | escapedStringBuilder.Clear(); 179 | 180 | foreach (char c in input) 181 | { 182 | // If the current character is a special character, prepend it with a backslash 183 | if (specialRegexCharacters.Contains(c)) 184 | { 185 | escapedStringBuilder.Append('\\'); 186 | } 187 | 188 | escapedStringBuilder.Append(c); 189 | } 190 | 191 | return escapedStringBuilder.ToString(); 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /Runtime/Scripts/OscAddressSpace.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de687d26a3dc4a9385afe3f0108c4aaf 3 | timeCreated: 1570860098 -------------------------------------------------------------------------------- /Runtime/Scripts/OscClient.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4f0940aa31fa45dd86d8d27795718b16 3 | timeCreated: 1571023061 -------------------------------------------------------------------------------- /Runtime/Scripts/OscParser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 44aa475a6d6a9864395997797e888078 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/OscServer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe8074b576bf4698af106ea42645b596 3 | timeCreated: 1570860040 -------------------------------------------------------------------------------- /Runtime/Scripts/OscSocket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Threading; 5 | using Unity.Profiling; 6 | using UnityEngine; 7 | using UnityEngine.Profiling; 8 | 9 | namespace OscCore 10 | { 11 | sealed class OscSocket : IDisposable 12 | { 13 | #if UNITY_EDITOR 14 | static readonly ProfilerMarker k_ProfilerMarker = new ProfilerMarker("Receive OSC"); 15 | #endif 16 | readonly Socket m_Socket; 17 | readonly Thread m_Thread; 18 | bool m_Disposed; 19 | bool m_Started; 20 | 21 | AutoResetEvent m_ThreadWakeup; 22 | bool m_CloseRequested; 23 | 24 | public int Port { get; } 25 | public OscServer Server { get; set; } 26 | 27 | public OscSocket(int port) 28 | { 29 | Port = port; 30 | m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) { ReceiveTimeout = int.MaxValue }; 31 | m_Thread = new Thread(Serve); 32 | } 33 | 34 | public void Start() 35 | { 36 | // make sure redundant calls don't do anything after the first 37 | if (m_Started) return; 38 | 39 | m_Disposed = false; 40 | if (!m_Socket.IsBound) 41 | m_Socket.Bind(new IPEndPoint(IPAddress.Any, Port)); 42 | 43 | m_ThreadWakeup = new AutoResetEvent(false); 44 | 45 | m_Thread.Start(); 46 | m_Started = true; 47 | } 48 | 49 | void Serve() 50 | { 51 | #if UNITY_EDITOR 52 | Profiler.BeginThreadProfiling("OscCore", "Server"); 53 | #endif 54 | var buffer = Server.Parser.Buffer; 55 | var socket = m_Socket; 56 | 57 | while (!m_Disposed) 58 | { 59 | try 60 | { 61 | int receivedByteCount = 0; 62 | socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, result => { 63 | try 64 | { 65 | receivedByteCount = socket.EndReceive(result); 66 | } 67 | catch (Exception e) 68 | { 69 | if (!m_Disposed && !m_CloseRequested) Debug.LogException(e); 70 | } 71 | finally 72 | { 73 | // even if this runs sync, the wakeup will stay signalled until the next WaitOne: 74 | // https://learn.microsoft.com/en-us/dotnet/api/system.threading.eventwaithandle.set?view=netframework-4.8#remarks 75 | m_ThreadWakeup.Set(); 76 | } 77 | }, null); 78 | 79 | // wait for the receive to complete, OR for Dispose to be called 80 | m_ThreadWakeup.WaitOne(); 81 | 82 | if (m_CloseRequested) 83 | { 84 | m_Socket.Close(); 85 | m_Socket.Dispose(); 86 | break; 87 | } 88 | 89 | if (receivedByteCount == 0) continue; 90 | #if UNITY_EDITOR 91 | k_ProfilerMarker.Begin(); 92 | Server.ParseBuffer(receivedByteCount); 93 | k_ProfilerMarker.End(); 94 | #else 95 | Server.ParseBuffer(receivedByteCount); 96 | #endif 97 | Profiler.EndSample(); 98 | } 99 | // a read timeout can result in a socket exception, should just be ok to ignore 100 | catch (SocketException) { } 101 | catch (ThreadAbortException) {} 102 | catch (Exception e) 103 | { 104 | if (!m_Disposed) Debug.LogException(e); 105 | break; 106 | } 107 | } 108 | 109 | m_ThreadWakeup.Dispose(); 110 | 111 | #if UNITY_EDITOR 112 | Profiler.EndThreadProfiling(); 113 | #endif 114 | } 115 | 116 | public void Dispose() 117 | { 118 | if (m_Disposed) return; 119 | if (m_ThreadWakeup != null) 120 | { 121 | // thread running, let it dispose itself async 122 | m_CloseRequested = true; 123 | m_ThreadWakeup.Set(); 124 | } 125 | else 126 | { 127 | // try close directly 128 | m_Socket.Close(); 129 | m_Socket.Dispose(); 130 | } 131 | m_Disposed = true; 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /Runtime/Scripts/OscSocket.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 01efa61da4e5478e83dac680df2b37bc 3 | timeCreated: 1581910116 -------------------------------------------------------------------------------- /Runtime/Scripts/OscWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c92b8c2028c04e779ccee0c92424cb89 3 | timeCreated: 1571011333 -------------------------------------------------------------------------------- /Runtime/Scripts/Structs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a07c4d2265c4fcc9af608c18683df8e 3 | timeCreated: 1570435370 -------------------------------------------------------------------------------- /Runtime/Scripts/Structs/MidiMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace OscCore 5 | { 6 | [StructLayout(LayoutKind.Explicit)] 7 | public struct MidiMessage : IEquatable 8 | { 9 | [FieldOffset(0)] readonly int data; 10 | 11 | [FieldOffset(0)] public readonly byte PortId; 12 | [FieldOffset(1)] public readonly byte Status; 13 | [FieldOffset(2)] public readonly byte Data1; 14 | [FieldOffset(3)] public readonly byte Data2; 15 | 16 | public MidiMessage(byte[] bytes, int offset) 17 | { 18 | data = 0; 19 | PortId = bytes[offset]; 20 | Status = bytes[offset + 1]; 21 | Data1 = bytes[offset + 2]; 22 | Data2 = bytes[offset + 3]; 23 | } 24 | 25 | public MidiMessage(byte portId, byte status, byte data1, byte data2) 26 | { 27 | data = 0; 28 | PortId = portId; 29 | Status = status; 30 | Data1 = data1; 31 | Data2 = data2; 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return $"Port ID: {PortId}, Status: {Status}, Data 1: {Data1} , 2: {Data2}"; 37 | } 38 | 39 | public bool Equals(MidiMessage other) 40 | { 41 | return PortId == other.PortId && Status == other.Status && Data1 == other.Data1 && Data2 == other.Data2; 42 | } 43 | 44 | public override bool Equals(object obj) 45 | { 46 | return obj is MidiMessage other && Equals(other); 47 | } 48 | 49 | public override int GetHashCode() 50 | { 51 | unchecked 52 | { 53 | var hashCode = Status.GetHashCode(); 54 | hashCode = (hashCode * 397) ^ Data1.GetHashCode(); 55 | hashCode = (hashCode * 397) ^ Data2.GetHashCode(); 56 | return hashCode; 57 | } 58 | } 59 | 60 | public static bool operator ==(MidiMessage left, MidiMessage right) 61 | { 62 | return left.Equals(right); 63 | } 64 | 65 | public static bool operator !=(MidiMessage left, MidiMessage right) 66 | { 67 | return !left.Equals(right); 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Runtime/Scripts/Structs/MidiMessage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c23d9a75f52d4564ae81c162ca22e1a5 3 | timeCreated: 1570138640 -------------------------------------------------------------------------------- /Runtime/Scripts/Utility.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d35ac0c5730f7fc44891b199c2d57307 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Utility/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace OscCore 5 | { 6 | static class ExtensionMethods 7 | { 8 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 9 | public static int Align4(this int self) 10 | { 11 | return (self + 3) & ~3; 12 | } 13 | 14 | internal static void SafeFree(this GCHandle handle) 15 | { 16 | if(handle.IsAllocated) handle.Free(); 17 | } 18 | 19 | internal static int ClampPort(this int port) 20 | { 21 | if (port < 1024) port = 1024; 22 | if (port >= 65535) port = 65535; 23 | return port; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Utility/ExtensionMethods.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b35c550cea64fc1b0744798e1938cc0 3 | timeCreated: 1570141733 -------------------------------------------------------------------------------- /Runtime/Scripts/Utility/OscAddressMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using BlobHandles; 5 | using Unity.IL2CPP.CompilerServices.BlobHandles; 6 | 7 | namespace OscCore 8 | { 9 | /// Maps from OSC address to the delegates associated with it 10 | internal sealed unsafe class OscAddressMethods: IDisposable 11 | { 12 | const int defaultSize = 16; 13 | 14 | /// 15 | /// Map from the unmanaged representation of an OSC address to the delegates associated with it 16 | /// 17 | public readonly Dictionary HandleToValue; 18 | 19 | /// 20 | /// Map from the source string of an OSC address to the unmanaged representation 21 | /// 22 | internal readonly Dictionary SourceToBlob; 23 | 24 | public OscAddressMethods(int initialCapacity = defaultSize) 25 | { 26 | HandleToValue = new Dictionary(initialCapacity); 27 | SourceToBlob = new Dictionary(initialCapacity); 28 | } 29 | 30 | /// Adds a callback to be executed when a message is received at the address 31 | /// The address to associate the method with 32 | /// The method(s) to be invoked 33 | [Il2CppSetOption(Option.NullChecks, false)] 34 | public void Add(string address, OscActionPair callbacks) 35 | { 36 | if (!SourceToBlob.TryGetValue(address, out var blobStr)) 37 | { 38 | blobStr = new BlobString(address); 39 | HandleToValue[blobStr.Handle] = callbacks; 40 | SourceToBlob.Add(address, blobStr); 41 | } 42 | else 43 | { 44 | if(HandleToValue.ContainsKey(blobStr.Handle)) 45 | HandleToValue[blobStr.Handle] += callbacks; 46 | else 47 | HandleToValue[blobStr.Handle] = callbacks; 48 | } 49 | } 50 | 51 | /// Adds a callback to be executed when a message is received at the address 52 | /// The address to associate the method with 53 | /// The method to be invoked 54 | [Il2CppSetOption(Option.NullChecks, false)] 55 | public void Add(string address, Action callback) 56 | { 57 | Add(address, new OscActionPair(callback, null)); 58 | } 59 | 60 | /// Adds a list of callbacks to be executed when a message is received at the address 61 | /// The address to associate the methods with 62 | /// The methods to be invoked 63 | [Il2CppSetOption(Option.NullChecks, false)] 64 | internal void Add(string address, List callbacks) 65 | { 66 | if (callbacks.Count == 0) return; 67 | 68 | var pair = callbacks[0]; 69 | if(callbacks.Count > 1) 70 | for (int i = 1; i < callbacks.Count; i++) 71 | pair += callbacks[i]; 72 | 73 | Add(address, pair); 74 | } 75 | 76 | /// Removes the callback at the specified address 77 | /// The address to remove 78 | /// The callback pair to remove 79 | /// true if the string was found and removed, false otherwise 80 | [Il2CppSetOption(Option.NullChecks, false)] 81 | public bool Remove(string address, OscActionPair callbacks) 82 | { 83 | if (!SourceToBlob.TryGetValue(address, out var blobStr)) 84 | return false; 85 | if (!HandleToValue.TryGetValue(blobStr.Handle, out var existingPair)) 86 | return false; 87 | 88 | var valueReadMethod = existingPair.ValueRead; 89 | if (valueReadMethod.GetInvocationList().Length == 1) 90 | { 91 | var removed = HandleToValue.Remove(blobStr.Handle) && SourceToBlob.Remove(address); 92 | blobStr.Dispose(); 93 | return removed; 94 | } 95 | 96 | HandleToValue[blobStr.Handle] -= callbacks; 97 | return true; 98 | } 99 | 100 | /// Removes the OSC address from the delegate map 101 | /// The address to remove 102 | /// True if the address was found and removed, false otherwise 103 | public bool RemoveAddress(string address) 104 | { 105 | if (!SourceToBlob.TryGetValue(address, out var blobStr)) 106 | return false; 107 | 108 | SourceToBlob.Remove(address); 109 | HandleToValue.Remove(blobStr.Handle); 110 | blobStr.Dispose(); 111 | return true; 112 | } 113 | 114 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 115 | [Il2CppSetOption(Option.NullChecks, false)] 116 | public bool TryGetValueFromBytes(byte* ptr, int byteCount, out OscActionPair value) 117 | { 118 | return HandleToValue.TryGetValue(new BlobHandle(ptr, byteCount), out value); 119 | } 120 | 121 | public void Clear() 122 | { 123 | HandleToValue.Clear(); 124 | SourceToBlob.Clear(); 125 | } 126 | 127 | public void Dispose() 128 | { 129 | foreach (var kvp in SourceToBlob) 130 | kvp.Value.Dispose(); 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Utility/OscAddressMethods.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 58ba07c18b7449289fe6c37ab37dac11 3 | timeCreated: 1570865400 -------------------------------------------------------------------------------- /Runtime/Scripts/Utility/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using BlobHandles; 8 | 9 | namespace OscCore 10 | { 11 | static class Utils 12 | { 13 | static readonly List k_TempChars = new List(); 14 | static readonly StringBuilder k_Builder = new StringBuilder(); 15 | 16 | public static bool ValidateAddress(ref string address) 17 | { 18 | if(string.IsNullOrEmpty(address)) 19 | address = "/"; 20 | if(address[0] != '/') address = 21 | address.Insert(0, "/"); 22 | if(address.EndsWith(" ")) 23 | address = address.TrimEnd(' '); 24 | 25 | address = ReplaceInvalidAddressCharacters(address); 26 | return true; 27 | } 28 | 29 | internal static string ReplaceInvalidAddressCharacters(string address) 30 | { 31 | k_TempChars.Clear(); 32 | k_TempChars.AddRange(address.Where(OscParser.CharacterIsValidInAddress)); 33 | return new string(k_TempChars.ToArray()); 34 | } 35 | 36 | public static unsafe TPtr* PinPtr(TData[] array, out GCHandle handle) 37 | where TData: unmanaged 38 | where TPtr : unmanaged 39 | { 40 | handle = GCHandle.Alloc(array, GCHandleType.Pinned); 41 | return (TPtr*) handle.AddrOfPinnedObject(); 42 | } 43 | 44 | internal static string GetLocalIpAddress() 45 | { 46 | string localIP = "unknown"; 47 | var host = Dns.GetHostEntry(Dns.GetHostName()); 48 | foreach (IPAddress ip in host.AddressList) 49 | { 50 | if (ip.AddressFamily == AddressFamily.InterNetwork) 51 | { 52 | localIP = ip.ToString(); 53 | break; 54 | } 55 | } 56 | return localIP; 57 | } 58 | 59 | public static string MonitorMessageToString(BlobString address, OscMessageValues values) 60 | { 61 | k_Builder.Clear(); 62 | k_Builder.Append(address.ToString()); 63 | const string divider = " ,"; 64 | k_Builder.Append(divider); 65 | values.ForEachElement((i, type) => { k_Builder.Append((char)type); }); 66 | k_Builder.Append(" "); 67 | 68 | var lastIndex = values.ElementCount - 1; 69 | values.ForEachElement((i, type) => 70 | { 71 | var elementText = values.ReadStringElement(i); 72 | k_Builder.Append(elementText); 73 | if(i != lastIndex) k_Builder.Append(' '); 74 | }); 75 | 76 | return k_Builder.ToString(); 77 | } 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Utility/Utils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a07b9a2e2390404988462c7e7d746357 3 | timeCreated: 1581310626 -------------------------------------------------------------------------------- /Samples~/OscCoreExamples.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vrchat/OscCore/a4df29620a79dbbee587c8e7650d03fe4f469582/Samples~/OscCoreExamples.unitypackage -------------------------------------------------------------------------------- /Tests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de40785c734b8764498091972b3c0086 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e51c581a04040a0489e72fc758e9a774 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Editor/MessageReadPerformanceTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using MiniNtp; 6 | using NUnit.Framework; 7 | using UnityEngine; 8 | using Debug = UnityEngine.Debug; 9 | using Random = UnityEngine.Random; 10 | 11 | namespace OscCore.Tests 12 | { 13 | public class MessageReadPerformanceTests 14 | { 15 | const int k_Count = 4096; 16 | 17 | static readonly Stopwatch Stopwatch = new Stopwatch(); 18 | 19 | int[] m_IntSourceData; 20 | float[] m_FloatSourceData; 21 | MidiMessage[] m_MidiSourceData; 22 | 23 | byte[] m_BigEndianIntSourceBytes; 24 | byte[] m_BigEndianFloatSourceBytes; 25 | byte[] m_MidiSourceBytes; 26 | byte[] m_TimeSourceBytes; 27 | 28 | List m_Handles = new List(); 29 | 30 | int[] m_IntReadData; 31 | float[] m_FloatReadData; 32 | 33 | [OneTimeSetUp] 34 | public void BeforeAll() 35 | { 36 | m_Handles.Clear(); 37 | m_IntSourceData = new int[k_Count]; 38 | m_FloatSourceData = new float[k_Count]; 39 | m_IntReadData = new int[k_Count]; 40 | m_BigEndianIntSourceBytes = new byte[k_Count * 4]; 41 | m_BigEndianFloatSourceBytes = new byte[k_Count * 4]; 42 | 43 | 44 | m_Handles.Add(GCHandle.Alloc(m_BigEndianIntSourceBytes, GCHandleType.Pinned)); 45 | m_Handles.Add(GCHandle.Alloc(m_BigEndianFloatSourceBytes, GCHandleType.Pinned)); 46 | 47 | m_MidiSourceBytes = TestUtil.RandomMidiBytes(k_Count * 4); 48 | m_TimeSourceBytes = TestUtil.RandomTimestampBytes(k_Count * 4); 49 | 50 | for (int i = 0; i < m_IntSourceData.Length; i++) 51 | m_IntSourceData[i] = Random.Range(-10000, 10000); 52 | 53 | for (int i = 0; i < m_FloatSourceData.Length; i++) 54 | m_FloatSourceData[i] = Random.Range(-100f, 100f); 55 | } 56 | 57 | [SetUp] 58 | public void BeforeEach() 59 | { 60 | for (int i = 0; i < m_IntSourceData.Length; i++) 61 | { 62 | var lBytes = BitConverter.GetBytes(m_IntSourceData[i]); 63 | var bBytes = TestUtil.ReversedCopy(lBytes); 64 | 65 | var elementStart = i * 4; 66 | for (int j = 0; j < bBytes.Length; j++) 67 | m_BigEndianIntSourceBytes[elementStart + j] = bBytes[j]; 68 | } 69 | 70 | for (int i = 0; i < m_FloatSourceData.Length; i++) 71 | { 72 | var lBytes = BitConverter.GetBytes(m_FloatSourceData[i]); 73 | var bBytes = TestUtil.ReversedCopy(lBytes); 74 | 75 | var elementStart = i * 4; 76 | for (int j = 0; j < bBytes.Length; j++) 77 | m_BigEndianFloatSourceBytes[elementStart + j] = bBytes[j]; 78 | } 79 | } 80 | 81 | static OscMessageValues FromBytes(byte[] bytes, int count, TypeTag tag, int byteSize = 4) 82 | { 83 | var values = new OscMessageValues(bytes, count); 84 | for (int i = 0; i < count; i++) 85 | { 86 | values.Offsets[i] = i * byteSize; 87 | values.Tags[i] = tag; 88 | } 89 | 90 | values.ElementCount = count; 91 | return values; 92 | } 93 | 94 | [Test] 95 | public void ReadFloatElement_CheckedVsUnchecked() 96 | { 97 | const int count = 2048; 98 | var values = FromBytes(m_BigEndianFloatSourceBytes, count, TypeTag.Float32); 99 | 100 | float value = 0f; 101 | Stopwatch.Restart(); 102 | for (int i = 0; i < count; i++) 103 | { 104 | value = values.ReadFloatElement(i); 105 | } 106 | Stopwatch.Stop(); 107 | 108 | Debug.Log($"{count / 4} elements, checked float32 element read: {Stopwatch.ElapsedTicks} ticks, last value {value}"); 109 | 110 | Stopwatch.Restart(); 111 | for (int i = 0; i < count; i++) 112 | { 113 | value = values.ReadFloatElementUnchecked(i); 114 | } 115 | Stopwatch.Stop(); 116 | 117 | Debug.Log($"{count / 4} elements, unchecked float32 element read: {Stopwatch.ElapsedTicks} ticks, last value {value}"); 118 | } 119 | 120 | [Test] 121 | public void ReadIntElement_CheckedVsUnchecked() 122 | { 123 | const int count = 2048; 124 | var values = FromBytes(m_BigEndianIntSourceBytes, count, TypeTag.Int32); 125 | 126 | float value = 0f; 127 | Stopwatch.Restart(); 128 | for (int i = 0; i < count; i++) 129 | { 130 | value = values.ReadIntElement(i); 131 | } 132 | Stopwatch.Stop(); 133 | 134 | Debug.Log($"{count / 4} elements, checked int32 element read: {Stopwatch.ElapsedTicks} ticks, last value {value}"); 135 | 136 | Stopwatch.Restart(); 137 | for (int i = 0; i < count; i++) 138 | { 139 | value = values.ReadIntElementUnchecked(i); 140 | } 141 | Stopwatch.Stop(); 142 | 143 | Debug.Log($"{count / 4} elements, unchecked int32 element read: {Stopwatch.ElapsedTicks} ticks, last value {value}"); 144 | } 145 | 146 | [Test] 147 | public void ReadMidiMessageElement_CheckedVsUnchecked() 148 | { 149 | const int count = 2048; 150 | var values = FromBytes(m_MidiSourceBytes, count, TypeTag.MIDI); 151 | 152 | MidiMessage midi; 153 | Stopwatch.Restart(); 154 | for (int i = 0; i < count; i++) 155 | { 156 | midi = values.ReadMidiElement(i); 157 | } 158 | Stopwatch.Stop(); 159 | 160 | Debug.Log($"{count / 4} elements, checked MIDI element read: {Stopwatch.ElapsedTicks} ticks"); 161 | 162 | Stopwatch.Restart(); 163 | for (int i = 0; i < count; i++) 164 | { 165 | midi = values.ReadMidiElementUnchecked(i); 166 | } 167 | Stopwatch.Stop(); 168 | 169 | Debug.Log($"{count / 4} elements, unchecked MIDI element read: {Stopwatch.ElapsedTicks} ticks"); 170 | } 171 | 172 | [Test] 173 | public void ReadColor32MessageElement_CheckedVsUnchecked() 174 | { 175 | const int count = 2048; 176 | var values = FromBytes(m_MidiSourceBytes, count, TypeTag.Color32); 177 | 178 | Color32 color32; 179 | Stopwatch.Restart(); 180 | for (int i = 0; i < count; i++) 181 | { 182 | color32 = values.ReadColor32Element(i); 183 | } 184 | Stopwatch.Stop(); 185 | 186 | Debug.Log($"{count / 4} elements, checked Color32 element read: {Stopwatch.ElapsedTicks} ticks"); 187 | 188 | Stopwatch.Restart(); 189 | for (int i = 0; i < count; i++) 190 | { 191 | color32 = values.ReadColor32ElementUnchecked(i); 192 | } 193 | Stopwatch.Stop(); 194 | 195 | Debug.Log($"{count / 4} elements, unchecked Color32 element read: {Stopwatch.ElapsedTicks} ticks"); 196 | } 197 | 198 | [Test] 199 | public void ReadTimestampElement_CheckedVsUnchecked() 200 | { 201 | const int count = 2048; 202 | var values = FromBytes(m_TimeSourceBytes, count, TypeTag.TimeTag); 203 | 204 | NtpTimestamp ts; 205 | Stopwatch.Restart(); 206 | for (int i = 0; i < count; i++) 207 | { 208 | ts = values.ReadTimestampElement(i); 209 | } 210 | Stopwatch.Stop(); 211 | 212 | Debug.Log($"{count / 8} elements, checked NTP timestamp element read: {Stopwatch.ElapsedTicks} ticks"); 213 | 214 | Stopwatch.Restart(); 215 | for (int i = 0; i < count; i++) 216 | { 217 | ts = values.ReadTimestampElementUnchecked(i); 218 | } 219 | Stopwatch.Stop(); 220 | 221 | Debug.Log($"{count / 8} elements, unchecked NTP timestamp element read: {Stopwatch.ElapsedTicks} ticks"); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Tests/Editor/MessageReadPerformanceTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 895a89552e6240298ed85fed7fc6bb28 3 | timeCreated: 1570401405 -------------------------------------------------------------------------------- /Tests/Editor/OscCore.Tests.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OscCore.Tests.Editor", 3 | "references": [ 4 | "OscCore.Runtime", 5 | "NtpTimestamp", 6 | "BlobHandles.Runtime" 7 | ], 8 | "optionalUnityReferences": [ 9 | "TestAssemblies" 10 | ], 11 | "includePlatforms": [ 12 | "Editor" 13 | ], 14 | "excludePlatforms": [], 15 | "allowUnsafeCode": true, 16 | "overrideReferences": false, 17 | "precompiledReferences": [], 18 | "autoReferenced": true, 19 | "defineConstraints": [], 20 | "versionDefines": [] 21 | } -------------------------------------------------------------------------------- /Tests/Editor/OscCore.Tests.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cd9e2e171a660454e84c60baaa0779f6 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Tests/Editor/OscSenderTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using UnityEngine; 3 | 4 | namespace OscCore.Tests 5 | { 6 | public class OscSenderTests 7 | { 8 | GameObject m_Object; 9 | OscSender m_Sender; 10 | 11 | [OneTimeSetUp] 12 | public void BeforeAll() 13 | { 14 | m_Object = new GameObject("OscSender Test Object", typeof(OscSender)); 15 | m_Sender = m_Object.GetComponent(); 16 | } 17 | 18 | [OneTimeTearDown] 19 | public void AfterAll() 20 | { 21 | Object.DestroyImmediate(m_Object); 22 | } 23 | 24 | [Test] 25 | public void SwitchPort() 26 | { 27 | const int newPort = 10100; 28 | Assert.AreNotEqual(m_Sender.Port, newPort); 29 | m_Sender.Port = newPort; 30 | Assert.AreEqual(m_Sender.Port, newPort); 31 | } 32 | 33 | [Test] 34 | public void SwitchIpAddress_ValidInput() 35 | { 36 | const string newAddress = "127.0.0.10"; 37 | Assert.AreNotEqual(m_Sender.IpAddress, newAddress); 38 | m_Sender.IpAddress = newAddress; 39 | Assert.AreEqual(m_Sender.IpAddress, newAddress); 40 | } 41 | 42 | [Test] 43 | public void SwitchIpAddress_InvalidInput() 44 | { 45 | const string invalidAddress1 = "69000.420000.0.1"; 46 | m_Sender.IpAddress = invalidAddress1; 47 | Assert.AreNotEqual(m_Sender.IpAddress, invalidAddress1); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/Editor/OscSenderTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 42187dde7cbbb1f47aeb32831b1d9d09 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Editor/OscWriterTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0474e20ff08ff34180389ae349ac28c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Editor/ParseTestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using MiniNtp; 6 | using NUnit.Framework; 7 | using UnityEngine; 8 | 9 | namespace OscCore.Tests 10 | { 11 | internal static class MessageTestData 12 | { 13 | public static IEnumerable Basic 14 | { 15 | get 16 | { 17 | var msg1 = SingleFloatMessage("/composition/layers/1/video/mixer/opacity", 0.69f); 18 | yield return new TestCaseData(msg1, msg1.Length); 19 | 20 | var msg2 = SingleFloatMessage("/composition/layers/1/clips/1/video/source/solidcolor/color/blue", 0.4f); 21 | yield return new TestCaseData(msg2, msg2.Length); 22 | } 23 | } 24 | 25 | static byte[] SingleFloatMessage(string address, float value) 26 | { 27 | var addressBytes = Encoding.ASCII.GetBytes(address); 28 | var alignedByteCount = (addressBytes.Length + 3) & ~3; 29 | 30 | var bytes = new byte[alignedByteCount + 8]; 31 | for (var i = 0; i < addressBytes.Length; i++) 32 | bytes[i] = addressBytes[i]; 33 | 34 | bytes[alignedByteCount] = Constant.Comma; 35 | bytes[alignedByteCount + 1] = (byte) TypeTag.Float32; 36 | 37 | var floatBytes = BitConverter.GetBytes(value); 38 | Buffer.BlockCopy(floatBytes, 0, bytes, alignedByteCount + 4, 4); 39 | 40 | return bytes; 41 | } 42 | } 43 | 44 | public class TypeTagParseTestCase 45 | { 46 | public readonly byte[] Bytes; 47 | public readonly int Start; 48 | public readonly TypeTag[] Expected; 49 | 50 | public TypeTagParseTestCase(byte[] bytes, int start, TypeTag[] expected) 51 | { 52 | Bytes = bytes; 53 | Start = start; 54 | Expected = expected; 55 | } 56 | } 57 | 58 | internal static class TagsTestData 59 | { 60 | public static IEnumerable StandardTagParseCases 61 | { 62 | get 63 | { 64 | var expected1 = new[] { TypeTag.Float32, TypeTag.Float32, TypeTag.Int32, TypeTag.String }; 65 | var bytes1 = new[] 66 | { 67 | (byte) ',', (byte) TypeTag.Float32, (byte) TypeTag.Float32, (byte) TypeTag.Int32, 68 | (byte) TypeTag.String, (byte) 0, (byte) 0, (byte) 0 69 | }; 70 | 71 | yield return new TypeTagParseTestCase(bytes1, 0, expected1); 72 | 73 | var expected2 = new[] 74 | { 75 | TypeTag.Int32, TypeTag.Float32, TypeTag.String, TypeTag.String, TypeTag.Blob, TypeTag.Int32 76 | }; 77 | var bytes2 = new[] 78 | { 79 | (byte) 0, (byte) 0, // offset of 2 bytes 80 | (byte) ',', 81 | (byte) TypeTag.Int32, (byte) TypeTag.Float32, (byte) TypeTag.String, 82 | (byte) TypeTag.String, (byte) TypeTag.Blob, (byte) TypeTag.Int32, 83 | (byte) 0, (byte) 0 // trailing bytes 84 | }; 85 | 86 | yield return new TypeTagParseTestCase(bytes2, 2, expected2); 87 | } 88 | } 89 | } 90 | 91 | static class MidiTestData 92 | { 93 | public static IEnumerable Basic 94 | { 95 | get 96 | { 97 | var expected1 = new[] 98 | { 99 | (byte)1, // port id 100 | (byte)144, // status - ch1 note on 101 | (byte) 60, // note # - 60 = middle c 102 | (byte)100 // note velocity 103 | }; 104 | var bytes1 = new[] 105 | { 106 | (byte) 0, (byte) 0, (byte) 1, (byte) 144, 107 | (byte) 60, (byte) 100, (byte) 0, (byte) 0, 108 | }; 109 | 110 | yield return new TestCaseData(bytes1, 2, expected1); 111 | 112 | var expected2 = new[] 113 | { 114 | (byte) 16, 115 | (byte) 128, // status - ch1 note off 116 | (byte) 72, // note C4 117 | (byte) 42, 118 | }; 119 | var bytes2 = new[] 120 | { 121 | (byte) 16, (byte) 128, (byte) 72, (byte) 42, (byte) 0, (byte) 0 122 | }; 123 | 124 | yield return new TestCaseData(bytes2, 0, expected2); 125 | } 126 | } 127 | } 128 | 129 | 130 | static class BundleData 131 | { 132 | public static byte[] GetRecursiveBundlesExample() 133 | { 134 | var writer = new OscWriter(512); 135 | 136 | var now = DateTime.Now; 137 | writer.WriteBundlePrefix(); 138 | writer.Write(new NtpTimestamp(now)); 139 | 140 | WriteFloatBundleElement(writer, "/composition/video/opacity", 0.5f); 141 | WriteFloatBundleElement(writer, "/composition/layers/2/video/opacity", 0.64f); 142 | 143 | writer.WriteBundlePrefix(); 144 | writer.Write(new NtpTimestamp(now)); 145 | 146 | WriteIntBundleElement(writer, "/composition/layers/1/video/mixer/blendmode", 24); 147 | WriteFloatBundleElement(writer, "/composition/layers/1/video/opacity", 0.72f); 148 | 149 | var bytes = new byte[writer.Length]; 150 | writer.CopyBuffer(bytes, 0); 151 | return bytes; 152 | } 153 | 154 | static void WriteIntBundleElement(OscWriter writer, string address, int value) 155 | { 156 | var typeTags = ",i"; 157 | var firstAddressByteCount = Encoding.ASCII.GetByteCount(address).Align4(); 158 | var firstTypeTagByteCount = Encoding.ASCII.GetByteCount(typeTags).Align4(); 159 | var elementSize = firstAddressByteCount + firstTypeTagByteCount + 4 ; 160 | 161 | writer.Write(elementSize); 162 | writer.Write(address); 163 | writer.Write(typeTags); 164 | writer.Write(value); 165 | } 166 | 167 | static void WriteFloatBundleElement(OscWriter writer, string address, float value) 168 | { 169 | var typeTags = ",f"; 170 | var firstAddressByteCount = Encoding.ASCII.GetByteCount(address).Align4(); 171 | var firstTypeTagByteCount = Encoding.ASCII.GetByteCount(typeTags).Align4(); 172 | var elementSize = firstAddressByteCount + firstTypeTagByteCount + 4 ; 173 | 174 | writer.Write(elementSize); 175 | writer.Write(address); 176 | writer.Write(typeTags); 177 | writer.Write(value); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /Tests/Editor/ParseTestData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e825944fab1642d9aae4591d4e3b3723 3 | timeCreated: 1570328187 -------------------------------------------------------------------------------- /Tests/Editor/ParsingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data.SqlTypes; 5 | using System.Runtime.InteropServices; 6 | using NUnit.Framework; 7 | using UnityEngine; 8 | 9 | namespace OscCore.Tests 10 | { 11 | public class ParsingTests 12 | { 13 | const int k_BufferSize = 4096; 14 | readonly byte[] m_Buffer = new byte[k_BufferSize]; 15 | GCHandle m_BufferHandle; 16 | OscParser m_Parser; 17 | 18 | [OneTimeSetUp] 19 | public void BeforeAll() 20 | { 21 | m_BufferHandle = GCHandle.Alloc(m_Buffer, GCHandleType.Pinned); 22 | m_Parser = new OscParser(m_Buffer); 23 | } 24 | 25 | [SetUp] 26 | public void BeforeEach() 27 | { 28 | m_Parser.MessageValues.ElementCount = 0; 29 | } 30 | 31 | [OneTimeTearDown] 32 | public void AfterAll() 33 | { 34 | if(m_BufferHandle.IsAllocated) m_BufferHandle.Free(); 35 | } 36 | 37 | [TestCaseSource(typeof(TagsTestData), nameof(TagsTestData.StandardTagParseCases))] 38 | public void SimpleTagParsing(TypeTagParseTestCase test) 39 | { 40 | var tagSize = m_Parser.ParseTags(test.Bytes, test.Start); 41 | var tagCount = tagSize - 1; // remove ',' 42 | 43 | Assert.AreEqual(test.Expected.Length, tagCount); 44 | var tags = m_Parser.MessageValues.Tags; 45 | for (var i = 0; i < tagCount; i++) 46 | { 47 | var tag = tags[i]; 48 | Debug.Log(tag); 49 | Assert.AreEqual(test.Expected[i], tag); 50 | } 51 | } 52 | 53 | [Test] 54 | public void TagParsing_MustStartWithComma() 55 | { 56 | var commaAfterStart = new byte[] { 0, (byte) ',', 1, 2 }; 57 | Assert.Zero(m_Parser.ParseTags(commaAfterStart)); 58 | Assert.Zero(m_Parser.MessageValues.ElementCount); 59 | 60 | var noCommaBeforeTags = new byte[] { (byte)'f', (byte)'i', 1, 2 }; 61 | Assert.Zero(m_Parser.ParseTags(noCommaBeforeTags)); 62 | Assert.Zero(m_Parser.MessageValues.ElementCount); 63 | } 64 | 65 | [TestCaseSource(typeof(MidiTestData), nameof(MidiTestData.Basic))] 66 | public void BasicMidiParsing(byte[] bytes, int offset, byte[] expected) 67 | { 68 | var midi = new MidiMessage(bytes, offset); 69 | Debug.Log(midi); 70 | Assert.AreEqual(expected[0], midi.PortId); 71 | Assert.AreEqual(expected[1], midi.Status); 72 | Assert.AreEqual(expected[2], midi.Data1); 73 | Assert.AreEqual(expected[3], midi.Data2); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Tests/Editor/ParsingTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8551d42a836ee204989e3c4587c11df8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Editor/TestUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Random = UnityEngine.Random; 3 | 4 | namespace OscCore.Tests 5 | { 6 | public static class TestUtil 7 | { 8 | static readonly byte[] k_Swap32 = new byte[4]; 9 | static readonly byte[] k_Swap64 = new byte[8]; 10 | 11 | public static int ReverseBytes(this int self) 12 | { 13 | k_Swap32[0] = (byte) (self >> 24); 14 | k_Swap32[1] = (byte) (self >> 16); 15 | k_Swap32[2] = (byte) (self >> 8); 16 | k_Swap32[3] = (byte) (self); 17 | return BitConverter.ToInt32(k_Swap32, 0); 18 | } 19 | 20 | public static float ReverseBytes(this float self) 21 | { 22 | var fBytes = BitConverter.GetBytes(self); 23 | k_Swap32[0] = fBytes[3]; 24 | k_Swap32[1] = fBytes[2]; 25 | k_Swap32[2] = fBytes[1]; 26 | k_Swap32[3] = fBytes[0]; 27 | return BitConverter.ToSingle(k_Swap32, 0); 28 | } 29 | 30 | public static double ReverseBytes(this double self) 31 | { 32 | var dBytes = BitConverter.GetBytes(self); 33 | k_Swap64[0] = dBytes[7]; 34 | k_Swap64[1] = dBytes[6]; 35 | k_Swap64[2] = dBytes[5]; 36 | k_Swap64[3] = dBytes[4]; 37 | k_Swap64[4] = dBytes[3]; 38 | k_Swap64[5] = dBytes[2]; 39 | k_Swap64[6] = dBytes[1]; 40 | k_Swap64[7] = dBytes[0]; 41 | return BitConverter.ToDouble(k_Swap64, 0); 42 | } 43 | 44 | public static byte[] ReversedCopy(byte[] source) 45 | { 46 | var copy = new byte[source.Length]; 47 | Array.Copy(source, copy, source.Length); 48 | Array.Reverse(copy); 49 | return copy; 50 | } 51 | 52 | public static byte[] RandomFloatBytes(int byteCount = 2048) 53 | { 54 | var bytes = new byte[byteCount]; 55 | for (int i = 0; i < bytes.Length; i += 4) 56 | { 57 | var f = Random.Range(-1f, 1f); 58 | var fBytes = BitConverter.GetBytes(f); 59 | for (int j = 0; j < fBytes.Length; j++) 60 | { 61 | bytes[i + j] = fBytes[j]; 62 | } 63 | } 64 | 65 | return bytes; 66 | } 67 | 68 | public static byte[] RandomIntBytes(int byteCount = 2048) 69 | { 70 | var bytes = new byte[byteCount]; 71 | for (int i = 0; i < bytes.Length; i += 4) 72 | { 73 | var iValue = Random.Range(-1000, 1000); 74 | var iBytes = BitConverter.GetBytes(iValue); 75 | for (int j = 0; j < iBytes.Length; j++) 76 | bytes[i + j] = iBytes[j]; 77 | } 78 | 79 | return bytes; 80 | } 81 | 82 | public static byte[] RandomColor32Bytes(int byteCount = 2048) 83 | { 84 | var bytes = new byte[byteCount]; 85 | for (int i = 0; i < bytes.Length; i += 4) 86 | { 87 | var iValue = Random.Range(0, 255); 88 | var iBytes = BitConverter.GetBytes(iValue); 89 | for (int j = 0; j < iBytes.Length; j++) 90 | bytes[i + j] = iBytes[j]; 91 | } 92 | 93 | return bytes; 94 | } 95 | 96 | public static byte[] RandomMidiBytes(int byteCount = 2048) 97 | { 98 | var bytes = new byte[byteCount]; 99 | for (int i = 0; i < bytes.Length; i += 4) 100 | { 101 | var port = (byte) Random.Range(1, 16); 102 | var status = (byte) Random.Range(0, 127); 103 | var data1 = (byte) Random.Range(10, 255); 104 | var data2 = (byte) Random.Range(10, 255); 105 | bytes[i] = port; 106 | bytes[i + 1] = status; 107 | bytes[i + 2] = data1; 108 | bytes[i + 3] = data2; 109 | } 110 | 111 | return bytes; 112 | } 113 | 114 | public static byte[] RandomTimestampBytes(int count = 2048) 115 | { 116 | var bytes = new byte[count]; 117 | for (int i = 0; i < bytes.Length; i += 8) 118 | { 119 | var seconds = Random.Range(0, 255); 120 | var sBytes = BitConverter.GetBytes(seconds); 121 | for (int j = 0; j < sBytes.Length; j++) 122 | bytes[i + j] = sBytes[j]; 123 | 124 | var fractions = Random.Range(0, 10000000); 125 | var fBytes = BitConverter.GetBytes(fractions); 126 | 127 | var end = 4 + fBytes.Length; 128 | for (int j = 4; j < end; j++) 129 | bytes[i + j] = fBytes[j - 4]; 130 | } 131 | 132 | return bytes; 133 | } 134 | 135 | } 136 | } -------------------------------------------------------------------------------- /Tests/Editor/TestUtil.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5e76c9ac80274c0682f36bfb9fc51e1e 3 | timeCreated: 1570401212 -------------------------------------------------------------------------------- /Tests/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40da13fbd6245cf4080b6da5eb8529b8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Runtime/OscCore.Tests.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OscCore.Tests.Runtime", 3 | "references": [ 4 | "OscCore.Runtime" 5 | ], 6 | "optionalUnityReferences": [], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": true, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": false, 13 | "defineConstraints": [], 14 | "versionDefines": [] 15 | } -------------------------------------------------------------------------------- /Tests/Runtime/OscCore.Tests.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6600063d82066c4b9112d0afec000b5 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Tests/Runtime/StandardTypeMessageSendTest.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using UnityEngine; 3 | using Random = UnityEngine.Random; 4 | 5 | namespace OscCore.Tests 6 | { 7 | public class StandardTypeMessageSendTest : MonoBehaviour 8 | { 9 | static readonly StringBuilder k_Builder = new StringBuilder(); 10 | 11 | [Range(4, 32)] 12 | [SerializeField] 13 | #pragma warning disable 649 14 | int m_RandomAddressCount = 16; 15 | #pragma warning restore 649 16 | 17 | [SerializeField] 18 | int m_Port = 7000; 19 | 20 | [Range(4, 64)] 21 | [SerializeField] 22 | int m_RandomCharCount = 16; 23 | 24 | string[] m_Addresses; 25 | string[] m_StringElements; 26 | 27 | byte[][] m_Blobs = new byte[4][]; 28 | 29 | OscClient m_Client; 30 | 31 | void Awake() 32 | { 33 | m_Client = new OscClient("127.0.0.1", m_Port); 34 | m_Addresses = new string[m_RandomAddressCount]; 35 | m_StringElements = new string[16]; 36 | MakeRandomStrings(); 37 | MakeRandomBlobs(); 38 | } 39 | 40 | void Update() 41 | { 42 | if(Random.Range(0f, 1f) > 0.9f) 43 | RandomFloatMessage(); 44 | 45 | if(Random.Range(0f, 1f) > 0.9f) 46 | RandomIntMessage(); 47 | 48 | if(Random.Range(0f, 1f) > 0.95f) 49 | RandomBlobMessage(); 50 | 51 | if(Random.Range(0f, 1f) > 0.92f) 52 | RandomStringMessage(); 53 | } 54 | 55 | string RandomAddress() 56 | { 57 | return m_Addresses[Random.Range(0, m_Addresses.Length - 1)]; 58 | } 59 | 60 | void RandomStringMessage() 61 | { 62 | var str = m_StringElements[Random.Range(0, m_StringElements.Length - 1)]; 63 | m_Client.Send(RandomAddress(), str); 64 | } 65 | 66 | void RandomFloatMessage() 67 | { 68 | m_Client.Send(RandomAddress(), Random.Range(0f, 1f)); 69 | } 70 | 71 | void RandomIntMessage() 72 | { 73 | m_Client.Send(RandomAddress(), Random.Range(0, 50)); 74 | } 75 | 76 | void RandomBlobMessage() 77 | { 78 | var blob = m_Blobs[Random.Range(0, m_Blobs.GetLength(0) - 1)]; 79 | m_Client.Send(RandomAddress(), blob, blob.Length); 80 | } 81 | 82 | void MakeRandomBlobs() 83 | { 84 | for (int i = 0; i < m_Blobs.GetLength(0); i++) 85 | { 86 | var length = 64 + i * 64; 87 | var blob = new byte[length]; 88 | for (int j = 0; j < blob.Length; j++) 89 | { 90 | blob[j] = (byte) Random.Range(0, 255); 91 | } 92 | 93 | m_Blobs[i] = blob; 94 | } 95 | } 96 | 97 | void MakeRandomStrings() 98 | { 99 | for (int i = 0; i < m_Addresses.Length; i++) 100 | { 101 | k_Builder.Clear(); 102 | var prefix = Random.Range(0f, 1f) > 0.75f ? "/layer/" : "/composition/"; 103 | 104 | k_Builder.Append(prefix); 105 | for (int j = 0; j < m_RandomCharCount; j++) 106 | { 107 | char randChar; 108 | do 109 | { 110 | randChar = (char) Random.Range(32, 255); 111 | } while (!OscParser.CharacterIsValidInAddress(randChar)); 112 | k_Builder.Append((byte) randChar); 113 | } 114 | 115 | k_Builder.Append((byte) ' '); 116 | m_Addresses[i] = k_Builder.ToString(); 117 | } 118 | 119 | for (int i = 0; i < m_StringElements.Length; i++) 120 | { 121 | k_Builder.Clear(); 122 | for (int j = 0; j < m_RandomCharCount; j++) 123 | { 124 | char randChar; 125 | do 126 | { 127 | randChar = (char) Random.Range(32, 255); 128 | } while (!OscParser.CharacterIsValidInAddress(randChar)); 129 | k_Builder.Append((byte) randChar); 130 | } 131 | 132 | k_Builder.Append((byte) ' '); 133 | m_StringElements[i] = k_Builder.ToString(); 134 | } 135 | } 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /Tests/Runtime/StandardTypeMessageSendTest.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83986006cc85e4f43bf77778c24c17ec 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "com.stella3d.osccore", 3 | "version" : "1.1.5", 4 | "displayName" : "Open Sound Control Core", 5 | "description" : "An Open Sound Control (OSC) protocol driver", 6 | "unity" : "2019.4", 7 | "unityRelease" : "", 8 | "keywords" : [ "OSC" ], 9 | "author" : { 10 | "name" : "Stella Cannefax", 11 | "email" : "stella@unity3d.com", 12 | "url" : "https://github.com/stella3d/OscCore" 13 | }, 14 | "samples" : [ 15 | { 16 | "displayName" : "Basic Examples", 17 | "description" : "Message Receive and Property Sender Examples", 18 | "path" : "Samples~" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83d6553e9f258374894a5a771c45a022 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------