// Undocumented on purpose. public override bool TryWriteTo(byte[] data, ref int index) { // Null check. if (data == null) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Write failed. Buffer cannot be null.\n"); Debug.LogWarning(sb.ToString()); return(false); } // Capacity check. int size = Size(); if (index + size > data.Length) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Write failed. Buffer capacity insufficient.\n"); Debug.LogWarning(sb.ToString()); return(false); } // Address. if (!StringOscData.TryWriteTo(_address, data, ref index)) { Debug.Log(OscDebug.FailedWritingBytesWarning(this)); return(false); } // Tag prefix. data[index++] = OscConst.tagPrefixByte; // Argument tags. int argCount = _argInfo.Count; for (int i = 0; i < argCount; i++) { data[index++] = _argInfo[i].tagByte; } // Followed by at least one trailing zero, multiple of four bytes. int trailingNullCount = 4 - (index % 4); for (int i = 0; i < trailingNullCount; i++) { data[index++] = 0; } //Debug.Log( "WRITE: Args data start index: " + index ); // Argument data. _argData.CopyTo(data, index); index += _argData.Count; // Cache byte count. _cachedSize = size; _dirtySize = false; return(true); }
bool GetOrCreateMapping(string address, OscMessageType type, out OscMapping mapping) { mapping = _mappings.Find(m => m.address == address); if (mapping == null) { mapping = new OscMapping(address, type); //mapping = ScriptableObject.CreateInstance<OscMapping>(); //mapping.Init( address, type ); _mappings.Add(mapping); } else if (mapping.type != type) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Failed to map address \""); sb.Append(address); sb.Append("\" to method with argument type "); sb.Append(type); sb.Append(". \nAddress is already mapped to a method with argument type "); sb.Append(mapping.type); sb.Append(", either in the editor, or by a script. Only one type per address is allowed.\n"); Debug.LogWarning(sb.ToString()); } return(false); } return(true); }
bool ValidateAddressForMapping(string address) { // Check for address prefix. if (address.Length < 2 || address[0] != OscConst.addressPrefix) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.AppendLine("Ignored attempt to create mapping. OSC addresses must begin with slash '/'."); sb.Append("\"" + address + "\""); Debug.LogWarning(sb.ToString()); } return(false); } // Check for whitespace. if (address.Contains(" ")) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.AppendLine("Ignored attempt to create mapping. OSC addresses cannot contain whitespaces."); sb.Append("\"" + address + "\""); Debug.LogWarning(sb.ToString()); } return(false); } return(true); }
bool TrySendCache(int byteCount) { // TODO this should be moved to a UdpSender class. try { // Send!! _udpClient.Send(_cache, byteCount, _endPoint); // Socket error reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx } catch (SocketException ex) { if (ex.ErrorCode == 10051) // "Network is unreachable" // Ignore. We get this when broadcasting while having no access to a network. { } else if (ex.ErrorCode == 10065) // "No route to host" // Ignore. We get this sometimes when unicasting. { } else if (ex.ErrorCode == 10049) // "The requested address is not valid in this context" // Ignore. We get this when we broadcast and have no access to the local network. For example if we are using a VPN. { } else if (ex.ErrorCode == 10061) // "Connection refused" // Ignore. { } else if (ex.ErrorCode == 10040) // "Message too long" { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Failed to send message. Packet size at "); sb.Append(byteCount); sb.Append(" bytes exceeds udp buffer size at "); sb.Append(_udpBufferSize); sb.Append(" bytes. Try increasing the buffer size or enable 'splitBundlesAvoidingBufferOverflow.'\n"); Debug.LogWarning(sb.ToString()); } else { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Failed to send message to "); sb.Append(_remoteIpAddress); sb.Append(" on port "); sb.Append(port); sb.Append(".\n"); sb.Append(ex.ErrorCode); sb.Append(ex); Debug.LogWarning(sb.ToString()); } return(false); } catch (Exception ex) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Failed to send message to "); sb.Append(_remoteIpAddress); sb.Append(" on port "); sb.Append(port); sb.Append(".\n"); sb.Append(ex); Debug.LogWarning(sb.ToString()); return(false); } return(true); }
/// <summary> /// Set argument at specified index, expanding message capacity if necessary. /// </summary> public OscMessage Set(int index, OscMidiMessage value) { int dataStartIndex = AdaptiveSet(index, OscArgInfo.midiInfo); if (!new FourByteOscData(value).TryWriteTo(_argData, ref dataStartIndex)) { Debug.Log(OscDebug.FailedWritingBytesWarning(this)); } return(this); }
/// <summary> /// Set a value as a byte blob at specified index, expanding message capacity if necessary. /// </summary> public OscMessage SetBlob(int index, Matrix4x4 value) { int dataStartIndex = AdaptiveSet(index, OscArgInfo.sixtyfourByteBlobInfo); if (!BlobOscData.TryWriteTo(value, _argData, ref dataStartIndex)) { Debug.Log(OscDebug.FailedWritingBytesWarning(this)); } return(this); }
/// <summary> /// Send an OscMessage or OscBundle. Data is serialized and no reference is stored, so you can safely /// change values and send packet immediately again. /// Returns success status. /// </summary> public bool Send( OscPacket packet ) { if( !isOpen ) return false; // On any message. if( _onAnyMessage != null ) InvokeAnyMessageEventRecursively( packet ); // Individual messages are always send in bundles at end of frame. if( packet is OscMessage ){ _endOfFrameBuffer.Add( packet as OscMessage ); return true; // Assume success. } // Split bundle case. if( packet.Size() > _udpBufferSize ){ ExtractMessages( packet, _tempMessageQueue ); int bundleByteCount = OscConst.bundleHeaderSize; OscBundle splitBundle = OscPool.GetBundle(); while( _tempMessageQueue.Count > 0 ) { OscMessage message = _tempMessageQueue.Dequeue(); // Check if message is too big. int messageSize = message.Size() + FourByteOscData.byteCount; // Bundle stores size of each message in a 4 byte integer. if( messageSize > _udpBufferSize ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Failed to send message. Message size at " ); sb.Append( messageSize ); sb.Append( " bytes exceeds udp buffer size at " ); sb.Append( _udpBufferSize ); sb.Append( " bytes. Try increasing the buffer size.'\n" ); Debug.LogWarning( sb.ToString() ); return false; } // If bundle is full, send it and prepare for new bundle. if( bundleByteCount + messageSize > _udpBufferSize ) { if( !Send( splitBundle ) ) return false; bundleByteCount = OscConst.bundleHeaderSize; splitBundle.Clear(); } splitBundle.packets.Add( message ); bundleByteCount += messageSize; } if( splitBundle.packets.Count > 0 && !Send( splitBundle ) ) return false; OscPool.Recycle( splitBundle ); return true; } // Try to pack the message. int index = 0; if( !packet.TryWriteTo( _cache, ref index ) ) return false; //Debug.Log( $"Sending byte count {index}" ); // Send data! return TrySendCache( index ); }
/// <summary> /// Writes a list of values to a byte blob at specified index, expanding message capacity if necessary. /// </summary> public OscMessage SetBlob(int index, IList <int> values) { OscArgInfo info = new OscArgInfo(OscConst.tagBlobByte, (1 + values.Count) * FourByteOscData.byteCount); int dataStartIndex = AdaptiveSet(index, info); if (!BlobOscData.TryWriteTo(values, _argData, ref dataStartIndex)) { Debug.Log(OscDebug.FailedWritingBytesWarning(this)); } return(this); }
/// <summary> /// Writes a string with a given encoding to a byte blob at specified index, expanding message capacity if necessary. /// </summary> public OscMessage SetBlob(int index, Encoding encoding, string value) { OscArgInfo info = new OscArgInfo(OscConst.tagBlobByte, BlobOscData.EvaluateByteCount(value, encoding)); int dataStartIndex = AdaptiveSet(index, info); if (!BlobOscData.TryWriteTo(value, encoding, _argData, ref dataStartIndex)) { Debug.Log(OscDebug.FailedWritingBytesWarning(this)); } return(this); }
bool ValidateMethodTarget(object target, string address) { if (target == null) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Ignored attempt to create mapping. Method cannot be null.\n"); sb.Append(address); Debug.LogWarning(sb); return(false); } return(true); }
/// <summary> /// Tries to read a list of values from a byte blob at argument index. Returns success status. /// </summary> public bool TryGetBlob(int index, ref List <int> values) { if (!ValidateTryGet(index, OscArgType.Blob)) { return(false); } // Get. int dataStartIndex = GetDataIndex(index); if (!BlobOscData.TryReadFrom(_argData, ref dataStartIndex, ref values)) { Debug.Log(OscDebug.FailedReadingBytesWarning(this)); return(false); } return(true); }
/// <summary> /// Tries to read string with a given encoding from a byte blob at argument index. Returns success status. /// </summary> public bool TryGetBlob(int index, Encoding encoding, out string value) { if (!ValidateTryGet(index, OscArgType.Blob)) { value = string.Empty; return(false); } // Get. int dataStartIndex = GetDataIndex(index); if (!BlobOscData.TryReadFrom(_argData, ref dataStartIndex, encoding, out value)) { Debug.Log(OscDebug.FailedReadingBytesWarning(this)); return(false); } return(true); }
bool ValidateTryGet(int index, OscArgType requestedType) { // Arg bounds. if (index < 0 || index >= _argInfo.Count) { StringBuilder sb = StartBuildingInvalidTryGetString(OscDebug.BuildText(this)); sb.Append("Requested argument index "); sb.Append(index); sb.Append(" is out of bounds. Message has "); sb.Append(_argInfo.Count); sb.Append(" arguments.\n"); Debug.LogWarning(sb.ToString()); return(false); } // Arg type. OscArgInfo info = _argInfo[index]; OscArgType type = OscConverter.ToArgType(info.tagByte); if (requestedType != type) { StringBuilder sb = StartBuildingInvalidTryGetString(OscDebug.BuildText(this)); sb.Append("Argument at index "); sb.Append(index); sb.Append(" is not type "); sb.Append(requestedType); sb.Append(" ('"); sb.Append((char)OscConverter.ToTagByte(requestedType)); sb.Append("')"); sb.Append(", it is "); sb.Append(type); sb.Append(" ('"); sb.Append((char)info.tagByte); sb.Append("').\n"); Debug.LogWarning(sb.ToString()); return(false); } // Data capacity. if (index + info.size > _argData.Count) { StringBuilder sb = StartBuildingInvalidTryGetString(OscDebug.BuildText(this)); sb.Append("Argument at index "); sb.Append(index); sb.Append(" has incomplete data\n"); Debug.LogWarning(sb.ToString()); return(false); } return(true); }
bool GetOrCreateMapping(string address, OscMessageType type, out OscMapping mapping) { mapping = _mappings.Find(m => m.address == address); if (mapping == null) { mapping = new OscMapping(address, type); _mappings.Add(mapping); } else if (mapping.type != type) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Failed to map address'"); sb.Append(address); sb.Append("' to method with argument type '"); sb.Append(type); sb.Append("'. Address is already set to receive type '"); sb.Append(mapping.type); sb.Append("', either in the editor, or by a script.\nOnly one type per address is allowed.\n"); Debug.LogWarning(sb.ToString()); return(false); } return(true); }
/// <summary> /// Tries to get argument at index of type time tag. Returns success status. /// </summary> public bool TryGet(int index, out OscTimeTag value) { if (!ValidateTryGet(index, OscArgType.TimeTag)) { value = new OscTimeTag(); return(false); } // Get. int dataStartIndex = GetDataIndex(index); EightByteOscData dataValue; if (!EightByteOscData.TryReadFrom(_argData, ref dataStartIndex, out dataValue)) { value = new OscTimeTag(); Debug.Log(OscDebug.FailedReadingBytesWarning(this)); return(false); } value = dataValue.timeTagValue; return(true); }
/// <summary> /// Tries to get argument at index of type char. Returns success status. /// </summary> public bool TryGet(int index, out char value) { if (!ValidateTryGet(index, OscArgType.Char)) { value = (char)OscConst.tagUnsupportedByte; return(false); } // Get. int dataStartIndex = GetDataIndex(index); FourByteOscData dataValue; if (!FourByteOscData.TryReadFrom(_argData, ref dataStartIndex, out dataValue)) { value = ' '; Debug.Log(OscDebug.FailedReadingBytesWarning(this)); return(false); } value = dataValue.charValue; return(true); }
/// <summary> /// Tries to get argument at index of type float. Returns success status. /// </summary> public bool TryGet(int index, out float value) { if (!ValidateTryGet(index, OscArgType.Float)) { value = 0; return(false); } // Get. int dataStartIndex = GetDataIndex(index); FourByteOscData dataValue; if (!FourByteOscData.TryReadFrom(_argData, ref dataStartIndex, out dataValue)) { value = 0; Debug.Log(OscDebug.FailedReadingBytesWarning(this)); return(false); } value = dataValue.floatValue; return(true); }
/// <summary> /// Tries to get argument at index of midi message. Returns success status. /// </summary> public bool TryGet(int index, out OscMidiMessage value) { if (!ValidateTryGet(index, OscArgType.Midi)) { value = new OscMidiMessage(); return(false); } // Get. int dataStartIndex = GetDataIndex(index); FourByteOscData dataValue; if (!FourByteOscData.TryReadFrom(_argData, ref dataStartIndex, out dataValue)) { value = new OscMidiMessage(); Debug.Log(OscDebug.FailedReadingBytesWarning(this)); return(false); } value = dataValue.midiMessage; return(true); }
bool ValidateMethodTarget(object target, string address) { if (target == null) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Ignored attempt to create mapping. Method cannot be null.\n"); sb.Append(address); Debug.LogWarning(sb); return(false); } if (!(target is UnityEngine.Object)) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Ignored attempt to create mapping. Method must be a member of an object that inrehits from ScriptableObject or MonoBehaviour.\n"); sb.Append(address); Debug.LogWarning(sb); return(false); } return(true); }
void SetTime(DateTime time) { // Silently fix invalid time values. // We cannot go below the epoch, since the offset from epoch will be stored in an unsigned integer. if (time.Ticks - epochTicks < 0) { StringBuilder sb = OscDebug.BuildText(); sb.Append("OSC timtags cannot store time values before epoch time 01/01/1900. Received time: "); sb.Append(time); sb.AppendLine(); Debug.Log(sb); time = new DateTime(epochTicks); } // Store 'immediately' flag bool tempImmediate = immediately; // DateTime to OSC NTP Decimal timeDec = (time.Ticks - epochTicks) / ntp2Ticks; if (timeDec > ulong.MaxValue) { timeDec = ulong.MaxValue; } _oscNtp = (ulong)timeDec; // DateTime to NTP (Readable version) //long offTicks = time.Ticks - epochTicks; // Get the offset from epoch time in ticks //uint offSec = (uint) ( offTicks / TimeSpan.TicksPerSecond ); // Get the offset from epoch time in seconds //long fracTicks = offTicks - ( offSec * TimeSpan.TicksPerSecond ); // Get the remaining ticks //double fracSec = fracTicks * (double) secondsPerDateTimeTick; // Convert to seconds (normalize) //uint frac = (uint) ( fracSec * uint.MaxValue ); // Scale up value to use full resoultion of the uint //_oscNtp = offSec; // Set seconds //_oscNtp = _oscNtp << 32; // Push the seconds to the high-order 32 bits //_oscNtp += frac; // Add factional second // Set 'immediately' flag immediately = tempImmediate; }
/// <summary> /// Open to send messages to specified port and (optional) IP address. /// If no IP address is given, messages will be send locally on this device. /// Returns success status. /// </summary> public bool Open( int port, string remoteIpAddress = "" ) { // TODO this should be moved to a UdpSender class. // Close and stop pinging. if( _udpClient != null ) Close(); // Validate IP. IPAddress ip; if( string.IsNullOrEmpty( remoteIpAddress ) ) remoteIpAddress = IPAddress.Loopback.ToString(); if( remoteIpAddress == IPAddress.Any.ToString() || !IPAddress.TryParse( remoteIpAddress, out ip ) ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Open failed. Invalid IP address " ); sb.Append( remoteIpAddress ); sb.Append( ".\n" ); Debug.LogWarning( sb.ToString() ); return false; } if( ip.AddressFamily != AddressFamily.InterNetwork ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Open failed. Only IPv4 addresses are supported. " ); sb.Append( remoteIpAddress ); sb.Append( " is " ); sb.Append( ip.AddressFamily ); sb.Append( ".\n" ); Debug.LogWarning( sb.ToString() ); return false; } _remoteIpAddress = remoteIpAddress; // Detect and set transmission mode. if( _remoteIpAddress == IPAddress.Loopback.ToString() ){ _mode = OscSendMode.UnicastToSelf; } else if( _remoteIpAddress == IPAddress.Broadcast.ToString() ){ _mode = OscSendMode.Broadcast; } else if( Regex.IsMatch( _remoteIpAddress, OscConst.multicastAddressPattern ) ){ _mode = OscSendMode.Multicast; } else { _mode = OscSendMode.Unicast; } // Validate port number range if( port < OscConst.portMin || port > OscConst.portMax ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Open failed. Port " ); sb.Append( port ); sb.Append( " is out of range.\n" ); Debug.LogWarning( sb.ToString() ); return false; } _port = port; // Create new client and end point. _udpClient = new UdpClient(); _endPoint = new CachedIpEndPoint( ip, _port ); // Multicast senders do not need to join a multicast group, but we need to set a few options. if( _mode == OscSendMode.Multicast ) { // Set a time to live, indicating how many routers the messages is allowed to be forwarded by. _udpClient.Client.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, OscConst.timeToLive ); // Apply out multicastLoopback field. _udpClient.MulticastLoopback = _multicastLoopback; } // Set time to live to max. I haven't observed any difference, but we better be safe. _udpClient.Ttl = OscConst.timeToLive; // If an outgoing packet happen to exceeds the MTU (Maximum Transfer Unit) then throw an error instead of fragmenting. _udpClient.DontFragment = true; // Set buffer size. _udpClient.Client.SendBufferSize = _udpBufferSize; // DO NOT CONNECT UDP CLIENT! //_udpClient.Connect( _endPoint ); // Note to self about connecting UdpClient: // We do not use udpClient.Connect(). Instead we pass the IPEndPoint directly to _udpClient.Send(). // This is because a connected UdpClient purposed for sending will throw a number of (for our use) unwanted exceptions and in some cases disconnect. // 10061: SocketException: Connection refused - When we attempt to unicast to loopback address when no application is listening. // 10049: SocketException: The requested address is not valid in this context - When we attempt to broadcast while having no access to the local network. // 10051: SocketException: Network is unreachable - When we pass a unicast or broadcast target to udpClient.Connect() while having no access to a network. // Handle pinging if( Application.isPlaying ) { _remoteStatus = _mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; if( _mode == OscSendMode.Unicast ){ _pingCoroutine = PingCoroutine(); StartCoroutine( _pingCoroutine ); } } // Create cache buffer. if( _cache == null || _cache.Length != _udpBufferSize ) _cache = new byte[_udpBufferSize]; // Log if( Application.isPlaying ){ string addressTypeString = string.Empty; switch( _mode ){ case OscSendMode.Broadcast: addressTypeString = "broadcast"; break; case OscSendMode.Multicast: addressTypeString = "multicast"; break; case OscSendMode.Unicast: addressTypeString = "IP"; break; case OscSendMode.UnicastToSelf: addressTypeString = "local"; break; } StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Ready to send to " ); sb.Append( addressTypeString ); sb.Append( " address " ); sb.Append( _remoteIpAddress ); sb.Append( " on port " ); sb.Append( port ); sb.Append( ".\n" ); Debug.Log( sb.ToString() ); } return true; }
/// <summary> /// Open to receive messages on specified port and (optionally) from specified multicast IP address. /// Returns success status. /// </summary> public bool Open(int port, string multicastAddress = "") { // Ensure that we have a receiver, even when not in Play mode. if (_receiver == null) { _receiver = new UdpReceiver(OnDataReceivedAsync); } // Close and existing receiver. if (isOpen) { Close(); } // Validate port number range. if (port < OscConst.portMin || port > OscConst.portMax) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Open failed. Port "); sb.Append(port); sb.Append(" is out of range.\n"); Debug.LogWarning(sb.ToString()); return(false); } _port = port; // Derive mode from multicastAddress. IPAddress multicastIP; if (!string.IsNullOrEmpty(multicastAddress) && IPAddress.TryParse(multicastAddress, out multicastIP)) { if (Regex.IsMatch(multicastAddress, OscConst.multicastAddressPattern)) { _mode = OscReceiveMode.UnicastBroadcastMulticast; _multicastAddress = multicastAddress; } else { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Open failed. Multicast IP address "); sb.Append(multicastAddress); sb.Append(" is out not valid. It must be in range 224.0.0.0 to 239.255.255.255.\n"); Debug.LogWarning(sb.ToString()); return(false); } } else { _multicastAddress = string.Empty; _mode = OscReceiveMode.UnicastBroadcast; } // Set buffer size. _receiver.bufferSize = _udpBufferSize; // Try open. if (!_receiver.Open(_port, _multicastAddress)) { Debug.Log("Failed to open"); return(false); } // Deal with the success if (Application.isPlaying) { StringBuilder sb = OscDebug.BuildText(this); if (_mode == OscReceiveMode.UnicastBroadcast) { sb.Append("Ready to receive unicast and broadcast messages on port "); } else { sb.Append("Ready to receive multicast messages on address "); sb.Append(_multicastAddress); sb.Append(", unicast and broadcast messages on port "); } sb.Append(_port); sb.AppendLine(); Debug.Log(sb.ToString()); } return(true); }
/// <summary> /// Send an OscMessage or OscBundle. Data is serialized and no reference is stored, so you can safely /// change values and send packet immediately again. /// Returns success status. /// </summary> public bool Send(OscPacket packet) { if (!isOpen) { return(false); } int index = 0; // On any message. if (_onAnyMessage != null) { InvokeAnyMessageEventRecursively(packet); } // Adapt buffer size. if (_sendBuffer == null || _sendBuffer.Length != _udpBufferSize) { _sendBuffer = new byte[_udpBufferSize]; } // Handle user messages. if (packet is OscMessage) { if (_bundleMessagesAutomatically) { // Collect to be bundled and send by end of the Unity frame. _autoBundleMessageBuffer.Add(packet as OscMessage); return(true); // Assume success. } else { // Add to cache and send immediately. OscMessage message = packet as OscMessage; message.TryWriteTo(_sendBuffer, ref index); bool success = TrySendBuffer(index); if (success) { _messageCountThisFrame++; } return(success); } } // Handle user bundles. Bundles provided by the user are send immediately. If too big, they are split into more bundles. OscBundle bundle = packet as OscBundle; if (bundle.Size() > _udpBufferSize) { ExtractMessages(packet, _userBundleTempMessages); int bundleByteCount = OscConst.bundleHeaderSize; OscBundle splitBundle = OscPool.GetBundle(); while (_userBundleTempMessages.Count > 0) { OscMessage message = _userBundleTempMessages.Dequeue(); // Check if message is too big. int messageSize = message.Size() + FourByteOscData.byteCount; // Bundle stores size of each message in a 4 byte integer. if (messageSize > _udpBufferSize) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Failed to send message. Message size at "); sb.Append(messageSize); sb.Append(" bytes exceeds udp buffer size at "); sb.Append(_udpBufferSize); sb.Append(" bytes. Try increasing the buffer size.'\n"); Debug.LogWarning(sb.ToString()); } return(false); } // If bundle is full, send it and prepare for new bundle. if (bundleByteCount + messageSize > _udpBufferSize) { if (!Send(splitBundle)) { return(false); } bundleByteCount = OscConst.bundleHeaderSize; splitBundle.Clear(); } splitBundle.packets.Add(message); bundleByteCount += messageSize; } if (splitBundle.packets.Count > 0 && !Send(splitBundle)) { return(false); } OscPool.Recycle(splitBundle); return(true); } // Try to pack the message. if (!bundle.TryWriteTo(_sendBuffer, ref index)) { return(false); } _messageCountThisFrame += bundle.packets.Count; // Send data! return(TrySendBuffer(index)); }
// Undocumented on purpose. public static bool TryReadFrom(byte[] data, ref int index, int size, ref OscMessage message) { int beginIndex = index; // If we are not provided with a message, then read the lossy hash and try reuse from the pool. if (message == null) { int hash = OscStringHash.Pack(data, index); message = OscPool.GetMessage(hash); } else { if (message._argInfo.Count > 0) { message.Clear(); // Ensure that arguments are cleared. } } // Address. string address = message._address; if (!StringOscData.TryReadFrom(data, ref index, ref address)) { Debug.Log(OscDebug.FailedReadingBytesWarning(message)); return(false); } message._address = address; // Tag prefix. if (data[index] != OscConst.tagPrefixByte) { StringBuilder sb = OscDebug.BuildText(message); sb.Append("Read failed. Tag prefix missing.\n"); Debug.LogWarning(sb.ToString()); return(false); } index++; // Argument tags. for (int i = index; i < data.Length && data[i] != 0; i++) { message._argInfo.Add(new OscArgInfo(data[i], 0)); } index += message._argInfo.Count; // Followed by at least one trailing zero, multiple of four bytes. index += 4 - (index % 4); //Debug.Log( "READ: Args data start index: " + index ); // Argument data info. int argDataByteCount = 0; for (int i = 0; i < message._argInfo.Count; i++) { byte tagByte = message._argInfo[i].tagByte; int argByteCount = 0; switch (tagByte) { case OscConst.tagNullByte: case OscConst.tagImpulseByte: case OscConst.tagTrueByte: case OscConst.tagFalseByte: break; case OscConst.tagFloatByte: case OscConst.tagIntByte: case OscConst.tagCharByte: case OscConst.tagColorByte: case OscConst.tagMidiByte: argByteCount = 4; break; case OscConst.tagDoubleByte: case OscConst.tagLongByte: case OscConst.tagTimetagByte: argByteCount = 8; break; case OscConst.tagStringByte: case OscConst.tagSymbolByte: argByteCount = StringOscData.EvaluateByteCount(data, index + argDataByteCount); break; case OscConst.tagBlobByte: BlobOscData.TryEvaluateByteCount(data, index + argDataByteCount, out argByteCount); break; default: StringBuilder sb = OscDebug.BuildText(message); sb.Append("Read failed. Tag '"); sb.Append((char)tagByte); sb.Append("' is not supported\n"); Debug.LogWarning(sb.ToString()); return(false); } message._argInfo[i] = new OscArgInfo(tagByte, argByteCount); //Debug.Log( "i; " + i + ", info: " + message._argInfo[i] ); argDataByteCount += argByteCount; } // AdaptiveSet data list. if (message._argData.Capacity < argDataByteCount) { message._argData.Capacity = argDataByteCount; } // Read data. for (int i = 0; i < argDataByteCount; i++) { message._argData.Add(data[index++]); } // Cache byte count. message._cachedSize = index - beginIndex; message._dirtySize = false; return(true); }
/// <summary> /// Open to send messages to specified port and (optional) IP address. /// If no IP address is given, messages will be send locally on this device. /// Returns success status. /// </summary> public bool Open(int port, string remoteIpAddress = "") { // Validate IP. IPAddress ip; if (string.IsNullOrEmpty(remoteIpAddress)) { remoteIpAddress = IPAddress.Loopback.ToString(); } if (remoteIpAddress == IPAddress.Any.ToString() || !IPAddress.TryParse(remoteIpAddress, out ip)) { if (OscGlobals.logWarnings) { Debug.LogWarning(string.Format("{0}Open failed. Invalid IP address {1}\n", logPrepend, remoteIpAddress)); } return(false); } if (ip.AddressFamily != AddressFamily.InterNetwork) { if (OscGlobals.logWarnings) { Debug.LogWarning(string.Format("{0}Open failed. Only IPv4 addresses are supported. {1} is {2}\n", logPrepend, remoteIpAddress, ip.AddressFamily)); } return(false); } _remoteIpAddress = remoteIpAddress; // Detect and set transmission mode. if (_remoteIpAddress == IPAddress.Loopback.ToString()) { _mode = OscSendMode.UnicastToSelf; } else if (_remoteIpAddress == IPAddress.Broadcast.ToString()) { _mode = OscSendMode.Broadcast; } else if (Regex.IsMatch(_remoteIpAddress, OscConst.multicastAddressPattern)) { _mode = OscSendMode.Multicast; } else { _mode = OscSendMode.Unicast; } // Validate port number range if (port < OscConst.portMin || port > OscConst.portMax) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Open failed. Port "); sb.Append(port); sb.Append(" is out of range.\n"); Debug.LogWarning(sb.ToString()); } return(false); } _port = port; // Create new client and end point. if (_sender == null) { _sender = new UdpSender(_udpBufferSize); } _sender.SetRemoteTarget(_port, ip); // Handle pinging if (Application.isPlaying) { _remoteStatus = _mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; if (_mode == OscSendMode.Unicast) { _pingCoroutine = PingCoroutine(); StartCoroutine(_pingCoroutine); } } // Log if (Application.isPlaying) { string addressTypeString = string.Empty; switch (_mode) { case OscSendMode.Broadcast: addressTypeString = "broadcast"; break; case OscSendMode.Multicast: addressTypeString = "multicast"; break; case OscSendMode.Unicast: addressTypeString = "IP"; break; case OscSendMode.UnicastToSelf: addressTypeString = "local"; break; } if (OscGlobals.logStatuses) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Ready to send to "); sb.Append(addressTypeString); sb.Append(" address "); sb.Append(_remoteIpAddress); sb.Append(" on port "); sb.Append(port); sb.Append(".\n"); Debug.Log(sb.ToString()); } } return(true); }
// Undocumented on purpose. public override bool TryWriteTo(byte[] data, ref int index) { // Null check. if (data == null) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Write failed. Buffer cannot be null.\n"); Debug.LogWarning(sb.ToString()); } return(false); } // Capacity check. int size = Size(); if (index + size > data.Length) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Write failed. Buffer capacity insufficient.\n"); Debug.LogWarning(sb.ToString()); } return(false); } // Write header. TryWriteHeader(_timeTag, data, ref index); // Write packets. foreach (OscPacket packet in _packets) { if (packet == null) { continue; } // Silently ensure that nested bundle's timetag is >= than parent bundle's timetag. if (packet is OscBundle && (packet as OscBundle).timeTag < _timeTag) { (packet as OscBundle).timeTag = _timeTag; } // Reserve space for writing package length. int packetSizeIndex = index; index += 4; // Write packet to bytes. int packetContentIndex = index; if (!packet.TryWriteTo(data, ref index)) { return(false); } int packetByteCount = index - packetContentIndex; // Write packet byte size (before packet data). if (!new FourByteOscData(packetByteCount).TryWriteTo(data, ref packetSizeIndex)) { if (OscGlobals.logWarnings) { Debug.Log(OscDebug.FailedWritingBytesWarning(this)); } return(false); } } return(true); }