mirror of
https://github.com/jwetzell/QControlKit.git
synced 2026-08-10 01:43:42 +00:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpOSC
|
||||
{
|
||||
public abstract class OscPacket
|
||||
{
|
||||
public static OscPacket GetPacket(byte[] OscData)
|
||||
{
|
||||
if (OscData[0] == '#')
|
||||
return parseBundle(OscData);
|
||||
else
|
||||
return parseMessage(OscData);
|
||||
}
|
||||
|
||||
public abstract byte[] GetBytes();
|
||||
|
||||
#region Parse OSC packages
|
||||
|
||||
/// <summary>
|
||||
/// Takes in an OSC bundle package in byte form and parses it into a more usable OscBundle object
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns>Message containing various arguments and an address</returns>
|
||||
private static OscMessage parseMessage(byte[] msg)
|
||||
{
|
||||
int index = 0;
|
||||
//Log.Debug("Raw ASCII DATA: " + System.Text.Encoding.ASCII.GetString(msg));
|
||||
//Log.Debug("Raw UTF-8 DATA: " + System.Text.Encoding.UTF8.GetString(msg));
|
||||
string address = null;
|
||||
char[] types = new char[0];
|
||||
List<object> arguments = new List<object>();
|
||||
List<object> mainArray = arguments; // used as a reference when we are parsing arrays to get the main array back
|
||||
// Get address
|
||||
address = getAddress(msg, index);
|
||||
index += msg.FirstIndexAfter(address.Length, x => x == ',');
|
||||
|
||||
if (index % 4 != 0)
|
||||
throw new Exception("Misaligned OSC Packet data. Address string is not padded correctly and does not align to 4 byte interval");
|
||||
|
||||
// Get type tags
|
||||
types = getTypes(msg, index);
|
||||
index += types.Length;
|
||||
|
||||
while (index % 4 != 0)
|
||||
index++;
|
||||
|
||||
bool commaParsed = false;
|
||||
|
||||
foreach (char type in types)
|
||||
{
|
||||
// skip leading comma
|
||||
if (type == ',' && !commaParsed)
|
||||
{
|
||||
commaParsed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ('\0'):
|
||||
break;
|
||||
|
||||
case('i'):
|
||||
int intVal = getInt(msg, index);
|
||||
arguments.Add(intVal);
|
||||
index += 4;
|
||||
break;
|
||||
|
||||
case('f'):
|
||||
float floatVal = getFloat(msg, index);
|
||||
arguments.Add(floatVal);
|
||||
index += 4;
|
||||
break;
|
||||
|
||||
case('s'):
|
||||
string stringVal = getString(msg, index);
|
||||
arguments.Add(stringVal);
|
||||
index += stringVal.Length;
|
||||
break;
|
||||
|
||||
case('b'):
|
||||
byte[] blob = getBlob(msg, index);
|
||||
arguments.Add(blob);
|
||||
index += 4 + blob.Length;
|
||||
break;
|
||||
|
||||
case ('h'):
|
||||
Int64 hval = getLong(msg, index);
|
||||
arguments.Add(hval);
|
||||
index += 8;
|
||||
break;
|
||||
|
||||
case ('t'):
|
||||
UInt64 sval = getULong(msg, index);
|
||||
arguments.Add(new Timetag(sval));
|
||||
index += 8;
|
||||
break;
|
||||
|
||||
case ('d'):
|
||||
double dval = getDouble(msg, index);
|
||||
arguments.Add(dval);
|
||||
index += 8;
|
||||
break;
|
||||
|
||||
case ('S'):
|
||||
string SymbolVal = getString(msg, index);
|
||||
arguments.Add(new Symbol(SymbolVal));
|
||||
index += SymbolVal.Length;
|
||||
break;
|
||||
|
||||
case ('c'):
|
||||
char cval = getChar(msg, index);
|
||||
arguments.Add(cval);
|
||||
index += 4;
|
||||
break;
|
||||
|
||||
case ('r'):
|
||||
RGBA rgbaval = getRGBA(msg, index);
|
||||
arguments.Add(rgbaval);
|
||||
index += 4;
|
||||
break;
|
||||
|
||||
case ('m'):
|
||||
Midi midival = getMidi(msg, index);
|
||||
arguments.Add(midival);
|
||||
index += 4;
|
||||
break;
|
||||
|
||||
case ('T'):
|
||||
arguments.Add(true);
|
||||
break;
|
||||
|
||||
case ('F'):
|
||||
arguments.Add(false);
|
||||
break;
|
||||
|
||||
case ('N'):
|
||||
arguments.Add(null);
|
||||
break;
|
||||
|
||||
case ('I'):
|
||||
arguments.Add(double.PositiveInfinity);
|
||||
break;
|
||||
|
||||
case ('['):
|
||||
if (arguments != mainArray)
|
||||
throw new Exception("SharpOSC does not support nested arrays");
|
||||
arguments = new List<object>(); // make arguments point to a new object array
|
||||
break;
|
||||
|
||||
case (']'):
|
||||
mainArray.Add(arguments); // add the array to the main array
|
||||
arguments = mainArray; // make arguments point back to the main array
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("OSC type tag '" + type + "' is unknown.");
|
||||
}
|
||||
|
||||
while (index % 4 != 0)
|
||||
index++;
|
||||
}
|
||||
|
||||
return new OscMessage(address, arguments.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes in an OSC bundle package in byte form and parses it into a more usable OscBundle object
|
||||
/// </summary>
|
||||
/// <param name="bundle"></param>
|
||||
/// <returns>Bundle containing elements and a timetag</returns>
|
||||
private static OscBundle parseBundle(byte[] bundle)
|
||||
{
|
||||
UInt64 timetag;
|
||||
List<OscMessage> messages = new List<OscMessage>();
|
||||
|
||||
int index = 0;
|
||||
|
||||
var bundleTag = Encoding.UTF8.GetString(bundle.SubArray(0, 8));
|
||||
index += 8;
|
||||
|
||||
timetag = getULong(bundle, index);
|
||||
index += 8;
|
||||
|
||||
if (bundleTag != "#bundle\0")
|
||||
throw new Exception("Not a bundle");
|
||||
|
||||
while (index < bundle.Length)
|
||||
{
|
||||
int size = getInt(bundle, index);
|
||||
index += 4;
|
||||
|
||||
byte[] messageBytes = bundle.SubArray(index, size);
|
||||
var message = parseMessage(messageBytes);
|
||||
|
||||
messages.Add(message);
|
||||
|
||||
index += size;
|
||||
while (index % 4 != 0)
|
||||
index++;
|
||||
}
|
||||
|
||||
OscBundle output = new OscBundle(timetag, messages.ToArray());
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get arguments from byte array
|
||||
|
||||
private static string getAddress(byte[] msg, int index)
|
||||
{
|
||||
int i = index;
|
||||
string address = "";
|
||||
for (; i < msg.Length; i += 4)
|
||||
{
|
||||
if (msg[i] == ',')
|
||||
{
|
||||
if (i == 0)
|
||||
return "";
|
||||
|
||||
address = Encoding.UTF8.GetString(msg.SubArray(index, i - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= msg.Length && address == null)
|
||||
throw new Exception("no comma found");
|
||||
|
||||
return address.Replace("\0", "");
|
||||
}
|
||||
|
||||
private static char[] getTypes(byte[] msg, int index)
|
||||
{
|
||||
int i = index + 4;
|
||||
char[] types = null;
|
||||
|
||||
for (; i < msg.Length; i += 4)
|
||||
{
|
||||
if (msg[i - 1] == 0)
|
||||
{
|
||||
types = Encoding.UTF8.GetChars(msg.SubArray(index, i - index));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (types == null)
|
||||
{
|
||||
byte[] term = { 0 };
|
||||
types = Encoding.UTF8.GetChars(term);
|
||||
}
|
||||
|
||||
if (i >= msg.Length && types == null)
|
||||
throw new Exception("No null terminator after type string");
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private static int getInt(byte[] msg, int index)
|
||||
{
|
||||
int val = (msg[index] << 24) + (msg[index + 1] << 16) + (msg[index + 2] << 8) + (msg[index + 3] << 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
private static float getFloat(byte[] msg, int index)
|
||||
{
|
||||
byte[] reversed = new byte[4];
|
||||
reversed[3] = msg[index];
|
||||
reversed[2] = msg[index+1];
|
||||
reversed[1] = msg[index+2];
|
||||
reversed[0] = msg[index + 3];
|
||||
float val = System.BitConverter.ToSingle(reversed, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
private static string getString(byte[] msg, int index)
|
||||
{
|
||||
string output = null;
|
||||
int i = index + 4;
|
||||
for (; (i-1) < msg.Length; i += 4)
|
||||
{
|
||||
if (msg[i - 1] == 0)
|
||||
{
|
||||
output = Encoding.UTF8.GetString(msg.SubArray(index, i - index));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (output == null)
|
||||
{
|
||||
byte[] term = { 0 };
|
||||
output = Encoding.UTF8.GetString(term);
|
||||
}
|
||||
if (i >= msg.Length && output == null)
|
||||
throw new Exception("No null terminator after type string");
|
||||
|
||||
return output.Replace("\0", "");
|
||||
}
|
||||
|
||||
private static byte[] getBlob(byte[] msg, int index)
|
||||
{
|
||||
int size = getInt(msg, index);
|
||||
return msg.SubArray(index + 4, size);
|
||||
}
|
||||
|
||||
private static UInt64 getULong(byte[] msg, int index)
|
||||
{
|
||||
UInt64 val = ((UInt64)msg[index] << 56) + ((UInt64)msg[index + 1] << 48) + ((UInt64)msg[index + 2] << 40) + ((UInt64)msg[index + 3] << 32)
|
||||
+ ((UInt64)msg[index + 4] << 24) + ((UInt64)msg[index + 5] << 16) + ((UInt64)msg[index + 6] << 8) + ((UInt64)msg[index + 7] << 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
private static Int64 getLong(byte[] msg, int index)
|
||||
{
|
||||
byte[] var = new byte[8];
|
||||
var[7] = msg[index];
|
||||
var[6] = msg[index+1];
|
||||
var[5] = msg[index+2];
|
||||
var[4] = msg[index+3];
|
||||
var[3] = msg[index+4];
|
||||
var[2] = msg[index+5];
|
||||
var[1] = msg[index+6];
|
||||
var[0] = msg[index+7];
|
||||
|
||||
Int64 val = BitConverter.ToInt64(var, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
private static double getDouble(byte[] msg, int index)
|
||||
{
|
||||
byte[] var = new byte[8];
|
||||
var[7] = msg[index];
|
||||
var[6] = msg[index + 1];
|
||||
var[5] = msg[index + 2];
|
||||
var[4] = msg[index + 3];
|
||||
var[3] = msg[index + 4];
|
||||
var[2] = msg[index + 5];
|
||||
var[1] = msg[index + 6];
|
||||
var[0] = msg[index + 7];
|
||||
|
||||
double val = BitConverter.ToDouble(var, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
private static char getChar(byte[] msg, int index)
|
||||
{
|
||||
return (char)msg[index + 3];
|
||||
}
|
||||
|
||||
private static RGBA getRGBA(byte[] msg, int index)
|
||||
{
|
||||
return new RGBA(msg[index], msg[index + 1], msg[index + 2], msg[index + 3]);
|
||||
}
|
||||
|
||||
private static Midi getMidi(byte[] msg, int index)
|
||||
{
|
||||
return new Midi(msg[index], msg[index + 1], msg[index + 2], msg[index + 3]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create byte arrays for arguments
|
||||
|
||||
protected static byte[] setInt(int value)
|
||||
{
|
||||
byte[] msg = new byte[4];
|
||||
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
msg[0] = bytes[3];
|
||||
msg[1] = bytes[2];
|
||||
msg[2] = bytes[1];
|
||||
msg[3] = bytes[0];
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
protected static byte[] setFloat(float value)
|
||||
{
|
||||
byte[] msg = new byte[4];
|
||||
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
msg[0] = bytes[3];
|
||||
msg[1] = bytes[2];
|
||||
msg[2] = bytes[1];
|
||||
msg[3] = bytes[0];
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
protected static byte[] setString(string value)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(value);
|
||||
int len = bytes.Length + (4 - bytes.Length % 4);
|
||||
if (len <= bytes.Length) len = len + 4;
|
||||
|
||||
byte[] msg = new byte[len];
|
||||
bytes.CopyTo(msg, 0);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
protected static byte[] setBlob(byte[] value)
|
||||
{
|
||||
int len = value.Length + 4;
|
||||
len = len + (4 - len % 4);
|
||||
|
||||
byte[] msg = new byte[len];
|
||||
byte[] size = setInt(value.Length);
|
||||
size.CopyTo(msg, 0);
|
||||
value.CopyTo(msg, 4);
|
||||
return msg;
|
||||
}
|
||||
|
||||
protected static byte[] setLong(Int64 value)
|
||||
{
|
||||
byte[] rev = BitConverter.GetBytes(value);
|
||||
byte[] output = new byte[8];
|
||||
output[0] = rev[7];
|
||||
output[1] = rev[6];
|
||||
output[2] = rev[5];
|
||||
output[3] = rev[4];
|
||||
output[4] = rev[3];
|
||||
output[5] = rev[2];
|
||||
output[6] = rev[1];
|
||||
output[7] = rev[0];
|
||||
return output;
|
||||
}
|
||||
|
||||
protected static byte[] setULong(UInt64 value)
|
||||
{
|
||||
byte[] rev = BitConverter.GetBytes(value);
|
||||
byte[] output = new byte[8];
|
||||
output[0] = rev[7];
|
||||
output[1] = rev[6];
|
||||
output[2] = rev[5];
|
||||
output[3] = rev[4];
|
||||
output[4] = rev[3];
|
||||
output[5] = rev[2];
|
||||
output[6] = rev[1];
|
||||
output[7] = rev[0];
|
||||
return output;
|
||||
}
|
||||
|
||||
protected static byte[] setDouble(double value)
|
||||
{
|
||||
byte[] rev = BitConverter.GetBytes(value);
|
||||
byte[] output = new byte[8];
|
||||
output[0] = rev[7];
|
||||
output[1] = rev[6];
|
||||
output[2] = rev[5];
|
||||
output[3] = rev[4];
|
||||
output[4] = rev[3];
|
||||
output[5] = rev[2];
|
||||
output[6] = rev[1];
|
||||
output[7] = rev[0];
|
||||
return output;
|
||||
}
|
||||
|
||||
protected static byte[] setChar(char value)
|
||||
{
|
||||
byte[] output = new byte[4];
|
||||
output[0] = 0;
|
||||
output[1] = 0;
|
||||
output[2] = 0;
|
||||
output[3] = (byte)value;
|
||||
return output;
|
||||
}
|
||||
|
||||
protected static byte[] setRGBA(RGBA value)
|
||||
{
|
||||
byte[] output = new byte[4];
|
||||
output[0] = value.R;
|
||||
output[1] = value.G;
|
||||
output[2] = value.B;
|
||||
output[3] = value.A;
|
||||
return output;
|
||||
}
|
||||
|
||||
protected static byte[] setMidi(Midi value)
|
||||
{
|
||||
byte[] output = new byte[4];
|
||||
output[0] = value.Port;
|
||||
output[1] = value.Status;
|
||||
output[2] = value.Data1;
|
||||
output[3] = value.Data2;
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user