// 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);
    }
Example #2
0
 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);
 }
Example #3
0
    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);
    }
Example #7
0
	/// <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);
    }
Example #10
0
    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);
    }
Example #14
0
 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);
    }
Example #19
0
    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);
    }
Example #20
0
    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;
    }
Example #21
0
	/// <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;
	}
Example #22
0
    /// <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);
    }
Example #26
0
    // 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);
    }