// Create a binary OSC bundle from the specified parts public OscBundle(ulong timestamp, IEnumerable<OscData> parts) { // Check parameters are valid if (parts == null) { throw new ArgumentNullException("parts", "Parts must not be null."); } // Store parameters this.Timestamp = timestamp; int numParts = 0; foreach (OscData part in parts) { numParts++; } this.Parts = new OscData[numParts]; numParts = 0; foreach (OscData part in parts) { this.Parts[numParts++] = part; } // Memory stream for OSC bundle using (MemoryStream stream = new MemoryStream()) { // An OSC Bundle consists of the OSC-string "#bundle" (null-terminated) stream.WriteOscString(OscBundle.BUNDLE_ADDRESS); // ...followed by an OSC Time Tag (The OSC-timetag is a 64-bit fixed point time tag, high 32 bits are seconds since 1/1/1900, low 32 bits are fractions of second) stream.WriteOscLong((long)this.Timestamp); // ...followed by zero or more OSC Bundle Elements. foreach (OscData part in Parts) { // Check part is valid if (part == null) { throw new ArgumentNullException("parts", "None of the parts may be null."); } if (part.Buffer == null) { throw new ArgumentException("None of the parts may have null data.", "parts"); } if (part.Buffer.Length % 4 != 0) { throw new ArgumentException("Every part must have a length that is a multiple of 4.", "parts"); } // An OSC Bundle Element consists of its size (b-e int-32, multiple of 4)... stream.WriteOscInt(part.Buffer.Length); // ...and its contents stream.Write(part.Buffer, 0, part.Buffer.Length); } // Return bundle bytes Buffer = stream.ToArray(); } }
// Create a binary OSC message for the specified address from the specified parameters public OscMessage(string address, IEnumerable<object> arguments) { // Check parameters are valid if (address == null) { throw new ArgumentNullException("address", "Address must not be null."); } if (!address.StartsWith(OscMessage.ADDRESS_PREFIX.ToString())) { throw new ArgumentException("Address must begin with a forward-slash ('/').", "address"); } if (address.IndexOfAny(OscMessage.ADDRESS_DISALLOWED_CHARS) >= 0) { throw new ArgumentException("Address contains one or more invalid characters.", "address"); } if (arguments == null) { throw new ArgumentNullException("arguments", "Arguments array must not be null."); } // Store address and arguments this.Address = address; int numArguments = 0; foreach (object o in arguments) { numArguments++; } this.Arguments = new object[numArguments]; numArguments = 0; foreach (object o in arguments) { this.Arguments[numArguments++] = o; } // Memory stream for OSC message using (MemoryStream stream = new MemoryStream()) { // Write address stream.WriteOscString(address); // Write param type tag: ",iiii\0" (i = 32-bit integer) StringBuilder typeTag = new StringBuilder(); typeTag.Append(','); // comma indicates start of parameter type tag list foreach (object o in Arguments) { if (o is SByte || o is Byte || o is Int16 || o is UInt16 || o is Int32 || o is UInt32) { typeTag.Append(OscMessage.TYPE_INT); } // signed two's complement int32 (big-endian) else if (o is Int64 || o is UInt64) { typeTag.Append(OscMessage.TYPE_LONG); } // (non-standard long int-64 type) else if (o is Single) { typeTag.Append(OscMessage.TYPE_FLOAT); } // IEEE 32-bit float (big-endian) else if (o is Double) { typeTag.Append(OscMessage.TYPE_DOUBLE); } // (non-standard double type) else if (o is Char) { typeTag.Append(OscMessage.TYPE_CHAR); } // (non-standard char type) else if (o is String) { typeTag.Append(OscMessage.TYPE_STRING); } // ASCII null-terminated string else if (o is byte[]) { typeTag.Append(OscMessage.TYPE_BLOB); } // blob (byte[]) with size else if (o is Boolean) { typeTag.Append((bool)o ? OscMessage.TYPE_TRUE : OscMessage.TYPE_FALSE); } // true or false type else if (o == null) { typeTag.Append(OscMessage.TYPE_NULL); } // null else if (o is DateTime) { typeTag.Append(OscMessage.TYPE_TIME); } // time tag (RFC-1305 NTP Timestamp format - upper 32 bits is the number of seconds since midnight on January 1, 1900, lower 32 bits specify the fractional part of a second) else if (o.Equals(typeof(Object))) { typeTag.Append(OscMessage.TYPE_IMPULSE); } // impulse (not sure of a good .NET representation for this) else if (o is Array) { throw new ArgumentException("Arrays not yet supported.", "arguments"); } else { throw new ArgumentException("One or more arguments are of an unhandled type.", "arguments"); } } stream.WriteOscString(typeTag.ToString()); // Values (all values are big-endian and will be padded to 4 bytes) foreach (object o in Arguments) { if (o is SByte) { stream.WriteOscInt((int)(SByte)o); } else if (o is Byte) { stream.WriteOscInt((int)(Byte)o); } else if (o is Int16) { stream.WriteOscInt((int)(Int16)o); } else if (o is UInt16) { stream.WriteOscInt((int)(UInt16)o); } else if (o is Int32) { stream.WriteOscInt((int)(Int32)o); } else if (o is UInt32) { stream.WriteOscInt((int)(UInt32)o); } else if (o is Int64) { stream.WriteOscLong((long)(Int64)o); } // (non-standard long int-64 type) else if (o is UInt64) { stream.WriteOscLong((long)(UInt64)o); } // (non-standard long int-64 type) else if (o is Single) { stream.WriteOscFloat((float)(Single)o); } else if (o is Double) { stream.WriteOscDouble((float)(Double)o); } // (non-standard double type) else if (o is Char) { stream.WriteOscInt((int)(Char)o); } // (non-standard char type, written as int-32) else if (o is String) { stream.WriteOscString((String)o); } else if (o is byte[]) { stream.WriteOscBlob((byte[])o); } else if (o is Boolean) { } // true or false type -- neither has data else if (o == null) { } // null type -- no data else if (o is DateTime) { stream.WriteOscLong((long)OscData.TimestampFromDateTime((DateTime)o)); } else if (o.Equals(typeof(Object))) { } // impulse (not sure of a good .NET representation for this) else if (o is Array) { throw new ArgumentException("Arrays not yet supported.", "arguments"); } else { throw new ArgumentException("One or more arguments are of an unhandled type.", "arguments"); } } // Return message bytes Buffer = stream.ToArray(); } }