├── UE-OSC ├── Resources │ └── Icon128.png ├── Source │ └── UE_OSC │ │ ├── Public │ │ ├── Osc.h │ │ ├── Common │ │ │ ├── OscMessageStruct.h │ │ │ ├── OscDataElemStruct.h │ │ │ └── OscFunctionLibrary.h │ │ └── Receive │ │ │ ├── OscReceiverInterface.h │ │ │ ├── OscReceiverActor.h │ │ │ ├── OscReceiverComponent.h │ │ │ └── OscDispatcher.h │ │ ├── Private │ │ ├── Common │ │ │ ├── OscUtils.h │ │ │ ├── oscpack │ │ │ │ └── osc │ │ │ │ │ ├── OscTypes.cpp │ │ │ │ │ ├── OscHostEndianness.h │ │ │ │ │ ├── OscOutboundPacketStream.h │ │ │ │ │ ├── OscTypes.h │ │ │ │ │ ├── OscReceivedElements.h │ │ │ │ │ ├── OscOutboundPacketStream.cpp │ │ │ │ │ └── OscReceivedElements.cpp │ │ │ └── OscFunctionLibrary.cpp │ │ ├── Receive │ │ │ ├── OscReceiverInputKey.h │ │ │ ├── OscReceiverActor.cpp │ │ │ ├── OscReceiverComponent.cpp │ │ │ ├── OscReceiverInputKey.cpp │ │ │ ├── OscDispatcher.cpp │ │ │ └── oscpkt.hh │ │ ├── OscSettings.h │ │ ├── OscModule.cpp │ │ └── OscSettings.cpp │ │ └── UE_OSC.Build.cs └── UE-OSC.uplugin ├── VERSION ├── .gitignore ├── LICENSE └── README.md /UE-OSC/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monsieurgustav/UE-OSC/HEAD/UE-OSC/Resources/Icon128.png -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.3: [breaking] parameters are stored as a standard array, allowing use of blueprint array functions, as well a Push/Pop functions 2 | 0.2: add receiver actor class 3 | 0.1: receiver component, send function, float integer boolean and string parameters 4 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Osc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | 5 | // Target engine version. Used to enable compatibility code. 6 | #define OSC_ENGINE_VERSION 40800 7 | 8 | #ifndef OSC_EDITOR_BUILD 9 | #error OSC_EDITOR_BUILD must be set by the OSC.Build.cs 10 | #endif 11 | 12 | DECLARE_LOG_CATEGORY_EXTERN(LogUE_OSC, Log, All); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/OscUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "UObject/NameTypes.h" 3 | 4 | namespace Osc 5 | { 6 | template 7 | void NameToLower(CharType(&Str)[NAME_SIZE]) 8 | { 9 | for (int32 i = 0; i < NAME_SIZE; ++i) 10 | { 11 | if (Str[i] == '\0') 12 | { 13 | return; 14 | } 15 | Str[i] = TChar::ToLower(Str[i]); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /UE-OSC/UE-OSC.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | "FriendlyName" : "UE-OSC ", 4 | "Version" : 1, 5 | "VersionName" : "1.0", 6 | "CreatedBy" : "Guillaume Buisson", 7 | "CreatedByURL" : "http://guillaume.dev", 8 | "Description" : "", 9 | "Category" : "OSC", 10 | "EnabledByDefault" : true, 11 | "Modules" : 12 | [ 13 | { 14 | "Name" : "UE_OSC", 15 | "Type" : "Runtime", 16 | "LoadingPhase" : "PreDefault" 17 | } 18 | ], 19 | "CanContainContent" : false 20 | } 21 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Common/OscMessageStruct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "OscDataElemStruct.h" 5 | #include "OscMessageStruct.generated.h" 6 | 7 | 8 | USTRUCT(BlueprintType) 9 | struct UE_OSC_API FOscMessageStruct 10 | { 11 | GENERATED_USTRUCT_BODY() 12 | 13 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=OSC) 14 | FName Address; 15 | 16 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=OSC) 17 | TArray Data; 18 | }; 19 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Receive/OscReceiverInputKey.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "Receive/OscReceiverInterface.h" 5 | #include "Common/OscDataElemStruct.h" 6 | 7 | #include "InputCoreTypes.h" 8 | 9 | 10 | class OscReceiverInputKey : public IOscReceiverInterface 11 | { 12 | public: 13 | OscReceiverInputKey(FName address); 14 | 15 | OscReceiverInputKey(const OscReceiverInputKey &) = delete; 16 | OscReceiverInputKey& operator=(const OscReceiverInputKey &) = delete; 17 | 18 | private: 19 | 20 | /// Adds the custom input key to the Slate application. 21 | void RegisterKey() const; 22 | 23 | FName GetAddressFilter() const override 24 | { 25 | return _address; 26 | } 27 | 28 | void SendEvent(const FName & Address, const TArray & Data, const FString & SenderIp) override; 29 | 30 | private: 31 | FName _address; 32 | FKey _key; 33 | }; 34 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Receive/OscReceiverInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "Common/OscDataElemStruct.h" 5 | 6 | 7 | /// Base class to receive OSC messages. 8 | struct UE_OSC_API IOscReceiverInterface 9 | { 10 | virtual ~IOscReceiverInterface() {} 11 | virtual FName GetAddressFilter() const = 0; 12 | virtual void SendEvent(const FName & Address, const TArray & Data, const FString & SenderIp) = 0; 13 | }; 14 | 15 | 16 | /// Forward calls to an impl object. 17 | template 18 | struct UE_OSC_API BasicOscReceiver : IOscReceiverInterface 19 | { 20 | T * const _impl; 21 | 22 | BasicOscReceiver(T * impl) : _impl(impl) 23 | { 24 | } 25 | 26 | FName GetAddressFilter() const final 27 | { 28 | return _impl->GetAddressFilter(); 29 | } 30 | 31 | void SendEvent(const FName & Address, const TArray & Data, const FString & SenderIp) final 32 | { 33 | _impl->SendEvent(Address, Data, SenderIp); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Receive/OscReceiverActor.cpp: -------------------------------------------------------------------------------- 1 | #include "Receive/OscReceiverActor.h" 2 | #include "Receive/OscDispatcher.h" 3 | 4 | 5 | AOscReceiverActor::AOscReceiverActor() 6 | : _listener(this) 7 | { 8 | } 9 | 10 | AOscReceiverActor::AOscReceiverActor(FVTableHelper& helper) 11 | : _listener(this) 12 | { 13 | } 14 | 15 | void AOscReceiverActor::BeginPlay() 16 | { 17 | Super::BeginPlay(); 18 | 19 | auto instance = UOscDispatcher::Get(); 20 | if (instance && !HasAnyFlags(RF_ClassDefaultObject)) 21 | { 22 | instance->RegisterReceiver(&_listener); 23 | 24 | UE_LOG(LogUE_OSC, Verbose, TEXT("Registering actor %s"), *GetName()); 25 | } 26 | } 27 | 28 | void AOscReceiverActor::EndPlay(const EEndPlayReason::Type EndPlayReason) 29 | { 30 | auto instance = UOscDispatcher::Get(); 31 | if (instance && !HasAnyFlags(RF_ClassDefaultObject)) 32 | { 33 | instance->UnregisterReceiver(&_listener); 34 | 35 | UE_LOG(LogUE_OSC, Verbose, TEXT("Unregistering actor %s"), *GetName()); 36 | } 37 | 38 | Super::EndPlay(EndPlayReason); 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Guillaume Buisson 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 | 23 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/UE_OSC.Build.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace UnrealBuildTool.Rules 5 | { 6 | public class UE_OSC : ModuleRules 7 | { 8 | public UE_OSC(ReadOnlyTargetRules Target) : base(Target) 9 | { 10 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 11 | 12 | PublicDependencyModuleNames.AddRange( 13 | new string[] 14 | { 15 | "Networking", 16 | } 17 | ); 18 | 19 | PrivateDependencyModuleNames.AddRange( 20 | new string[] { 21 | "Core", 22 | "CoreUObject", 23 | "Engine", 24 | "Sockets", 25 | "InputCore", 26 | "Slate", 27 | "SlateCore", 28 | } 29 | ); 30 | 31 | PrivateIncludePathModuleNames.AddRange( 32 | new string[] { 33 | "Settings", 34 | } 35 | ); 36 | 37 | PrivateDefinitions.Add("_CRT_SECURE_NO_WARNINGS=1"); 38 | if (Target.Type == TargetRules.TargetType.Editor) 39 | { 40 | PublicDefinitions.Add("OSC_EDITOR_BUILD=1"); 41 | 42 | PrivateDependencyModuleNames.Add("UnrealEd"); 43 | } 44 | else 45 | { 46 | PublicDefinitions.Add("OSC_EDITOR_BUILD=0"); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Receive/OscReceiverActor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "Receive/OscReceiverInterface.h" 5 | #include "Common/OscDataElemStruct.h" 6 | 7 | #include "GameFramework/Actor.h" 8 | #include "OscReceiverActor.generated.h" 9 | 10 | 11 | UCLASS(ClassGroup=OSC) 12 | class UE_OSC_API AOscReceiverActor : public AActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | 18 | /** 19 | * Receive a message only if its address starts with the filter. 20 | */ 21 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=OSC) 22 | FName AddressFilter; 23 | 24 | UFUNCTION(BlueprintImplementableEvent, Category=OSC) 25 | void OnOscReceived(const FName & Address, const TArray & Data, const FString & SenderIp); 26 | 27 | public: 28 | 29 | AOscReceiverActor(); 30 | 31 | /// Hot reload constructor 32 | AOscReceiverActor(FVTableHelper & helper); 33 | 34 | FName GetAddressFilter() const 35 | { 36 | return AddressFilter; 37 | } 38 | 39 | void SendEvent(const FName & Address, const TArray & Data, const FString & SenderIp) 40 | { 41 | OnOscReceived(Address, Data, SenderIp); 42 | } 43 | 44 | private: 45 | void BeginPlay() override; 46 | 47 | void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 48 | 49 | private: 50 | BasicOscReceiver _listener; 51 | }; 52 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Receive/OscReceiverComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "Receive/OscReceiverInterface.h" 5 | #include "Common/OscDataElemStruct.h" 6 | 7 | #include "Components/ActorComponent.h" 8 | #include "OscReceiverComponent.generated.h" 9 | 10 | 11 | // declare the OnOscReceived event type 12 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FComponentOscReceivedSignature, const FName &, Address, const TArray &, Data, const FString &, SenderIp); 13 | 14 | 15 | UCLASS(ClassGroup=OSC, meta = (BlueprintSpawnableComponent)) 16 | class UE_OSC_API UOscReceiverComponent : public UActorComponent 17 | { 18 | GENERATED_BODY() 19 | 20 | public: 21 | 22 | /** 23 | * Receive a message only if its address starts with the filter. 24 | */ 25 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=OSC) 26 | FName AddressFilter; 27 | 28 | UPROPERTY(BlueprintAssignable, Category=OSC) 29 | FComponentOscReceivedSignature OnOscReceived; 30 | 31 | public: 32 | 33 | UOscReceiverComponent(); 34 | 35 | /// Hot reload constructor 36 | UOscReceiverComponent(FVTableHelper & helper); 37 | 38 | FName GetAddressFilter() const 39 | { 40 | return AddressFilter; 41 | } 42 | 43 | void SendEvent(const FName & Address, const TArray & Data, const FString & SenderIp) 44 | { 45 | OnOscReceived.Broadcast(Address, Data, SenderIp); 46 | } 47 | 48 | private: 49 | void BeginPlay() override; 50 | 51 | void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 52 | 53 | private: 54 | BasicOscReceiver _listener; 55 | }; 56 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Receive/OscReceiverComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "Receive/OscReceiverComponent.h" 2 | #include "Receive/OscDispatcher.h" 3 | #include "GameFramework/Actor.h" 4 | 5 | 6 | UOscReceiverComponent::UOscReceiverComponent() 7 | : _listener(this) 8 | { 9 | 10 | } 11 | 12 | UOscReceiverComponent::UOscReceiverComponent(FVTableHelper & helper) 13 | : _listener(this) 14 | { 15 | } 16 | 17 | void UOscReceiverComponent::BeginPlay() 18 | { 19 | Super::BeginPlay(); 20 | 21 | auto instance = UOscDispatcher::Get(); 22 | if (instance && 23 | !HasAnyFlags(RF_ClassDefaultObject) && 24 | (!GetOwner() || !GetOwner()->HasAnyFlags(RF_ClassDefaultObject))) 25 | { 26 | instance->RegisterReceiver(&_listener); 27 | 28 | if(GetOwner()) 29 | { 30 | UE_LOG(LogUE_OSC, Verbose, TEXT("Registering component %s of %s"), *GetName(), *GetOwner()->GetName()); 31 | } 32 | else 33 | { 34 | UE_LOG(LogUE_OSC, Verbose, TEXT("Registering component %s"), *GetName()); 35 | } 36 | } 37 | } 38 | 39 | void UOscReceiverComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) 40 | { 41 | auto instance = UOscDispatcher::Get(); 42 | if (instance && 43 | !HasAnyFlags(RF_ClassDefaultObject) && 44 | (!GetOwner() || !GetOwner()->HasAnyFlags(RF_ClassDefaultObject))) 45 | { 46 | instance->UnregisterReceiver(&_listener); 47 | 48 | if(GetOwner()) 49 | { 50 | UE_LOG(LogUE_OSC, Verbose, TEXT("Unregistering component %s of %s"), *GetName(), *GetOwner()->GetName()); 51 | } 52 | else 53 | { 54 | UE_LOG(LogUE_OSC, Verbose, TEXT("Unregistering component %s"), *GetName()); 55 | } 56 | } 57 | 58 | Super::EndPlay(EndPlayReason); 59 | } 60 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Receive/OscReceiverInputKey.cpp: -------------------------------------------------------------------------------- 1 | #include "OscReceiverInputKey.h" 2 | #include "Receive/OscDispatcher.h" 3 | 4 | #include "Framework/Application/SlateApplication.h" 5 | #include "Input/Events.h" 6 | 7 | 8 | OscReceiverInputKey::OscReceiverInputKey(FName address) 9 | : _address(address) 10 | { 11 | check(!address.GetDisplayNameEntry()->IsWide()); 12 | ANSICHAR addressANSI[NAME_SIZE]; 13 | address.GetPlainANSIString(addressANSI); 14 | 15 | char buffer[512]; // address truncated after 500 chars. 16 | const auto length = sprintf(buffer, "OSC%.500s", addressANSI); 17 | 18 | // replace '/' by '_' because '/' is not a valid FKey character. 19 | for(int i=0; i!=length; ++i) 20 | { 21 | if(buffer[i] == '/') 22 | { 23 | buffer[i] = '_'; 24 | } 25 | } 26 | 27 | _key = FName(buffer); 28 | 29 | RegisterKey(); 30 | } 31 | 32 | void OscReceiverInputKey::RegisterKey() const 33 | { 34 | if(!_address.IsNone() && !EKeys::GetKeyDetails(_key).IsValid()) 35 | { 36 | EKeys::AddKey(FKeyDetails(_key, 37 | FText::FromString(_key.ToString()), 38 | FKeyDetails::Axis1D)); 39 | } 40 | } 41 | 42 | void OscReceiverInputKey::SendEvent(const FName & Address, const TArray & Data, const FString & SenderIp) 43 | { 44 | if(!Address.IsNone()) // must be an exact match of GetAddressFilter() <=> resulting Address is None 45 | { 46 | return; 47 | } 48 | 49 | auto & app = FSlateApplication::Get(); 50 | 51 | const auto value = Data.Num() ? Data[0].GetValue() : 0.f; 52 | 53 | FKeyEvent keyEvent(_key, app.GetModifierKeys(), 0, false, 0, 0); 54 | if(value < 0.5f) 55 | { 56 | app.ProcessKeyDownEvent(keyEvent); 57 | } 58 | else 59 | { 60 | app.ProcessKeyUpEvent(keyEvent); 61 | } 62 | 63 | FAnalogInputEvent axisEvent(_key, app.GetModifierKeys(), 0, false, 0, 0, value); 64 | app.ProcessAnalogInputEvent(axisEvent); 65 | } 66 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Receive/OscDispatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "Common/OscDataElemStruct.h" 5 | #include "Common/UdpSocketReceiver.h" 6 | 7 | #include "UObject/Object.h" 8 | #include "Containers/Queue.h" 9 | #include "Interfaces/IPv4/IPv4Address.h" 10 | 11 | #include 12 | #include 13 | 14 | #include "OscDispatcher.generated.h" 15 | 16 | struct IOscReceiverInterface; 17 | class FSocket; 18 | class FUdpSocketReceiver; 19 | struct FIPv4Endpoint; 20 | 21 | /// Singleton that dispatches the OSC messages to listeners. 22 | UCLASS() 23 | class UE_OSC_API UOscDispatcher : public UObject 24 | { 25 | GENERATED_BODY() 26 | 27 | friend class FOscModule; 28 | 29 | void Listen(FIPv4Address address, uint32_t port, FIPv4Address multicastAddress, bool multicastLoopback); 30 | void Stop(); 31 | 32 | public: 33 | /// Default constructor 34 | UOscDispatcher(); 35 | 36 | /// Hot reload constructor 37 | UOscDispatcher(FVTableHelper & helper); 38 | 39 | /// Get the unique instance 40 | /// 41 | /// The instance is the UObject default instance. 42 | static UOscDispatcher * Get(); 43 | 44 | /// Add the receiver in the listeners list 45 | void RegisterReceiver(IOscReceiverInterface * receiver); 46 | 47 | /// Remove the receiver in the listeners list 48 | void UnregisterReceiver(IOscReceiverInterface * receiver); 49 | 50 | /// Dispatch an OSC message from an external source. 51 | /// 52 | /// The dispatcher generally uses its own UDP socket to receive OSC packets. 53 | /// This function is useful to also receive OSC packets from another source, 54 | /// like an TCP socket, etc. 55 | void DispatchPacket(const TArrayView& data, const FIPv4Endpoint&); 56 | 57 | private: 58 | void Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&); 59 | 60 | void CallbackMainThread(); 61 | 62 | void BeginDestroy() override; 63 | 64 | private: 65 | TArray _receivers; 66 | struct SocketReceiver 67 | { 68 | FSocket * Socket; 69 | FUdpSocketReceiver * Receiver; 70 | }; 71 | TArray _socketReceivers; 72 | TQueue, FIPv4Address>, EQueueMode::Mpsc> _pendingMessages; // supports multiple-producers 73 | int32 _taskSpawned; 74 | 75 | /// Protects _receivers 76 | FCriticalSection _receiversMutex; 77 | 78 | /// Prevent new receivers to be added/removed while iterating over existing receivers 79 | bool _sendingMessages = false; 80 | TArray> _deferredReceivers; 81 | }; 82 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/OscSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "Receive/OscReceiverInputKey.h" 5 | 6 | #include "UObject/Object.h" 7 | #include "Common/UdpSocketSender.h" 8 | 9 | #include 10 | #include 11 | 12 | #include "OscSettings.generated.h" 13 | 14 | class UOscDispatcher; 15 | struct FIPv4Address; 16 | class FSocket; 17 | class FInternetAddr; 18 | 19 | 20 | UCLASS(Config=Engine, DefaultConfig) 21 | class UOscSettings : public UObject 22 | { 23 | GENERATED_BODY() 24 | 25 | public: 26 | 27 | /// Default constructor 28 | UOscSettings(); 29 | 30 | /// Hot reload constructor 31 | UOscSettings(FVTableHelper & helper); 32 | 33 | /** 34 | * Specify the [address:]port to listen to. 35 | * 36 | * e.g. 37 | * - "8000" listen to messages from any sender on port 8000. [default] 38 | * - "224.0.0.100:8000" listen multi-cast messages of group 224.0.0.100 on port 8000. 39 | * - "224.0.0.100/192.168.0.1:8000" listen multi-cast messages of group 224.0.0.100 on the 192.168.0.1 specific interface on port 8000. 40 | * - "224.0.0.100/192.168.0.255:8000" listen multi-cast messages of group 224.0.0.100 on any network interface matching the mask 192.168.0.255 on port 8000. 41 | * - "192.168.0.1:8000" listen messages addressed specifically to 192.168.0.1 on port 8000, useful if there are several addresses for this machine. 42 | * 43 | * General purpose remark: multicast happens on one of the machine actual network interface. 44 | * If none is specified, the system chooses one for you, which is fine most of the time. 45 | * On complex network setup, one might need to specify the interface or the packets may come to a dead end. 46 | */ 47 | UPROPERTY(Config, EditAnywhere, Category=Receive) 48 | TArray ReceiveFrom; 49 | 50 | /** 51 | * Specify the addresses (ip:port) to send messages to. 52 | * 53 | * Addresses can also be added at runtime with the AddSendOscTarget function. 54 | */ 55 | UPROPERTY(Config, EditAnywhere, Category=Send) 56 | TArray SendTargets; 57 | 58 | /** 59 | * List of the messages treated as inputs. 60 | * 61 | * Every entry adds a key in the input mapping project setting. 62 | * e.g. "/position/x" -> "OSC_position_x" 63 | */ 64 | UPROPERTY(Config, EditAnywhere, Category=Input) 65 | TArray Inputs; 66 | 67 | UPROPERTY(Config, EditAnywhere, Category=Network) 68 | bool MulticastLoopback; 69 | 70 | public: 71 | void InitSendTargets(); 72 | 73 | int32 GetOrAddSendTarget(const FString & ip_port); 74 | 75 | void Send(const uint8 *buffer, int32 length, int32 targetIndex); 76 | 77 | void ClearKeyInputs(UOscDispatcher & dispatcher); 78 | 79 | void UpdateKeyInputs(UOscDispatcher & dispatcher); 80 | 81 | #if WITH_EDITOR 82 | void PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent) override; 83 | #endif 84 | 85 | public: 86 | 87 | enum class ParseOption 88 | { 89 | Strict, /// "192.168.0.12:8000" 90 | OptionalPort, /// "192.168.0.12" or "192.168.0.12:8000" 91 | OptionalAddress, /// "8000" or "192.168.0.12:8000" 92 | }; 93 | 94 | /** 95 | * @brief Parse "8000" and "192.168.0.12" to IP and port values. 96 | * @return true if succeed 97 | */ 98 | static bool Parse(const FString & ip_port, FIPv4Address * address, uint32_t * port, FIPv4Address * multicastAddress, ParseOption option); 99 | 100 | private: 101 | int32 AddSendTarget(const FString & ip_port); 102 | 103 | FUdpSocketSender* GetOrAddMulticastInterfaceSender(const FIPv4Address& multicastInterface); 104 | 105 | private: 106 | TSharedPtr _defaultSendSocket; 107 | TSharedPtr _defaultSocketSender; 108 | TArray> _sendAddressAndSenders; 109 | TMap _sendAddressesIndex; 110 | struct MulticastInterfaceSender 111 | { 112 | FIPv4Address Interface; 113 | TSharedPtr Socket; 114 | TSharedPtr Sender; 115 | }; 116 | TArray _multicastInterfaceSenders; 117 | TArray> _keyReceivers; 118 | }; 119 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscTypes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | 38 | #include "OscTypes.h" 39 | #include "Misc/AssertionMacros.h" 40 | 41 | namespace osc{ 42 | 43 | BundleInitiator BeginBundleImmediate(1); 44 | BundleTerminator EndBundle; 45 | MessageTerminator EndMessage; 46 | NilType OscNil; 47 | #ifndef _OBJC_OBJC_H_ 48 | NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead 49 | #endif 50 | InfinitumType Infinitum; 51 | ArrayInitiator BeginArray; 52 | ArrayTerminator EndArray; 53 | 54 | const TCHAR * errorString(Errors value) 55 | { 56 | static const TCHAR * ERROR_STR[] = { 57 | TEXT("No Error"), 58 | 59 | TEXT("Malformed packet"), 60 | TEXT("Malformed packet: invalid size"), 61 | TEXT("Malformed packet: zero size"), 62 | TEXT("Malformed packet: not multiple of 4"), 63 | 64 | TEXT("Malformed message"), 65 | TEXT("Malformed message: invalid size"), 66 | TEXT("Malformed message: zero size"), 67 | TEXT("Malformed message: not multiple of 4"), 68 | TEXT("Malformed message: invalid blob size"), 69 | TEXT("Malformed message: unterminated address pattern"), 70 | TEXT("Malformed message: unterminated string"), 71 | TEXT("Malformed message: unterminated array"), 72 | TEXT("Malformed message: no type tags"), 73 | TEXT("Malformed message: unterminated type tags"), 74 | TEXT("Malformed message: argument exceed msg size"), 75 | TEXT("Malformed message: unknown type tag"), 76 | 77 | TEXT("Malformed bundle"), 78 | TEXT("Malformed bundle: invalid size"), 79 | TEXT("Malformed bundle: invalid address pattern"), 80 | TEXT("Malformed bundle: too short"), 81 | TEXT("Malformed bundle: too short for element size"), 82 | TEXT("Malformed bundle: too short for element"), 83 | TEXT("Malformed bundle: not multiple of 4"), 84 | TEXT("Malformed bundle: element not multiple of 4"), 85 | TEXT("Malformed bundle: content"), 86 | 87 | TEXT("Wrong argument type"), 88 | TEXT("Missing argument"), 89 | TEXT("Excess argument"), 90 | TEXT("Out of buffer memory"), 91 | }; 92 | 93 | static_assert((sizeof(ERROR_STR) / sizeof(*ERROR_STR) == ERRORS_COUNT), "Error messages mismatch"); 94 | if(value < 0 || value >= ERRORS_COUNT) 95 | { 96 | check(false); 97 | return TEXT("Unknown error code"); 98 | } 99 | return ERROR_STR[value]; 100 | } 101 | 102 | } // namespace osc 103 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/OscModule.cpp: -------------------------------------------------------------------------------- 1 | #include "OscSettings.h" 2 | #include "Receive/OscDispatcher.h" 3 | 4 | #include "Modules/ModuleManager.h" 5 | #include "ISettingsModule.h" 6 | #include "ISettingsSection.h" 7 | 8 | #if OSC_EDITOR_BUILD 9 | #include "Editor.h" 10 | #endif 11 | 12 | 13 | #define LOCTEXT_NAMESPACE "FOscModule" 14 | 15 | 16 | class FOscModule : public IModuleInterface 17 | { 18 | public: 19 | virtual void StartupModule( ) override 20 | { 21 | if(!FModuleManager::Get().LoadModule(TEXT("Networking"))) 22 | { 23 | UE_LOG(LogUE_OSC, Error, TEXT("Required module Networking failed to load")); 24 | return; 25 | } 26 | 27 | #if OSC_EDITOR_BUILD 28 | _mustListen = !GIsEditor; // must not listen now if IsEditor (listen when PIE), else (Standalone Game) listen now 29 | FEditorDelegates::BeginPIE.AddRaw(this, &FOscModule::OnBeginPIE); 30 | FEditorDelegates::EndPIE.AddRaw(this, &FOscModule::OnEndPIE); 31 | #endif 32 | 33 | _dispatcher = UOscDispatcher::Get(); 34 | 35 | HandleSettingsSaved(); 36 | 37 | // register settings 38 | auto settingsModule = FModuleManager::GetModulePtr("Settings"); 39 | if(settingsModule) 40 | { 41 | auto settingsSection = settingsModule->RegisterSettings("Project", "Plugins", "OSC", 42 | LOCTEXT("OscSettingsName", "OSC"), 43 | LOCTEXT("OscSettingsDescription", "Configure the OSC plug-in."), 44 | GetMutableDefault() 45 | ); 46 | 47 | if (settingsSection.IsValid()) 48 | { 49 | settingsSection->OnModified().BindRaw(this, &FOscModule::HandleSettingsSaved); 50 | } 51 | } 52 | else 53 | { 54 | #if OSC_EDITOR_BUILD 55 | UE_LOG(LogUE_OSC, Warning, TEXT("Settings changed registration failed")); 56 | #endif 57 | } 58 | UE_LOG(LogUE_OSC, Display, TEXT("Startup succeed")); 59 | } 60 | 61 | virtual void ShutdownModule( ) override 62 | { 63 | UE_LOG(LogUE_OSC, Display, TEXT("Shutdown")); 64 | 65 | if(_dispatcher.IsValid()) 66 | { 67 | auto settings = GetMutableDefault(); 68 | settings->ClearKeyInputs(*_dispatcher); 69 | 70 | _dispatcher->Stop(); 71 | } 72 | } 73 | 74 | bool HandleSettingsSaved() 75 | { 76 | if(!_dispatcher.IsValid()) 77 | { 78 | UE_LOG(LogUE_OSC, Warning, TEXT("Cannot update settings")); 79 | return false; 80 | } 81 | 82 | UE_LOG(LogUE_OSC, Display, TEXT("Update settings")); 83 | 84 | auto settings = GetMutableDefault(); 85 | 86 | // receive settings 87 | #if OSC_EDITOR_BUILD 88 | if(_mustListen) 89 | { 90 | Listen(settings); 91 | } 92 | #else 93 | Listen(settings); 94 | #endif 95 | 96 | // send settings 97 | settings->InitSendTargets(); 98 | 99 | // input settings 100 | settings->UpdateKeyInputs(*_dispatcher); 101 | 102 | return true; 103 | } 104 | 105 | void Listen(UOscSettings * settings) 106 | { 107 | for (const auto& receiveFrom : settings->ReceiveFrom) 108 | { 109 | FIPv4Address receiveAddress(0); 110 | uint32_t receivePort; 111 | FIPv4Address receiveMulticastAddress(0); 112 | if(UOscSettings::Parse(receiveFrom, &receiveAddress, &receivePort, &receiveMulticastAddress, UOscSettings::ParseOption::OptionalAddress)) 113 | { 114 | _dispatcher->Listen(receiveAddress, receivePort, receiveMulticastAddress, settings->MulticastLoopback); 115 | } 116 | else 117 | { 118 | UE_LOG(LogUE_OSC, Error, TEXT("Fail to parse receive address: %s"), *receiveFrom); 119 | } 120 | } 121 | } 122 | 123 | private: 124 | #if OSC_EDITOR_BUILD 125 | void OnBeginPIE(bool isSimulating) 126 | { 127 | _mustListen = true; 128 | 129 | check(_dispatcher.IsValid()) 130 | auto settings = GetMutableDefault(); 131 | Listen(settings); 132 | } 133 | 134 | void OnEndPIE(bool isSimulating) 135 | { 136 | _mustListen = false; 137 | _dispatcher->Stop(); 138 | } 139 | 140 | bool _mustListen; 141 | #endif 142 | 143 | private: 144 | 145 | TWeakObjectPtr _dispatcher; 146 | }; 147 | 148 | 149 | IMPLEMENT_MODULE(FOscModule, UE_OSC); 150 | DEFINE_LOG_CATEGORY(LogUE_OSC); 151 | 152 | 153 | #undef LOCTEXT_NAMESPACE 154 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscHostEndianness.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H 38 | #define INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H 39 | 40 | /* 41 | Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined 42 | 43 | We try to use preprocessor symbols to deduce the host endianness. 44 | 45 | Alternatively you can define one of the above symbols from the command line. 46 | Usually you do this with the -D flag to the compiler. e.g.: 47 | 48 | $ g++ -DOSC_HOST_LITTLE_ENDIAN ... 49 | */ 50 | 51 | #if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN) 52 | 53 | // endianness defined on the command line. nothing to do here. 54 | 55 | #elif defined(__WIN32__) || defined(WIN32) || defined(WINCE) 56 | 57 | // assume that __WIN32__ is only defined on little endian systems 58 | 59 | #define OSC_HOST_LITTLE_ENDIAN 1 60 | #undef OSC_HOST_BIG_ENDIAN 61 | 62 | #elif defined(__APPLE__) 63 | 64 | #if defined(__LITTLE_ENDIAN__) 65 | 66 | #define OSC_HOST_LITTLE_ENDIAN 1 67 | #undef OSC_HOST_BIG_ENDIAN 68 | 69 | #elif defined(__BIG_ENDIAN__) 70 | 71 | #define OSC_HOST_BIG_ENDIAN 1 72 | #undef OSC_HOST_LITTLE_ENDIAN 73 | 74 | #endif 75 | 76 | #elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) 77 | 78 | // should cover gcc and clang 79 | 80 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) 81 | 82 | #define OSC_HOST_LITTLE_ENDIAN 1 83 | #undef OSC_HOST_BIG_ENDIAN 84 | 85 | #elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 86 | 87 | #define OSC_HOST_BIG_ENDIAN 1 88 | #undef OSC_HOST_LITTLE_ENDIAN 89 | 90 | #endif 91 | 92 | #else 93 | 94 | // gcc defines __LITTLE_ENDIAN__ and __BIG_ENDIAN__ 95 | // for others used here see http://sourceforge.net/p/predef/wiki/Endianness/ 96 | #if (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ 97 | || (defined(__ARMEL__) && !defined(__ARMEB__)) \ 98 | || (defined(__AARCH64EL__) && !defined(__AARCH64EB__)) \ 99 | || (defined(_MIPSEL) && !defined(_MIPSEB)) \ 100 | || (defined(__MIPSEL) && !defined(__MIPSEB)) \ 101 | || (defined(__MIPSEL__) && !defined(__MIPSEB__)) 102 | 103 | #define OSC_HOST_LITTLE_ENDIAN 1 104 | #undef OSC_HOST_BIG_ENDIAN 105 | 106 | #elif (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ 107 | || (defined(__ARMEB__) && !defined(__ARMEL__)) \ 108 | || (defined(__AARCH64EB__) && !defined(__AARCH64EL__)) \ 109 | || (defined(_MIPSEB) && !defined(_MIPSEL)) \ 110 | || (defined(__MIPSEB) && !defined(__MIPSEL)) \ 111 | || (defined(__MIPSEB__) && !defined(__MIPSEL__)) 112 | 113 | #define OSC_HOST_BIG_ENDIAN 1 114 | #undef OSC_HOST_LITTLE_ENDIAN 115 | 116 | #endif 117 | 118 | #endif 119 | 120 | #if !defined(OSC_HOST_LITTLE_ENDIAN) && !defined(OSC_HOST_BIG_ENDIAN) 121 | 122 | #error please edit OSCHostEndianness.h or define one of {OSC_HOST_LITTLE_ENDIAN, OSC_HOST_BIG_ENDIAN} to configure endianness 123 | 124 | #endif 125 | 126 | #endif /* INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H */ 127 | 128 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H 38 | #define INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H 39 | 40 | #include // size_t 41 | 42 | #include "OscTypes.h" 43 | 44 | 45 | namespace osc{ 46 | 47 | 48 | class OutboundPacketStream{ 49 | public: 50 | OutboundPacketStream( char *buffer, std::size_t capacity ); 51 | ~OutboundPacketStream(); 52 | 53 | Errors State() const { return state_; } 54 | 55 | void Clear(); 56 | 57 | std::size_t Capacity() const; 58 | 59 | // invariant: size() is valid even while building a message. 60 | std::size_t Size() const; 61 | 62 | const char *Data() const; 63 | 64 | // indicates that all messages have been closed with a matching EndMessage 65 | // and all bundles have been closed with a matching EndBundle 66 | bool IsReady() const; 67 | 68 | bool IsMessageInProgress() const; 69 | bool IsBundleInProgress() const; 70 | 71 | OutboundPacketStream& operator<<( const BundleInitiator& rhs ); 72 | OutboundPacketStream& operator<<( const BundleTerminator& rhs ); 73 | 74 | OutboundPacketStream& operator<<( const BeginMessage& rhs ); 75 | OutboundPacketStream& operator<<( const MessageTerminator& rhs ); 76 | 77 | OutboundPacketStream& operator<<( bool rhs ); 78 | OutboundPacketStream& operator<<( const NilType& rhs ); 79 | OutboundPacketStream& operator<<( const InfinitumType& rhs ); 80 | OutboundPacketStream& operator<<( int32 rhs ); 81 | 82 | #if !(defined(__x86_64__) || defined(_M_X64)) 83 | OutboundPacketStream& operator<<( int rhs ) 84 | { *this << (int32)rhs; return *this; } 85 | #endif 86 | 87 | OutboundPacketStream& operator<<( float rhs ); 88 | OutboundPacketStream& operator<<( char rhs ); 89 | OutboundPacketStream& operator<<( const RgbaColor& rhs ); 90 | OutboundPacketStream& operator<<( const MidiMessage& rhs ); 91 | OutboundPacketStream& operator<<( int64 rhs ); 92 | OutboundPacketStream& operator<<( const TimeTag& rhs ); 93 | OutboundPacketStream& operator<<( double rhs ); 94 | OutboundPacketStream& operator<<( const char* rhs ); 95 | OutboundPacketStream& operator<<( const Symbol& rhs ); 96 | OutboundPacketStream& operator<<( const Blob& rhs ); 97 | 98 | OutboundPacketStream& operator<<( const ArrayInitiator& rhs ); 99 | OutboundPacketStream& operator<<( const ArrayTerminator& rhs ); 100 | 101 | private: 102 | 103 | char *BeginElement( char *beginPtr ); 104 | void EndElement( char *endPtr ); 105 | 106 | bool ElementSizeSlotRequired() const; 107 | Errors CheckForAvailableBundleSpace(); 108 | Errors CheckForAvailableMessageSpace( const char *addressPattern ); 109 | Errors CheckForAvailableArgumentSpace( std::size_t argumentLength ); 110 | 111 | char *data_; 112 | char *end_; 113 | 114 | char *typeTagsCurrent_; // stored in reverse order 115 | char *messageCursor_; 116 | char *argumentCurrent_; 117 | 118 | // elementSizePtr_ has two special values: 0 indicates that a bundle 119 | // isn't open, and elementSizePtr_==data_ indicates that a bundle is 120 | // open but that it doesn't have a size slot (ie the outermost bundle) 121 | uint32 *elementSizePtr_; 122 | 123 | bool messageIsInProgress_; 124 | 125 | Errors state_; 126 | }; 127 | 128 | } // namespace osc 129 | 130 | #endif /* INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H */ 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UE-OSC 2 | ======= 3 | 4 | OSC plugin for Unreal Engine 4 and Unreal Engine 5 to send and receive OSC messages with blueprints! 5 | 6 | It uses oscpack, a C++ library for packing/unpacking OSC messages, by Ross Bencina. http://www.rossbencina.com/code/oscpack 7 | It also uses OscPkt, a very minimalistic OSC library, by Julien Pommier. http://gruntthepeon.free.fr/oscpkt/ 8 | 9 | 10 | # Migrating from UE4-OSC to UE-OSC 11 | 12 | This plugin has been working for Unreal since 2014 (UE4.4!) and it continues today with UE5! 13 | 14 | I decided to change its name from UE4-OSC to a more generic UE-OSC. 15 | 16 | To migrate an existing project using UE4-OSC to UE-OSC, add the following in the Config/DefaultEngine.ini file. 17 | ``` 18 | [CoreRedirects] 19 | +PackageRedirects=(OldName="/Script/UE4_OSC", NewName="/Script/UE_OSC") 20 | ``` 21 | 22 | # Tutorial 23 | 24 | Dannington made a super tutorial! 25 | 26 | Watch it to properly install the plugin and get some brilliant ideas on how to use it! 27 | 28 | https://www.youtube.com/watch?v=GGGs-n-CKtY 29 | 30 | 31 | Also, go to the UE forum dedicated thread: 32 | 33 | https://forums.unrealengine.com/showthread.php?49627-Plugin-OSC-for-UE4 34 | 35 | 36 | # Integration 37 | 38 | Install Visual Studio 2019 on Windows (Community edition works) or XCode on MaxOSX. 39 | 40 | Create a "C++" project, or convert a "Blueprint" project to "C++". 41 | 42 | Create a "Plugins" directory in the project root (near the "Content" directory) and put the "OSC" folder of the plugin in it. 43 | (e.g. "/MyProject/Plugins/OSC") 44 | 45 | Run the "*.uproject" file: the plugin is compiled automatically. 46 | 47 | Alternatively, right-clic the "*.uproject" to generate Visual Studio or XCode project files. 48 | 49 | 50 | # Usage 51 | 52 | ### Receiver 53 | 54 | Set the listening port as "Receive From" in the plugin settings. (Edit/Project Settings/Plugins/OSC) 55 | 56 | Add a OscReceiverComponent to your Blueprint class and bind the OnOscReceived custom event, or 57 | subclass the OscReceiverActor and bind the OnOscReceived custom event. 58 | 59 | The OnOscReceived event gives: the OSC address, the OSC parameters and the IP of the sender. 60 | 61 | The OSC parameters is an array of OSC elements. To read the content, either: 62 | - chain PopFloat/Int/Bool/String/Blob functions to get the first value, or 63 | - use the standard GET function and AsFloat/Int/Bool/String/Blob functions to cast the element to its value. This is slightly more efficient. 64 | 65 | #### AddressFilter 66 | 67 | OscReceiverComponent has an AddressFilter parameter. If not empty, 68 | OnOscReceived is called if and only if the address pattern of the incoming message 69 | matches the AddressFilter. 70 | 71 | Standard OSC pattern matching is supported against AddressFilter. 72 | 73 | Pattern matching is case insensitive. 74 | 75 | When using AddressFilter, the address given in the OnOscReceived event is *not* the address of the incoming 76 | message, but what is left after the address filter. 77 | 78 | e.g. 79 | - AddressFilter=/Foo + OSC message address pattern=/Foo 80 | => OnOscReceived event with Address=None 81 | - AddressFilter=/Foo + OSC message address pattern=/Foo/Bar 82 | => OnOscReceived event with Address=/Bar 83 | - AddressFilter=/Foo + OSC message address pattern=/*/Bar 84 | => OnOscReceived event with Address=/Bar 85 | 86 | #### Alternative pattern matching 87 | 88 | If AddressFilter does not work for you, the function MatchOscAddressPattern() provides a similar feature 89 | you can use in C++ or Blueprint. 90 | 91 | Pattern matching is case insensitive. 92 | 93 | 94 | ### Sender 95 | 96 | Set the "Send Targets" addresses in the plugin settings. (Edit/Project Settings/Plugins/OSC) 97 | 98 | You can also add new targets dynamically, using the "Add Send Osc Target" function. 99 | It is especially useful for "ping"-like behavior. 100 | 101 | Send a single message with "Send Osc" or a bundle of messages with "Send Osc Bundle". 102 | 103 | The "Send Osc" function takes: the OSC address, the OSC parameters and the "target index" of the destination. 104 | 105 | Build the OSC parameters for a message: 106 | - chain PushFloat/Int/Bool/String/Blob functions, or 107 | - fill an array of "Osc Data Elem" with the standard array functions and the FromFloat/Int/Bool/String/Blob functions. 108 | 109 | The "target index" refers to an index in the "Send Targets" array in the settings, 110 | or to the result of the "Add Send Osc Target" function. 111 | 112 | ### Inputs (experimental) 113 | 114 | List the messages that should be UE inputs (like a game controller). A new input is created for each message. 115 | 116 | Bind OSC inputs to UE action in the usual "Input" project settings. 117 | 118 | 119 | # General information 120 | 121 | ### Troubleshooting 122 | 123 | The plugin uses the standard UE logging system. 124 | 125 | Check your logs (Window/Developers Tools/Output Log) before asking for help! 126 | 127 | ### String limitation 128 | 129 | Historically, the plugin handles OSC string using "FName". Unfortunately, the length of 130 | a "FName" is limited to 1024 characters. So this plugin does not handle long strings correctly. 131 | 132 | If you need long strings, prefer using blobs. 133 | 134 | ### Blob 135 | 136 | The OSC protocol supports buffers as "blob". 137 | 138 | This plugin implements them as "Array". It plays nicely with the "Extended Standard Library" plugin by "Low Entry". 139 | 140 | 141 | # Engine version 142 | 143 | Get the right branch for your engine version: "master" for the latest version, "before4.8" for 4.4, ..., 4.7, etc. 144 | 145 | If the plugin does not work/build for the current version, feel free to create an issue or email me! 146 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Common/OscDataElemStruct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "OscDataElemStruct.generated.h" 7 | 8 | 9 | USTRUCT(BlueprintType) 10 | struct UE_OSC_API FOscDataElemStruct 11 | { 12 | GENERATED_USTRUCT_BODY() 13 | 14 | enum 15 | { 16 | FLOAT = 0, 17 | INT = 1, 18 | BOOL = 2, 19 | STRING = 3, 20 | BLOB = 4, 21 | ARRAY_BEGIN = 5, 22 | ARRAY_END = 6, 23 | }; 24 | 25 | public: 26 | 27 | FOscDataElemStruct() : Type(0), Data(0) 28 | { 29 | } 30 | 31 | FOscDataElemStruct(double value) 32 | { 33 | SetFloat(value); 34 | } 35 | 36 | FOscDataElemStruct(int64 value) 37 | { 38 | SetInt(value); 39 | } 40 | 41 | FOscDataElemStruct(bool value) 42 | { 43 | SetBool(value); 44 | } 45 | 46 | FOscDataElemStruct(FName value) 47 | { 48 | SetString(value); 49 | } 50 | 51 | FOscDataElemStruct(TArray value) 52 | { 53 | SetBlob(std::move(value)); 54 | } 55 | 56 | //------------------------------------------------------------------------- 57 | 58 | void SetFloat(double value) 59 | { 60 | static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the float value."); 61 | Type = FLOAT; 62 | Data = *reinterpret_cast(&value); 63 | check(value == AsFloatValue()); 64 | } 65 | 66 | double AsFloatValue() const 67 | { 68 | check(IsFloat()); 69 | return *reinterpret_cast(&Data); 70 | } 71 | 72 | bool IsFloat() const 73 | { 74 | return Type == FLOAT; 75 | } 76 | 77 | //------------------------------------------------------------------------- 78 | 79 | void SetInt(int64 value) 80 | { 81 | static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the integer value."); 82 | Type = INT; 83 | Data = value; 84 | check(value == AsIntValue()); 85 | } 86 | 87 | int64 AsIntValue() const 88 | { 89 | check(IsInt()); 90 | return Data; 91 | } 92 | 93 | bool IsInt() const 94 | { 95 | return Type == INT; 96 | } 97 | 98 | //------------------------------------------------------------------------- 99 | 100 | void SetBool(bool value) 101 | { 102 | static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the bool value."); 103 | Type = BOOL; 104 | Data = value; 105 | check(value == AsBoolValue()); 106 | } 107 | 108 | bool AsBoolValue() const 109 | { 110 | check(IsBool()); 111 | return Data != 0; 112 | } 113 | 114 | bool IsBool() const 115 | { 116 | return Type == BOOL; 117 | } 118 | 119 | //------------------------------------------------------------------------- 120 | 121 | void SetString(FName value) 122 | { 123 | static_assert(sizeof(FMinimalName) <= sizeof(Data), "Too short to hold the string value."); 124 | const auto mininal = NameToMinimalName(value); 125 | Data = *reinterpret_cast(&mininal); 126 | Type = STRING; 127 | check(value == AsStringValue()); 128 | } 129 | 130 | FName AsStringValue() const 131 | { 132 | check(IsString()); 133 | return MinimalNameToName(*reinterpret_cast(&Data)); 134 | } 135 | 136 | bool IsString() const 137 | { 138 | return Type == STRING; 139 | } 140 | 141 | //------------------------------------------------------------------------- 142 | 143 | void SetBlob(TArray value) 144 | { 145 | Data = 0; 146 | Type = BLOB; 147 | Blob = MakeShared>(std::move(value)); 148 | } 149 | 150 | const TArray & AsBlobValue() const 151 | { 152 | check(IsBlob()); 153 | static const TArray empty; 154 | return Blob.IsValid() ? *Blob : empty; 155 | } 156 | 157 | bool IsBlob() const 158 | { 159 | return Type == BLOB; 160 | } 161 | 162 | //------------------------------------------------------------------------- 163 | 164 | void SetArrayBegin() 165 | { 166 | Data = 0; 167 | Type = ARRAY_BEGIN; 168 | } 169 | 170 | bool IsArrayBegin() const 171 | { 172 | return Type == ARRAY_BEGIN; 173 | } 174 | 175 | void SetArrayEnd() 176 | { 177 | Data = 0; 178 | Type = ARRAY_END; 179 | } 180 | 181 | bool IsArrayEnd() const 182 | { 183 | return Type == ARRAY_END; 184 | } 185 | 186 | //------------------------------------------------------------------------- 187 | 188 | /// Cast the value to T. 189 | template 190 | T GetValue() const 191 | { 192 | static const bool isNumber = (std::is_integral::value || std::is_floating_point::value); 193 | if(IsFloat()) 194 | { 195 | if(isNumber) 196 | { 197 | const auto value = AsFloatValue(); 198 | return Cast::eval(value); 199 | } 200 | } 201 | else if(IsInt()) 202 | { 203 | if(isNumber) 204 | { 205 | const auto value = AsIntValue(); 206 | return Cast::eval(value); 207 | } 208 | } 209 | else if(IsBool()) 210 | { 211 | if(isNumber) 212 | { 213 | const auto value = AsBoolValue(); 214 | return Cast::eval(value); 215 | } 216 | } 217 | else if(IsString()) 218 | { 219 | const bool isString = std::is_same::value; 220 | if(isString) 221 | { 222 | const auto value = AsStringValue(); 223 | return Cast::eval(value); 224 | } 225 | } 226 | else if(IsBlob()) 227 | { 228 | const bool isBlob = std::is_same>::value; 229 | if(isBlob) 230 | { 231 | return Cast::eval(AsBlobValue()); 232 | } 233 | } 234 | return T(); 235 | } 236 | 237 | private: 238 | template 239 | struct Cast 240 | { 241 | template 242 | static T eval(const U &) 243 | { 244 | return T(); 245 | } 246 | }; 247 | 248 | private: 249 | int32 Type; 250 | 251 | int64 Data; 252 | 253 | TSharedPtr> Blob; 254 | }; 255 | 256 | 257 | template 258 | struct FOscDataElemStruct::Cast 259 | { 260 | static T eval(double value) 261 | { 262 | if(std::is_integral::value) 263 | { 264 | value += 0.4999999; 265 | } 266 | return static_cast(value); 267 | } 268 | 269 | template 270 | static T eval(const U & value) 271 | { 272 | return static_cast(value); 273 | } 274 | }; 275 | 276 | template <> 277 | struct FOscDataElemStruct::Cast 278 | { 279 | template 280 | static bool eval(const U & value) 281 | { 282 | return value != 0; 283 | } 284 | }; 285 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Public/Common/OscFunctionLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Osc.h" 4 | #include "OscDataElemStruct.h" 5 | #include "OscMessageStruct.h" 6 | 7 | #include "Kismet/BlueprintFunctionLibrary.h" 8 | #include "OscFunctionLibrary.generated.h" 9 | 10 | 11 | UCLASS() 12 | class UE_OSC_API UOscFunctionLibrary : public UBlueprintFunctionLibrary 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | 18 | /// Check an OSC pattern matching. 19 | /// 20 | /// Returns true if the pattern matches partially or fully. 21 | /// 22 | /// PatternRest is "None" if it is a full match. 23 | /// PatternRest is the unmatched part of the pattern if it is a partial match. 24 | /// 25 | /// Examples: 26 | /// - Path="/foo/bar" Pattern="/foo" => Return=false 27 | /// - Path="/foo/bar" Pattern="/*" => Return=false 28 | /// - Path="/foo/bar" Pattern="/foo/bar" => Return=true PatternRest=None 29 | /// - Path="/foo/bar" Pattern="/*/bar" => Return=true PatternRest=None 30 | /// - Path="/foo/bar" Pattern="/*/bar/value" => Return=true PatternRest="/value" 31 | UFUNCTION(BlueprintCallable, Category=OSC) 32 | static bool MatchOscAddressPattern(FName Path, FName Pattern, FName& PatternRest); 33 | 34 | /// Get the next value from an OSC message as a boolean. 35 | UFUNCTION(BlueprintPure, Category=OSC) 36 | static void PopBool(const TArray & input, TArray & output, bool & Value); 37 | 38 | /// Get the next value from an OSC message as a floating point. 39 | UFUNCTION(BlueprintPure, Category=OSC) 40 | static void PopFloat(const TArray & input, TArray & output, float & Value); 41 | 42 | /// Get the next value from an OSC message as a integer. 43 | UFUNCTION(BlueprintPure, Category=OSC) 44 | static void PopInt(const TArray & input, TArray & output, int32 & Value); 45 | 46 | /// Get the next value from an OSC message as a string. 47 | UFUNCTION(BlueprintPure, Category=OSC) 48 | static void PopString(const TArray & input, TArray & output, FName & Value); 49 | 50 | /// Get the next value from an OSC message as a blob. 51 | UFUNCTION(BlueprintPure, Category=OSC) 52 | static void PopBlob(const TArray & input, TArray & output, TArray & Value); 53 | 54 | 55 | /// Add a boolean value to an OSC message. 56 | UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) 57 | static void PushBool(const TArray & input, bool Value, TArray & output); 58 | 59 | /// Add a floating point value to an OSC message. 60 | UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) 61 | static void PushFloat(const TArray & input, float Value, TArray & output); 62 | 63 | /// Add a integer value to an OSC message. 64 | UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) 65 | static void PushInt(const TArray & input, int32 Value, TArray & output); 66 | 67 | /// Add a string value to an OSC message. 68 | UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) 69 | static void PushString(const TArray & input, FName Value, TArray & output); 70 | 71 | /// Add a blob to an OSC message. 72 | UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) 73 | static void PushBlob(const TArray & input, const TArray & Value, TArray & output); 74 | 75 | 76 | /// Interpret an OSC argument as a boolean 77 | UFUNCTION(BlueprintPure, Category=OSC) 78 | static bool AsBool(const FOscDataElemStruct & input); 79 | 80 | /// Interpret an OSC argument as a floating point. 81 | UFUNCTION(BlueprintPure, Category=OSC) 82 | static float AsFloat(const FOscDataElemStruct & input); 83 | 84 | /// Interpret an OSC argument as a integer. 85 | UFUNCTION(BlueprintPure, Category=OSC) 86 | static int32 AsInt(const FOscDataElemStruct & input); 87 | 88 | /// Interpret an OSC argument as a string. 89 | UFUNCTION(BlueprintPure, Category=OSC) 90 | static FName AsString(const FOscDataElemStruct & input); 91 | 92 | /// Interpret an OSC argument as a blob. 93 | UFUNCTION(BlueprintPure, Category=OSC) 94 | static TArray AsBlob(const FOscDataElemStruct & input); 95 | 96 | /// True if the element is the beginning of an array. 97 | UFUNCTION(BlueprintPure, Category=OSC) 98 | static bool AsArrayBegin(const FOscDataElemStruct & input); 99 | 100 | /// True if the element is the end of an array. 101 | UFUNCTION(BlueprintPure, Category=OSC) 102 | static bool AsArrayEnd(const FOscDataElemStruct & input); 103 | 104 | 105 | /// Create an OSC argument from a boolean 106 | UFUNCTION(BlueprintPure, Category=OSC) 107 | static FOscDataElemStruct FromBool(bool input); 108 | 109 | /// Create an OSC argument from a floating point. 110 | UFUNCTION(BlueprintPure, Category=OSC) 111 | static FOscDataElemStruct FromFloat(float input); 112 | 113 | /// Create an OSC argument from a integer. 114 | UFUNCTION(BlueprintPure, Category=OSC) 115 | static FOscDataElemStruct FromInt(int32 input); 116 | 117 | /// Create an OSC argument from a string. 118 | UFUNCTION(BlueprintPure, Category=OSC) 119 | static FOscDataElemStruct FromString(FName input); 120 | 121 | /// Create an OSC argument from a blob. 122 | UFUNCTION(BlueprintPure, Category=OSC) 123 | static FOscDataElemStruct FromBlob(const TArray & input); 124 | 125 | /// Create an OSC argument to begin an array. Must match an ArrayEnd element. 126 | UFUNCTION(BlueprintPure, Category=OSC) 127 | static FOscDataElemStruct FromArrayBegin(); 128 | 129 | /// Create an OSC argument to end an array. Must match an ArrayBegin element. 130 | UFUNCTION(BlueprintPure, Category=OSC) 131 | static FOscDataElemStruct FromArrayEnd(); 132 | 133 | 134 | /** 135 | * @brief Send an OSC message. 136 | * @param Address OSC address. 137 | * @param Data result of successive PushFloat/Int/String/etc. 138 | * @param TargetIndex index of the destination, -1 for all destinations. (SendTarget list of the plugin settings) 139 | */ 140 | UFUNCTION(BlueprintCallable, Category=OSC, meta=(AutoCreateRefTerm = "Data")) 141 | static void SendOsc(FName Address, const TArray & Data, int32 TargetIndex); 142 | 143 | /** 144 | * @brief Send several OSC messages in an OSC bundle. 145 | * @param Messages of the bundle. 146 | * @param TargetIndex index of the destination, -1 for all destinations. (SendTarget list of the plugin settings) 147 | */ 148 | UFUNCTION(BlueprintCallable, Category=OSC) 149 | static void SendOscBundle(const TArray & Messages, int32 TargetIndex); 150 | 151 | /** 152 | * @brief Add Ip:Port to the available OSC send targets. 153 | * @param IpPort "ip:port". e.g. "192.168.0.1:7777" 154 | * @return The created TargetIndex to pass to the SendOsc function. 155 | * 156 | * Use this function to add target at runtime. Generally, it is best 157 | * to define your targets in the OSC plugin settings. 158 | * 159 | * @see SendOsc 160 | */ 161 | UFUNCTION(BlueprintCallable, Category=OSC) 162 | static int32 AddSendOscTarget(const FString & IpPort = "127.0.0.1:8000"); 163 | }; 164 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCTYPES_H 38 | #define INCLUDED_OSCPACK_OSCTYPES_H 39 | 40 | #include "HAL/Platform.h" 41 | 42 | namespace osc{ 43 | 44 | // basic types 45 | 46 | #if defined(__BORLANDC__) || defined(_MSC_VER) 47 | 48 | typedef __int64 int64; 49 | typedef unsigned __int64 uint64; 50 | 51 | #elif defined(__x86_64__) || defined(_M_X64) 52 | 53 | typedef long int64; 54 | typedef unsigned long uint64; 55 | 56 | #else 57 | 58 | typedef long long int64; 59 | typedef unsigned long long uint64; 60 | 61 | #endif 62 | 63 | 64 | 65 | #if defined(__x86_64__) || defined(_M_X64) 66 | 67 | typedef signed int int32; 68 | typedef unsigned int uint32; 69 | 70 | #else 71 | 72 | typedef signed long int32; 73 | typedef unsigned long uint32; 74 | 75 | #endif 76 | 77 | 78 | enum ValueTypeSizes{ 79 | OSC_SIZEOF_INT32 = 4, 80 | OSC_SIZEOF_UINT32 = 4, 81 | OSC_SIZEOF_INT64 = 8, 82 | OSC_SIZEOF_UINT64 = 8, 83 | }; 84 | 85 | 86 | // osc_bundle_element_size_t is used for the size of bundle elements and blobs 87 | // the OSC spec specifies these as int32 (signed) but we ensure that they 88 | // are always positive since negative field sizes make no sense. 89 | 90 | typedef int32 osc_bundle_element_size_t; 91 | 92 | enum { 93 | OSC_INT32_MAX = 0x7FFFFFFF, 94 | 95 | // Element sizes are specified to be int32, and are always rounded up to nearest 96 | // multiple of 4. Therefore their values can't be greater than 0x7FFFFFFC. 97 | OSC_BUNDLE_ELEMENT_SIZE_MAX = 0x7FFFFFFC 98 | }; 99 | 100 | 101 | inline bool IsValidElementSizeValue( osc_bundle_element_size_t x ) 102 | { 103 | // sizes may not be negative or exceed OSC_BUNDLE_ELEMENT_SIZE_MAX 104 | return x >= 0 && x <= OSC_BUNDLE_ELEMENT_SIZE_MAX; 105 | } 106 | 107 | 108 | inline bool IsMultipleOf4( osc_bundle_element_size_t x ) 109 | { 110 | return (x & ((osc_bundle_element_size_t)0x03)) == 0; 111 | } 112 | 113 | 114 | // round up to the next highest multiple of 4. unless x is already a multiple of 4 115 | static inline std::size_t RoundUp4( std::size_t x ) 116 | { 117 | return (x + 3) & ~((std::size_t)0x03); 118 | } 119 | 120 | 121 | enum TypeTagValues { 122 | TRUE_TYPE_TAG = 'T', 123 | FALSE_TYPE_TAG = 'F', 124 | NIL_TYPE_TAG = 'N', 125 | INFINITUM_TYPE_TAG = 'I', 126 | INT32_TYPE_TAG = 'i', 127 | FLOAT_TYPE_TAG = 'f', 128 | CHAR_TYPE_TAG = 'c', 129 | RGBA_COLOR_TYPE_TAG = 'r', 130 | MIDI_MESSAGE_TYPE_TAG = 'm', 131 | INT64_TYPE_TAG = 'h', 132 | TIME_TAG_TYPE_TAG = 't', 133 | DOUBLE_TYPE_TAG = 'd', 134 | STRING_TYPE_TAG = 's', 135 | SYMBOL_TYPE_TAG = 'S', 136 | BLOB_TYPE_TAG = 'b', 137 | ARRAY_BEGIN_TYPE_TAG = '[', 138 | ARRAY_END_TYPE_TAG = ']' 139 | }; 140 | 141 | 142 | 143 | // i/o manipulators used for streaming interfaces 144 | 145 | struct BundleInitiator{ 146 | explicit BundleInitiator( uint64 timeTag_ ) : timeTag( timeTag_ ) {} 147 | uint64 timeTag; 148 | }; 149 | 150 | extern BundleInitiator BeginBundleImmediate; 151 | 152 | inline BundleInitiator BeginBundle( uint64 timeTag=1 ) 153 | { 154 | return BundleInitiator(timeTag); 155 | } 156 | 157 | 158 | struct BundleTerminator{ 159 | }; 160 | 161 | extern BundleTerminator EndBundle; 162 | 163 | struct BeginMessage{ 164 | explicit BeginMessage( const char *addressPattern_ ) : addressPattern( addressPattern_ ) {} 165 | const char *addressPattern; 166 | }; 167 | 168 | struct MessageTerminator{ 169 | }; 170 | 171 | extern MessageTerminator EndMessage; 172 | 173 | 174 | // osc specific types. they are defined as structs so they can be used 175 | // as separately identifiable types with the streaming operators. 176 | 177 | struct NilType{ 178 | }; 179 | 180 | extern NilType OscNil; 181 | 182 | #ifndef _OBJC_OBJC_H_ 183 | extern NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead 184 | #endif 185 | 186 | struct InfinitumType{ 187 | }; 188 | 189 | extern InfinitumType Infinitum; 190 | 191 | struct RgbaColor{ 192 | RgbaColor() {} 193 | explicit RgbaColor( uint32 value_ ) : value( value_ ) {} 194 | uint32 value; 195 | 196 | operator uint32() const { return value; } 197 | }; 198 | 199 | 200 | struct MidiMessage{ 201 | MidiMessage() {} 202 | explicit MidiMessage( uint32 value_ ) : value( value_ ) {} 203 | uint32 value; 204 | 205 | operator uint32() const { return value; } 206 | }; 207 | 208 | 209 | struct TimeTag{ 210 | TimeTag() {} 211 | explicit TimeTag( uint64 value_ ) : value( value_ ) {} 212 | uint64 value; 213 | 214 | operator uint64() const { return value; } 215 | }; 216 | 217 | 218 | struct Symbol{ 219 | Symbol() {} 220 | explicit Symbol( const char* value_ ) : value( value_ ) {} 221 | const char* value; 222 | 223 | operator const char *() const { return value; } 224 | }; 225 | 226 | 227 | struct Blob{ 228 | Blob() {} 229 | explicit Blob( const void* data_, osc_bundle_element_size_t size_ ) 230 | : data( data_ ), size( size_ ) {} 231 | const void* data; 232 | osc_bundle_element_size_t size; 233 | }; 234 | 235 | struct ArrayInitiator{ 236 | }; 237 | 238 | extern ArrayInitiator BeginArray; 239 | 240 | struct ArrayTerminator{ 241 | }; 242 | 243 | extern ArrayTerminator EndArray; 244 | 245 | 246 | enum Errors { 247 | SUCCESS = 0, 248 | 249 | MALFORMED_PACKET_ERROR, 250 | MALFORMED_PACKET_INVALID_SIZE_ERROR, 251 | MALFORMED_PACKET_ZERO_SIZE_ERROR, 252 | MALFORMED_PACKET_NOT_MULTIPLE_OF_4_ERROR, 253 | 254 | MALFORMED_MESSAGE_ERROR, 255 | MALFORMED_MESSAGE_INVALID_SIZE_ERROR, 256 | MALFORMED_MESSAGE_ZERO_SIZE_ERROR, 257 | MALFORMED_MESSAGE_NOT_MULTIPLE_OF_4_ERROR, 258 | MALFORMED_MESSAGE_INVALID_BLOB_SIZE_ERROR, 259 | MALFORMED_MESSAGE_UNTERMINATED_ADDRESS_PATTERN_ERROR, 260 | MALFORMED_MESSAGE_UNTERMINATED_STRING_ERROR, 261 | MALFORMED_MESSAGE_UNTERMINATED_ARRAY_ERROR, 262 | MALFORMED_MESSAGE_NO_TYPE_TAGS_ERROR, 263 | MALFORMED_MESSAGE_UNTERMINATED_TYPE_TAGS_ERROR, 264 | MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR, 265 | MALFORMED_MESSAGE_UNKNOWN_TYPE_TAG_ERROR, 266 | 267 | MALFORMED_BUNDLE_ERROR, 268 | MALFORMED_BUNDLE_INVALID_SIZE_ERROR, 269 | MALFORMED_BUNDLE_INVALID_ADDRESS_PATTERN_ERROR, 270 | MALFORMED_BUNDLE_TOO_SHORT_ERROR, 271 | MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_SIZE_ERROR, 272 | MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_ERROR, 273 | MALFORMED_BUNDLE_NOT_MULTIPLE_OF_4_ERROR, 274 | MALFORMED_BUNDLE_ELEMENT_NOT_MULTIPLE_OF_4_ERROR, 275 | MALFORMED_BUNDLE_CONTENT_ERROR, 276 | 277 | WRONG_ARGUMENT_TYPE_ERROR, 278 | MISSING_ARGUMENT_ERROR, 279 | EXCESS_ARGUMENT_ERROR, 280 | OUT_OF_BUFFER_MEMORY_ERROR, 281 | 282 | ERRORS_COUNT, 283 | }; 284 | 285 | const TCHAR * errorString(Errors value); 286 | 287 | } // namespace osc 288 | 289 | 290 | #endif /* INCLUDED_OSCPACK_OSCTYPES_H */ 291 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Receive/OscDispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "Receive/OscDispatcher.h" 2 | #include "Receive/OscReceiverInterface.h" 3 | #include "Common/OscUtils.h" 4 | #include "Common/oscpack/osc/OscReceivedElements.h" 5 | 6 | #include "Serialization/ArrayReader.h" 7 | #include "Common/UdpSocketBuilder.h" 8 | #include "Common/UdpSocketReceiver.h" 9 | #include "Interfaces/IPv4/IPv4Address.h" 10 | #include "Sockets.h" 11 | #include "SocketSubsystem.h" 12 | #include "Async/TaskGraphInterfaces.h" 13 | #include "Misc/Base64.h" 14 | 15 | #include "oscpkt.hh" // only for pattern matching (oscpack is preferred because it makes no memory allocation) 16 | 17 | 18 | UOscDispatcher::UOscDispatcher() 19 | : _taskSpawned(0) 20 | { 21 | } 22 | 23 | UOscDispatcher::UOscDispatcher(FVTableHelper & helper) 24 | { 25 | // Does not need to be a valid object. 26 | } 27 | 28 | UOscDispatcher * UOscDispatcher::Get() 29 | { 30 | return UOscDispatcher::StaticClass()->GetDefaultObject(); 31 | } 32 | 33 | void UOscDispatcher::Listen(FIPv4Address address, uint32_t port, FIPv4Address multicastAddress, bool multicastLoopback) 34 | { 35 | FUdpSocketBuilder builder(TEXT("OscListener")); 36 | builder.BoundToPort(port); 37 | if(multicastAddress.IsMulticastAddress()) 38 | { 39 | builder.JoinedToGroup(multicastAddress, address); 40 | if(multicastLoopback) 41 | { 42 | builder.WithMulticastLoopback(); 43 | } 44 | } 45 | else 46 | { 47 | builder.BoundToAddress(address); 48 | } 49 | 50 | auto socket = builder.Build(); 51 | if(socket) 52 | { 53 | auto receiver = new FUdpSocketReceiver(socket, FTimespan::FromMilliseconds(100), TEXT("OSCListener")); 54 | receiver->OnDataReceived().BindUObject(this, &UOscDispatcher::Callback); 55 | receiver->Start(); 56 | _socketReceivers.Add({ socket, receiver }); 57 | 58 | UE_LOG(LogUE_OSC, Display, TEXT("Listen to port %d"), port); 59 | } 60 | else 61 | { 62 | UE_LOG(LogUE_OSC, Warning, TEXT("Cannot listen port %d"), port); 63 | } 64 | } 65 | 66 | void UOscDispatcher::Stop() 67 | { 68 | UE_LOG(LogUE_OSC, Display, TEXT("Stop listening")); 69 | 70 | for (auto& socketReceiver : _socketReceivers) 71 | { 72 | delete socketReceiver.Receiver; 73 | ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(socketReceiver.Socket); 74 | } 75 | _socketReceivers.Reset(); 76 | } 77 | 78 | void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver) 79 | { 80 | FScopeLock ScopeLock(&_receiversMutex); 81 | if (_sendingMessages) 82 | { 83 | _deferredReceivers.Add({ receiver, true }); 84 | } 85 | else 86 | { 87 | _receivers.AddUnique(receiver); 88 | } 89 | } 90 | 91 | void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver) 92 | { 93 | FScopeLock ScopeLock(&_receiversMutex); 94 | if (_sendingMessages) // defer remove 95 | { 96 | _deferredReceivers.Add({ receiver, false }); 97 | } 98 | else 99 | { 100 | _receivers.Remove(receiver); 101 | } 102 | } 103 | 104 | static void SendMessage(TQueue, FIPv4Address>, EQueueMode::Mpsc> & _pendingMessages, 105 | const osc::ReceivedMessage & message, 106 | const FIPv4Address & senderIp) 107 | { 108 | if(message.State() != osc::SUCCESS) 109 | { 110 | UE_LOG(LogUE_OSC, Warning, TEXT("OSC Received Message Error: %s"), osc::errorString(message.State())); 111 | return; 112 | } 113 | const FName address(message.AddressPattern()); 114 | if(address.GetDisplayNameEntry()->IsWide()) 115 | { 116 | UE_LOG(LogUE_OSC, Warning, TEXT("OSC Received Message Error: address contains non ASCII characters")); 117 | return; 118 | } 119 | 120 | TArray data; 121 | 122 | const auto argBegin = message.ArgumentsBegin(); 123 | const auto argEnd = message.ArgumentsEnd(); 124 | for(auto it = argBegin; it != argEnd; ++it) 125 | { 126 | FOscDataElemStruct elem; 127 | if(it->IsFloat()) 128 | { 129 | elem.SetFloat(it->AsFloatUnchecked()); 130 | } 131 | else if(it->IsDouble()) 132 | { 133 | elem.SetFloat(it->AsDoubleUnchecked()); 134 | } 135 | else if(it->IsInt32()) 136 | { 137 | elem.SetInt(it->AsInt32Unchecked()); 138 | } 139 | else if(it->IsInt64()) 140 | { 141 | elem.SetInt(it->AsInt64Unchecked()); 142 | } 143 | else if(it->IsBool()) 144 | { 145 | osc::Errors error = osc::SUCCESS; 146 | elem.SetBool(it->AsBoolUnchecked(error)); 147 | check(error == osc::SUCCESS); 148 | } 149 | else if(it->IsString()) 150 | { 151 | elem.SetString(FName(reinterpret_cast(it->AsStringUnchecked()))); 152 | } 153 | else if(it->IsBlob()) 154 | { 155 | const void* buffer; 156 | osc::osc_bundle_element_size_t size; 157 | osc::Errors error = osc::SUCCESS; 158 | it->AsBlobUnchecked(buffer, size, error); 159 | 160 | TArray blob; 161 | if(size && !error) 162 | { 163 | blob.SetNumUninitialized(size); 164 | FMemory::Memcpy(blob.GetData(), buffer, size); 165 | } 166 | else if(error) 167 | { 168 | UE_LOG(LogUE_OSC, Warning, TEXT("OSC Received Message Error: %s"), osc::errorString(error)); 169 | } 170 | elem.SetBlob(std::move(blob)); 171 | } 172 | else if(it->IsArrayBegin()) 173 | { 174 | elem.SetArrayBegin(); 175 | } 176 | else if (it->IsArrayEnd()) 177 | { 178 | elem.SetArrayEnd(); 179 | } 180 | data.Add(elem); 181 | } 182 | 183 | // save it in pending messages 184 | const auto added = _pendingMessages.Enqueue(std::make_tuple(address, data, senderIp)); 185 | 186 | // the circular buffer may be full. 187 | if(!added) 188 | { 189 | UE_LOG(LogUE_OSC, Warning, TEXT("Circular Buffer Full: Message Ignored")); 190 | } 191 | } 192 | 193 | static void SendBundle(TQueue, FIPv4Address>, EQueueMode::Mpsc> & _pendingMessages, 194 | const osc::ReceivedBundle & bundle, 195 | const FIPv4Address & senderIp) 196 | { 197 | if(bundle.State() != osc::SUCCESS) 198 | { 199 | UE_LOG(LogUE_OSC, Warning, TEXT("OSC Received Bundle Error: %s"), osc::errorString(bundle.State())); 200 | return; 201 | } 202 | 203 | const auto begin = bundle.ElementsBegin(); 204 | const auto end = bundle.ElementsEnd(); 205 | for(auto it = begin; it != end; ++it) 206 | { 207 | if(it->IsBundle()) 208 | { 209 | SendBundle(_pendingMessages, osc::ReceivedBundle(*it), senderIp); 210 | } 211 | else 212 | { 213 | SendMessage(_pendingMessages, osc::ReceivedMessage(*it), senderIp); 214 | } 215 | } 216 | } 217 | 218 | void UOscDispatcher::Callback(const FArrayReaderPtr& data, const FIPv4Endpoint& endpoint) 219 | { 220 | DispatchPacket(MakeArrayView(data->GetData(), data->Num()), endpoint); 221 | } 222 | 223 | void UOscDispatcher::DispatchPacket(const TArrayView& data, const FIPv4Endpoint& endpoint) 224 | { 225 | const osc::ReceivedPacket packet((const char *)data.GetData(), data.Num()); 226 | if(packet.State() != osc::SUCCESS) 227 | { 228 | UE_LOG(LogUE_OSC, Warning, TEXT("OSC Received Packet Error: %s"), osc::errorString(packet.State())); 229 | return; 230 | } 231 | 232 | if(packet.IsBundle()) 233 | { 234 | SendBundle(_pendingMessages, osc::ReceivedBundle(packet), endpoint.Address); 235 | } 236 | else 237 | { 238 | SendMessage(_pendingMessages, osc::ReceivedMessage(packet), endpoint.Address); 239 | } 240 | 241 | // Set a single callback in the main thread per frame. 242 | if(!_pendingMessages.IsEmpty() && !FPlatformAtomics::InterlockedCompareExchange(&_taskSpawned, 1, 0)) 243 | { 244 | check(_taskSpawned == 1); 245 | FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( 246 | FSimpleDelegateGraphTask::FDelegate::CreateUObject(this, &UOscDispatcher::CallbackMainThread), 247 | TStatId(), 248 | nullptr, 249 | ENamedThreads::GameThread); 250 | } 251 | 252 | #if !NO_LOGGING 253 | // Log received packet 254 | if(!LogUE_OSC.IsSuppressed(ELogVerbosity::Verbose)) 255 | { 256 | const auto encoded = FBase64::Encode(data.GetData(), data.Num()); 257 | UE_LOG(LogUE_OSC, Verbose, TEXT("Received: %s"), *encoded); 258 | } 259 | #endif 260 | } 261 | 262 | void UOscDispatcher::CallbackMainThread() 263 | { 264 | // Release before dequeue. 265 | // If it was released after dequeue, when a message arrives after the while 266 | // loop and before the release, it would not be processed. 267 | check(_taskSpawned == 1); 268 | FPlatformAtomics::InterlockedCompareExchange(&_taskSpawned, 0, 1); 269 | 270 | FScopeLock ScopeLock(&_receiversMutex); 271 | _sendingMessages = true; 272 | 273 | FString addressStr; 274 | ANSICHAR addressPatternANSI[NAME_SIZE]; 275 | ANSICHAR addressFilterANSI[NAME_SIZE]; 276 | std::tuple, FIPv4Address> message; 277 | while(_pendingMessages.Dequeue(message)) 278 | { 279 | const FIPv4Address & senderIp = std::get<2>(message); 280 | const FString senderIpStr = FString::Printf(TEXT("%i.%i.%i.%i"), senderIp.A, senderIp.B, senderIp.C, senderIp.D); 281 | 282 | FName address = std::get<0>(message); 283 | address.GetPlainANSIString(addressPatternANSI); // it is check as ANSI string when the messae is received 284 | Osc::NameToLower(addressPatternANSI); 285 | 286 | for(auto receiver : _receivers) 287 | { 288 | const FName addressFilter = receiver->GetAddressFilter(); 289 | if (!addressFilter.IsNone()) 290 | { 291 | if (addressFilter.GetDisplayNameEntry()->IsWide()) 292 | { 293 | // only ANSI characters are supported as address pattern, it would necessarly fail. 294 | continue; 295 | } 296 | else 297 | { 298 | addressFilter.GetPlainANSIString(addressFilterANSI); 299 | Osc::NameToLower(addressFilterANSI); 300 | const ANSICHAR* result = oscpkt::internalPatternMatch(addressPatternANSI, addressFilterANSI); 301 | if (!result) 302 | { 303 | continue; 304 | } 305 | 306 | address = FName{ result }; 307 | } 308 | } 309 | receiver->SendEvent(address, std::get<1>(message), senderIpStr); 310 | } 311 | } 312 | 313 | // merge registered receivers 314 | for (const auto& deferredReceiver : _deferredReceivers) 315 | { 316 | if (deferredReceiver.Value) 317 | { 318 | _receivers.AddUnique(deferredReceiver.Key); 319 | } 320 | else 321 | { 322 | _receivers.Remove(deferredReceiver.Key); 323 | } 324 | } 325 | _deferredReceivers.Empty(); 326 | _sendingMessages = false; 327 | } 328 | 329 | void UOscDispatcher::BeginDestroy() 330 | { 331 | Stop(); 332 | Super::BeginDestroy(); 333 | } 334 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/OscSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "OscSettings.h" 2 | #include "Receive/OscDispatcher.h" 3 | 4 | #include "Runtime/Core/Public/Misc/Base64.h" 5 | #include "Interfaces/IPv4/IPv4Address.h" 6 | #include "Common/UdpSocketBuilder.h" 7 | 8 | #include "Sockets.h" 9 | 10 | 11 | UOscSettings::UOscSettings() 12 | : MulticastLoopback(true) 13 | { 14 | FIPv4Endpoint::Initialize(); 15 | _defaultSendSocket = MakeShareable(FUdpSocketBuilder(TEXT("OscSender")).Build()); 16 | _defaultSocketSender = MakeShared(_defaultSendSocket.Get(), TEXT("OSCSender")); 17 | ReceiveFrom.Add(TEXT("8000")); 18 | SendTargets.Add(TEXT("127.0.0.1:8000")); 19 | } 20 | 21 | UOscSettings::UOscSettings(FVTableHelper & helper) 22 | { 23 | // Does not need to be a valid object. 24 | } 25 | 26 | void UOscSettings::InitSendTargets() 27 | { 28 | UE_LOG(LogUE_OSC, Display, TEXT("Send targets cleared")); 29 | 30 | _sendAddressAndSenders.Empty(); 31 | _sendAddressAndSenders.Reserve(SendTargets.Num()); 32 | 33 | _sendAddressesIndex.Empty(); 34 | _sendAddressesIndex.Reserve(SendTargets.Num()); 35 | 36 | _multicastInterfaceSenders.Empty(); 37 | 38 | for(int32 i=0, n=SendTargets.Num(); i!=n; ++i) 39 | { 40 | const auto result = AddSendTarget(SendTargets[i]); 41 | check(result == i); 42 | } 43 | } 44 | 45 | int32 UOscSettings::GetOrAddSendTarget(const FString & ip_port) 46 | { 47 | const int32 * result = _sendAddressesIndex.Find(ip_port); 48 | if(result) 49 | { 50 | return *result; 51 | } 52 | 53 | return AddSendTarget(ip_port); 54 | } 55 | 56 | int32 UOscSettings::AddSendTarget(const FString & ip_port) 57 | { 58 | FIPv4Endpoint target; 59 | FUdpSocketSender* sender = _defaultSocketSender.Get(); 60 | FIPv4Address address(0); 61 | uint32_t port; 62 | FIPv4Address multicastAddress(0); 63 | if(Parse(ip_port, &address, &port, &multicastAddress, ParseOption::Strict)) 64 | { 65 | if(multicastAddress.IsMulticastAddress()) 66 | { 67 | target.Address = multicastAddress; 68 | sender = GetOrAddMulticastInterfaceSender(address); 69 | } 70 | else 71 | { 72 | target.Address = address; 73 | } 74 | target.Port = port; 75 | UE_LOG(LogUE_OSC, Display, TEXT("Send target added: %s"), *ip_port); 76 | } 77 | else 78 | { 79 | UE_LOG(LogUE_OSC, Error, TEXT("Fail to parse or invalid send target: %s"), *ip_port); 80 | } 81 | 82 | const auto result = _sendAddressAndSenders.Num(); 83 | _sendAddressesIndex.Emplace(ip_port, result); 84 | _sendAddressAndSenders.Emplace(target, sender); 85 | return result; 86 | } 87 | 88 | FUdpSocketSender* UOscSettings::GetOrAddMulticastInterfaceSender(const FIPv4Address& multicastInterface) 89 | { 90 | if(multicastInterface == FIPv4Address::Any) 91 | { 92 | return _defaultSocketSender.Get(); 93 | } 94 | else 95 | { 96 | for(const auto & item : _multicastInterfaceSenders) 97 | { 98 | if(item.Interface == multicastInterface) 99 | { 100 | return item.Sender.Get(); 101 | } 102 | } 103 | 104 | // add 105 | TSharedPtr socket = MakeShareable(FUdpSocketBuilder(TEXT("OscSenderMulticast")).WithMulticastInterface(multicastInterface).Build()); 106 | TSharedPtr sender = MakeShared(socket.Get(), TEXT("OSCSenderMulticast")); 107 | _multicastInterfaceSenders.Add({ multicastInterface, socket, sender }); 108 | return sender.Get(); 109 | } 110 | } 111 | 112 | void UOscSettings::Send(const uint8 *buffer, int32 length, int32 targetIndex) 113 | { 114 | auto data = MakeShared, ESPMode::ThreadSafe>(buffer, length); 115 | 116 | if (targetIndex == -1) 117 | { 118 | bool error = false; 119 | for(const auto& item : _sendAddressAndSenders) 120 | { 121 | if(!item.Value->Send(data, item.Key)) 122 | { 123 | const auto target = item.Key.ToString(); 124 | UE_LOG(LogUE_OSC, Error, TEXT("Cannot send OSC: %s : socket cannot send data"), *target); 125 | error = true; 126 | } 127 | } 128 | 129 | #if !NO_LOGGING 130 | // Log sent packet 131 | if(!error && !LogUE_OSC.IsSuppressed(ELogVerbosity::Verbose)) 132 | { 133 | const auto encoded = FBase64::Encode(buffer, length); 134 | UE_LOG(LogUE_OSC, Verbose, TEXT("SentAll: %s"), *encoded); 135 | } 136 | #endif 137 | } 138 | else if(targetIndex < _sendAddressAndSenders.Num()) 139 | { 140 | bool error = false; 141 | const auto& item = _sendAddressAndSenders[targetIndex]; 142 | if(!item.Value->Send(data, item.Key)) 143 | { 144 | const auto target = item.Key.ToString(); 145 | UE_LOG(LogUE_OSC, Error, TEXT("Cannot send OSC: %s : socket cannot send data"), *target); 146 | error = true; 147 | } 148 | 149 | #if !NO_LOGGING 150 | // Log sent packet 151 | if(!error && !LogUE_OSC.IsSuppressed(ELogVerbosity::Verbose)) 152 | { 153 | const auto encoded = FBase64::Encode(buffer, length); 154 | const auto target = item.Key.ToString(); 155 | UE_LOG(LogUE_OSC, Verbose, TEXT("SentTo %s: %s"), *target, *encoded); 156 | } 157 | #endif 158 | } 159 | else 160 | { 161 | UE_LOG(LogUE_OSC, Error, TEXT("Cannot send OSC: invalid targetIndex %d"), targetIndex); 162 | } 163 | } 164 | 165 | namespace 166 | { 167 | bool ParseIpAndMulticast(const FString& addressStr, FIPv4Address* address, FIPv4Address* multicastAddress) 168 | { 169 | FIPv4Address addressResult = FIPv4Address::Any; 170 | FIPv4Address multicastAddressResult = FIPv4Address::Any; 171 | 172 | int32 multicastSep = -1; 173 | const bool hasMulticastSep = addressStr.FindChar(TEXT('/'), multicastSep); 174 | if (hasMulticastSep) 175 | { 176 | const auto multicastIp = addressStr.Left(multicastSep).TrimStartAndEnd(); 177 | const auto interfaceIp = addressStr.RightChop(multicastSep+1).TrimStartAndEnd(); 178 | if (!FIPv4Address::Parse(multicastIp, multicastAddressResult)) 179 | { 180 | return false; 181 | } 182 | 183 | if (interfaceIp.EndsWith(".255")) 184 | { 185 | // look up actual network interface using a mask 186 | FIPv4SubnetMask mask; 187 | if (FIPv4SubnetMask::Parse(interfaceIp, mask)) 188 | { 189 | TArray> localAdapterAddresses; 190 | if (ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalAdapterAddresses(localAdapterAddresses)) 191 | { 192 | for (const auto& localAdapterAddress : localAdapterAddresses) 193 | { 194 | uint32 localAdapterIp = 0; 195 | localAdapterAddress->GetIp(localAdapterIp); 196 | 197 | const FIPv4Address localAdapterIpv4(localAdapterIp); 198 | if ((localAdapterIpv4 & mask) == localAdapterIpv4) 199 | { 200 | addressResult = localAdapterIpv4; 201 | break; 202 | } 203 | } 204 | } 205 | } 206 | 207 | if (addressResult == FIPv4Address::Any) 208 | { 209 | UE_LOG(LogUE_OSC, Error, TEXT("Cannot parse multicast address: no network interface matches the provided mask %s."), *interfaceIp); 210 | return false; 211 | } 212 | } 213 | else 214 | { 215 | if (!FIPv4Address::Parse(interfaceIp, addressResult)) 216 | { 217 | return false; 218 | } 219 | } 220 | } 221 | else 222 | { 223 | if (!FIPv4Address::Parse(addressStr, addressResult)) 224 | { 225 | return false; 226 | } 227 | 228 | if (addressResult.IsMulticastAddress()) 229 | { 230 | using std::swap; 231 | swap(addressResult, multicastAddressResult); 232 | } 233 | } 234 | 235 | *address = addressResult; 236 | *multicastAddress = multicastAddressResult; 237 | return true; 238 | } 239 | } 240 | 241 | bool UOscSettings::Parse(const FString & ip_port, FIPv4Address * address, uint32_t * port, FIPv4Address* multicastAddress, ParseOption option) 242 | { 243 | if(ip_port.IsEmpty()) 244 | { 245 | return false; 246 | } 247 | 248 | FIPv4Address addressResult = FIPv4Address::Any; 249 | uint32_t portResult = 0; 250 | FIPv4Address multicastAddressResult = FIPv4Address::Any; 251 | 252 | int32 portSep = -1; 253 | const bool hasPortSep = ip_port.FindChar(TEXT(':'), portSep); 254 | 255 | if(hasPortSep) 256 | { 257 | portResult = FCString::Atoi(&ip_port.GetCharArray()[portSep+1]); 258 | if(portResult == 0) 259 | { 260 | return false; 261 | } 262 | 263 | const auto ip = ip_port.Left(portSep).TrimStartAndEnd(); 264 | if(!ParseIpAndMulticast(ip, &addressResult, &multicastAddressResult)) 265 | { 266 | return false; 267 | } 268 | } 269 | else 270 | { 271 | if(option == ParseOption::Strict) 272 | { 273 | return false; 274 | } 275 | else if(option == ParseOption::OptionalAddress) 276 | { 277 | portResult = FCString::Atoi(ip_port.GetCharArray().GetData()); 278 | if(portResult == 0) 279 | { 280 | return false; 281 | } 282 | } 283 | else if(option == ParseOption::OptionalPort) 284 | { 285 | if (!ParseIpAndMulticast(ip_port, &addressResult, &multicastAddressResult)) 286 | { 287 | return false; 288 | } 289 | } 290 | } 291 | 292 | *address = addressResult; 293 | *port = portResult; 294 | *multicastAddress = multicastAddressResult; 295 | return true; 296 | } 297 | 298 | void UOscSettings::ClearKeyInputs(UOscDispatcher & dispatcher) 299 | { 300 | for(auto & receiver : _keyReceivers) 301 | { 302 | // Unregister here, not in the OscReceiverInputKey destructor 303 | // because it would crash at the application exit. 304 | dispatcher.UnregisterReceiver(receiver.get()); 305 | } 306 | _keyReceivers.Reset(0); 307 | } 308 | 309 | void UOscSettings::UpdateKeyInputs(UOscDispatcher & dispatcher) 310 | { 311 | ClearKeyInputs(dispatcher); 312 | for(const auto & address : Inputs) 313 | { 314 | auto receiver = std::make_unique(address); 315 | dispatcher.RegisterReceiver(receiver.get()); 316 | _keyReceivers.Add(std::move(receiver)); 317 | } 318 | } 319 | 320 | #if WITH_EDITOR 321 | void UOscSettings::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) 322 | { 323 | static const FName ReceiveFromName("ReceiveFrom"); 324 | static const FName SendTargetsName("SendTargets"); 325 | 326 | if( PropertyChangedEvent.GetPropertyName() == ReceiveFromName ) 327 | { 328 | for(auto & target : ReceiveFrom) 329 | { 330 | FIPv4Address address; 331 | uint32_t port; 332 | FIPv4Address multicastAddress; 333 | if( !Parse(target, &address, &port, &multicastAddress, ParseOption::OptionalAddress) ) 334 | { 335 | target = "8000"; 336 | continue; 337 | } 338 | } 339 | } 340 | 341 | if( PropertyChangedEvent.GetPropertyName() == SendTargetsName ) 342 | { 343 | for(auto & target : SendTargets) 344 | { 345 | FIPv4Address address; 346 | uint32_t port; 347 | FIPv4Address multicastAddress; 348 | if( !Parse(target, &address, &port, &multicastAddress, ParseOption::OptionalPort) || (address == FIPv4Address::Any && !multicastAddress.IsMulticastAddress()) ) 349 | { 350 | target = "127.0.0.1:8000"; 351 | continue; 352 | } 353 | 354 | const FIPv4Address& targetAddress = multicastAddress.IsMulticastAddress() ? multicastAddress : address; 355 | if( port == 0 ) 356 | { 357 | target = targetAddress.ToString() + ":8000"; 358 | } 359 | } 360 | } 361 | 362 | Super::PostEditChangeProperty(PropertyChangedEvent); 363 | } 364 | #endif 365 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/OscFunctionLibrary.cpp: -------------------------------------------------------------------------------- 1 | #include "Common/OscFunctionLibrary.h" 2 | #include "Common/OscUtils.h" 3 | #include "Receive/oscpkt.hh" 4 | #include "OscSettings.h" 5 | #include "oscpack/osc/OscOutboundPacketStream.h" 6 | 7 | 8 | bool UOscFunctionLibrary::MatchOscAddressPattern(FName Path, FName Pattern, FName& PatternRest) 9 | { 10 | if (Path.GetDisplayNameEntry()->IsWide() || Pattern.GetDisplayNameEntry()->IsWide()) 11 | { 12 | PatternRest = FName{}; 13 | return false; 14 | } 15 | 16 | if (Path == Pattern) 17 | { 18 | PatternRest = FName{}; 19 | return true; 20 | } 21 | 22 | if (Pattern.IsNone()) 23 | { 24 | PatternRest = Path; 25 | return true; 26 | } 27 | 28 | ANSICHAR PathANSI[NAME_SIZE]; 29 | Path.GetPlainANSIString(PathANSI); 30 | Osc::NameToLower(PathANSI); 31 | 32 | ANSICHAR PatternANSI[NAME_SIZE]; 33 | Pattern.GetPlainANSIString(PatternANSI); 34 | Osc::NameToLower(PatternANSI); 35 | 36 | const ANSICHAR* Result = oscpkt::internalPatternMatch(PatternANSI, PathANSI); 37 | if (!Result) 38 | { 39 | PatternRest = FName{}; 40 | return false; 41 | } 42 | 43 | PatternRest = FName{ Result }; 44 | return true; 45 | } 46 | 47 | template 48 | static inline 49 | void PopValueImpl(const TArray & input, TArray & output, T & Value) 50 | { 51 | if(input.Num() > 0) 52 | { 53 | output.Reserve(input.Num() - 1); 54 | for(int32 i=1, n=input.Num(); i!=n; ++i) 55 | { 56 | output.Add(input[i]); 57 | } 58 | Value = input[0].GetValue(); 59 | } 60 | else 61 | { 62 | output.Empty(); 63 | Value = FOscDataElemStruct().GetValue(); 64 | } 65 | } 66 | 67 | void UOscFunctionLibrary::PopBool(const TArray & input, TArray & output, bool & Value) 68 | { 69 | PopValueImpl(input, output, Value); 70 | } 71 | 72 | void UOscFunctionLibrary::PopFloat(const TArray & input, TArray & output, float & Value) 73 | { 74 | PopValueImpl(input, output, Value); 75 | } 76 | 77 | void UOscFunctionLibrary::PopInt(const TArray & input, TArray & output, int32 & Value) 78 | { 79 | PopValueImpl(input, output, Value); 80 | } 81 | 82 | void UOscFunctionLibrary::PopString(const TArray & input, TArray & output, FName & Value) 83 | { 84 | PopValueImpl(input, output, Value); 85 | } 86 | 87 | void UOscFunctionLibrary::PopBlob(const TArray & input, TArray & output, TArray & Value) 88 | { 89 | PopValueImpl(input, output, Value); 90 | } 91 | 92 | 93 | void UOscFunctionLibrary::PushBool(const TArray & input, bool Value, TArray & output) 94 | { 95 | output = input; 96 | FOscDataElemStruct elem; 97 | elem.SetBool(Value); 98 | output.Add(elem); 99 | } 100 | 101 | void UOscFunctionLibrary::PushFloat(const TArray & input, float Value, TArray & output) 102 | { 103 | output = input; 104 | FOscDataElemStruct elem; 105 | elem.SetFloat(Value); 106 | output.Add(elem); 107 | } 108 | 109 | void UOscFunctionLibrary::PushInt(const TArray & input, int32 Value, TArray & output) 110 | { 111 | output = input; 112 | FOscDataElemStruct elem; 113 | elem.SetInt(Value); 114 | output.Add(elem); 115 | } 116 | 117 | void UOscFunctionLibrary::PushString(const TArray & input, FName Value, TArray & output) 118 | { 119 | if(Value.GetDisplayNameEntry()->IsWide()) 120 | { 121 | const auto tmp = Value.GetPlainNameString(); 122 | UE_LOG(LogUE_OSC, Error, TEXT("Invalid string argument \"%s\": ASCII only"), *tmp); 123 | return; 124 | } 125 | 126 | output = input; 127 | FOscDataElemStruct elem; 128 | elem.SetString(Value); 129 | output.Add(elem); 130 | } 131 | 132 | void UOscFunctionLibrary::PushBlob(const TArray & input, const TArray & Value, TArray & output) 133 | { 134 | output = input; 135 | FOscDataElemStruct elem; 136 | elem.SetBlob(Value); 137 | output.Add(elem); 138 | } 139 | 140 | 141 | bool UOscFunctionLibrary::AsBool(const FOscDataElemStruct & input) 142 | { 143 | return input.GetValue(); 144 | } 145 | 146 | float UOscFunctionLibrary::AsFloat(const FOscDataElemStruct & input) 147 | { 148 | return input.GetValue(); 149 | } 150 | 151 | int32 UOscFunctionLibrary::AsInt(const FOscDataElemStruct & input) 152 | { 153 | return input.GetValue(); 154 | } 155 | 156 | FName UOscFunctionLibrary::AsString(const FOscDataElemStruct & input) 157 | { 158 | return input.GetValue(); 159 | } 160 | 161 | TArray UOscFunctionLibrary::AsBlob(const FOscDataElemStruct & input) 162 | { 163 | return input.GetValue>(); 164 | } 165 | 166 | bool UOscFunctionLibrary::AsArrayBegin(const FOscDataElemStruct& input) 167 | { 168 | return input.IsArrayBegin(); 169 | } 170 | 171 | bool UOscFunctionLibrary::AsArrayEnd(const FOscDataElemStruct& input) 172 | { 173 | return input.IsArrayEnd(); 174 | } 175 | 176 | 177 | FOscDataElemStruct UOscFunctionLibrary::FromBool(bool input) 178 | { 179 | FOscDataElemStruct result; 180 | result.SetBool(input); 181 | return result; 182 | } 183 | 184 | FOscDataElemStruct UOscFunctionLibrary::FromFloat(float input) 185 | { 186 | FOscDataElemStruct result; 187 | result.SetFloat(input); 188 | return result; 189 | } 190 | 191 | FOscDataElemStruct UOscFunctionLibrary::FromInt(int32 input) 192 | { 193 | FOscDataElemStruct result; 194 | result.SetInt(input); 195 | return result; 196 | } 197 | 198 | FOscDataElemStruct UOscFunctionLibrary::FromString(FName input) 199 | { 200 | FOscDataElemStruct result; 201 | result.SetString(input); 202 | return result; 203 | } 204 | 205 | FOscDataElemStruct UOscFunctionLibrary::FromBlob(const TArray & input) 206 | { 207 | FOscDataElemStruct result; 208 | result.SetBlob(input); 209 | return result; 210 | } 211 | 212 | FOscDataElemStruct UOscFunctionLibrary::FromArrayBegin() 213 | { 214 | FOscDataElemStruct result; 215 | result.SetArrayBegin(); 216 | return result; 217 | } 218 | 219 | FOscDataElemStruct UOscFunctionLibrary::FromArrayEnd() 220 | { 221 | FOscDataElemStruct result; 222 | result.SetArrayEnd(); 223 | return result; 224 | } 225 | 226 | 227 | namespace 228 | { 229 | bool isValidAddress(const FName & Address) 230 | { 231 | if(!Address.IsValid()) 232 | { 233 | UE_LOG(LogUE_OSC, Error, TEXT("Empty OSC address")); 234 | return false; 235 | } 236 | 237 | if(Address.GetDisplayNameEntry()->IsWide()) 238 | { 239 | const auto tmp = Address.GetPlainNameString(); 240 | UE_LOG(LogUE_OSC, Error, TEXT("Invalid OSC address \"%s\": ASCII only"), *tmp); 241 | return false; 242 | } 243 | 244 | ANSICHAR ansiString[NAME_SIZE]; 245 | Address.GetPlainANSIString(ansiString); 246 | if(ansiString[0] != '/') 247 | { 248 | const auto tmp = Address.GetPlainNameString(); 249 | UE_LOG(LogUE_OSC, Error, TEXT("Invalid OSC address \"%s\": must start with '/'"), *tmp); 250 | return false; 251 | } 252 | 253 | return true; 254 | } 255 | 256 | void appendMessage(osc::OutboundPacketStream & output, FName Address, const TArray & Data) 257 | { 258 | ANSICHAR ansiString[NAME_SIZE]; 259 | Address.GetPlainANSIString(ansiString); 260 | output << osc::BeginMessage(ansiString); 261 | if(output.State() != osc::SUCCESS) 262 | { 263 | return; 264 | } 265 | 266 | for(const auto & elem : Data) 267 | { 268 | if(elem.IsFloat()) 269 | { 270 | output << (float)elem.AsFloatValue(); 271 | } 272 | else if(elem.IsInt()) 273 | { 274 | output << (int32)elem.AsIntValue(); 275 | } 276 | else if(elem.IsBool()) 277 | { 278 | output << elem.AsBoolValue(); 279 | } 280 | else if(elem.IsString()) 281 | { 282 | const FName stringValue = elem.AsStringValue(); 283 | if (stringValue.IsNone()) 284 | { 285 | output << ""; 286 | } 287 | else if (stringValue.GetDisplayNameEntry()->IsWide()) 288 | { 289 | WIDECHAR wideString[NAME_SIZE]; 290 | stringValue.GetPlainWIDEString(wideString); 291 | output << TCHAR_TO_UTF8(wideString); 292 | } 293 | else 294 | { 295 | stringValue.GetPlainANSIString(ansiString); 296 | output << ansiString; 297 | } 298 | } 299 | else if(elem.IsBlob()) 300 | { 301 | const TArray & value = elem.AsBlobValue(); 302 | output << osc::Blob(value.GetData(), value.Num()); 303 | } 304 | else if (elem.IsArrayBegin()) 305 | { 306 | output << osc::BeginArray; 307 | } 308 | else if (elem.IsArrayEnd()) 309 | { 310 | output << osc::EndArray; 311 | } 312 | 313 | if(output.State() != osc::SUCCESS) 314 | { 315 | return; 316 | } 317 | } 318 | output << osc::EndMessage; 319 | } 320 | 321 | TArray GlobalBuffer(TArray(), 1024); 322 | } 323 | 324 | void UOscFunctionLibrary::SendOsc(FName Address, const TArray & Data, int32 TargetIndex) 325 | { 326 | if(!isValidAddress(Address)) 327 | { 328 | return; 329 | } 330 | 331 | static_assert(sizeof(uint8) == sizeof(char), "Cannot cast uint8 to char"); 332 | 333 | osc::OutboundPacketStream output((char *)GlobalBuffer.GetData(), GlobalBuffer.Max()); 334 | check(reinterpret_cast(GlobalBuffer.GetData()) == reinterpret_cast(output.Data())); 335 | 336 | appendMessage(output, Address, Data); 337 | 338 | if(output.State() == osc::OUT_OF_BUFFER_MEMORY_ERROR) 339 | { 340 | GlobalBuffer.Reserve(GlobalBuffer.Max() * 2); // not enough memory: double the size 341 | SendOsc(Address, Data, TargetIndex); // try again 342 | return; 343 | } 344 | 345 | if(output.State() == osc::SUCCESS) 346 | { 347 | GetMutableDefault()->Send(GlobalBuffer.GetData(), output.Size(), TargetIndex); 348 | } 349 | else 350 | { 351 | UE_LOG(LogUE_OSC, Error, TEXT("OSC Send Message Error: %s"), osc::errorString(output.State())); 352 | } 353 | } 354 | 355 | void UOscFunctionLibrary::SendOscBundle(const TArray & Messages, int32 TargetIndex) 356 | { 357 | static_assert(sizeof(uint8) == sizeof(char), "Cannot cast uint8 to char"); 358 | 359 | osc::OutboundPacketStream output((char *)GlobalBuffer.GetData(), GlobalBuffer.Max()); 360 | check(reinterpret_cast(GlobalBuffer.GetData()) == reinterpret_cast(output.Data())); 361 | 362 | output << osc::BeginBundle(); 363 | for(const auto & message : Messages) 364 | { 365 | if(!isValidAddress(message.Address)) 366 | { 367 | return; 368 | } 369 | 370 | appendMessage(output, message.Address, message.Data); 371 | 372 | if(output.State() == osc::OUT_OF_BUFFER_MEMORY_ERROR) 373 | { 374 | GlobalBuffer.Reserve(GlobalBuffer.Max() * 2); // not enough memory: double the size 375 | SendOscBundle(Messages, TargetIndex); // try again 376 | return; 377 | } 378 | if(output.State() != osc::SUCCESS) 379 | { 380 | UE_LOG(LogUE_OSC, Error, TEXT("OSC Send Bundle Error: %s"), osc::errorString(output.State())); 381 | return; 382 | } 383 | } 384 | output << osc::EndBundle; 385 | 386 | if(output.State() == osc::OUT_OF_BUFFER_MEMORY_ERROR) 387 | { 388 | GlobalBuffer.Reserve(GlobalBuffer.Max() * 2); // not enough memory: double the size 389 | SendOscBundle(Messages, TargetIndex); // try again 390 | return; 391 | } 392 | 393 | if(output.State() == osc::SUCCESS) 394 | { 395 | GetMutableDefault()->Send(GlobalBuffer.GetData(), output.Size(), TargetIndex); 396 | } 397 | else 398 | { 399 | UE_LOG(LogUE_OSC, Error, TEXT("OSC Send Bundle Error: %s"), osc::errorString(output.State())); 400 | } 401 | } 402 | 403 | int32 UOscFunctionLibrary::AddSendOscTarget(const FString & IpPort) 404 | { 405 | return GetMutableDefault()->GetOrAddSendTarget(IpPort); 406 | } 407 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscReceivedElements.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H 38 | #define INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H 39 | 40 | #include 41 | #include 42 | #include // size_t 43 | 44 | #include "OscTypes.h" 45 | 46 | 47 | namespace osc{ 48 | 49 | 50 | class ReceivedPacket{ 51 | public: 52 | // Although the OSC spec is not entirely clear on this, we only support 53 | // packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value 54 | // representable by an int32). An exception will be raised if you pass a 55 | // larger value to the ReceivedPacket() constructor. 56 | 57 | ReceivedPacket( const char *contents, osc_bundle_element_size_t size ) 58 | : contents_( contents ) 59 | , size_( ValidateSize(size, &state_) ) {} 60 | 61 | ReceivedPacket( const char *contents, std::size_t size ) 62 | : contents_( contents ) 63 | , size_( ValidateSize( (osc_bundle_element_size_t)size, &state_ ) ) {} 64 | 65 | #if !(defined(__x86_64__) || defined(_M_X64)) 66 | ReceivedPacket( const char *contents, int size ) 67 | : contents_( contents ) 68 | , size_( ValidateSize( (osc_bundle_element_size_t)size, &state_ ) ) {} 69 | #endif 70 | 71 | bool IsMessage() const { return !IsBundle(); } 72 | bool IsBundle() const; 73 | 74 | osc_bundle_element_size_t Size() const { return size_; } 75 | const char *Contents() const { return contents_; } 76 | Errors State() const { return state_; } 77 | 78 | private: 79 | const char *contents_; 80 | osc_bundle_element_size_t size_; 81 | Errors state_; 82 | 83 | static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size, Errors * state ) 84 | { 85 | // sanity check integer types declared in OscTypes.h 86 | // you'll need to fix OscTypes.h if any of these asserts fail 87 | assert( sizeof(osc::int32) == 4 ); 88 | assert( sizeof(osc::uint32) == 4 ); 89 | assert( sizeof(osc::int64) == 8 ); 90 | assert( sizeof(osc::uint64) == 8 ); 91 | 92 | if( !IsValidElementSizeValue(size) ) 93 | { 94 | *state = MALFORMED_PACKET_INVALID_SIZE_ERROR; 95 | return 0; 96 | } 97 | 98 | if( size == 0 ) 99 | { 100 | *state = MALFORMED_PACKET_ZERO_SIZE_ERROR; 101 | return 0; 102 | } 103 | 104 | if( !IsMultipleOf4(size) ) 105 | { 106 | *state = MALFORMED_PACKET_NOT_MULTIPLE_OF_4_ERROR; 107 | return 0; 108 | } 109 | 110 | *state = SUCCESS; 111 | return size; 112 | } 113 | }; 114 | 115 | 116 | class ReceivedBundleElement{ 117 | public: 118 | ReceivedBundleElement( const char *sizePtr ) 119 | : sizePtr_( sizePtr ) {} 120 | 121 | friend class ReceivedBundleElementIterator; 122 | 123 | bool IsMessage() const { return !IsBundle(); } 124 | bool IsBundle() const; 125 | 126 | osc_bundle_element_size_t Size() const; 127 | const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; } 128 | 129 | private: 130 | const char *sizePtr_; 131 | }; 132 | 133 | 134 | class ReceivedBundleElementIterator{ 135 | public: 136 | ReceivedBundleElementIterator( const char *sizePtr ) 137 | : value_( sizePtr ) {} 138 | 139 | ReceivedBundleElementIterator operator++() 140 | { 141 | Advance(); 142 | return *this; 143 | } 144 | 145 | ReceivedBundleElementIterator operator++(int) 146 | { 147 | ReceivedBundleElementIterator old( *this ); 148 | Advance(); 149 | return old; 150 | } 151 | 152 | const ReceivedBundleElement& operator*() const { return value_; } 153 | 154 | const ReceivedBundleElement* operator->() const { return &value_; } 155 | 156 | friend bool operator==(const ReceivedBundleElementIterator& lhs, 157 | const ReceivedBundleElementIterator& rhs ); 158 | 159 | private: 160 | ReceivedBundleElement value_; 161 | 162 | void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); } 163 | 164 | bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const 165 | { 166 | return value_.sizePtr_ == rhs.value_.sizePtr_; 167 | } 168 | }; 169 | 170 | inline bool operator==(const ReceivedBundleElementIterator& lhs, 171 | const ReceivedBundleElementIterator& rhs ) 172 | { 173 | return lhs.IsEqualTo( rhs ); 174 | } 175 | 176 | inline bool operator!=(const ReceivedBundleElementIterator& lhs, 177 | const ReceivedBundleElementIterator& rhs ) 178 | { 179 | return !( lhs == rhs ); 180 | } 181 | 182 | 183 | class ReceivedMessageArgument{ 184 | public: 185 | ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr ) 186 | : typeTagPtr_( typeTagPtr ) 187 | , argumentPtr_( argumentPtr ) {} 188 | 189 | friend class ReceivedMessageArgumentIterator; 190 | 191 | char TypeTag() const { return *typeTagPtr_; } 192 | 193 | // the unchecked methods below don't check whether the argument actually 194 | // is of the specified type. they should only be used if you've already 195 | // checked the type tag or the associated IsType() method. 196 | 197 | bool IsBool() const 198 | { return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; } 199 | bool AsBool(Errors & state) const; 200 | bool AsBoolUnchecked(Errors & state) const; 201 | 202 | bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; } 203 | bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; } 204 | 205 | bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; } 206 | int32 AsInt32(Errors & state) const; 207 | int32 AsInt32Unchecked() const; 208 | 209 | bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; } 210 | float AsFloat(Errors & state) const; 211 | float AsFloatUnchecked() const; 212 | 213 | bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; } 214 | char AsChar(Errors & state) const; 215 | char AsCharUnchecked() const; 216 | 217 | bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; } 218 | uint32 AsRgbaColor(Errors & state) const; 219 | uint32 AsRgbaColorUnchecked() const; 220 | 221 | bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; } 222 | uint32 AsMidiMessage(Errors & state) const; 223 | uint32 AsMidiMessageUnchecked() const; 224 | 225 | bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; } 226 | int64 AsInt64(Errors & state) const; 227 | int64 AsInt64Unchecked() const; 228 | 229 | bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; } 230 | uint64 AsTimeTag(Errors & state) const; 231 | uint64 AsTimeTagUnchecked() const; 232 | 233 | bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; } 234 | double AsDouble(Errors & state) const; 235 | double AsDoubleUnchecked() const; 236 | 237 | bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; } 238 | const char* AsString(Errors & state) const; 239 | const char* AsStringUnchecked() const { return argumentPtr_; } 240 | 241 | bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; } 242 | const char* AsSymbol(Errors & state) const; 243 | const char* AsSymbolUnchecked() const { return argumentPtr_; } 244 | 245 | bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; } 246 | void AsBlob( const void*& data, osc_bundle_element_size_t& size, Errors & state ) const; 247 | void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size, Errors & state ) const; 248 | 249 | bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; } 250 | bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; } 251 | // Calculate the number of top-level items in the array. Nested arrays count as one item. 252 | // Only valid at array start. Will throw an exception if IsArrayStart() == false. 253 | std::size_t ComputeArrayItemCount(Errors & state) const; 254 | 255 | private: 256 | const char *typeTagPtr_; 257 | const char *argumentPtr_; 258 | }; 259 | 260 | 261 | class ReceivedMessageArgumentIterator{ 262 | public: 263 | ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) 264 | : value_( typeTags, arguments ) {} 265 | 266 | ReceivedMessageArgumentIterator operator++() 267 | { 268 | Advance(); 269 | return *this; 270 | } 271 | 272 | ReceivedMessageArgumentIterator operator++(int) 273 | { 274 | ReceivedMessageArgumentIterator old( *this ); 275 | Advance(); 276 | return old; 277 | } 278 | 279 | const ReceivedMessageArgument& operator*() const { return value_; } 280 | 281 | const ReceivedMessageArgument* operator->() const { return &value_; } 282 | 283 | friend bool operator==(const ReceivedMessageArgumentIterator& lhs, 284 | const ReceivedMessageArgumentIterator& rhs ); 285 | 286 | private: 287 | ReceivedMessageArgument value_; 288 | 289 | void Advance(); 290 | 291 | bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const 292 | { 293 | return value_.typeTagPtr_ == rhs.value_.typeTagPtr_; 294 | } 295 | }; 296 | 297 | inline bool operator==(const ReceivedMessageArgumentIterator& lhs, 298 | const ReceivedMessageArgumentIterator& rhs ) 299 | { 300 | return lhs.IsEqualTo( rhs ); 301 | } 302 | 303 | inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, 304 | const ReceivedMessageArgumentIterator& rhs ) 305 | { 306 | return !( lhs == rhs ); 307 | } 308 | 309 | 310 | class ReceivedMessageArgumentStream{ 311 | friend class ReceivedMessage; 312 | ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, 313 | const ReceivedMessageArgumentIterator& end ) 314 | : p_( begin ) 315 | , end_( end ) 316 | , state_( SUCCESS ) {} 317 | 318 | ReceivedMessageArgumentIterator p_, end_; 319 | Errors state_; 320 | 321 | public: 322 | 323 | Errors State() const { return state_; } 324 | 325 | // end of stream 326 | bool Eos() const { return p_ == end_; } 327 | 328 | ReceivedMessageArgumentStream& operator>>( bool& rhs ) 329 | { 330 | if( Eos() ) 331 | { 332 | state_ = MISSING_ARGUMENT_ERROR; 333 | } 334 | else 335 | { 336 | rhs = (*p_++).AsBool(state_); 337 | } 338 | return *this; 339 | } 340 | 341 | // not sure if it would be useful to stream Nil and Infinitum 342 | // for now it's not possible 343 | // same goes for array boundaries 344 | 345 | ReceivedMessageArgumentStream& operator>>( int32& rhs ) 346 | { 347 | if( Eos() ) 348 | { 349 | state_ = MISSING_ARGUMENT_ERROR; 350 | } 351 | else 352 | { 353 | rhs = (*p_++).AsInt32(state_); 354 | } 355 | return *this; 356 | } 357 | 358 | ReceivedMessageArgumentStream& operator>>( float& rhs ) 359 | { 360 | if( Eos() ) 361 | { 362 | state_ = MISSING_ARGUMENT_ERROR; 363 | } 364 | else 365 | { 366 | rhs = (*p_++).AsFloat(state_); 367 | } 368 | return *this; 369 | } 370 | 371 | ReceivedMessageArgumentStream& operator>>( char& rhs ) 372 | { 373 | if( Eos() ) 374 | { 375 | state_ = MISSING_ARGUMENT_ERROR; 376 | } 377 | else 378 | { 379 | rhs = (*p_++).AsChar(state_); 380 | } 381 | return *this; 382 | } 383 | 384 | ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) 385 | { 386 | if( Eos() ) 387 | { 388 | state_ = MISSING_ARGUMENT_ERROR; 389 | } 390 | else 391 | { 392 | rhs.value = (*p_++).AsRgbaColor(state_); 393 | } 394 | return *this; 395 | } 396 | 397 | ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) 398 | { 399 | if( Eos() ) 400 | { 401 | state_ = MISSING_ARGUMENT_ERROR; 402 | } 403 | else 404 | { 405 | rhs.value = (*p_++).AsMidiMessage(state_); 406 | } 407 | return *this; 408 | } 409 | 410 | ReceivedMessageArgumentStream& operator>>( int64& rhs ) 411 | { 412 | if( Eos() ) 413 | { 414 | state_ = MISSING_ARGUMENT_ERROR; 415 | } 416 | else 417 | { 418 | rhs = (*p_++).AsInt64(state_); 419 | } 420 | return *this; 421 | } 422 | 423 | ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) 424 | { 425 | if( Eos() ) 426 | { 427 | state_ = MISSING_ARGUMENT_ERROR; 428 | } 429 | else 430 | { 431 | rhs.value = (*p_++).AsTimeTag(state_); 432 | } 433 | return *this; 434 | } 435 | 436 | ReceivedMessageArgumentStream& operator>>( double& rhs ) 437 | { 438 | if( Eos() ) 439 | { 440 | state_ = MISSING_ARGUMENT_ERROR; 441 | } 442 | else 443 | { 444 | rhs = (*p_++).AsDouble(state_); 445 | } 446 | return *this; 447 | } 448 | 449 | ReceivedMessageArgumentStream& operator>>( Blob& rhs ) 450 | { 451 | if( Eos() ) 452 | { 453 | state_ = MISSING_ARGUMENT_ERROR; 454 | } 455 | else 456 | { 457 | (*p_++).AsBlob( rhs.data, rhs.size, state_ ); 458 | } 459 | return *this; 460 | } 461 | 462 | ReceivedMessageArgumentStream& operator>>( const char*& rhs ) 463 | { 464 | if( Eos() ) 465 | { 466 | state_ = MISSING_ARGUMENT_ERROR; 467 | } 468 | else 469 | { 470 | rhs = (*p_++).AsString(state_); 471 | } 472 | return *this; 473 | } 474 | 475 | ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) 476 | { 477 | if( Eos() ) 478 | { 479 | state_ = MISSING_ARGUMENT_ERROR; 480 | } 481 | else 482 | { 483 | rhs.value = (*p_++).AsSymbol(state_); 484 | } 485 | return *this; 486 | } 487 | 488 | ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) 489 | { 490 | (void) rhs; // suppress unused parameter warning 491 | 492 | if( Eos() ) 493 | { 494 | state_ = EXCESS_ARGUMENT_ERROR; 495 | } 496 | return *this; 497 | } 498 | }; 499 | 500 | 501 | class ReceivedMessage{ 502 | void Init( const char *bundle, osc_bundle_element_size_t size ); 503 | public: 504 | explicit ReceivedMessage( const ReceivedPacket& packet ); 505 | explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); 506 | 507 | Errors State() const { return state_; } 508 | 509 | const char *AddressPattern() const { return addressPattern_; } 510 | 511 | // Support for non-standard SuperCollider integer address patterns: 512 | bool AddressPatternIsUInt32() const; 513 | uint32 AddressPatternAsUInt32() const; 514 | 515 | uint32 ArgumentCount() const { return static_cast(typeTagsEnd_ - typeTagsBegin_); } 516 | 517 | const char *TypeTags() const { return typeTagsBegin_; } 518 | 519 | 520 | typedef ReceivedMessageArgumentIterator const_iterator; 521 | 522 | ReceivedMessageArgumentIterator ArgumentsBegin() const 523 | { 524 | return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); 525 | } 526 | 527 | ReceivedMessageArgumentIterator ArgumentsEnd() const 528 | { 529 | return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); 530 | } 531 | 532 | ReceivedMessageArgumentStream ArgumentStream() const 533 | { 534 | return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); 535 | } 536 | 537 | private: 538 | const char *addressPattern_; 539 | const char *typeTagsBegin_; 540 | const char *typeTagsEnd_; 541 | const char *arguments_; 542 | Errors state_; 543 | }; 544 | 545 | 546 | class ReceivedBundle{ 547 | void Init( const char *message, osc_bundle_element_size_t size ); 548 | public: 549 | explicit ReceivedBundle( const ReceivedPacket& packet ); 550 | explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); 551 | 552 | Errors State() const { return state_; } 553 | 554 | uint64 TimeTag() const; 555 | 556 | uint32 ElementCount() const { return elementCount_; } 557 | 558 | typedef ReceivedBundleElementIterator const_iterator; 559 | 560 | ReceivedBundleElementIterator ElementsBegin() const 561 | { 562 | return ReceivedBundleElementIterator( timeTag_ + 8 ); 563 | } 564 | 565 | ReceivedBundleElementIterator ElementsEnd() const 566 | { 567 | return ReceivedBundleElementIterator( end_ ); 568 | } 569 | 570 | private: 571 | const char *timeTag_; 572 | const char *end_; 573 | uint32 elementCount_; 574 | Errors state_; 575 | }; 576 | 577 | 578 | } // namespace osc 579 | 580 | 581 | #endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */ 582 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | 38 | #include "OscOutboundPacketStream.h" 39 | 40 | #if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) 41 | #include // for alloca 42 | #else 43 | //#include // alloca on Linux (also OSX) 44 | #include // alloca on OSX and FreeBSD (and Linux?) 45 | #endif 46 | 47 | #include 48 | #include // memcpy, memmove, strcpy, strlen 49 | #include // ptrdiff_t 50 | 51 | #include "OscHostEndianness.h" 52 | 53 | #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug 54 | namespace std { 55 | using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'. 56 | } 57 | #endif 58 | 59 | namespace osc{ 60 | 61 | static void FromInt32( char *p, int32 x ) 62 | { 63 | #ifdef OSC_HOST_LITTLE_ENDIAN 64 | union{ 65 | osc::int32 i; 66 | char c[4]; 67 | } u; 68 | 69 | u.i = x; 70 | 71 | p[3] = u.c[0]; 72 | p[2] = u.c[1]; 73 | p[1] = u.c[2]; 74 | p[0] = u.c[3]; 75 | #else 76 | *reinterpret_cast(p) = x; 77 | #endif 78 | } 79 | 80 | 81 | static void FromUInt32( char *p, uint32 x ) 82 | { 83 | #ifdef OSC_HOST_LITTLE_ENDIAN 84 | union{ 85 | osc::uint32 i; 86 | char c[4]; 87 | } u; 88 | 89 | u.i = x; 90 | 91 | p[3] = u.c[0]; 92 | p[2] = u.c[1]; 93 | p[1] = u.c[2]; 94 | p[0] = u.c[3]; 95 | #else 96 | *reinterpret_cast(p) = x; 97 | #endif 98 | } 99 | 100 | 101 | static void FromInt64( char *p, int64 x ) 102 | { 103 | #ifdef OSC_HOST_LITTLE_ENDIAN 104 | union{ 105 | osc::int64 i; 106 | char c[8]; 107 | } u; 108 | 109 | u.i = x; 110 | 111 | p[7] = u.c[0]; 112 | p[6] = u.c[1]; 113 | p[5] = u.c[2]; 114 | p[4] = u.c[3]; 115 | p[3] = u.c[4]; 116 | p[2] = u.c[5]; 117 | p[1] = u.c[6]; 118 | p[0] = u.c[7]; 119 | #else 120 | *reinterpret_cast(p) = x; 121 | #endif 122 | } 123 | 124 | 125 | static void FromUInt64( char *p, uint64 x ) 126 | { 127 | #ifdef OSC_HOST_LITTLE_ENDIAN 128 | union{ 129 | osc::uint64 i; 130 | char c[8]; 131 | } u; 132 | 133 | u.i = x; 134 | 135 | p[7] = u.c[0]; 136 | p[6] = u.c[1]; 137 | p[5] = u.c[2]; 138 | p[4] = u.c[3]; 139 | p[3] = u.c[4]; 140 | p[2] = u.c[5]; 141 | p[1] = u.c[6]; 142 | p[0] = u.c[7]; 143 | #else 144 | *reinterpret_cast(p) = x; 145 | #endif 146 | } 147 | 148 | 149 | OutboundPacketStream::OutboundPacketStream( char *buffer, std::size_t capacity ) 150 | : data_( buffer ) 151 | , end_( data_ + capacity ) 152 | , typeTagsCurrent_( end_ ) 153 | , messageCursor_( data_ ) 154 | , argumentCurrent_( data_ ) 155 | , elementSizePtr_( 0 ) 156 | , messageIsInProgress_( false ) 157 | , state_( SUCCESS ) 158 | { 159 | // sanity check integer types declared in OscTypes.h 160 | // you'll need to fix OscTypes.h if any of these asserts fail 161 | assert( sizeof(osc::int32) == 4 ); 162 | assert( sizeof(osc::uint32) == 4 ); 163 | assert( sizeof(osc::int64) == 8 ); 164 | assert( sizeof(osc::uint64) == 8 ); 165 | } 166 | 167 | 168 | OutboundPacketStream::~OutboundPacketStream() 169 | { 170 | 171 | } 172 | 173 | 174 | char *OutboundPacketStream::BeginElement( char *beginPtr ) 175 | { 176 | if( elementSizePtr_ == 0 ){ 177 | 178 | elementSizePtr_ = reinterpret_cast(data_); 179 | 180 | return beginPtr; 181 | 182 | }else{ 183 | // store an offset to the old element size ptr in the element size slot 184 | // we store an offset rather than the actual pointer to be 64 bit clean. 185 | *reinterpret_cast(beginPtr) = 186 | (uint32)(reinterpret_cast(elementSizePtr_) - data_); 187 | 188 | elementSizePtr_ = reinterpret_cast(beginPtr); 189 | 190 | return beginPtr + 4; 191 | } 192 | } 193 | 194 | 195 | void OutboundPacketStream::EndElement( char *endPtr ) 196 | { 197 | assert( elementSizePtr_ != 0 ); 198 | 199 | if( elementSizePtr_ == reinterpret_cast(data_) ){ 200 | 201 | elementSizePtr_ = 0; 202 | 203 | }else{ 204 | // while building an element, an offset to the containing element's 205 | // size slot is stored in the elements size slot (or a ptr to data_ 206 | // if there is no containing element). We retrieve that here 207 | uint32 *previousElementSizePtr = 208 | reinterpret_cast(data_ + *elementSizePtr_); 209 | 210 | // then we store the element size in the slot. note that the element 211 | // size does not include the size slot, hence the - 4 below. 212 | 213 | std::ptrdiff_t d = endPtr - reinterpret_cast(elementSizePtr_); 214 | // assert( d >= 4 && d <= 0x7FFFFFFF ); // assume packets smaller than 2Gb 215 | 216 | uint32 elementSize = static_cast(d - 4); 217 | FromUInt32( reinterpret_cast(elementSizePtr_), elementSize ); 218 | 219 | // finally, we reset the element size ptr to the containing element 220 | elementSizePtr_ = previousElementSizePtr; 221 | } 222 | } 223 | 224 | 225 | bool OutboundPacketStream::ElementSizeSlotRequired() const 226 | { 227 | return (elementSizePtr_ != 0); 228 | } 229 | 230 | 231 | Errors OutboundPacketStream::CheckForAvailableBundleSpace() 232 | { 233 | std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16; 234 | return (required > Capacity()) ? OUT_OF_BUFFER_MEMORY_ERROR : SUCCESS; 235 | } 236 | 237 | 238 | Errors OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern ) 239 | { 240 | // plus 4 for at least four bytes of type tag 241 | std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) 242 | + RoundUp4(std::strlen(addressPattern) + 1) + 4; 243 | 244 | return (required > Capacity()) ? OUT_OF_BUFFER_MEMORY_ERROR : SUCCESS; 245 | } 246 | 247 | 248 | Errors OutboundPacketStream::CheckForAvailableArgumentSpace( std::size_t argumentLength ) 249 | { 250 | // plus three for extra type tag, comma and null terminator 251 | std::size_t required = (argumentCurrent_ - data_) + argumentLength 252 | + RoundUp4( (end_ - typeTagsCurrent_) + 3 ); 253 | 254 | return (required > Capacity()) ? OUT_OF_BUFFER_MEMORY_ERROR : SUCCESS; 255 | } 256 | 257 | 258 | void OutboundPacketStream::Clear() 259 | { 260 | typeTagsCurrent_ = end_; 261 | messageCursor_ = data_; 262 | argumentCurrent_ = data_; 263 | elementSizePtr_ = 0; 264 | messageIsInProgress_ = false; 265 | state_ = SUCCESS; 266 | } 267 | 268 | 269 | std::size_t OutboundPacketStream::Capacity() const 270 | { 271 | return end_ - data_; 272 | } 273 | 274 | 275 | std::size_t OutboundPacketStream::Size() const 276 | { 277 | std::size_t result = argumentCurrent_ - data_; 278 | if( IsMessageInProgress() ){ 279 | // account for the length of the type tag string. the total type tag 280 | // includes an initial comma, plus at least one terminating \0 281 | result += RoundUp4( (end_ - typeTagsCurrent_) + 2 ); 282 | } 283 | 284 | return result; 285 | } 286 | 287 | 288 | const char *OutboundPacketStream::Data() const 289 | { 290 | return data_; 291 | } 292 | 293 | 294 | bool OutboundPacketStream::IsReady() const 295 | { 296 | return (!IsMessageInProgress() && !IsBundleInProgress()); 297 | } 298 | 299 | 300 | bool OutboundPacketStream::IsMessageInProgress() const 301 | { 302 | return messageIsInProgress_; 303 | } 304 | 305 | 306 | bool OutboundPacketStream::IsBundleInProgress() const 307 | { 308 | return (elementSizePtr_ != 0); 309 | } 310 | 311 | 312 | OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs ) 313 | { 314 | assert(!IsMessageInProgress()); 315 | assert(state_ == SUCCESS); 316 | state_ = CheckForAvailableBundleSpace(); 317 | if(state_ == SUCCESS) 318 | { 319 | messageCursor_ = BeginElement( messageCursor_ ); 320 | 321 | std::memcpy( messageCursor_, "#bundle\0", 8 ); 322 | FromUInt64( messageCursor_ + 8, rhs.timeTag ); 323 | 324 | messageCursor_ += 16; 325 | argumentCurrent_ = messageCursor_; 326 | } 327 | 328 | return *this; 329 | } 330 | 331 | 332 | OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs ) 333 | { 334 | (void) rhs; 335 | 336 | assert(IsBundleInProgress()); 337 | assert(!IsMessageInProgress()); 338 | assert(state_ == SUCCESS); 339 | 340 | EndElement( messageCursor_ ); 341 | 342 | return *this; 343 | } 344 | 345 | 346 | OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs ) 347 | { 348 | assert(!IsMessageInProgress()); 349 | assert(state_ == SUCCESS); 350 | 351 | state_ = CheckForAvailableMessageSpace( rhs.addressPattern ); 352 | 353 | if(state_ == SUCCESS) 354 | { 355 | messageCursor_ = BeginElement( messageCursor_ ); 356 | 357 | std::strcpy( messageCursor_, rhs.addressPattern ); 358 | std::size_t rhsLength = std::strlen(rhs.addressPattern); 359 | messageCursor_ += rhsLength + 1; 360 | 361 | // zero pad to 4-byte boundary 362 | std::size_t i = rhsLength + 1; 363 | while( i & 0x3 ){ 364 | *messageCursor_++ = '\0'; 365 | ++i; 366 | } 367 | 368 | argumentCurrent_ = messageCursor_; 369 | typeTagsCurrent_ = end_; 370 | 371 | messageIsInProgress_ = true; 372 | } 373 | 374 | return *this; 375 | } 376 | 377 | 378 | OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs ) 379 | { 380 | (void) rhs; 381 | 382 | assert(IsMessageInProgress()); 383 | assert(state_ == SUCCESS); 384 | 385 | std::size_t typeTagsCount = end_ - typeTagsCurrent_; 386 | 387 | if( typeTagsCount ){ 388 | 389 | char *tempTypeTags = (char*)alloca(typeTagsCount); 390 | std::memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount ); 391 | 392 | // slot size includes comma and null terminator 393 | std::size_t typeTagSlotSize = RoundUp4( typeTagsCount + 2 ); 394 | 395 | std::size_t argumentsSize = argumentCurrent_ - messageCursor_; 396 | 397 | std::memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize ); 398 | 399 | messageCursor_[0] = ','; 400 | // copy type tags in reverse (really forward) order 401 | for( std::size_t i=0; i < typeTagsCount; ++i ) 402 | messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ]; 403 | 404 | char *p = messageCursor_ + 1 + typeTagsCount; 405 | for( std::size_t i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i ) 406 | *p++ = '\0'; 407 | 408 | typeTagsCurrent_ = end_; 409 | 410 | // advance messageCursor_ for next message 411 | messageCursor_ += typeTagSlotSize + argumentsSize; 412 | 413 | }else{ 414 | // send an empty type tags string 415 | std::memcpy( messageCursor_, ",\0\0\0", 4 ); 416 | 417 | // advance messageCursor_ for next message 418 | messageCursor_ += 4; 419 | } 420 | 421 | argumentCurrent_ = messageCursor_; 422 | 423 | EndElement( messageCursor_ ); 424 | 425 | messageIsInProgress_ = false; 426 | 427 | return *this; 428 | } 429 | 430 | 431 | OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs ) 432 | { 433 | assert(state_ == SUCCESS); 434 | state_ = CheckForAvailableArgumentSpace(0); 435 | if(state_ == SUCCESS) 436 | { 437 | *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG); 438 | } 439 | 440 | return *this; 441 | } 442 | 443 | 444 | OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs ) 445 | { 446 | (void) rhs; 447 | assert(state_ == SUCCESS); 448 | state_ = CheckForAvailableArgumentSpace(0); 449 | if(state_ == SUCCESS) 450 | { 451 | *(--typeTagsCurrent_) = NIL_TYPE_TAG; 452 | } 453 | 454 | return *this; 455 | } 456 | 457 | 458 | OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs ) 459 | { 460 | (void) rhs; 461 | assert(state_ == SUCCESS); 462 | state_ = CheckForAvailableArgumentSpace(0); 463 | if(state_ == SUCCESS) 464 | { 465 | *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG; 466 | } 467 | 468 | return *this; 469 | } 470 | 471 | 472 | OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs ) 473 | { 474 | assert(state_ == SUCCESS); 475 | state_ = CheckForAvailableArgumentSpace(4); 476 | if(state_ == SUCCESS) 477 | { 478 | *(--typeTagsCurrent_) = INT32_TYPE_TAG; 479 | FromInt32( argumentCurrent_, rhs ); 480 | argumentCurrent_ += 4; 481 | } 482 | 483 | return *this; 484 | } 485 | 486 | 487 | OutboundPacketStream& OutboundPacketStream::operator<<( float rhs ) 488 | { 489 | assert(state_ == SUCCESS); 490 | state_ = CheckForAvailableArgumentSpace(4); 491 | if(state_ == SUCCESS) 492 | { 493 | *(--typeTagsCurrent_) = FLOAT_TYPE_TAG; 494 | 495 | #ifdef OSC_HOST_LITTLE_ENDIAN 496 | union{ 497 | float f; 498 | char c[4]; 499 | } u; 500 | 501 | u.f = rhs; 502 | 503 | argumentCurrent_[3] = u.c[0]; 504 | argumentCurrent_[2] = u.c[1]; 505 | argumentCurrent_[1] = u.c[2]; 506 | argumentCurrent_[0] = u.c[3]; 507 | #else 508 | *reinterpret_cast(argumentCurrent_) = rhs; 509 | #endif 510 | 511 | argumentCurrent_ += 4; 512 | } 513 | 514 | return *this; 515 | } 516 | 517 | 518 | OutboundPacketStream& OutboundPacketStream::operator<<( char rhs ) 519 | { 520 | assert(state_ == SUCCESS); 521 | state_ = CheckForAvailableArgumentSpace(4); 522 | if(state_ == SUCCESS) 523 | { 524 | *(--typeTagsCurrent_) = CHAR_TYPE_TAG; 525 | FromInt32( argumentCurrent_, rhs ); 526 | argumentCurrent_ += 4; 527 | } 528 | 529 | return *this; 530 | } 531 | 532 | 533 | OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs ) 534 | { 535 | assert(state_ == SUCCESS); 536 | state_ = CheckForAvailableArgumentSpace(4); 537 | if(state_ == SUCCESS) 538 | { 539 | *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG; 540 | FromUInt32( argumentCurrent_, rhs ); 541 | argumentCurrent_ += 4; 542 | } 543 | 544 | return *this; 545 | } 546 | 547 | 548 | OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs ) 549 | { 550 | assert(state_ == SUCCESS); 551 | state_ = CheckForAvailableArgumentSpace(4); 552 | if(state_ == SUCCESS) 553 | { 554 | *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG; 555 | FromUInt32( argumentCurrent_, rhs ); 556 | argumentCurrent_ += 4; 557 | } 558 | 559 | return *this; 560 | } 561 | 562 | 563 | OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs ) 564 | { 565 | assert(state_ == SUCCESS); 566 | state_ = CheckForAvailableArgumentSpace(8); 567 | if(state_ == SUCCESS) 568 | { 569 | *(--typeTagsCurrent_) = INT64_TYPE_TAG; 570 | FromInt64( argumentCurrent_, rhs ); 571 | argumentCurrent_ += 8; 572 | } 573 | 574 | return *this; 575 | } 576 | 577 | 578 | OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs ) 579 | { 580 | assert(state_ == SUCCESS); 581 | state_ = CheckForAvailableArgumentSpace(8); 582 | if(state_ == SUCCESS) 583 | { 584 | *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG; 585 | FromUInt64( argumentCurrent_, rhs ); 586 | argumentCurrent_ += 8; 587 | } 588 | 589 | return *this; 590 | } 591 | 592 | 593 | OutboundPacketStream& OutboundPacketStream::operator<<( double rhs ) 594 | { 595 | assert(state_ == SUCCESS); 596 | state_ = CheckForAvailableArgumentSpace(8); 597 | if(state_ == SUCCESS) 598 | { 599 | *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG; 600 | 601 | #ifdef OSC_HOST_LITTLE_ENDIAN 602 | union{ 603 | double f; 604 | char c[8]; 605 | } u; 606 | 607 | u.f = rhs; 608 | 609 | argumentCurrent_[7] = u.c[0]; 610 | argumentCurrent_[6] = u.c[1]; 611 | argumentCurrent_[5] = u.c[2]; 612 | argumentCurrent_[4] = u.c[3]; 613 | argumentCurrent_[3] = u.c[4]; 614 | argumentCurrent_[2] = u.c[5]; 615 | argumentCurrent_[1] = u.c[6]; 616 | argumentCurrent_[0] = u.c[7]; 617 | #else 618 | *reinterpret_cast(argumentCurrent_) = rhs; 619 | #endif 620 | 621 | argumentCurrent_ += 8; 622 | } 623 | 624 | return *this; 625 | } 626 | 627 | 628 | OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs ) 629 | { 630 | assert(state_ == SUCCESS); 631 | state_ = CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) ); 632 | if(state_ == SUCCESS) 633 | { 634 | *(--typeTagsCurrent_) = STRING_TYPE_TAG; 635 | std::strcpy( argumentCurrent_, rhs ); 636 | std::size_t rhsLength = std::strlen(rhs); 637 | argumentCurrent_ += rhsLength + 1; 638 | 639 | // zero pad to 4-byte boundary 640 | std::size_t i = rhsLength + 1; 641 | while( i & 0x3 ){ 642 | *argumentCurrent_++ = '\0'; 643 | ++i; 644 | } 645 | } 646 | 647 | return *this; 648 | } 649 | 650 | 651 | OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs ) 652 | { 653 | assert(state_ == SUCCESS); 654 | state_ = CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) ); 655 | if(state_ == SUCCESS) 656 | { 657 | *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG; 658 | std::strcpy( argumentCurrent_, rhs ); 659 | std::size_t rhsLength = std::strlen(rhs); 660 | argumentCurrent_ += rhsLength + 1; 661 | 662 | // zero pad to 4-byte boundary 663 | std::size_t i = rhsLength + 1; 664 | while( i & 0x3 ){ 665 | *argumentCurrent_++ = '\0'; 666 | ++i; 667 | } 668 | } 669 | 670 | return *this; 671 | } 672 | 673 | 674 | OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs ) 675 | { 676 | assert(state_ == SUCCESS); 677 | state_ = CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) ); 678 | if(state_ == SUCCESS) 679 | { 680 | *(--typeTagsCurrent_) = BLOB_TYPE_TAG; 681 | FromUInt32( argumentCurrent_, rhs.size ); 682 | argumentCurrent_ += 4; 683 | 684 | std::memcpy( argumentCurrent_, rhs.data, rhs.size ); 685 | argumentCurrent_ += rhs.size; 686 | 687 | // zero pad to 4-byte boundary 688 | unsigned long i = rhs.size; 689 | while( i & 0x3 ){ 690 | *argumentCurrent_++ = '\0'; 691 | ++i; 692 | } 693 | } 694 | 695 | return *this; 696 | } 697 | 698 | OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayInitiator& rhs ) 699 | { 700 | (void) rhs; 701 | assert(state_ == SUCCESS); 702 | state_ = CheckForAvailableArgumentSpace(0); 703 | if(state_ == SUCCESS) 704 | { 705 | *(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG; 706 | } 707 | 708 | return *this; 709 | } 710 | 711 | OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs ) 712 | { 713 | (void) rhs; 714 | assert(state_ == SUCCESS); 715 | state_ = CheckForAvailableArgumentSpace(0); 716 | if(state_ == SUCCESS) 717 | { 718 | *(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG; 719 | } 720 | 721 | return *this; 722 | } 723 | 724 | } // namespace osc 725 | 726 | 727 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Common/oscpack/osc/OscReceivedElements.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | 38 | #include "OscReceivedElements.h" 39 | 40 | #include "OscHostEndianness.h" 41 | 42 | #include // ptrdiff_t 43 | 44 | namespace osc{ 45 | 46 | 47 | // return the first 4 byte boundary after the end of a str4 48 | // be careful about calling this version if you don't know whether 49 | // the string is terminated correctly. 50 | static inline const char* FindStr4End( const char *p ) 51 | { 52 | if( p[0] == '\0' ) // special case for SuperCollider integer address pattern 53 | return p + 4; 54 | 55 | p += 3; 56 | 57 | while( *p ) 58 | p += 4; 59 | 60 | return p + 1; 61 | } 62 | 63 | 64 | // return the first 4 byte boundary after the end of a str4 65 | // returns 0 if p == end or if the string is unterminated 66 | static inline const char* FindStr4End( const char *p, const char *end ) 67 | { 68 | if( p >= end ) 69 | return 0; 70 | 71 | if( p[0] == '\0' ) // special case for SuperCollider integer address pattern 72 | return p + 4; 73 | 74 | p += 3; 75 | end -= 1; 76 | 77 | while( p < end && *p ) 78 | p += 4; 79 | 80 | if( *p ) 81 | return 0; 82 | else 83 | return p + 1; 84 | } 85 | 86 | 87 | static inline int32 ToInt32( const char *p ) 88 | { 89 | #ifdef OSC_HOST_LITTLE_ENDIAN 90 | union{ 91 | osc::int32 i; 92 | char c[4]; 93 | } u; 94 | 95 | u.c[0] = p[3]; 96 | u.c[1] = p[2]; 97 | u.c[2] = p[1]; 98 | u.c[3] = p[0]; 99 | 100 | return u.i; 101 | #else 102 | return *(int32*)p; 103 | #endif 104 | } 105 | 106 | 107 | static inline uint32 ToUInt32( const char *p ) 108 | { 109 | #ifdef OSC_HOST_LITTLE_ENDIAN 110 | union{ 111 | osc::uint32 i; 112 | char c[4]; 113 | } u; 114 | 115 | u.c[0] = p[3]; 116 | u.c[1] = p[2]; 117 | u.c[2] = p[1]; 118 | u.c[3] = p[0]; 119 | 120 | return u.i; 121 | #else 122 | return *(uint32*)p; 123 | #endif 124 | } 125 | 126 | 127 | static inline int64 ToInt64( const char *p ) 128 | { 129 | #ifdef OSC_HOST_LITTLE_ENDIAN 130 | union{ 131 | osc::int64 i; 132 | char c[8]; 133 | } u; 134 | 135 | u.c[0] = p[7]; 136 | u.c[1] = p[6]; 137 | u.c[2] = p[5]; 138 | u.c[3] = p[4]; 139 | u.c[4] = p[3]; 140 | u.c[5] = p[2]; 141 | u.c[6] = p[1]; 142 | u.c[7] = p[0]; 143 | 144 | return u.i; 145 | #else 146 | return *(int64*)p; 147 | #endif 148 | } 149 | 150 | 151 | static inline uint64 ToUInt64( const char *p ) 152 | { 153 | #ifdef OSC_HOST_LITTLE_ENDIAN 154 | union{ 155 | osc::uint64 i; 156 | char c[8]; 157 | } u; 158 | 159 | u.c[0] = p[7]; 160 | u.c[1] = p[6]; 161 | u.c[2] = p[5]; 162 | u.c[3] = p[4]; 163 | u.c[4] = p[3]; 164 | u.c[5] = p[2]; 165 | u.c[6] = p[1]; 166 | u.c[7] = p[0]; 167 | 168 | return u.i; 169 | #else 170 | return *(uint64*)p; 171 | #endif 172 | } 173 | 174 | //------------------------------------------------------------------------------ 175 | 176 | bool ReceivedPacket::IsBundle() const 177 | { 178 | return (Size() > 0 && Contents()[0] == '#'); 179 | } 180 | 181 | //------------------------------------------------------------------------------ 182 | 183 | bool ReceivedBundleElement::IsBundle() const 184 | { 185 | return (Size() > 0 && Contents()[0] == '#'); 186 | } 187 | 188 | 189 | osc_bundle_element_size_t ReceivedBundleElement::Size() const 190 | { 191 | return ToInt32( sizePtr_ ); 192 | } 193 | 194 | //------------------------------------------------------------------------------ 195 | 196 | bool ReceivedMessageArgument::AsBool(Errors & state) const 197 | { 198 | state = SUCCESS; 199 | if( !typeTagPtr_ ) 200 | state = MISSING_ARGUMENT_ERROR; 201 | else if( *typeTagPtr_ == TRUE_TYPE_TAG ) 202 | return true; 203 | else if( *typeTagPtr_ == FALSE_TYPE_TAG ) 204 | return false; 205 | else 206 | state = WRONG_ARGUMENT_TYPE_ERROR; 207 | return false; 208 | } 209 | 210 | 211 | bool ReceivedMessageArgument::AsBoolUnchecked(Errors & state) const 212 | { 213 | state = SUCCESS; 214 | if( !typeTagPtr_ ) 215 | state = MISSING_ARGUMENT_ERROR; 216 | else if( *typeTagPtr_ == TRUE_TYPE_TAG ) 217 | return true; 218 | else 219 | return false; 220 | return false; 221 | } 222 | 223 | 224 | int32 ReceivedMessageArgument::AsInt32(Errors & state) const 225 | { 226 | state = SUCCESS; 227 | if( !typeTagPtr_ ) 228 | state = MISSING_ARGUMENT_ERROR; 229 | else if( *typeTagPtr_ == INT32_TYPE_TAG ) 230 | return AsInt32Unchecked(); 231 | else 232 | state = WRONG_ARGUMENT_TYPE_ERROR; 233 | return 0; 234 | } 235 | 236 | 237 | int32 ReceivedMessageArgument::AsInt32Unchecked() const 238 | { 239 | #ifdef OSC_HOST_LITTLE_ENDIAN 240 | union{ 241 | osc::int32 i; 242 | char c[4]; 243 | } u; 244 | 245 | u.c[0] = argumentPtr_[3]; 246 | u.c[1] = argumentPtr_[2]; 247 | u.c[2] = argumentPtr_[1]; 248 | u.c[3] = argumentPtr_[0]; 249 | 250 | return u.i; 251 | #else 252 | return *(int32*)argument_; 253 | #endif 254 | } 255 | 256 | 257 | float ReceivedMessageArgument::AsFloat(Errors & state) const 258 | { 259 | state = SUCCESS; 260 | if( !typeTagPtr_ ) 261 | state = MISSING_ARGUMENT_ERROR; 262 | else if( *typeTagPtr_ == FLOAT_TYPE_TAG ) 263 | return AsFloatUnchecked(); 264 | else 265 | state = WRONG_ARGUMENT_TYPE_ERROR; 266 | return 0.f; 267 | } 268 | 269 | 270 | float ReceivedMessageArgument::AsFloatUnchecked() const 271 | { 272 | #ifdef OSC_HOST_LITTLE_ENDIAN 273 | union{ 274 | float f; 275 | char c[4]; 276 | } u; 277 | 278 | u.c[0] = argumentPtr_[3]; 279 | u.c[1] = argumentPtr_[2]; 280 | u.c[2] = argumentPtr_[1]; 281 | u.c[3] = argumentPtr_[0]; 282 | 283 | return u.f; 284 | #else 285 | return *(float*)argument_; 286 | #endif 287 | } 288 | 289 | 290 | char ReceivedMessageArgument::AsChar(Errors & state) const 291 | { 292 | state = SUCCESS; 293 | if( !typeTagPtr_ ) 294 | state = MISSING_ARGUMENT_ERROR; 295 | else if( *typeTagPtr_ == CHAR_TYPE_TAG ) 296 | return AsCharUnchecked(); 297 | else 298 | state = WRONG_ARGUMENT_TYPE_ERROR; 299 | return '\0'; 300 | } 301 | 302 | 303 | char ReceivedMessageArgument::AsCharUnchecked() const 304 | { 305 | return (char)ToInt32( argumentPtr_ ); 306 | } 307 | 308 | 309 | uint32 ReceivedMessageArgument::AsRgbaColor(Errors & state) const 310 | { 311 | state = SUCCESS; 312 | if( !typeTagPtr_ ) 313 | state = MISSING_ARGUMENT_ERROR; 314 | else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG ) 315 | return AsRgbaColorUnchecked(); 316 | else 317 | state = WRONG_ARGUMENT_TYPE_ERROR; 318 | return 0u; 319 | } 320 | 321 | 322 | uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const 323 | { 324 | return ToUInt32( argumentPtr_ ); 325 | } 326 | 327 | 328 | uint32 ReceivedMessageArgument::AsMidiMessage(Errors & state) const 329 | { 330 | state = SUCCESS; 331 | if( !typeTagPtr_ ) 332 | state = MISSING_ARGUMENT_ERROR; 333 | else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG ) 334 | return AsMidiMessageUnchecked(); 335 | else 336 | state = WRONG_ARGUMENT_TYPE_ERROR; 337 | return 0u; 338 | } 339 | 340 | 341 | uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const 342 | { 343 | return ToUInt32( argumentPtr_ ); 344 | } 345 | 346 | 347 | int64 ReceivedMessageArgument::AsInt64(Errors & state) const 348 | { 349 | state = SUCCESS; 350 | if( !typeTagPtr_ ) 351 | state = MISSING_ARGUMENT_ERROR; 352 | else if( *typeTagPtr_ == INT64_TYPE_TAG ) 353 | return AsInt64Unchecked(); 354 | else 355 | state = WRONG_ARGUMENT_TYPE_ERROR; 356 | return 0LL; 357 | } 358 | 359 | 360 | int64 ReceivedMessageArgument::AsInt64Unchecked() const 361 | { 362 | return ToInt64( argumentPtr_ ); 363 | } 364 | 365 | 366 | uint64 ReceivedMessageArgument::AsTimeTag(Errors & state) const 367 | { 368 | state = SUCCESS; 369 | if( !typeTagPtr_ ) 370 | state = MISSING_ARGUMENT_ERROR; 371 | else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG ) 372 | return AsTimeTagUnchecked(); 373 | else 374 | state = WRONG_ARGUMENT_TYPE_ERROR; 375 | return 0ULL; 376 | } 377 | 378 | 379 | uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const 380 | { 381 | return ToUInt64( argumentPtr_ ); 382 | } 383 | 384 | 385 | double ReceivedMessageArgument::AsDouble(Errors & state) const 386 | { 387 | state = SUCCESS; 388 | if( !typeTagPtr_ ) 389 | state = MISSING_ARGUMENT_ERROR; 390 | else if( *typeTagPtr_ == DOUBLE_TYPE_TAG ) 391 | return AsDoubleUnchecked(); 392 | else 393 | state = WRONG_ARGUMENT_TYPE_ERROR; 394 | return 0.; 395 | } 396 | 397 | 398 | double ReceivedMessageArgument::AsDoubleUnchecked() const 399 | { 400 | #ifdef OSC_HOST_LITTLE_ENDIAN 401 | union{ 402 | double d; 403 | char c[8]; 404 | } u; 405 | 406 | u.c[0] = argumentPtr_[7]; 407 | u.c[1] = argumentPtr_[6]; 408 | u.c[2] = argumentPtr_[5]; 409 | u.c[3] = argumentPtr_[4]; 410 | u.c[4] = argumentPtr_[3]; 411 | u.c[5] = argumentPtr_[2]; 412 | u.c[6] = argumentPtr_[1]; 413 | u.c[7] = argumentPtr_[0]; 414 | 415 | return u.d; 416 | #else 417 | return *(double*)argument_; 418 | #endif 419 | } 420 | 421 | 422 | const char* ReceivedMessageArgument::AsString(Errors & state) const 423 | { 424 | state = SUCCESS; 425 | if( !typeTagPtr_ ) 426 | state = MISSING_ARGUMENT_ERROR; 427 | else if( *typeTagPtr_ == STRING_TYPE_TAG ) 428 | return argumentPtr_; 429 | else 430 | state = WRONG_ARGUMENT_TYPE_ERROR; 431 | return ""; 432 | } 433 | 434 | 435 | const char* ReceivedMessageArgument::AsSymbol(Errors & state) const 436 | { 437 | state = SUCCESS; 438 | if( !typeTagPtr_ ) 439 | state = MISSING_ARGUMENT_ERROR; 440 | else if( *typeTagPtr_ == SYMBOL_TYPE_TAG ) 441 | return argumentPtr_; 442 | else 443 | state = WRONG_ARGUMENT_TYPE_ERROR; 444 | return ""; 445 | } 446 | 447 | 448 | void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size, Errors & state ) const 449 | { 450 | if( !typeTagPtr_ ) 451 | state = MISSING_ARGUMENT_ERROR; 452 | else if( *typeTagPtr_ == BLOB_TYPE_TAG ) 453 | AsBlobUnchecked( data, size, state ); 454 | else 455 | state = WRONG_ARGUMENT_TYPE_ERROR; 456 | } 457 | 458 | 459 | void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size, Errors & state ) const 460 | { 461 | // read blob size as an unsigned int then validate 462 | osc_bundle_element_size_t sizeResult = (osc_bundle_element_size_t)ToUInt32( argumentPtr_ ); 463 | if( !IsValidElementSizeValue(sizeResult) ) 464 | { 465 | state = MALFORMED_MESSAGE_INVALID_BLOB_SIZE_ERROR; 466 | } 467 | else 468 | { 469 | state = SUCCESS; 470 | size = sizeResult; 471 | data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32); 472 | } 473 | } 474 | 475 | std::size_t ReceivedMessageArgument::ComputeArrayItemCount(Errors & state) const 476 | { 477 | // it is only valid to call ComputeArrayItemCount when the argument is the array start marker 478 | if( !IsArrayBegin() ) 479 | { 480 | state = WRONG_ARGUMENT_TYPE_ERROR; 481 | return 0; 482 | } 483 | 484 | state = SUCCESS; 485 | 486 | std::size_t result = 0; 487 | unsigned int level = 0; 488 | const char *typeTag = typeTagPtr_ + 1; 489 | 490 | // iterate through all type tags. note that ReceivedMessage::Init 491 | // has already checked that the message is well formed. 492 | while( *typeTag ) { 493 | switch( *typeTag++ ) { 494 | case ARRAY_BEGIN_TYPE_TAG: 495 | level += 1; 496 | break; 497 | 498 | case ARRAY_END_TYPE_TAG: 499 | if(level == 0) 500 | return result; 501 | level -= 1; 502 | break; 503 | 504 | default: 505 | if( level == 0 ) // only count items at level 0 506 | ++result; 507 | } 508 | } 509 | 510 | return result; 511 | } 512 | 513 | //------------------------------------------------------------------------------ 514 | 515 | void ReceivedMessageArgumentIterator::Advance() 516 | { 517 | if( !value_.typeTagPtr_ ) 518 | return; 519 | 520 | switch( *value_.typeTagPtr_++ ){ 521 | case '\0': 522 | // don't advance past end 523 | --value_.typeTagPtr_; 524 | break; 525 | 526 | case TRUE_TYPE_TAG: 527 | case FALSE_TYPE_TAG: 528 | case NIL_TYPE_TAG: 529 | case INFINITUM_TYPE_TAG: 530 | 531 | // zero length 532 | break; 533 | 534 | case INT32_TYPE_TAG: 535 | case FLOAT_TYPE_TAG: 536 | case CHAR_TYPE_TAG: 537 | case RGBA_COLOR_TYPE_TAG: 538 | case MIDI_MESSAGE_TYPE_TAG: 539 | 540 | value_.argumentPtr_ += 4; 541 | break; 542 | 543 | case INT64_TYPE_TAG: 544 | case TIME_TAG_TYPE_TAG: 545 | case DOUBLE_TYPE_TAG: 546 | 547 | value_.argumentPtr_ += 8; 548 | break; 549 | 550 | case STRING_TYPE_TAG: 551 | case SYMBOL_TYPE_TAG: 552 | 553 | // we use the unsafe function FindStr4End(char*) here because all of 554 | // the arguments have already been validated in 555 | // ReceivedMessage::Init() below. 556 | 557 | value_.argumentPtr_ = FindStr4End( value_.argumentPtr_ ); 558 | break; 559 | 560 | case BLOB_TYPE_TAG: 561 | { 562 | // treat blob size as an unsigned int for the purposes of this calculation 563 | uint32 blobSize = ToUInt32( value_.argumentPtr_ ); 564 | value_.argumentPtr_ = value_.argumentPtr_ + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize ); 565 | } 566 | break; 567 | 568 | case ARRAY_BEGIN_TYPE_TAG: 569 | case ARRAY_END_TYPE_TAG: 570 | 571 | // [ Indicates the beginning of an array. The tags following are for 572 | // data in the Array until a close brace tag is reached. 573 | // ] Indicates the end of an array. 574 | 575 | // zero length, don't advance argument ptr 576 | break; 577 | 578 | default: // unknown type tag 579 | // don't advance 580 | --value_.typeTagPtr_; 581 | break; 582 | } 583 | } 584 | 585 | //------------------------------------------------------------------------------ 586 | 587 | ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet ) 588 | : addressPattern_( packet.Contents() ) 589 | , state_( SUCCESS ) 590 | { 591 | Init( packet.Contents(), packet.Size() ); 592 | } 593 | 594 | 595 | ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement ) 596 | : addressPattern_( bundleElement.Contents() ) 597 | , state_( SUCCESS ) 598 | { 599 | Init( bundleElement.Contents(), bundleElement.Size() ); 600 | } 601 | 602 | 603 | bool ReceivedMessage::AddressPatternIsUInt32() const 604 | { 605 | return (addressPattern_[0] == '\0'); 606 | } 607 | 608 | 609 | uint32 ReceivedMessage::AddressPatternAsUInt32() const 610 | { 611 | return ToUInt32( addressPattern_ ); 612 | } 613 | 614 | 615 | void ReceivedMessage::Init( const char *message, osc_bundle_element_size_t size ) 616 | { 617 | if( !IsValidElementSizeValue(size) ) 618 | { 619 | state_ = MALFORMED_MESSAGE_INVALID_SIZE_ERROR; 620 | return; 621 | } 622 | 623 | if( size == 0 ) 624 | { 625 | state_ = MALFORMED_MESSAGE_ZERO_SIZE_ERROR; 626 | return; 627 | } 628 | 629 | if( !IsMultipleOf4(size) ) 630 | { 631 | state_ = MALFORMED_MESSAGE_NOT_MULTIPLE_OF_4_ERROR; 632 | return; 633 | } 634 | 635 | const char *end = message + size; 636 | 637 | typeTagsBegin_ = FindStr4End( addressPattern_, end ); 638 | if( typeTagsBegin_ == 0 ){ 639 | // address pattern was not terminated before end 640 | state_ = MALFORMED_MESSAGE_UNTERMINATED_ADDRESS_PATTERN_ERROR; 641 | return; 642 | } 643 | 644 | if( typeTagsBegin_ == end ){ 645 | // message consists of only the address pattern - no arguments or type tags. 646 | typeTagsBegin_ = 0; 647 | typeTagsEnd_ = 0; 648 | arguments_ = 0; 649 | 650 | }else{ 651 | if( *typeTagsBegin_ != ',' ) 652 | { 653 | state_ = MALFORMED_MESSAGE_NO_TYPE_TAGS_ERROR; 654 | return; 655 | } 656 | 657 | if( *(typeTagsBegin_ + 1) == '\0' ){ 658 | // zero length type tags 659 | typeTagsBegin_ = 0; 660 | typeTagsEnd_ = 0; 661 | arguments_ = 0; 662 | 663 | }else{ 664 | // check that all arguments are present and well formed 665 | 666 | arguments_ = FindStr4End( typeTagsBegin_, end ); 667 | if( arguments_ == 0 ) 668 | { 669 | state_ = MALFORMED_MESSAGE_UNTERMINATED_TYPE_TAGS_ERROR; 670 | return; 671 | } 672 | 673 | ++typeTagsBegin_; // advance past initial ',' 674 | 675 | const char *typeTag = typeTagsBegin_; 676 | const char *argument = arguments_; 677 | unsigned int arrayLevel = 0; 678 | 679 | do{ 680 | switch( *typeTag ){ 681 | case TRUE_TYPE_TAG: 682 | case FALSE_TYPE_TAG: 683 | case NIL_TYPE_TAG: 684 | case INFINITUM_TYPE_TAG: 685 | // zero length 686 | break; 687 | 688 | // [ Indicates the beginning of an array. The tags following are for 689 | // data in the Array until a close brace tag is reached. 690 | // ] Indicates the end of an array. 691 | case ARRAY_BEGIN_TYPE_TAG: 692 | ++arrayLevel; 693 | // (zero length argument data) 694 | break; 695 | 696 | case ARRAY_END_TYPE_TAG: 697 | --arrayLevel; 698 | // (zero length argument data) 699 | break; 700 | 701 | case INT32_TYPE_TAG: 702 | case FLOAT_TYPE_TAG: 703 | case CHAR_TYPE_TAG: 704 | case RGBA_COLOR_TYPE_TAG: 705 | case MIDI_MESSAGE_TYPE_TAG: 706 | 707 | if( argument == end ) 708 | { 709 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 710 | return; 711 | } 712 | argument += 4; 713 | if( argument > end ) 714 | { 715 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 716 | return; 717 | } 718 | break; 719 | 720 | case INT64_TYPE_TAG: 721 | case TIME_TAG_TYPE_TAG: 722 | case DOUBLE_TYPE_TAG: 723 | 724 | if( argument == end ) 725 | { 726 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 727 | return; 728 | } 729 | argument += 8; 730 | if( argument > end ) 731 | { 732 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 733 | return; 734 | } 735 | break; 736 | 737 | case STRING_TYPE_TAG: 738 | case SYMBOL_TYPE_TAG: 739 | 740 | if( argument == end ) 741 | { 742 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 743 | return; 744 | } 745 | argument = FindStr4End( argument, end ); 746 | if( argument == 0 ) 747 | { 748 | state_ = MALFORMED_MESSAGE_UNTERMINATED_STRING_ERROR; 749 | return; 750 | } 751 | break; 752 | 753 | case BLOB_TYPE_TAG: 754 | { 755 | if( argument + osc::OSC_SIZEOF_INT32 > end ) 756 | { 757 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 758 | return; 759 | } 760 | 761 | // treat blob size as an unsigned int for the purposes of this calculation 762 | uint32 blobSize = ToUInt32( argument ); 763 | argument = argument + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize ); 764 | if( argument > end ) 765 | { 766 | state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR; 767 | return; 768 | } 769 | } 770 | break; 771 | 772 | default: 773 | state_ = MALFORMED_MESSAGE_UNKNOWN_TYPE_TAG_ERROR; 774 | return; 775 | } 776 | 777 | }while( *++typeTag != '\0' ); 778 | typeTagsEnd_ = typeTag; 779 | 780 | if( arrayLevel != 0 ) 781 | { 782 | state_ = MALFORMED_MESSAGE_UNTERMINATED_ARRAY_ERROR; 783 | return; 784 | } 785 | } 786 | 787 | // These invariants should be guaranteed by the above code. 788 | // we depend on them in the implementation of ArgumentCount() 789 | #ifndef NDEBUG 790 | std::ptrdiff_t argumentCount = typeTagsEnd_ - typeTagsBegin_; 791 | assert( argumentCount >= 0 ); 792 | assert( argumentCount <= OSC_INT32_MAX ); 793 | #endif 794 | } 795 | } 796 | 797 | //------------------------------------------------------------------------------ 798 | 799 | ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet ) 800 | : elementCount_( 0 ) 801 | , state_( SUCCESS ) 802 | { 803 | Init( packet.Contents(), packet.Size() ); 804 | } 805 | 806 | 807 | ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement ) 808 | : elementCount_( 0 ) 809 | , state_( SUCCESS ) 810 | { 811 | Init( bundleElement.Contents(), bundleElement.Size() ); 812 | } 813 | 814 | 815 | void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size ) 816 | { 817 | 818 | if( !IsValidElementSizeValue(size) ) 819 | { 820 | state_ = MALFORMED_BUNDLE_INVALID_SIZE_ERROR; 821 | return; 822 | } 823 | 824 | if( size < 16 ) 825 | { 826 | state_ = MALFORMED_BUNDLE_TOO_SHORT_ERROR; 827 | return; 828 | } 829 | 830 | if( !IsMultipleOf4(size) ) 831 | { 832 | state_ = MALFORMED_BUNDLE_NOT_MULTIPLE_OF_4_ERROR; 833 | return; 834 | } 835 | 836 | if( bundle[0] != '#' 837 | || bundle[1] != 'b' 838 | || bundle[2] != 'u' 839 | || bundle[3] != 'n' 840 | || bundle[4] != 'd' 841 | || bundle[5] != 'l' 842 | || bundle[6] != 'e' 843 | || bundle[7] != '\0' ) 844 | { 845 | state_ = MALFORMED_BUNDLE_INVALID_ADDRESS_PATTERN_ERROR; 846 | return; 847 | } 848 | 849 | end_ = bundle + size; 850 | 851 | timeTag_ = bundle + 8; 852 | 853 | const char *p = timeTag_ + 8; 854 | 855 | while( p < end_ ){ 856 | if( p + osc::OSC_SIZEOF_INT32 > end_ ) 857 | { 858 | state_ = MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_SIZE_ERROR; 859 | return; 860 | } 861 | 862 | // treat element size as an unsigned int for the purposes of this calculation 863 | uint32 elementSize = ToUInt32( p ); 864 | if( (elementSize & ((uint32)0x03)) != 0 ) 865 | { 866 | state_ = MALFORMED_BUNDLE_ELEMENT_NOT_MULTIPLE_OF_4_ERROR; 867 | return; 868 | } 869 | 870 | p += osc::OSC_SIZEOF_INT32 + elementSize; 871 | if( p > end_ ) 872 | { 873 | state_ = MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_ERROR; 874 | return; 875 | } 876 | 877 | ++elementCount_; 878 | } 879 | 880 | if( p != end_ ) 881 | { 882 | state_ = MALFORMED_BUNDLE_CONTENT_ERROR; 883 | } 884 | } 885 | 886 | 887 | uint64 ReceivedBundle::TimeTag() const 888 | { 889 | return ToUInt64( timeTag_ ); 890 | } 891 | 892 | 893 | } // namespace osc 894 | 895 | -------------------------------------------------------------------------------- /UE-OSC/Source/UE_OSC/Private/Receive/oscpkt.hh: -------------------------------------------------------------------------------- 1 | /** @mainpage OSCPKT : a minimalistic OSC ( http://opensoundcontrol.org ) c++ library 2 | 3 | Before using this file please take the time to read the OSC spec, it 4 | is short and not complicated: http://opensoundcontrol.org/spec-1_0 5 | 6 | Features: 7 | - handles basic OSC types: TFihfdsb 8 | - handles bundles 9 | - handles OSC pattern-matching rules (wildcards etc in message paths) 10 | - portable on win / macos / linux 11 | - robust wrt malformed packets 12 | - optional udp transport for packets 13 | - concise, all in a single .h file 14 | - does not throw exceptions 15 | 16 | does not: 17 | - take into account timestamp values. 18 | - provide a cpu-scalable message dispatching. 19 | - not suitable for use inside a realtime thread as it allocates memory when 20 | building or reading messages. 21 | 22 | 23 | There are basically 3 classes of interest: 24 | - oscpkt::Message : read/write the content of an OSC message 25 | - oscpkt::PacketReader : read the bundles/messages embedded in an OSC packet 26 | - oscpkt::PacketWriter : write bundles/messages into an OSC packet 27 | 28 | And optionaly: 29 | - oscpkt::UdpSocket : read/write OSC packets over UDP. 30 | 31 | @example: oscpkt_demo.cc 32 | @example: oscpkt_test.cc 33 | */ 34 | 35 | /* Copyright (C) 2010 Julien Pommier 36 | 37 | This software is provided 'as-is', without any express or implied 38 | warranty. In no event will the authors be held liable for any damages 39 | arising from the use of this software. 40 | 41 | Permission is granted to anyone to use this software for any purpose, 42 | including commercial applications, and to alter it and redistribute it 43 | freely, subject to the following restrictions: 44 | 45 | 1. The origin of this software must not be misrepresented; you must not 46 | claim that you wrote the original software. If you use this software 47 | in a product, an acknowledgment in the product documentation would be 48 | appreciated but is not required. 49 | 2. Altered source versions must be plainly marked as such, and must not be 50 | misrepresented as being the original software. 51 | 3. This notice may not be removed or altered from any source distribution. 52 | 53 | (this is the zlib license) 54 | */ 55 | 56 | #ifndef OSCPKT_HH 57 | #define OSCPKT_HH 58 | 59 | #ifndef _MSC_VER 60 | #include 61 | #else 62 | namespace oscpkt { 63 | typedef __int32 int32_t; 64 | typedef unsigned __int32 uint32_t; 65 | typedef __int64 int64_t; 66 | typedef unsigned __int64 uint64_t; 67 | } 68 | #endif 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | #if defined(OSCPKT_OSTREAM_OUTPUT) || defined(OSCPKT_TEST) 76 | #include 77 | #endif 78 | 79 | namespace oscpkt { 80 | 81 | /** 82 | OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second. 83 | */ 84 | class TimeTag { 85 | uint64_t v; 86 | public: 87 | TimeTag() : v(1) {} 88 | explicit TimeTag(uint64_t w): v(w) {} 89 | operator uint64_t() const { return v; } 90 | static TimeTag immediate() { return TimeTag(1); } 91 | }; 92 | 93 | /* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */ 94 | enum { 95 | TYPE_TAG_TRUE = 'T', 96 | TYPE_TAG_FALSE = 'F', 97 | TYPE_TAG_INT32 = 'i', 98 | TYPE_TAG_INT64 = 'h', 99 | TYPE_TAG_FLOAT = 'f', 100 | TYPE_TAG_DOUBLE = 'd', 101 | TYPE_TAG_STRING = 's', 102 | TYPE_TAG_BLOB = 'b' 103 | }; 104 | 105 | /* a few utility functions follow.. */ 106 | 107 | // round to the next multiple of 4, works for size_t and pointer arguments 108 | template Type ceil4(Type p) { return (Type)((size_t(p) + 3)&(~size_t(3))); } 109 | 110 | // check that a memory area is zero padded until the next address which is a multiple of 4 111 | inline bool isZeroPaddingCorrect(const char *p) { 112 | const char *q = ceil4(p); 113 | for (;p < q; ++p) 114 | if (*p != 0) { return false; } 115 | return true; 116 | } 117 | 118 | // stuff for reading / writing POD ("Plain Old Data") variables to unaligned bytes. 119 | template union PodBytes { 120 | char bytes[sizeof(POD)]; 121 | POD value; 122 | }; 123 | 124 | inline bool isBigEndian() { // a compile-time constant would certainly improve performances.. 125 | PodBytes p; p.value = 0x12345678; 126 | return p.bytes[0] == 0x12; 127 | } 128 | 129 | /** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */ 130 | template POD bytes2pod(const char *bytes) { 131 | PodBytes p; 132 | for (size_t i=0; i < sizeof(POD); ++i) { 133 | if (isBigEndian()) 134 | p.bytes[i] = bytes[i]; 135 | else 136 | p.bytes[i] = bytes[sizeof(POD) - i - 1]; 137 | } 138 | return p.value; 139 | } 140 | 141 | /** stored a POD type into an unaligned bytes array, using little endian representation */ 142 | template void pod2bytes(const POD value, char *bytes) { 143 | PodBytes p; p.value = value; 144 | for (size_t i=0; i < sizeof(POD); ++i) { 145 | if (isBigEndian()) 146 | bytes[i] = p.bytes[i]; 147 | else 148 | bytes[i] = p.bytes[sizeof(POD) - i - 1]; 149 | } 150 | } 151 | 152 | /** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */ 153 | struct Storage { 154 | std::vector data; 155 | Storage() { data.reserve(200); } 156 | char *getBytes(size_t sz) { 157 | assert((data.size() & 3) == 0); 158 | if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz)*2); } 159 | size_t sz4 = ceil4(sz); 160 | size_t pos = data.size(); 161 | data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK 162 | return &(data[pos]); 163 | } 164 | char *begin() { return data.size() ? &data.front() : 0; } 165 | char *end() { return begin() + size(); } 166 | const char *begin() const { return data.size() ? &data.front() : 0; } 167 | const char *end() const { return begin() + size(); } 168 | size_t size() const { return data.size(); } 169 | void assign(const char *beg, const char *end) { data.assign(beg, end); } 170 | void clear() { data.resize(0); } 171 | }; 172 | 173 | /** check if the path matches the supplied path pattern , according to the OSC spec pattern 174 | rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */ 175 | bool fullPatternMatch(const std::string &pattern, const std::string &path); 176 | /** check if the path matches the beginning of pattern */ 177 | bool partialPatternMatch(const std::string &pattern, const std::string &path); 178 | 179 | #if defined(OSCPKT_DEBUG) 180 | #define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0) 181 | #else 182 | #define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0) 183 | #endif 184 | 185 | typedef enum { OK_NO_ERROR=0, 186 | // errors raised by the Message class: 187 | MALFORMED_ADDRESS_PATTERN, MALFORMED_TYPE_TAGS, MALFORMED_ARGUMENTS, UNHANDLED_TYPE_TAGS, 188 | // errors raised by ArgReader 189 | TYPE_MISMATCH, NOT_ENOUGH_ARG, PATTERN_MISMATCH, 190 | // errors raised by PacketReader/PacketWriter 191 | INVALID_BUNDLE, INVALID_PACKET_SIZE, BUNDLE_REQUIRED_FOR_MULTI_MESSAGES } ErrorCode; 192 | 193 | /** 194 | struct used to hold an OSC message that will be written or read. 195 | 196 | The list of arguments is exposed as a sort of queue. You "pop" 197 | arguments from the front of the queue when reading, you push 198 | arguments at the back of the queue when writing. 199 | 200 | Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")... 201 | 202 | Example of use: 203 | 204 | creation of a message: 205 | @code 206 | msg.init("/foo").pushInt32(4).pushStr("bar"); 207 | @endcode 208 | reading a message, with error detection: 209 | @code 210 | if (msg.match("/foo/b*ar/plop")) { 211 | int i; std::string s; std::vector b; 212 | if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) { 213 | process message...; 214 | } else arguments mismatch; 215 | } 216 | @endcode 217 | */ 218 | class Message { 219 | TimeTag time_tag; 220 | std::string address; 221 | std::string type_tags; 222 | std::vector > arguments; // array of pairs (pos,size), pos being an index into the 'storage' array. 223 | Storage storage; // the arguments data is stored here 224 | ErrorCode err; 225 | public: 226 | /** ArgReader is used for popping arguments from a Message, holds a 227 | pointer to the original Message, and maintains a local error code */ 228 | class ArgReader { 229 | const Message *msg; 230 | ErrorCode err; 231 | size_t arg_idx; // arg index of the next arg that will be popped out. 232 | public: 233 | ArgReader(const Message &m, ErrorCode e = OK_NO_ERROR) : msg(&m), err(msg->getErr()), arg_idx(0) { 234 | if (e != OK_NO_ERROR && err == OK_NO_ERROR) err=e; 235 | } 236 | ArgReader(const ArgReader &other) : msg(other.msg), err(other.err), arg_idx(other.arg_idx) {} 237 | bool isBool() { return currentTypeTag() == TYPE_TAG_TRUE || currentTypeTag() == TYPE_TAG_FALSE; } 238 | bool isInt32() { return currentTypeTag() == TYPE_TAG_INT32; } 239 | bool isInt64() { return currentTypeTag() == TYPE_TAG_INT64; } 240 | bool isFloat() { return currentTypeTag() == TYPE_TAG_FLOAT; } 241 | bool isDouble() { return currentTypeTag() == TYPE_TAG_DOUBLE; } 242 | bool isStr() { return currentTypeTag() == TYPE_TAG_STRING; } 243 | bool isBlob() { return currentTypeTag() == TYPE_TAG_BLOB; } 244 | 245 | size_t nbArgRemaining() const { return msg->arguments.size() - arg_idx; } 246 | bool isOk() const { return err == OK_NO_ERROR; } 247 | operator bool() const { return isOk(); } // implicit bool conversion is handy here 248 | /** call this at the end of the popXXX() chain to make sure everything is ok and 249 | all arguments have been popped */ 250 | bool isOkNoMoreArgs() const { return err == OK_NO_ERROR && nbArgRemaining() == 0; } 251 | ErrorCode getErr() const { return err; } 252 | 253 | /** retrieve an int32 argument */ 254 | ArgReader &popInt32(int32_t &i) { return popPod(TYPE_TAG_INT32, i); } 255 | /** retrieve an int64 argument */ 256 | ArgReader &popInt64(int64_t &i) { return popPod(TYPE_TAG_INT64, i); } 257 | /** retrieve a single precision floating point argument */ 258 | ArgReader &popFloat(float &f) { return popPod(TYPE_TAG_FLOAT, f); } 259 | /** retrieve a double precision floating point argument */ 260 | ArgReader &popDouble(double &d) { return popPod(TYPE_TAG_DOUBLE, d); } 261 | /** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */ 262 | ArgReader &popStr(std::string &s) { 263 | if (precheck(TYPE_TAG_STRING)) { 264 | s = argBeg(arg_idx++); 265 | } 266 | return *this; 267 | } 268 | /** retrieve a binary blob */ 269 | ArgReader &popBlob(std::vector &b) { 270 | if (precheck(TYPE_TAG_BLOB)) { 271 | b.assign(argBeg(arg_idx)+4, argEnd(arg_idx)); 272 | ++arg_idx; 273 | } 274 | return *this; 275 | } 276 | /** retrieve a boolean argument */ 277 | ArgReader &popBool(bool &b) { 278 | b = false; 279 | if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 280 | else if (currentTypeTag() == TYPE_TAG_TRUE) b = true; 281 | else if (currentTypeTag() == TYPE_TAG_FALSE) b = false; 282 | else OSCPKT_SET_ERR(TYPE_MISMATCH); 283 | ++arg_idx; 284 | return *this; 285 | } 286 | /** skip whatever comes next */ 287 | ArgReader &pop() { 288 | if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 289 | else ++arg_idx; 290 | return *this; 291 | } 292 | private: 293 | const char *argBeg(size_t idx) { 294 | if (err || idx >= msg->arguments.size()) return 0; 295 | else return msg->storage.begin() + msg->arguments[idx].first; 296 | } 297 | const char *argEnd(size_t idx) { 298 | if (err || idx >= msg->arguments.size()) return 0; 299 | else return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second; 300 | } 301 | int currentTypeTag() { 302 | if (!err && arg_idx < msg->type_tags.size()) return msg->type_tags[arg_idx]; 303 | else OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 304 | return -1; 305 | } 306 | template ArgReader &popPod(int tag, POD &v) { 307 | if (precheck(tag)) { 308 | v = bytes2pod(argBeg(arg_idx)); 309 | ++arg_idx; 310 | } else v = POD(0); 311 | return *this; 312 | } 313 | /* pre-check stuff before popping an argument from the message */ 314 | bool precheck(int tag) { 315 | if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 316 | else if (!err && currentTypeTag() != tag) OSCPKT_SET_ERR(TYPE_MISMATCH); 317 | return err == OK_NO_ERROR; 318 | } 319 | }; 320 | 321 | Message() { clear(); } 322 | Message(const std::string &s, TimeTag tt = TimeTag::immediate()) : time_tag(tt), address(s), err(OK_NO_ERROR) {} 323 | Message(const void *ptr, size_t sz, TimeTag tt = TimeTag::immediate()) { buildFromRawData(ptr, sz); time_tag = tt; } 324 | 325 | bool isOk() const { return err == OK_NO_ERROR; } 326 | ErrorCode getErr() const { return err; } 327 | 328 | /** return the type_tags string, with its initial ',' stripped. */ 329 | const std::string &typeTags() const { return type_tags; } 330 | /** retrieve the address pattern. If you want to follow to the whole OSC spec, you 331 | have to handle its matching rules for address specifications -- this file does 332 | not provide this functionality */ 333 | const std::string &addressPattern() const { return address; } 334 | TimeTag timeTag() const { return time_tag; } 335 | /** clear the message and start a new message with the supplied address and time_tag. */ 336 | Message &init(const std::string &addr, TimeTag tt = TimeTag::immediate()) { 337 | clear(); 338 | address = addr; time_tag = tt; 339 | if (address.empty() || address[0] != '/') OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); 340 | return *this; 341 | } 342 | 343 | /** start a matching test. The typical use-case is to follow this by 344 | a sequence of calls to popXXX() and a final call to 345 | isOkNoMoreArgs() which will allow to check that everything went 346 | fine. For example: 347 | @code 348 | if (msg.match("/foo").popInt32(i).isOkNoMoreArgs()) { blah(i); } 349 | else if (msg.match("/bar").popStr(s).popInt32(i).isOkNoMoreArgs()) { plop(s,i); } 350 | else cerr << "unhandled message: " << msg << "\n"; 351 | @endcode 352 | */ 353 | ArgReader match(const std::string &test) const { 354 | return ArgReader(*this, fullPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH); 355 | } 356 | /** return true if the 'test' path matched by the first characters of addressPattern(). 357 | For ex. ("/foo/bar").partialMatch("/foo/") is true */ 358 | ArgReader partialMatch(const std::string &test) const { 359 | return ArgReader(*this, partialPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH); 360 | } 361 | ArgReader arg() const { return ArgReader(*this, OK_NO_ERROR); } 362 | 363 | /** build the osc message for raw data (the message will keep a copy of that data) */ 364 | void buildFromRawData(const void *ptr, size_t sz) { 365 | clear(); 366 | storage.assign((const char*)ptr, (const char*)ptr + sz); 367 | const char *address_beg = storage.begin(); 368 | const char *address_end = (const char*)memchr(address_beg, 0, storage.end()-address_beg); 369 | if (!address_end || !isZeroPaddingCorrect(address_end+1) || address_beg[0] != '/') { 370 | OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); return; 371 | } else address.assign(address_beg, address_end); 372 | 373 | const char *type_tags_beg = ceil4(address_end+1); 374 | const char *type_tags_end = (const char*)memchr(type_tags_beg, 0, storage.end()-type_tags_beg); 375 | if (!type_tags_end || !isZeroPaddingCorrect(type_tags_end+1) || type_tags_beg[0] != ',') { 376 | OSCPKT_SET_ERR(MALFORMED_TYPE_TAGS); return; 377 | } else type_tags.assign(type_tags_beg+1, type_tags_end); // we do not copy the initial ',' 378 | 379 | const char *arg = ceil4(type_tags_end+1); assert(arg <= storage.end()); 380 | size_t iarg = 0; 381 | while (isOk() && iarg < type_tags.size()) { 382 | assert(arg <= storage.end()); 383 | size_t len = getArgSize(type_tags[iarg], arg); 384 | if (isOk()) arguments.push_back(std::make_pair(arg - storage.begin(), len)); 385 | arg += ceil4(len); ++iarg; 386 | } 387 | if (iarg < type_tags.size() || arg != storage.end()) { 388 | OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); 389 | } 390 | } 391 | 392 | /* below are all the functions that serve when *writing* a message */ 393 | Message &pushBool(bool b) { 394 | type_tags += (b ? TYPE_TAG_TRUE : TYPE_TAG_FALSE); 395 | arguments.push_back(std::make_pair(storage.size(), storage.size())); 396 | return *this; 397 | } 398 | Message &pushInt32(int32_t i) { return pushPod(TYPE_TAG_INT32, i); } 399 | Message &pushInt64(int64_t h) { return pushPod(TYPE_TAG_INT64, h); } 400 | Message &pushFloat(float f) { return pushPod(TYPE_TAG_FLOAT, f); } 401 | Message &pushDouble(double d) { return pushPod(TYPE_TAG_DOUBLE, d); } 402 | Message &pushStr(const std::string &s) { 403 | assert(s.size() < 2147483647); // insane values are not welcome 404 | type_tags += TYPE_TAG_STRING; 405 | arguments.push_back(std::make_pair(storage.size(), s.size() + 1)); 406 | strcpy(storage.getBytes(s.size()+1), s.c_str()); 407 | return *this; 408 | } 409 | Message &pushBlob(void *ptr, size_t num_bytes) { 410 | assert(num_bytes < 2147483647); // insane values are not welcome 411 | type_tags += TYPE_TAG_BLOB; 412 | arguments.push_back(std::make_pair(storage.size(), num_bytes+4)); 413 | pod2bytes((int32_t)num_bytes, storage.getBytes(4)); 414 | if (num_bytes) 415 | memcpy(storage.getBytes(num_bytes), ptr, num_bytes); 416 | return *this; 417 | } 418 | 419 | /** reset the message to a clean state */ 420 | void clear() { 421 | address.clear(); type_tags.clear(); storage.clear(); arguments.clear(); 422 | err = OK_NO_ERROR; time_tag = TimeTag::immediate(); 423 | } 424 | 425 | /** write the raw message data (used by PacketWriter) */ 426 | void packMessage(Storage &s, bool write_size) const { 427 | if (!isOk()) return; 428 | size_t l_addr = address.size()+1, l_type = type_tags.size()+2; 429 | if (write_size) 430 | pod2bytes(uint32_t(ceil4(l_addr) + ceil4(l_type) + ceil4(storage.size())), s.getBytes(4)); 431 | strcpy(s.getBytes(l_addr), address.c_str()); 432 | strcpy(s.getBytes(l_type), ("," + type_tags).c_str()); 433 | if (storage.size()) 434 | memcpy(s.getBytes(storage.size()), const_cast(storage).begin(), storage.size()); 435 | } 436 | 437 | private: 438 | 439 | /* get the number of bytes occupied by the argument */ 440 | size_t getArgSize(int type, const char *p) { 441 | if (err) return 0; 442 | size_t sz = 0; 443 | assert(p >= storage.begin() && p <= storage.end()); 444 | switch (type) { 445 | case TYPE_TAG_TRUE: 446 | case TYPE_TAG_FALSE: sz = 0; break; 447 | case TYPE_TAG_INT32: 448 | case TYPE_TAG_FLOAT: sz = 4; break; 449 | case TYPE_TAG_INT64: 450 | case TYPE_TAG_DOUBLE: sz = 8; break; 451 | case TYPE_TAG_STRING: { 452 | const char *q = (const char*)memchr(p, 0, storage.end()-p); 453 | if (!q) OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); 454 | else sz = (q-p)+1; 455 | } break; 456 | case TYPE_TAG_BLOB: { 457 | if (p == storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; } 458 | sz = 4+bytes2pod(p); 459 | } break; 460 | default: { 461 | OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS); return 0; 462 | } break; 463 | } 464 | if (p+sz > storage.end() || /* string or blob too large.. */ 465 | p+sz < p /* or even blob so large that it did overflow */) { 466 | OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; 467 | } 468 | if (!isZeroPaddingCorrect(p+sz)) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; } 469 | return sz; 470 | } 471 | 472 | template Message &pushPod(int tag, POD v) { 473 | type_tags += (char)tag; 474 | arguments.push_back(std::make_pair(storage.size(), sizeof(POD))); 475 | pod2bytes(v, storage.getBytes(sizeof(POD))); 476 | return *this; 477 | } 478 | 479 | #ifdef OSCPKT_OSTREAM_OUTPUT 480 | friend std::ostream &operator<<(std::ostream &os, const Message &msg) { 481 | os << "osc_address: '" << msg.address << "', types: '" << msg.type_tags << "', timetag=" << msg.time_tag << ", args=["; 482 | Message::ArgReader arg(msg); 483 | while (arg.nbArgRemaining() && arg.isOk()) { 484 | if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); } 485 | else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; } 486 | else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; } 487 | else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; } 488 | else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; } 489 | else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; } 490 | else if (arg.isBlob()) { std::vector b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; } 491 | else { 492 | assert(0); // I forgot a case.. 493 | } 494 | if (arg.nbArgRemaining()) os << ", "; 495 | } 496 | if (!arg.isOk()) { os << " ERROR#" << arg.getErr(); } 497 | os << "]"; 498 | return os; 499 | } 500 | #endif 501 | }; 502 | 503 | /** 504 | parse an OSC packet and extracts the embedded OSC messages. 505 | */ 506 | class PacketReader { 507 | public: 508 | PacketReader() { err = OK_NO_ERROR; } 509 | /** pointer and size of the osc packet to be parsed. */ 510 | PacketReader(const void *ptr, size_t sz) { init(ptr, sz); } 511 | 512 | void init(const void *ptr, size_t sz) { 513 | err = OK_NO_ERROR; messages.clear(); 514 | if ((sz%4) == 0) { 515 | parse((const char*)ptr, (const char *)ptr+sz, TimeTag::immediate()); 516 | } else OSCPKT_SET_ERR(INVALID_PACKET_SIZE); 517 | it_messages = messages.begin(); 518 | } 519 | 520 | /** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */ 521 | Message *popMessage() { 522 | if (!err && !messages.empty() && it_messages != messages.end()) return &*it_messages++; 523 | else return 0; 524 | } 525 | bool isOk() const { return err == OK_NO_ERROR; } 526 | ErrorCode getErr() const { return err; } 527 | 528 | private: 529 | std::list messages; 530 | std::list::iterator it_messages; 531 | ErrorCode err; 532 | 533 | void parse(const char *beg, const char *end, TimeTag time_tag) { 534 | assert(beg <= end && !err); assert(((end-beg)%4)==0); 535 | 536 | if (beg == end) return; 537 | if (*beg == '#') { 538 | /* it's a bundle */ 539 | if (end - beg >= 20 540 | && memcmp(beg, "#bundle\0", 8) == 0) { 541 | TimeTag time_tag2(bytes2pod(beg+8)); 542 | const char *pos = beg + 16; 543 | do { 544 | uint32_t sz = bytes2pod(pos); pos += 4; 545 | if ((sz&3) != 0 || pos + sz > end || pos+sz < pos) { 546 | OSCPKT_SET_ERR(INVALID_BUNDLE); 547 | } else { 548 | parse(pos, pos+sz, time_tag2); 549 | pos += sz; 550 | } 551 | } while (!err && pos != end); 552 | } else { 553 | OSCPKT_SET_ERR(INVALID_BUNDLE); 554 | } 555 | } else { 556 | messages.push_back(Message(beg, end-beg, time_tag)); 557 | if (!messages.back().isOk()) OSCPKT_SET_ERR(messages.back().getErr()); 558 | } 559 | } 560 | }; 561 | 562 | 563 | /** 564 | Assemble messages into an OSC packet. Example of use: 565 | @code 566 | PacketWriter pkt; 567 | Message msg; 568 | pkt.startBundle(); 569 | pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f)); 570 | pkt.addMessage(msg.init("/bar").pushBool(false)); 571 | pkt.endBundle(); 572 | if (pkt.isOk()) { 573 | send(pkt.data(), pkt.size()); 574 | } 575 | @endcode 576 | */ 577 | class PacketWriter { 578 | public: 579 | PacketWriter() { init(); } 580 | PacketWriter &init() { err = OK_NO_ERROR; storage.clear(); bundles.clear(); return *this; } 581 | 582 | /** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to 583 | put them in a bundle. Nested bundles inside bundles are also allowed. */ 584 | PacketWriter &startBundle(TimeTag ts = TimeTag::immediate()) { 585 | char *p; 586 | if (bundles.size()) storage.getBytes(4); // hold the bundle size 587 | p = storage.getBytes(8); strcpy(p, "#bundle"); bundles.push_back(p - storage.begin()); 588 | p = storage.getBytes(8); pod2bytes(ts, p); 589 | return *this; 590 | } 591 | /** close the current bundle. */ 592 | PacketWriter &endBundle() { 593 | if (bundles.size()) { 594 | if (storage.size() - bundles.back() == 16) { 595 | pod2bytes(0, storage.getBytes(4)); // the 'empty bundle' case, not very elegant 596 | } 597 | if (bundles.size()>1) { // no size stored for the top-level bundle 598 | pod2bytes(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back()-4); 599 | } 600 | bundles.pop_back(); 601 | } else OSCPKT_SET_ERR(INVALID_BUNDLE); 602 | return *this; 603 | } 604 | 605 | /** insert an Osc message into the current bundle / packet. 606 | */ 607 | PacketWriter &addMessage(const Message &msg) { 608 | if (storage.size() != 0 && bundles.empty()) OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES); 609 | else msg.packMessage(storage, bundles.size()>0); 610 | if (!msg.isOk()) OSCPKT_SET_ERR(msg.getErr()); 611 | return *this; 612 | } 613 | 614 | /** the error flag will be raised if an opened bundle is not closed, or if more than one message is 615 | inserted in the packet without a bundle */ 616 | bool isOk() { return err == OK_NO_ERROR; } 617 | ErrorCode getErr() { return err; } 618 | 619 | /** return the number of bytes of the osc packet -- will always be a 620 | multiple of 4 -- returns 0 if the construction of the packet has 621 | failed. */ 622 | uint32_t packetSize() { return err ? 0 : (uint32_t)storage.size(); } 623 | 624 | /** return the bytes of the osc packet (NULL if the construction of the packet has failed) */ 625 | char *packetData() { return err ? 0 : storage.begin(); } 626 | private: 627 | std::vector bundles; // hold the position in the storage array of the beginning marker of each bundle 628 | Storage storage; 629 | ErrorCode err; 630 | }; 631 | 632 | // see the OSC spec for the precise pattern matching rules 633 | inline const char *internalPatternMatch(const char *pattern, const char *path) { 634 | while (*pattern) { 635 | const char *p = pattern; 636 | if (*p == '?' && *path) { ++p; ++path; } 637 | else if (*p == '[' && *path) { // bracketted range, e.g. [a-zABC] 638 | ++p; 639 | bool reverse = false; 640 | if (*p == '!') { reverse = true; ++p; } 641 | bool match = reverse; 642 | for (; *p && *p != ']'; ++p) { 643 | char c0 = *p, c1 = c0; 644 | if (p[1] == '-' && p[2] && p[2] != ']') { p += 2; c1 = *p; } 645 | if (*path >= c0 && *path <= c1) { match = !reverse; } 646 | } 647 | if (!match || *p != ']') return pattern; 648 | ++p; ++path; 649 | } else if (*p == '*') { // wildcard '*' 650 | while (*p == '*') ++p; 651 | const char *best = 0; 652 | while (true) { 653 | const char *ret = internalPatternMatch(p, path); 654 | if (ret && ret > best) best = ret; 655 | if (*path == 0 || *path == '/') break; 656 | else ++path; 657 | } 658 | return best; 659 | } else if (*p == '/' && *(p+1) == '/') { // the super-wildcard '//' 660 | while (*(p+1)=='/') ++p; 661 | const char *best = 0; 662 | while (true) { 663 | const char *ret = internalPatternMatch(p, path); 664 | if (ret && ret > best) best = ret; 665 | if (*path == 0) break; 666 | if (*path == 0 || (path = strchr(path+1, '/')) == 0) break; 667 | } 668 | return best; 669 | } else if (*p == '{') { // braced list {foo,bar,baz} 670 | const char *end = strchr(p, '}'), *q; 671 | if (!end) return 0; // syntax error in brace list.. 672 | bool match = false; 673 | do { 674 | ++p; 675 | q = strchr(p, ','); 676 | if (q == 0 || q > end) q = end; 677 | if (strncmp(p, path, q-p)==0) { 678 | path += (q-p); p = end+1; match = true; 679 | } else p=q; 680 | } while (q != end && !match); 681 | if (!match) return pattern; 682 | } else if (*p == *path) { ++p; ++path; } // any other character 683 | else break; 684 | pattern = p; 685 | } 686 | return (*path == 0 ? pattern : 0); 687 | } 688 | 689 | inline bool partialPatternMatch(const std::string &pattern, const std::string &test) { 690 | const char *q = internalPatternMatch(pattern.c_str(), test.c_str()); 691 | return q != 0; 692 | } 693 | 694 | inline bool fullPatternMatch(const std::string &pattern, const std::string &test) { 695 | const char *q = internalPatternMatch(pattern.c_str(), test.c_str()); 696 | return q && *q == 0; 697 | } 698 | 699 | } // namespace oscpkt 700 | 701 | #endif // OSCPKT_HH 702 | --------------------------------------------------------------------------------