git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
44
deps/juce/modules/juce_osc/juce_osc.cpp
vendored
Normal file
44
deps/juce/modules/juce_osc/juce_osc.cpp
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_OSC_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#include "juce_osc.h"
|
||||
|
||||
#include "osc/juce_OSCTypes.cpp"
|
||||
#include "osc/juce_OSCTimeTag.cpp"
|
||||
#include "osc/juce_OSCArgument.cpp"
|
||||
#include "osc/juce_OSCAddress.cpp"
|
||||
#include "osc/juce_OSCMessage.cpp"
|
||||
#include "osc/juce_OSCBundle.cpp"
|
||||
#include "osc/juce_OSCReceiver.cpp"
|
||||
#include "osc/juce_OSCSender.cpp"
|
66
deps/juce/modules/juce_osc/juce_osc.h
vendored
Normal file
66
deps/juce/modules/juce_osc/juce_osc.h
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_osc
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE OSC classes
|
||||
description: Open Sound Control implementation.
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_events
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_OSC_H_INCLUDED
|
||||
|
||||
#include <juce_core/juce_core.h>
|
||||
#include <juce_events/juce_events.h>
|
||||
|
||||
//==============================================================================
|
||||
#include "osc/juce_OSCTypes.h"
|
||||
#include "osc/juce_OSCTimeTag.h"
|
||||
#include "osc/juce_OSCArgument.h"
|
||||
#include "osc/juce_OSCAddress.h"
|
||||
#include "osc/juce_OSCMessage.h"
|
||||
#include "osc/juce_OSCBundle.h"
|
||||
#include "osc/juce_OSCReceiver.h"
|
||||
#include "osc/juce_OSCSender.h"
|
792
deps/juce/modules/juce_osc/osc/juce_OSCAddress.cpp
vendored
Normal file
792
deps/juce/modules/juce_osc/osc/juce_OSCAddress.cpp
vendored
Normal file
@ -0,0 +1,792 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
//==============================================================================
|
||||
template <typename CharPointerType>
|
||||
class OSCPatternMatcherImpl
|
||||
{
|
||||
using CharPtr = CharPointerType;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
static bool match (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (pattern == patternEnd)
|
||||
return matchTerminator (target, targetEnd);
|
||||
|
||||
auto c = pattern.getAndAdvance();
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?': return matchAnyChar (pattern, patternEnd, target, targetEnd);
|
||||
case '*': return matchAnyOrNoChars (pattern, patternEnd, target, targetEnd);
|
||||
case '{': return matchInsideStringSet (pattern, patternEnd, target, targetEnd);
|
||||
case '[': return matchInsideCharSet (pattern, patternEnd, target, targetEnd);
|
||||
default: return matchChar (c, pattern, patternEnd, target, targetEnd);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static bool matchTerminator (CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
return target == targetEnd;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchChar (juce_wchar c, CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (target == targetEnd || c != target.getAndAdvance())
|
||||
return false;
|
||||
|
||||
return match (pattern, patternEnd, target, targetEnd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchAnyChar (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (target == targetEnd)
|
||||
return false;
|
||||
|
||||
return match (pattern, patternEnd, ++target, targetEnd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchAnyOrNoChars (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (target == targetEnd)
|
||||
return pattern == patternEnd;
|
||||
|
||||
if (match (pattern, patternEnd, target, targetEnd))
|
||||
return true;
|
||||
|
||||
return matchAnyOrNoChars (pattern, patternEnd, ++target, targetEnd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchInsideStringSet (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (pattern == patternEnd)
|
||||
return false;
|
||||
|
||||
// Note: In case this code is ever moved into the more generic CharPointerFunctions,
|
||||
// the next two lines probably will not compile as soon as this class is used with a
|
||||
// Char template type parameter that is not the same type as String::Char.
|
||||
StringArray set;
|
||||
String currentElement;
|
||||
|
||||
while (pattern != patternEnd)
|
||||
{
|
||||
auto c = pattern.getAndAdvance();
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '}':
|
||||
set.add (currentElement);
|
||||
currentElement.clear();
|
||||
return matchStringSet (set, pattern, patternEnd, target, targetEnd);
|
||||
|
||||
case ',':
|
||||
set.add (currentElement);
|
||||
currentElement.clear();
|
||||
continue;
|
||||
|
||||
default:
|
||||
currentElement += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchStringSet (const StringArray& set, CharPtr pattern,
|
||||
CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (set.size() == 0)
|
||||
return match (pattern, patternEnd, target, targetEnd);
|
||||
|
||||
for (auto& str : set)
|
||||
if (str.getCharPointer().compareUpTo (target, str.length()) == 0)
|
||||
if (match (pattern, patternEnd, target + str.length(), targetEnd))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchInsideCharSet (CharPtr pattern, CharPtr patternEnd,
|
||||
CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (pattern == patternEnd)
|
||||
return false;
|
||||
|
||||
Array<juce_wchar> set;
|
||||
bool setIsNegated = false;
|
||||
|
||||
while (pattern != patternEnd)
|
||||
{
|
||||
auto c = pattern.getAndAdvance();
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case ']':
|
||||
return matchCharSet (set, setIsNegated, pattern, patternEnd, target, targetEnd);
|
||||
|
||||
case '-':
|
||||
if (! addCharRangeToSet (set, pattern, patternEnd, target, targetEnd))
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
case '!':
|
||||
if (set.size() == 0 && setIsNegated == false)
|
||||
{
|
||||
setIsNegated = true;
|
||||
break;
|
||||
}
|
||||
// else = special case: fall through to default and treat '!' as a non-special character.
|
||||
JUCE_FALLTHROUGH
|
||||
|
||||
default:
|
||||
set.add (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchCharSet (const Array<juce_wchar>& set, bool setIsNegated,
|
||||
CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (set.size() == 0)
|
||||
return match (pattern, patternEnd, target, targetEnd);
|
||||
|
||||
if (target == targetEnd)
|
||||
return false;
|
||||
|
||||
return setIsNegated ? matchCharSetNegated (set, pattern, patternEnd, target, targetEnd)
|
||||
: matchCharSetNotNegated (set, pattern, patternEnd, target, targetEnd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchCharSetNegated (const Array<juce_wchar>& set, CharPtr pattern,
|
||||
CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
for (auto c : set)
|
||||
if (*target == c)
|
||||
return false;
|
||||
|
||||
return match (pattern, patternEnd, target + 1, targetEnd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool matchCharSetNotNegated (const Array<juce_wchar>& set, CharPtr pattern,
|
||||
CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
for (auto c : set)
|
||||
if (*target == c)
|
||||
if (match (pattern, patternEnd, target + 1, targetEnd))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool addCharRangeToSet (Array<juce_wchar>& set, CharPtr pattern,
|
||||
CharPtr /*patternEnd*/, CharPtr target, CharPtr targetEnd)
|
||||
{
|
||||
if (target == targetEnd)
|
||||
return false;
|
||||
|
||||
auto rangeStart = set.getLast();
|
||||
auto rangeEnd = pattern.getAndAdvance();
|
||||
|
||||
if (rangeEnd == ']')
|
||||
{
|
||||
set.add ('-'); // special case: '-' has no special meaning at the end.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rangeEnd == ',' || rangeEnd == '{' || rangeEnd == '}' || set.size() == 0)
|
||||
return false;
|
||||
|
||||
while (rangeEnd > rangeStart)
|
||||
set.add (++rangeStart);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static bool matchOscPattern (const String& pattern, const String& target)
|
||||
{
|
||||
return OSCPatternMatcherImpl<String::CharPointerType>::match (pattern.getCharPointer(),
|
||||
pattern.getCharPointer().findTerminatingNull(),
|
||||
target.getCharPointer(),
|
||||
target.getCharPointer().findTerminatingNull());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename OSCAddressType> struct OSCAddressTokeniserTraits;
|
||||
template <> struct OSCAddressTokeniserTraits<OSCAddress> { static const char* getDisallowedChars() { return " #*,?/[]{}"; } };
|
||||
template <> struct OSCAddressTokeniserTraits<OSCAddressPattern> { static const char* getDisallowedChars() { return " #/"; } };
|
||||
|
||||
//==============================================================================
|
||||
template <typename OSCAddressType>
|
||||
struct OSCAddressTokeniser
|
||||
{
|
||||
using Traits = OSCAddressTokeniserTraits<OSCAddressType>;
|
||||
|
||||
//==============================================================================
|
||||
static bool isPrintableASCIIChar (juce_wchar c) noexcept
|
||||
{
|
||||
return c >= ' ' && c <= '~';
|
||||
}
|
||||
|
||||
static bool isDisallowedChar (juce_wchar c) noexcept
|
||||
{
|
||||
return CharPointer_ASCII (Traits::getDisallowedChars()).indexOf (c, false) >= 0;
|
||||
}
|
||||
|
||||
static bool containsOnlyAllowedPrintableASCIIChars (const String& string) noexcept
|
||||
{
|
||||
for (auto charPtr = string.getCharPointer(); ! charPtr.isEmpty();)
|
||||
{
|
||||
auto c = charPtr.getAndAdvance();
|
||||
|
||||
if (! isPrintableASCIIChar (c) || isDisallowedChar (c))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static StringArray tokenise (const String& address)
|
||||
{
|
||||
if (address.isEmpty())
|
||||
throw OSCFormatError ("OSC format error: address string cannot be empty.");
|
||||
|
||||
if (! address.startsWithChar ('/'))
|
||||
throw OSCFormatError ("OSC format error: address string must start with a forward slash.");
|
||||
|
||||
StringArray oscSymbols;
|
||||
oscSymbols.addTokens (address, "/", StringRef());
|
||||
oscSymbols.removeEmptyStrings (false);
|
||||
|
||||
for (auto& token : oscSymbols)
|
||||
if (! containsOnlyAllowedPrintableASCIIChars (token))
|
||||
throw OSCFormatError ("OSC format error: encountered characters not allowed in address string.");
|
||||
|
||||
return oscSymbols;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
//==============================================================================
|
||||
OSCAddress::OSCAddress (const String& address)
|
||||
: oscSymbols (OSCAddressTokeniser<OSCAddress>::tokenise (address)),
|
||||
asString (address.trimCharactersAtEnd ("/"))
|
||||
{
|
||||
}
|
||||
|
||||
OSCAddress::OSCAddress (const char* address)
|
||||
: oscSymbols (OSCAddressTokeniser<OSCAddress>::tokenise (String (address))),
|
||||
asString (String (address).trimCharactersAtEnd ("/"))
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCAddress::operator== (const OSCAddress& other) const noexcept
|
||||
{
|
||||
return asString == other.asString;
|
||||
}
|
||||
|
||||
bool OSCAddress::operator!= (const OSCAddress& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String OSCAddress::toString() const noexcept
|
||||
{
|
||||
return asString;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
OSCAddressPattern::OSCAddressPattern (const String& address)
|
||||
: oscSymbols (OSCAddressTokeniser<OSCAddressPattern>::tokenise (address)),
|
||||
asString (address.trimCharactersAtEnd ("/")),
|
||||
wasInitialisedWithWildcards (asString.containsAnyOf ("*?{}[]"))
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
OSCAddressPattern::OSCAddressPattern (const char* address)
|
||||
: oscSymbols (OSCAddressTokeniser<OSCAddressPattern>::tokenise (String (address))),
|
||||
asString (String (address).trimCharactersAtEnd ("/")),
|
||||
wasInitialisedWithWildcards (asString.containsAnyOf ("*?{}[]"))
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCAddressPattern::operator== (const OSCAddressPattern& other) const noexcept
|
||||
{
|
||||
return asString == other.asString;
|
||||
}
|
||||
|
||||
bool OSCAddressPattern::operator!= (const OSCAddressPattern& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCAddressPattern::matches (const OSCAddress& address) const noexcept
|
||||
{
|
||||
if (! containsWildcards())
|
||||
return asString == address.asString;
|
||||
|
||||
if (oscSymbols.size() != address.oscSymbols.size())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < oscSymbols.size(); ++i)
|
||||
if (! matchOscPattern (oscSymbols[i], address.oscSymbols[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String OSCAddressPattern::toString() const noexcept
|
||||
{
|
||||
return asString;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class OSCAddressTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCAddressTests()
|
||||
: UnitTest ("OSCAddress class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("construction and parsing");
|
||||
{
|
||||
expectThrowsType (OSCAddress (""), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("noleadingslash"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar "), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar#"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar*"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar,"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar?"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar["), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar]"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar{"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/notallowedchar}andsomemorechars"), OSCFormatError);
|
||||
expectThrowsType (OSCAddress (String::fromUTF8 ("/nonasciicharacter\xc3\xbc""blabla")), OSCFormatError);
|
||||
expectThrowsType (OSCAddress ("/nonprintableasciicharacter\t"), OSCFormatError);
|
||||
|
||||
expectDoesNotThrow (OSCAddress ("/"));
|
||||
expectDoesNotThrow (OSCAddress ("/a"));
|
||||
expectDoesNotThrow (OSCAddress ("/a/"));
|
||||
expectDoesNotThrow (OSCAddress ("/a/bcd/"));
|
||||
expectDoesNotThrow (OSCAddress ("/abcd/efgh/ijkLMNOPq/666r/s"));
|
||||
expectDoesNotThrow (OSCAddress ("/allowedprintablecharacters!$%&()+-.^_`|~"));
|
||||
expectDoesNotThrow (OSCAddress ("/additonalslashes//will///be////ignored"));
|
||||
}
|
||||
|
||||
beginTest ("conversion to/from String");
|
||||
{
|
||||
OSCAddress address ("/this/is/a/very/long/address/");
|
||||
expectEquals (address.toString(), String ("/this/is/a/very/long/address"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCAddressTests OSCAddressUnitTests;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class OSCAddressPatternTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCAddressPatternTests()
|
||||
: UnitTest ("OSCAddressPattern class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("construction and parsing");
|
||||
{
|
||||
expectThrowsType (OSCAddressPattern (""), OSCFormatError);
|
||||
expectThrowsType (OSCAddressPattern ("noleadingslash"), OSCFormatError);
|
||||
expectThrowsType (OSCAddressPattern ("/notallowedchar "), OSCFormatError);
|
||||
expectThrowsType (OSCAddressPattern ("/notallowedchar#andsomemorechars"), OSCFormatError);
|
||||
expectThrowsType (OSCAddressPattern (String::fromUTF8 ("/nonasciicharacter\xc3\xbc""blabla")), OSCFormatError);
|
||||
expectThrowsType (OSCAddressPattern ("/nonprintableasciicharacter\t"), OSCFormatError);
|
||||
|
||||
expectDoesNotThrow (OSCAddressPattern ("/"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/a"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/a/"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/a/bcd/"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/abcd/efgh/ijkLMNOPq/666r/s"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/allowedprintablecharacters!$%&()+-.:;<=>@^_`|~"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/additonalslashes//will///be////ignored"));
|
||||
}
|
||||
|
||||
beginTest ("construction and parsing - with wildcards");
|
||||
{
|
||||
expectDoesNotThrow (OSCAddressPattern ("/foo/b?r/"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/?????"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/foo/b*r"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/**"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/?/b/*c"));
|
||||
}
|
||||
|
||||
beginTest ("construction and parsing - with match expressions");
|
||||
{
|
||||
expectDoesNotThrow (OSCAddressPattern ("/{}"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/{foo}"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/{foo,bar,baz}"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/[]"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/[abcde]"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/[a-e]"));
|
||||
expectDoesNotThrow (OSCAddressPattern ("/foo/[a-z]x{foo,bar}/*BAZ42/"));
|
||||
|
||||
/* Note: If malformed expressions are used, e.g. "bracenotclosed{" or "{a-e}" or "[-foo]",
|
||||
this should not throw at construction time. Instead it should simply fail any pattern match later.
|
||||
So there is no need to test for those.
|
||||
The reason is that we do not actually parse the expressions now, but only during matching.
|
||||
*/
|
||||
}
|
||||
|
||||
beginTest ("equality comparison");
|
||||
{
|
||||
{
|
||||
OSCAddressPattern lhs ("/test/1");
|
||||
OSCAddressPattern rhs ("/test/1");
|
||||
expect (lhs == rhs);
|
||||
expect (! (lhs != rhs));
|
||||
}
|
||||
{
|
||||
OSCAddressPattern lhs ("/test/1");
|
||||
OSCAddressPattern rhs ("/test/1/");
|
||||
expect (lhs == rhs);
|
||||
expect (! (lhs != rhs));
|
||||
}
|
||||
{
|
||||
OSCAddressPattern lhs ("/test/1");
|
||||
OSCAddressPattern rhs ("/test/2");
|
||||
expect (! (lhs == rhs));
|
||||
expect (lhs != rhs);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("basic string matching");
|
||||
{
|
||||
/* Note: The actual expression matching is tested in OSCPatternMatcher, so here we just
|
||||
do some basic tests and check if the matching works with multi-part addresses.
|
||||
*/
|
||||
{
|
||||
OSCAddressPattern pattern ("/foo/bar");
|
||||
expect (! pattern.containsWildcards());
|
||||
|
||||
OSCAddress address ("/foo/bar");
|
||||
expect (pattern.matches (address));
|
||||
}
|
||||
{
|
||||
OSCAddressPattern pattern ("/foo/bar/");
|
||||
expect (! pattern.containsWildcards());
|
||||
|
||||
OSCAddress address ("/foo/bar");
|
||||
expect (pattern.matches (address));
|
||||
}
|
||||
{
|
||||
OSCAddressPattern pattern ("/");
|
||||
expect (! pattern.containsWildcards());
|
||||
|
||||
OSCAddress address ("/");
|
||||
expect (pattern.matches (address));
|
||||
}
|
||||
{
|
||||
OSCAddressPattern pattern ("/foo/bar");
|
||||
expect (! pattern.containsWildcards());
|
||||
|
||||
expect (! pattern.matches (OSCAddress ("/foo/baz")));
|
||||
expect (! pattern.matches (OSCAddress ("/foo/bar/baz")));
|
||||
expect (! pattern.matches (OSCAddress ("/foo")));
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("string matching with wildcards");
|
||||
{
|
||||
OSCAddressPattern pattern ("/*/*put/slider[0-9]");
|
||||
expect (pattern.containsWildcards());
|
||||
|
||||
expect (pattern.matches (OSCAddress ("/mypatch/input/slider0")));
|
||||
expect (pattern.matches (OSCAddress ("/myotherpatch/output/slider9")));
|
||||
expect (! pattern.matches (OSCAddress ("/myotherpatch/output/slider10")));
|
||||
expect (! pattern.matches (OSCAddress ("/output/slider9")));
|
||||
expect (! pattern.matches (OSCAddress ("/myotherpatch/output/slider9/position")));
|
||||
}
|
||||
|
||||
beginTest ("conversion to/from String");
|
||||
{
|
||||
{
|
||||
OSCAddressPattern ap ("/this/is/a/very/long/address/");
|
||||
expectEquals (ap.toString(), String ("/this/is/a/very/long/address"));
|
||||
}
|
||||
{
|
||||
OSCAddressPattern ap ("/*/*put/{fader,slider,knob}[0-9]/ba?/");
|
||||
expectEquals (ap.toString(), String ("/*/*put/{fader,slider,knob}[0-9]/ba?"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCAddressPatternTests OSCAddressPatternUnitTests;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class OSCPatternMatcherTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCPatternMatcherTests()
|
||||
: UnitTest ("OSCAddress class / pattern matching", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("basic string matching");
|
||||
{
|
||||
expect (matchOscPattern ("", ""));
|
||||
expect (! matchOscPattern ("", "x"));
|
||||
expect (! matchOscPattern ("x", ""));
|
||||
expect (matchOscPattern ("foo", "foo"));
|
||||
expect (! matchOscPattern ("foo", "bar"));
|
||||
expect (! matchOscPattern ("ooooo", "oooooo"));
|
||||
}
|
||||
|
||||
beginTest ("string matching with '?' wildcard");
|
||||
{
|
||||
expect (matchOscPattern ("?", "x"));
|
||||
expect (! matchOscPattern ("?", ""));
|
||||
expect (! matchOscPattern ("?", "xx"));
|
||||
expect (! matchOscPattern ("b?r", "br"));
|
||||
expect (matchOscPattern ("b?r", "bar"));
|
||||
expect (! matchOscPattern ("b?r", "baar"));
|
||||
expect (matchOscPattern ("f???o", "fabco"));
|
||||
expect (! matchOscPattern ("f???o", "fabo"));
|
||||
}
|
||||
|
||||
beginTest ("string matching with '*' wildcard");
|
||||
{
|
||||
expect (matchOscPattern ("*", ""));
|
||||
expect (matchOscPattern ("*", "x"));
|
||||
expect (matchOscPattern ("*", "foo"));
|
||||
expect (matchOscPattern ("*c", "aaaaaaabc"));
|
||||
expect (matchOscPattern ("*c", "aaaaaaabbbcccc"));
|
||||
expect (! matchOscPattern ("*c", "aaaaaaabbbcccca"));
|
||||
expect (matchOscPattern ("c*", "ccccbbbbaaaa"));
|
||||
expect (! matchOscPattern ("c*", "accccbbbaaaa"));
|
||||
|
||||
expect (matchOscPattern ("f*o", "fo"));
|
||||
expect (matchOscPattern ("f*o", "fuo"));
|
||||
expect (matchOscPattern ("f*o", "fuvwxyzo"));
|
||||
|
||||
expect (matchOscPattern ("*reallyreallylongstringstringstring", "reallyreallylongstringstringstrNOT"
|
||||
"reallyreallylongstringstringstrNOT"
|
||||
"reallyreallylongstringstringstrNOT"
|
||||
"reallyreallylongstringstringstrNOT"
|
||||
"reallyreallylongstringstringstrNOT"
|
||||
"reallyreallylongstringstringstring"));
|
||||
}
|
||||
|
||||
beginTest ("string matching with '{..., ...}' pattern");
|
||||
{
|
||||
expect (matchOscPattern ("{}", ""));
|
||||
expect (! matchOscPattern ("{}", "x"));
|
||||
expect (matchOscPattern ("{abcde}", "abcde"));
|
||||
expect (matchOscPattern ("{abcde,f}", "f"));
|
||||
expect (! matchOscPattern ("{abcde,f}", "ff"));
|
||||
expect (matchOscPattern ("a{bcd}e", "abcde"));
|
||||
expect (matchOscPattern ("a{bcd,bce}e", "abcde"));
|
||||
expect (! matchOscPattern ("a{bce,bcf}e", "abcde"));
|
||||
expect (! matchOscPattern ("a{bce,bcf}e", "ae"));
|
||||
expect (matchOscPattern ("a{bce,,bcf}e", "ae"));
|
||||
expect (matchOscPattern ("a{bcd,bcd,bcd}e", "abcde"));
|
||||
expect (matchOscPattern ("aaa{bc,def,ghij,klmnopqrstu}eee", "aaaghijeee"));
|
||||
expect (matchOscPattern ("{a,b,c}bcde", "abcde"));
|
||||
expect (! matchOscPattern ("{abc}bcde", "abcde"));
|
||||
expect (matchOscPattern ("bcde{a,b,c}", "bcdea"));
|
||||
expect (! matchOscPattern ("bcde{abc}", "bcdea"));
|
||||
expect (matchOscPattern ("f{o,}o", "fo"));
|
||||
expect (matchOscPattern ("f{,,,,,}o", "fo"));
|
||||
expect (matchOscPattern ("foo{b,ba,bar}x", "foobarx"));
|
||||
expect (matchOscPattern ("a{bc,de}fg{hij,klm}{n}{}", "adefghijn"));
|
||||
|
||||
// should fail gracefully in case of wrong syntax:
|
||||
expect (! matchOscPattern ("not{closing", "notclosing"));
|
||||
expect (! matchOscPattern ("not}opening", "notopening"));
|
||||
expect (! matchOscPattern ("{{nested}}", "nested"));
|
||||
expect (! matchOscPattern ("{a-c}bcde", "abcde"));
|
||||
expect (! matchOscPattern ("bcde{a-c}", "abcde"));
|
||||
}
|
||||
|
||||
|
||||
beginTest ("string matching with '[...]' pattern");
|
||||
{
|
||||
// using [] for a set of chars:
|
||||
|
||||
expect (matchOscPattern ("[]", ""));
|
||||
expect (! matchOscPattern ("[]", "x"));
|
||||
expect (! matchOscPattern ("[abcde]", "abcde"));
|
||||
expect (matchOscPattern ("[abcde]", "a"));
|
||||
expect (matchOscPattern ("[abcde]", "b"));
|
||||
expect (matchOscPattern ("[abcde]", "c"));
|
||||
expect (matchOscPattern ("[abcde]", "d"));
|
||||
expect (matchOscPattern ("[abcde]", "e"));
|
||||
expect (! matchOscPattern ("[abcde]", "f"));
|
||||
|
||||
expect (matchOscPattern ("f[oo]", "fo"));
|
||||
expect (! matchOscPattern ("f[oo]", "foo"));
|
||||
|
||||
expect (matchOscPattern ("fooba[rxz]foo", "foobarfoo"));
|
||||
expect (matchOscPattern ("fooba[rxz]foo", "foobaxfoo"));
|
||||
expect (matchOscPattern ("fooba[rxz]foo", "foobazfoo"));
|
||||
expect (! matchOscPattern ("fooba[rxz]foo", "foobasfoo"));
|
||||
|
||||
expect (matchOscPattern ("foo[abc]foo[defgh]foo[i]foo[]foo", "foobfoohfooifoofoo"));
|
||||
|
||||
// using [] for a range of chars:
|
||||
|
||||
expect (matchOscPattern ("fooba[r-z]foo", "foobarfoo"));
|
||||
expect (matchOscPattern ("fooba[r-z]foo", "foobaxfoo"));
|
||||
expect (matchOscPattern ("fooba[r-z]foo", "foobazfoo"));
|
||||
expect (matchOscPattern ("fooba[r-z]foo", "foobasfoo"));
|
||||
expect (! matchOscPattern ("fooba[r-z]foo", "foobaRfoo"));
|
||||
|
||||
expect (! matchOscPattern ("foo[1-8]bar", "foo0bar"));
|
||||
expect (matchOscPattern ("foo[1-8]bar", "foo1bar"));
|
||||
expect (matchOscPattern ("foo[1-8]bar", "foo6bar"));
|
||||
expect (matchOscPattern ("foo[1-8]bar", "foo8bar"));
|
||||
expect (! matchOscPattern ("foo[1-8]bar", "foo9bar"));
|
||||
|
||||
// special case: '-' does not have a special meaning if it is at the end of the set.
|
||||
|
||||
expect (matchOscPattern ("foo[abc-]bar", "fooabar"));
|
||||
expect (matchOscPattern ("foo[abc-]bar", "foo-bar"));
|
||||
expect (matchOscPattern ("foo[-]bar", "foo-bar"));
|
||||
|
||||
// mixing both set and range:
|
||||
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[a-b]r", "fooabar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[a-a]r", "foobbar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[aaaa-aaaa-aaaa]r", "foodbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooebar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foogbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooibar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foojbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fookbar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foolbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooobar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foopbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooubar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooybar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foozbar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo0bar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo1bar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo5bar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo8bar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo9bar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooCbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooDbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooEbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooFbar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooGbar"));
|
||||
expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooXbar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "fooZbar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "foobar"));
|
||||
expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "fooFXbar"));
|
||||
|
||||
// using [!...] for a negated set or range of chars:
|
||||
|
||||
expect (! matchOscPattern ("fooba[!rxz]foo", "foobarfoo"));
|
||||
expect (! matchOscPattern ("fooba[!rxz]foo", "foobaxfoo"));
|
||||
expect (! matchOscPattern ("fooba[!rxz]foo", "foobazfoo"));
|
||||
expect (matchOscPattern ("fooba[!rxz]foo", "foobasfoo"));
|
||||
|
||||
expect (! matchOscPattern ("fooba[!r-z]foo", "foobarfoo"));
|
||||
expect (! matchOscPattern ("fooba[!r-z]foo", "foobaxfoo"));
|
||||
expect (! matchOscPattern ("fooba[!r-z]foo", "foobazfoo"));
|
||||
expect (! matchOscPattern ("fooba[!r-z]foo", "foobasfoo"));
|
||||
expect (matchOscPattern ("fooba[!r-z]foo", "foobaRfoo"));
|
||||
|
||||
// special case: '!' does not have a special meaning if it is not the first char in the set.
|
||||
|
||||
expect (matchOscPattern ("foo[ab!c]bar", "fooabar"));
|
||||
expect (matchOscPattern ("foo[ab!c]bar", "foo!bar"));
|
||||
expect (! matchOscPattern ("foo[ab!c]bar", "fooxbar"));
|
||||
expect (! matchOscPattern ("foo[!!]bar", "foo!bar"));
|
||||
expect (matchOscPattern ("foo[!!]bar", "fooxbar"));
|
||||
expect (! matchOscPattern ("foo[!!]bar", "foobar"));
|
||||
|
||||
// should fail gracefully in case of wrong syntax:
|
||||
|
||||
expect (! matchOscPattern ("notclosin[g", "notclosing"));
|
||||
expect (! matchOscPattern ("n]otopening", "notopening"));
|
||||
expect (! matchOscPattern ("[[nested]]", "nested"));
|
||||
expect (! matchOscPattern ("norangestar[-t]", "norangestart"));
|
||||
expect (! matchOscPattern ("norangestar[-t]", "norangestar-"));
|
||||
}
|
||||
|
||||
beginTest ("string matching combining patterns");
|
||||
{
|
||||
expect (matchOscPattern ("*ea*ll[y-z0-9X-Zvwx]??m[o-q]l[e]x{fat,mat,pat}te{}r*?", "reallycomplexpattern"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCPatternMatcherTests OSCPatternMatcherUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
153
deps/juce/modules/juce_osc/osc/juce_OSCAddress.h
vendored
Normal file
153
deps/juce/modules/juce_osc/osc/juce_OSCAddress.h
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC address.
|
||||
|
||||
This address always starts with a forward slash and has a format similar
|
||||
to an URL, with several address parts separated by slashes.
|
||||
|
||||
Only a subset of ASCII characters are allowed in OSC addresses; see
|
||||
OpenSoundControl 1.0 specification for details.
|
||||
|
||||
OSC addresses can be used to register ListenerWithOSCAddress objects to an
|
||||
OSCReceiver if you wish them to only listen to certain messages with
|
||||
matching OSC address patterns.
|
||||
|
||||
@see OSCReceiver, OSCAddressPattern, OSCMessage
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCAddress
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructs a new OSCAddress from a String.
|
||||
@throw OSCFormatError if the string is not a valid OSC address.
|
||||
*/
|
||||
OSCAddress (const String& address);
|
||||
|
||||
/** Constructs a new OSCAddress from a C string.
|
||||
@throw OSCFormatError of the string is not a valid OSC address.
|
||||
*/
|
||||
OSCAddress (const char* address);
|
||||
|
||||
/** Compares two OSCAddress objects.
|
||||
@returns true if they contain the same address, false otherwise.
|
||||
*/
|
||||
bool operator== (const OSCAddress& other) const noexcept;
|
||||
|
||||
/** Compares two OSCAddress objects.
|
||||
@returns false if they contain the same address, true otherwise.
|
||||
*/
|
||||
bool operator!= (const OSCAddress& other) const noexcept;
|
||||
|
||||
/** Converts the OSCAddress to a String.
|
||||
Note: Trailing slashes are always removed automatically.
|
||||
|
||||
@returns a String object that represents the OSC address.
|
||||
*/
|
||||
String toString() const noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray oscSymbols;
|
||||
String asString;
|
||||
friend class OSCAddressPattern;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC address pattern.
|
||||
|
||||
Extends an OSC address by additionally allowing the following wildcards:
|
||||
?, *, [], {}
|
||||
|
||||
OSC messages always have an OSC address pattern to specify the destination(s)
|
||||
of the message.
|
||||
|
||||
@see OSCMessage, OSCAddress, OSCMessageListener
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCAddressPattern
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructs a new OSCAddressPattern from a String.
|
||||
@throw OSCFormatError if the string is not a valid OSC address pattern.
|
||||
*/
|
||||
OSCAddressPattern (const String& address);
|
||||
|
||||
/** Constructs a new OSCAddressPattern from a C string.
|
||||
@throw OSCFormatError of the string is not a valid OSC address pattern.
|
||||
*/
|
||||
OSCAddressPattern (const char* address);
|
||||
|
||||
/** Compares two OSCAddressPattern objects.
|
||||
@returns true if they contain the same address pattern, false otherwise.
|
||||
*/
|
||||
bool operator== (const OSCAddressPattern& other) const noexcept;
|
||||
|
||||
/** Compares two OSCAddressPattern objects.
|
||||
@returns false if they contain the same address pattern, true otherwise.
|
||||
*/
|
||||
bool operator!= (const OSCAddressPattern& other) const noexcept;
|
||||
|
||||
/** Checks if the OSCAddressPattern matches an OSC address with the wildcard
|
||||
rules defined by the OpenSoundControl 1.0 specification.
|
||||
|
||||
@returns true if the OSCAddressPattern matches the given OSC address,
|
||||
false otherwise.
|
||||
*/
|
||||
bool matches (const OSCAddress& address) const noexcept;
|
||||
|
||||
/** Checks whether the OSCAddressPattern contains any of the allowed OSC
|
||||
address pattern wildcards: ?, *, [], {}
|
||||
|
||||
@returns true if the OSCAddressPattern contains OSC wildcards, false otherwise.
|
||||
*/
|
||||
bool containsWildcards() const noexcept { return wasInitialisedWithWildcards; }
|
||||
|
||||
/** Converts the OSCAddressPattern to a String.
|
||||
Note: Trailing slashes are always removed automatically.
|
||||
|
||||
@returns a String object that represents the OSC address pattern.
|
||||
*/
|
||||
String toString() const noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray oscSymbols;
|
||||
String asString;
|
||||
bool wasInitialisedWithWildcards;
|
||||
};
|
||||
|
||||
} // namespace juce
|
249
deps/juce/modules/juce_osc/osc/juce_OSCArgument.cpp
vendored
Normal file
249
deps/juce/modules/juce_osc/osc/juce_OSCArgument.cpp
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
OSCArgument::OSCArgument (int32 v) : type (OSCTypes::int32), intValue (v) {}
|
||||
OSCArgument::OSCArgument (float v) : type (OSCTypes::float32), floatValue (v) {}
|
||||
OSCArgument::OSCArgument (const String& s) : type (OSCTypes::string), stringValue (s) {}
|
||||
OSCArgument::OSCArgument (MemoryBlock b) : type (OSCTypes::blob), blob (std::move (b)) {}
|
||||
OSCArgument::OSCArgument (OSCColour c) : type (OSCTypes::colour), intValue ((int32) c.toInt32()) {}
|
||||
|
||||
//==============================================================================
|
||||
String OSCArgument::getString() const noexcept
|
||||
{
|
||||
if (isString())
|
||||
return stringValue;
|
||||
|
||||
jassertfalse; // you must check the type of an argument before attempting to get its value!
|
||||
return {};
|
||||
}
|
||||
|
||||
int32 OSCArgument::getInt32() const noexcept
|
||||
{
|
||||
if (isInt32())
|
||||
return intValue;
|
||||
|
||||
jassertfalse; // you must check the type of an argument before attempting to get its value!
|
||||
return 0;
|
||||
}
|
||||
|
||||
float OSCArgument::getFloat32() const noexcept
|
||||
{
|
||||
if (isFloat32())
|
||||
return floatValue;
|
||||
|
||||
jassertfalse; // you must check the type of an argument before attempting to get its value!
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const MemoryBlock& OSCArgument::getBlob() const noexcept
|
||||
{
|
||||
// you must check the type of an argument before attempting to get its value!
|
||||
jassert (isBlob());
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
OSCColour OSCArgument::getColour() const noexcept
|
||||
{
|
||||
if (isColour())
|
||||
return OSCColour::fromInt32 ((uint32) intValue);
|
||||
|
||||
jassertfalse; // you must check the type of an argument before attempting to get its value!
|
||||
return { 0, 0, 0, 0 };
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class OSCArgumentTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCArgumentTests()
|
||||
: UnitTest ("OSCArgument class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
|
||||
MemoryBlock getMemoryBlockWithRandomData (size_t numBytes)
|
||||
{
|
||||
MemoryBlock block (numBytes);
|
||||
|
||||
Random rng = getRandom();
|
||||
|
||||
for (size_t i = 0; i < numBytes; ++i)
|
||||
block[i] = (char) rng.nextInt (256);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
runTestInitialisation();
|
||||
}
|
||||
|
||||
void runTestInitialisation()
|
||||
{
|
||||
beginTest ("Int32");
|
||||
{
|
||||
int value = 123456789;
|
||||
|
||||
OSCArgument arg (value);
|
||||
|
||||
expect (arg.getType() == OSCTypes::int32);
|
||||
expect (arg.isInt32());
|
||||
expect (! arg.isFloat32());
|
||||
expect (! arg.isString());
|
||||
expect (! arg.isBlob());
|
||||
expect (! arg.isColour());
|
||||
|
||||
expect (arg.getInt32() == value);
|
||||
}
|
||||
|
||||
beginTest ("Float32");
|
||||
{
|
||||
float value = 12345.6789f;
|
||||
|
||||
OSCArgument arg (value);
|
||||
|
||||
expect (arg.getType() == OSCTypes::float32);
|
||||
expect (! arg.isInt32());
|
||||
expect (arg.isFloat32());
|
||||
expect (! arg.isString());
|
||||
expect (! arg.isBlob());
|
||||
expect (! arg.isColour());
|
||||
|
||||
expect (arg.getFloat32() == value);
|
||||
}
|
||||
|
||||
beginTest ("String");
|
||||
{
|
||||
String value = "Hello, World!";
|
||||
OSCArgument arg (value);
|
||||
|
||||
expect (arg.getType() == OSCTypes::string);
|
||||
expect (! arg.isInt32());
|
||||
expect (! arg.isFloat32());
|
||||
expect (arg.isString());
|
||||
expect (! arg.isBlob());
|
||||
expect (! arg.isColour());
|
||||
|
||||
expect (arg.getString() == value);
|
||||
}
|
||||
|
||||
beginTest ("String (from C string)");
|
||||
{
|
||||
OSCArgument arg ("Hello, World!");
|
||||
|
||||
expect (arg.getType() == OSCTypes::string);
|
||||
expect (! arg.isInt32());
|
||||
expect (! arg.isFloat32());
|
||||
expect (arg.isString());
|
||||
expect (! arg.isBlob());
|
||||
expect (! arg.isColour());
|
||||
|
||||
expect (arg.getString() == "Hello, World!");
|
||||
}
|
||||
|
||||
beginTest ("Blob");
|
||||
{
|
||||
auto blob = getMemoryBlockWithRandomData (413);
|
||||
OSCArgument arg (blob);
|
||||
|
||||
expect (arg.getType() == OSCTypes::blob);
|
||||
expect (! arg.isInt32());
|
||||
expect (! arg.isFloat32());
|
||||
expect (! arg.isString());
|
||||
expect (arg.isBlob());
|
||||
expect (! arg.isColour());
|
||||
|
||||
expect (arg.getBlob() == blob);
|
||||
}
|
||||
|
||||
beginTest ("Colour");
|
||||
{
|
||||
Random rng = getRandom();
|
||||
|
||||
for (int i = 100; --i >= 0;)
|
||||
{
|
||||
OSCColour col = { (uint8) rng.nextInt (256),
|
||||
(uint8) rng.nextInt (256),
|
||||
(uint8) rng.nextInt (256),
|
||||
(uint8) rng.nextInt (256) };
|
||||
|
||||
OSCArgument arg (col);
|
||||
|
||||
expect (arg.getType() == OSCTypes::colour);
|
||||
expect (! arg.isInt32());
|
||||
expect (! arg.isFloat32());
|
||||
expect (! arg.isString());
|
||||
expect (! arg.isBlob());
|
||||
expect (arg.isColour());
|
||||
|
||||
expect (arg.getColour().toInt32() == col.toInt32());
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Copy, move and assignment");
|
||||
{
|
||||
{
|
||||
int value = -42;
|
||||
OSCArgument arg (value);
|
||||
|
||||
OSCArgument copy = arg;
|
||||
expect (copy.getType() == OSCTypes::int32);
|
||||
expect (copy.getInt32() == value);
|
||||
|
||||
OSCArgument assignment ("this will be overwritten!");
|
||||
assignment = copy;
|
||||
expect (assignment.getType() == OSCTypes::int32);
|
||||
expect (assignment.getInt32() == value);
|
||||
}
|
||||
{
|
||||
const size_t numBytes = 412;
|
||||
MemoryBlock blob = getMemoryBlockWithRandomData (numBytes);
|
||||
OSCArgument arg (blob);
|
||||
|
||||
OSCArgument copy = arg;
|
||||
expect (copy.getType() == OSCTypes::blob);
|
||||
expect (copy.getBlob() == blob);
|
||||
|
||||
OSCArgument assignment ("this will be overwritten!");
|
||||
assignment = copy;
|
||||
expect (assignment.getType() == OSCTypes::blob);
|
||||
expect (assignment.getBlob() == blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCArgumentTests OSCArgumentUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
124
deps/juce/modules/juce_osc/osc/juce_OSCArgument.h
vendored
Normal file
124
deps/juce/modules/juce_osc/osc/juce_OSCArgument.h
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC argument.
|
||||
|
||||
An OSC argument is a value of one of the following types: int32, float32, string,
|
||||
or blob (raw binary data).
|
||||
|
||||
OSCMessage objects are essentially arrays of OSCArgument objects.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCArgument
|
||||
{
|
||||
public:
|
||||
/** Constructs an OSCArgument with type int32 and a given value. */
|
||||
OSCArgument (int32 value);
|
||||
|
||||
/** Constructs an OSCArgument with type float32 and a given value. */
|
||||
OSCArgument (float value);
|
||||
|
||||
/** Constructs an OSCArgument with type string and a given value */
|
||||
OSCArgument (const String& value);
|
||||
|
||||
/** Constructs an OSCArgument with type blob and copies dataSize bytes
|
||||
from the memory pointed to by data into the blob.
|
||||
|
||||
The data owned by the blob will be released when the OSCArgument object
|
||||
gets destructed.
|
||||
*/
|
||||
OSCArgument (MemoryBlock blob);
|
||||
|
||||
/** Constructs an OSCArgument with type colour and a given colour value */
|
||||
OSCArgument (OSCColour colour);
|
||||
|
||||
/** Returns the type of the OSCArgument as an OSCType.
|
||||
OSCType is a char type, and its value will be the OSC type tag of the type.
|
||||
*/
|
||||
OSCType getType() const noexcept { return type; }
|
||||
|
||||
/** Returns whether the type of the OSCArgument is int32. */
|
||||
bool isInt32() const noexcept { return type == OSCTypes::int32; }
|
||||
|
||||
/** Returns whether the type of the OSCArgument is float. */
|
||||
bool isFloat32() const noexcept { return type == OSCTypes::float32; }
|
||||
|
||||
/** Returns whether the type of the OSCArgument is string. */
|
||||
bool isString() const noexcept { return type == OSCTypes::string; }
|
||||
|
||||
/** Returns whether the type of the OSCArgument is blob. */
|
||||
bool isBlob() const noexcept { return type == OSCTypes::blob; }
|
||||
|
||||
/** Returns whether the type of the OSCArgument is colour. */
|
||||
bool isColour() const noexcept { return type == OSCTypes::colour; }
|
||||
|
||||
/** Returns the value of the OSCArgument as an int32.
|
||||
If the type of the OSCArgument is not int32, the behaviour is undefined.
|
||||
*/
|
||||
int32 getInt32() const noexcept;
|
||||
|
||||
/** Returns the value of the OSCArgument as a float32.
|
||||
If the type of the OSCArgument is not float32, the behaviour is undefined.
|
||||
*/
|
||||
float getFloat32() const noexcept;
|
||||
|
||||
/** Returns the value of the OSCArgument as a string.
|
||||
If the type of the OSCArgument is not string, the behaviour is undefined.
|
||||
*/
|
||||
String getString() const noexcept;
|
||||
|
||||
/** Returns the binary data contained in the blob and owned by the OSCArgument,
|
||||
as a reference to a JUCE MemoryBlock object.
|
||||
|
||||
If the type of the OSCArgument is not blob, the behaviour is undefined.
|
||||
*/
|
||||
const MemoryBlock& getBlob() const noexcept;
|
||||
|
||||
/** Returns the value of the OSCArgument as an OSCColour.
|
||||
If the type of the OSCArgument is not a colour, the behaviour is undefined.
|
||||
*/
|
||||
OSCColour getColour() const noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OSCType type;
|
||||
|
||||
union
|
||||
{
|
||||
int32 intValue;
|
||||
float floatValue;
|
||||
};
|
||||
|
||||
String stringValue;
|
||||
MemoryBlock blob;
|
||||
};
|
||||
|
||||
} // namespace juce
|
249
deps/juce/modules/juce_osc/osc/juce_OSCBundle.cpp
vendored
Normal file
249
deps/juce/modules/juce_osc/osc/juce_OSCBundle.cpp
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
OSCBundle::OSCBundle()
|
||||
{
|
||||
}
|
||||
|
||||
OSCBundle::OSCBundle (OSCTimeTag t) : timeTag (t)
|
||||
{
|
||||
}
|
||||
|
||||
// Note: The class invariant of OSCBundle::Element is that
|
||||
// at least one of the pointers bundle and message is nullptr
|
||||
// and the other one always points to a valid object.
|
||||
|
||||
OSCBundle::Element::Element (OSCMessage m)
|
||||
: message (new OSCMessage (m)), bundle (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
OSCBundle::Element::Element (OSCBundle b)
|
||||
: message (nullptr), bundle (new OSCBundle (b))
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
OSCBundle::Element::Element (const Element& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
message = nullptr;
|
||||
bundle = nullptr;
|
||||
|
||||
if (other.isMessage())
|
||||
message.reset (new OSCMessage (other.getMessage()));
|
||||
else
|
||||
bundle.reset (new OSCBundle (other.getBundle()));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
OSCBundle::Element::~Element()
|
||||
{
|
||||
bundle = nullptr;
|
||||
message = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCBundle::Element::isMessage() const noexcept
|
||||
{
|
||||
return message != nullptr;
|
||||
}
|
||||
|
||||
bool OSCBundle::Element::isBundle() const noexcept
|
||||
{
|
||||
return bundle != nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const OSCMessage& OSCBundle::Element::getMessage() const
|
||||
{
|
||||
if (message == nullptr)
|
||||
{
|
||||
// This element is not a bundle! You must check this first before accessing.
|
||||
jassertfalse;
|
||||
throw OSCInternalError ("Access error in OSC bundle element.");
|
||||
}
|
||||
|
||||
return *message;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const OSCBundle& OSCBundle::Element::getBundle() const
|
||||
{
|
||||
if (bundle == nullptr)
|
||||
{
|
||||
// This element is not a bundle! You must check this first before accessing.
|
||||
jassertfalse;
|
||||
throw OSCInternalError ("Access error in OSC bundle element.");
|
||||
}
|
||||
|
||||
return *bundle;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class OSCBundleTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCBundleTests()
|
||||
: UnitTest ("OSCBundle class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("Construction");
|
||||
{
|
||||
OSCBundle bundle;
|
||||
expect (bundle.getTimeTag().isImmediately());
|
||||
}
|
||||
|
||||
beginTest ("Construction with time tag");
|
||||
{
|
||||
Time in100Seconds = (Time (Time::currentTimeMillis()) + RelativeTime (100.0));
|
||||
OSCBundle bundle (in100Seconds);
|
||||
expect (! bundle.getTimeTag().isImmediately());
|
||||
expect (bundle.getTimeTag().toTime() == in100Seconds);
|
||||
}
|
||||
|
||||
beginTest ("Usage when containing messages");
|
||||
{
|
||||
OSCBundle testBundle = generateTestBundle();
|
||||
expectBundleEqualsTestBundle (testBundle);
|
||||
|
||||
}
|
||||
|
||||
beginTest ("Usage when containing other bundles (recursively)");
|
||||
{
|
||||
OSCBundle complexTestBundle;
|
||||
complexTestBundle.addElement (generateTestBundle());
|
||||
complexTestBundle.addElement (OSCMessage ("/test/"));
|
||||
complexTestBundle.addElement (generateTestBundle());
|
||||
|
||||
expect (complexTestBundle.size() == 3);
|
||||
|
||||
OSCBundle::Element* elements = complexTestBundle.begin();
|
||||
|
||||
expect (! elements[0].isMessage());
|
||||
expect (elements[0].isBundle());
|
||||
expect (elements[1].isMessage());
|
||||
expect (! elements[1].isBundle());
|
||||
expect (! elements[2].isMessage());
|
||||
expect (elements[2].isBundle());
|
||||
|
||||
expectBundleEqualsTestBundle (elements[0].getBundle());
|
||||
expect (elements[1].getMessage().size() == 0);
|
||||
expect (elements[1].getMessage().getAddressPattern().toString() == "/test");
|
||||
expectBundleEqualsTestBundle (elements[2].getBundle());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int testInt = 127;
|
||||
float testFloat = 1.5;
|
||||
|
||||
OSCBundle generateTestBundle()
|
||||
{
|
||||
OSCBundle bundle;
|
||||
|
||||
OSCMessage msg1 ("/test/fader");
|
||||
msg1.addInt32 (testInt);
|
||||
|
||||
OSCMessage msg2 ("/test/foo");
|
||||
msg2.addString ("bar");
|
||||
msg2.addFloat32 (testFloat);
|
||||
|
||||
bundle.addElement (msg1);
|
||||
bundle.addElement (msg2);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
void expectBundleEqualsTestBundle (const OSCBundle& bundle)
|
||||
{
|
||||
expect (bundle.size() == 2);
|
||||
expect (bundle[0].isMessage());
|
||||
expect (! bundle[0].isBundle());
|
||||
expect (bundle[1].isMessage());
|
||||
expect (! bundle[1].isBundle());
|
||||
|
||||
int numElementsCounted = 0;
|
||||
for (auto& element : bundle)
|
||||
{
|
||||
expect (element.isMessage());
|
||||
expect (! element.isBundle());
|
||||
++numElementsCounted;
|
||||
}
|
||||
expectEquals (numElementsCounted, 2);
|
||||
|
||||
auto* e = bundle.begin();
|
||||
expect (e[0].getMessage().size() == 1);
|
||||
expect (e[0].getMessage().begin()->getInt32() == testInt);
|
||||
expect (e[1].getMessage().size() == 2);
|
||||
expect (e[1].getMessage()[1].getFloat32() == testFloat);
|
||||
}
|
||||
};
|
||||
|
||||
static OSCBundleTests OSCBundleUnitTests;
|
||||
|
||||
//==============================================================================
|
||||
class OSCBundleElementTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCBundleElementTests()
|
||||
: UnitTest ("OSCBundle::Element class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("Construction from OSCMessage");
|
||||
{
|
||||
float testFloat = -0.125;
|
||||
OSCMessage msg ("/test");
|
||||
msg.addFloat32 (testFloat);
|
||||
|
||||
OSCBundle::Element element (msg);
|
||||
|
||||
expect (element.isMessage());
|
||||
expect (element.getMessage().size() == 1);
|
||||
expect (element.getMessage()[0].getType() == OSCTypes::float32);
|
||||
expect (element.getMessage()[0].getFloat32() == testFloat);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCBundleElementTests OSCBundleElementUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
146
deps/juce/modules/juce_osc/osc/juce_OSCBundle.h
vendored
Normal file
146
deps/juce/modules/juce_osc/osc/juce_OSCBundle.h
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC bundle.
|
||||
|
||||
An OSCBundle contains an OSCTimeTag and zero or more OSCBundle Elements.
|
||||
The elements of a bundle can be OSC messages or other OSC bundles (this
|
||||
means that OSC bundles can be nested).
|
||||
|
||||
This is an advanced OSC structure useful to bundle OSC messages together
|
||||
whose effects must occur simultaneously at some given time. For most
|
||||
use cases it is probably enough to send and receive plain OSC messages.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCBundle
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructs an OSCBundle with no content and a default time tag ("immediately"). */
|
||||
OSCBundle();
|
||||
|
||||
/** Constructs an OSCBundle with no content and a given time tag. */
|
||||
OSCBundle (OSCTimeTag timeTag);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the OSCBundle's OSC time tag. */
|
||||
void setTimeTag (OSCTimeTag newTimeTag) noexcept { timeTag = newTimeTag; }
|
||||
|
||||
/** Returns the OSCBundle's OSC time tag. */
|
||||
OSCTimeTag getTimeTag() const noexcept { return timeTag; }
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC bundle element.
|
||||
|
||||
An OSCBundle Element contains either one OSCMessage or one OSCBundle.
|
||||
*/
|
||||
class JUCE_API Element
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructs an OSCBundle Element from an OSCMessage. */
|
||||
Element (OSCMessage message);
|
||||
|
||||
/** Constructs an OSCBundle Element from an OSCBundle. */
|
||||
Element (OSCBundle bundle);
|
||||
|
||||
/** Copy constructor. */
|
||||
Element (const Element& other);
|
||||
|
||||
/** Destructor. */
|
||||
~Element();
|
||||
|
||||
/** Returns true if the OSCBundle element is an OSCMessage. */
|
||||
bool isMessage() const noexcept;
|
||||
|
||||
/** Returns true if the OSCBundle element is an OSCBundle. */
|
||||
bool isBundle() const noexcept;
|
||||
|
||||
/** Returns a reference to the contained OSCMessage.
|
||||
If the OSCBundle element is not an OSCMessage, behaviour is undefined.
|
||||
*/
|
||||
const OSCMessage& getMessage() const;
|
||||
|
||||
/** Returns a reference to the contained OSCBundle.
|
||||
If the OSCBundle element is not an OSCBundle, behaviour is undefined.
|
||||
*/
|
||||
const OSCBundle& getBundle() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<OSCMessage> message;
|
||||
std::unique_ptr<OSCBundle> bundle;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of elements contained in the bundle. */
|
||||
int size() const noexcept { return elements.size(); }
|
||||
|
||||
/** Returns true if the bundle contains no elements; false otherwise. */
|
||||
bool isEmpty() const noexcept { return elements.isEmpty(); }
|
||||
|
||||
/** Returns a reference to the OSCBundle element at index i in this bundle.
|
||||
This method does not check the range and results in undefined behaviour
|
||||
in case i < 0 or i >= size().
|
||||
*/
|
||||
OSCBundle::Element& operator[] (const int i) noexcept
|
||||
{
|
||||
return elements.getReference (i);
|
||||
}
|
||||
|
||||
const OSCBundle::Element& operator[] (const int i) const noexcept
|
||||
{
|
||||
return elements.getReference (i);
|
||||
}
|
||||
|
||||
/** Adds an OSCBundleElement to the OSCBundle's content. s*/
|
||||
void addElement (const OSCBundle::Element& element) { elements.add (element); }
|
||||
|
||||
/** Returns a pointer to the first element of the OSCBundle. */
|
||||
OSCBundle::Element* begin() noexcept { return elements.begin(); }
|
||||
|
||||
/** Returns a pointer to the first element of the OSCBundle. */
|
||||
const OSCBundle::Element* begin() const noexcept { return elements.begin(); }
|
||||
|
||||
/** Returns a pointer past the last element of the OSCBundle. */
|
||||
OSCBundle::Element* end() noexcept { return elements.end(); }
|
||||
|
||||
/** Returns a pointer past the last element of the OSCBundle. */
|
||||
const OSCBundle::Element* end() const noexcept { return elements.end(); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<OSCBundle::Element> elements;
|
||||
OSCTimeTag timeTag;
|
||||
};
|
||||
|
||||
} // namespace juce
|
227
deps/juce/modules/juce_osc/osc/juce_OSCMessage.cpp
vendored
Normal file
227
deps/juce/modules/juce_osc/osc/juce_OSCMessage.cpp
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
OSCMessage::OSCMessage (const OSCAddressPattern& ap) noexcept : addressPattern (ap)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OSCMessage::setAddressPattern (const OSCAddressPattern& ap) noexcept
|
||||
{
|
||||
addressPattern = ap;
|
||||
}
|
||||
|
||||
OSCAddressPattern OSCMessage::getAddressPattern() const noexcept
|
||||
{
|
||||
return addressPattern;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int OSCMessage::size() const noexcept
|
||||
{
|
||||
return arguments.size();
|
||||
}
|
||||
|
||||
bool OSCMessage::isEmpty() const noexcept
|
||||
{
|
||||
return arguments.isEmpty();
|
||||
}
|
||||
|
||||
OSCArgument& OSCMessage::operator[] (const int i) noexcept
|
||||
{
|
||||
return arguments.getReference (i);
|
||||
}
|
||||
|
||||
const OSCArgument& OSCMessage::operator[] (const int i) const noexcept
|
||||
{
|
||||
return arguments.getReference (i);
|
||||
}
|
||||
|
||||
OSCArgument* OSCMessage::begin() noexcept
|
||||
{
|
||||
return arguments.begin();
|
||||
}
|
||||
|
||||
const OSCArgument* OSCMessage::begin() const noexcept
|
||||
{
|
||||
return arguments.begin();
|
||||
}
|
||||
|
||||
OSCArgument* OSCMessage::end() noexcept
|
||||
{
|
||||
return arguments.end();
|
||||
}
|
||||
|
||||
const OSCArgument* OSCMessage::end() const noexcept
|
||||
{
|
||||
return arguments.end();
|
||||
}
|
||||
|
||||
void OSCMessage::clear()
|
||||
{
|
||||
arguments.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OSCMessage::addInt32 (int32 value) { arguments.add (OSCArgument (value)); }
|
||||
void OSCMessage::addFloat32 (float value) { arguments.add (OSCArgument (value)); }
|
||||
void OSCMessage::addString (const String& value) { arguments.add (OSCArgument (value)); }
|
||||
void OSCMessage::addBlob (MemoryBlock blob) { arguments.add (OSCArgument (std::move (blob))); }
|
||||
void OSCMessage::addColour (OSCColour colour) { arguments.add (OSCArgument (colour)); }
|
||||
void OSCMessage::addArgument (OSCArgument arg) { arguments.add (arg); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class OSCMessageTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCMessageTests()
|
||||
: UnitTest ("OSCMessage class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("Basic usage");
|
||||
{
|
||||
OSCMessage msg ("/test/param0");
|
||||
expectEquals (msg.size(), 0);
|
||||
expect (msg.getAddressPattern().toString() == "/test/param0");
|
||||
|
||||
const int numTestArgs = 5;
|
||||
|
||||
const int testInt = 42;
|
||||
const float testFloat = 3.14159f;
|
||||
const String testString = "Hello, World!";
|
||||
const OSCColour testColour = { 10, 20, 150, 200 };
|
||||
|
||||
const uint8 testBlobData[5] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
|
||||
const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
|
||||
|
||||
msg.addInt32 (testInt);
|
||||
msg.addFloat32 (testFloat);
|
||||
msg.addString (testString);
|
||||
msg.addBlob (testBlob);
|
||||
msg.addColour (testColour);
|
||||
|
||||
expectEquals (msg.size(), numTestArgs);
|
||||
|
||||
expectEquals (msg[0].getType(), OSCTypes::int32);
|
||||
expectEquals (msg[1].getType(), OSCTypes::float32);
|
||||
expectEquals (msg[2].getType(), OSCTypes::string);
|
||||
expectEquals (msg[3].getType(), OSCTypes::blob);
|
||||
expectEquals (msg[4].getType(), OSCTypes::colour);
|
||||
|
||||
expect (msg[0].isInt32());
|
||||
expect (msg[1].isFloat32());
|
||||
expect (msg[2].isString());
|
||||
expect (msg[3].isBlob());
|
||||
expect (msg[4].isColour());
|
||||
|
||||
expectEquals (msg[0].getInt32(), testInt);
|
||||
expectEquals (msg[1].getFloat32(), testFloat);
|
||||
expectEquals (msg[2].getString(), testString);
|
||||
expect (msg[3].getBlob() == testBlob);
|
||||
expect (msg[4].getColour().toInt32() == testColour.toInt32());
|
||||
|
||||
expect (msg.begin() + numTestArgs == msg.end());
|
||||
|
||||
auto arg = msg.begin();
|
||||
expect (arg->isInt32());
|
||||
expectEquals (arg->getInt32(), testInt);
|
||||
++arg;
|
||||
expect (arg->isFloat32());
|
||||
expectEquals (arg->getFloat32(), testFloat);
|
||||
++arg;
|
||||
expect (arg->isString());
|
||||
expectEquals (arg->getString(), testString);
|
||||
++arg;
|
||||
expect (arg->isBlob());
|
||||
expect (arg->getBlob() == testBlob);
|
||||
++arg;
|
||||
expect (arg->isColour());
|
||||
expect (arg->getColour().toInt32() == testColour.toInt32());
|
||||
++arg;
|
||||
expect (arg == msg.end());
|
||||
}
|
||||
|
||||
|
||||
beginTest ("Initialisation with argument list (C++11 only)");
|
||||
{
|
||||
int testInt = 42;
|
||||
float testFloat = 5.5;
|
||||
String testString = "Hello, World!";
|
||||
|
||||
{
|
||||
OSCMessage msg ("/test", testInt);
|
||||
expect (msg.getAddressPattern().toString() == String ("/test"));
|
||||
expectEquals (msg.size(), 1);
|
||||
expect (msg[0].isInt32());
|
||||
expectEquals (msg[0].getInt32(), testInt);
|
||||
}
|
||||
{
|
||||
OSCMessage msg ("/test", testFloat);
|
||||
expect (msg.getAddressPattern().toString() == String ("/test"));
|
||||
expectEquals (msg.size(), 1);
|
||||
expect (msg[0].isFloat32());
|
||||
expectEquals (msg[0].getFloat32(), testFloat);
|
||||
}
|
||||
{
|
||||
OSCMessage msg ("/test", testString);
|
||||
expect (msg.getAddressPattern().toString() == String ("/test"));
|
||||
expectEquals (msg.size(), 1);
|
||||
expect (msg[0].isString());
|
||||
expectEquals (msg[0].getString(), testString);
|
||||
}
|
||||
{
|
||||
OSCMessage msg ("/test", testInt, testFloat, testString, testFloat, testInt);
|
||||
expect (msg.getAddressPattern().toString() == String ("/test"));
|
||||
expectEquals (msg.size(), 5);
|
||||
expect (msg[0].isInt32());
|
||||
expect (msg[1].isFloat32());
|
||||
expect (msg[2].isString());
|
||||
expect (msg[3].isFloat32());
|
||||
expect (msg[4].isInt32());
|
||||
|
||||
expectEquals (msg[0].getInt32(), testInt);
|
||||
expectEquals (msg[1].getFloat32(), testFloat);
|
||||
expectEquals (msg[2].getString(), testString);
|
||||
expectEquals (msg[3].getFloat32(), testFloat);
|
||||
expectEquals (msg[4].getInt32(), testInt);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCMessageTests OSCMessageUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
183
deps/juce/modules/juce_osc/osc/juce_OSCMessage.h
vendored
Normal file
183
deps/juce/modules/juce_osc/osc/juce_OSCMessage.h
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC Message.
|
||||
|
||||
An OSCMessage consists of an OSCAddressPattern and zero or more OSCArguments.
|
||||
|
||||
OSC messages are the elementary objects that are used to exchange any data
|
||||
via OSC. An OSCSender can send OSCMessage objects to an OSCReceiver.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCMessage
|
||||
{
|
||||
public:
|
||||
|
||||
//==============================================================================
|
||||
/** Constructs an OSCMessage object with the given address pattern and no
|
||||
arguments.
|
||||
|
||||
@param ap the address pattern of the message. This must be a valid OSC
|
||||
address (starting with a forward slash) and may contain
|
||||
OSC wildcard expressions. You can pass in a string literal
|
||||
or a juce String (they will be converted to an OSCAddressPattern
|
||||
automatically).
|
||||
*/
|
||||
OSCMessage (const OSCAddressPattern& ap) noexcept;
|
||||
|
||||
|
||||
/** Constructs an OSCMessage object with the given address pattern and list
|
||||
of arguments.
|
||||
|
||||
@param ap the address pattern of the message. This must be a valid OSC
|
||||
address (starting with a forward slash) and may contain
|
||||
OSC wildcard expressions. You can pass in a string literal
|
||||
or a juce String (they will be converted to an OSCAddressPattern
|
||||
automatically).
|
||||
|
||||
@param arg1 the first argument of the message.
|
||||
@param args an optional list of further arguments to add to the message.
|
||||
*/
|
||||
template <typename Arg1, typename... Args>
|
||||
OSCMessage (const OSCAddressPattern& ap, Arg1&& arg1, Args&&... args);
|
||||
|
||||
/** Sets the address pattern of the OSCMessage.
|
||||
|
||||
@param ap the address pattern of the message. This must be a valid OSC
|
||||
address (starting with a forward slash) and may contain
|
||||
OSC wildcard expressions. You can pass in a string literal
|
||||
or a juce String (they will be converted to an OSCAddressPattern
|
||||
automatically).
|
||||
*/
|
||||
void setAddressPattern (const OSCAddressPattern& ap) noexcept;
|
||||
|
||||
/** Returns the address pattern of the OSCMessage. */
|
||||
OSCAddressPattern getAddressPattern() const noexcept;
|
||||
|
||||
/** Returns the number of OSCArgument objects that belong to this OSCMessage. */
|
||||
int size() const noexcept;
|
||||
|
||||
/** Returns true if the OSCMessage contains no OSCArgument objects; false otherwise. */
|
||||
bool isEmpty() const noexcept;
|
||||
|
||||
/** Returns a reference to the OSCArgument at index i in the OSCMessage object.
|
||||
This method does not check the range and results in undefined behaviour
|
||||
in case i < 0 or i >= size().
|
||||
*/
|
||||
OSCArgument& operator[] (const int i) noexcept;
|
||||
const OSCArgument& operator[] (const int i) const noexcept;
|
||||
|
||||
/** Returns a pointer to the first OSCArgument in the OSCMessage object.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
OSCArgument* begin() noexcept;
|
||||
|
||||
/** Returns a pointer to the first OSCArgument in the OSCMessage object.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
const OSCArgument* begin() const noexcept;
|
||||
|
||||
/** Returns a pointer to the last OSCArgument in the OSCMessage object.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
OSCArgument* end() noexcept;
|
||||
|
||||
/** Returns a pointer to the last OSCArgument in the OSCMessage object.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
const OSCArgument* end() const noexcept;
|
||||
|
||||
/** Removes all arguments from the OSCMessage. */
|
||||
void clear();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a new OSCArgument of type int32 with the given value,
|
||||
and adds it to the OSCMessage object.
|
||||
*/
|
||||
void addInt32 (int32 value);
|
||||
|
||||
/** Creates a new OSCArgument of type float32 with the given value,
|
||||
and adds it to the OSCMessage object.
|
||||
*/
|
||||
void addFloat32 (float value);
|
||||
|
||||
/** Creates a new OSCArgument of type string with the given value,
|
||||
and adds it to the OSCMessage object.
|
||||
*/
|
||||
void addString (const String& value);
|
||||
|
||||
/** Creates a new OSCArgument of type blob with binary data content copied from
|
||||
the given MemoryBlock.
|
||||
|
||||
Note: If the argument passed is an lvalue, this may copy the binary data.
|
||||
*/
|
||||
void addBlob (MemoryBlock blob);
|
||||
|
||||
/** Creates a new OSCArgument of type colour with the given value,
|
||||
and adds it to the OSCMessage object.
|
||||
*/
|
||||
void addColour (OSCColour colour);
|
||||
|
||||
/** Adds the OSCArgument argument to the OSCMessage object.
|
||||
|
||||
Note: This method will result in a copy of the OSCArgument object if it is passed
|
||||
as an lvalue. If the OSCArgument is of type blob, this will also copy the underlying
|
||||
binary data. In general, you should use addInt32, addFloat32, etc. instead.
|
||||
*/
|
||||
void addArgument (OSCArgument argument);
|
||||
|
||||
private:
|
||||
|
||||
//==============================================================================
|
||||
template <typename Arg1, typename... Args>
|
||||
void addArguments (Arg1&& arg1, Args&&... args)
|
||||
{
|
||||
addArgument (arg1);
|
||||
addArguments (std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
void addArguments() {}
|
||||
|
||||
//==============================================================================
|
||||
OSCAddressPattern addressPattern;
|
||||
Array<OSCArgument> arguments;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Arg1, typename... Args>
|
||||
OSCMessage::OSCMessage (const OSCAddressPattern& ap, Arg1&& arg1, Args&&... args)
|
||||
: addressPattern (ap)
|
||||
{
|
||||
addArguments (std::forward<Arg1> (arg1), std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
} // namespace juce
|
1194
deps/juce/modules/juce_osc/osc/juce_OSCReceiver.cpp
vendored
Normal file
1194
deps/juce/modules/juce_osc/osc/juce_OSCReceiver.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
224
deps/juce/modules/juce_osc/osc/juce_OSCReceiver.h
vendored
Normal file
224
deps/juce/modules/juce_osc/osc/juce_OSCReceiver.h
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for receiving OSC data.
|
||||
|
||||
An OSCReceiver object allows you to receive OSC bundles and messages.
|
||||
It can connect to a network port, receive incoming OSC packets from the
|
||||
network via UDP, parse them, and forward the included OSCMessage and OSCBundle
|
||||
objects to its listeners.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCReceiver
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an OSCReceiver. */
|
||||
OSCReceiver();
|
||||
|
||||
/** Creates an OSCReceiver with a specific name for its thread. */
|
||||
OSCReceiver (const String& threadName);
|
||||
|
||||
/** Destructor. */
|
||||
~OSCReceiver();
|
||||
|
||||
//==============================================================================
|
||||
/** Connects to the specified UDP port using a datagram socket,
|
||||
and starts listening to OSC packets arriving on this port.
|
||||
|
||||
@returns true if the connection was successful; false otherwise.
|
||||
*/
|
||||
bool connect (int portNumber);
|
||||
|
||||
/** Connects to a UDP datagram socket that is already set up,
|
||||
and starts listening to OSC packets arriving on this port.
|
||||
Make sure that the object you give it doesn't get deleted while this
|
||||
object is still using it!
|
||||
@returns true if the connection was successful; false otherwise.
|
||||
*/
|
||||
bool connectToSocket (DatagramSocket& socketToUse);
|
||||
|
||||
//==============================================================================
|
||||
/** Disconnects from the currently used UDP port.
|
||||
@returns true if the disconnection was successful; false otherwise.
|
||||
*/
|
||||
bool disconnect();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Use this struct as the template parameter for Listener and
|
||||
ListenerWithOSCAddress to receive incoming OSC data on the message thread.
|
||||
This should be used by OSC callbacks that are not realtime-critical, but
|
||||
have significant work to do, for example updating Components in your app's
|
||||
user interface.
|
||||
|
||||
This is the default type of OSC listener.
|
||||
*/
|
||||
struct JUCE_API MessageLoopCallback {};
|
||||
|
||||
/** Use this struct as the template parameter for Listener and
|
||||
ListenerWithOSCAddress to receive incoming OSC data immediately after it
|
||||
arrives, called directly on the network thread that listens to incoming
|
||||
OSC traffic.
|
||||
This type can be used by OSC callbacks that don't do much, but are
|
||||
realtime-critical, for example, setting real-time audio parameters.
|
||||
*/
|
||||
struct JUCE_API RealtimeCallback {};
|
||||
|
||||
//==============================================================================
|
||||
/** A class for receiving OSC data from an OSCReceiver.
|
||||
|
||||
The template argument CallbackType determines how the callback will be called
|
||||
and has to be either MessageLoopCallback or RealtimeCallback. If not specified,
|
||||
MessageLoopCallback will be used by default.
|
||||
|
||||
@see OSCReceiver::addListener, OSCReceiver::ListenerWithOSCAddress,
|
||||
OSCReceiver::MessageLoopCallback, OSCReceiver::RealtimeCallback
|
||||
|
||||
*/
|
||||
template <typename CallbackType = MessageLoopCallback>
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when the OSCReceiver receives a new OSC message.
|
||||
You must implement this function.
|
||||
*/
|
||||
virtual void oscMessageReceived (const OSCMessage& message) = 0;
|
||||
|
||||
/** Called when the OSCReceiver receives a new OSC bundle.
|
||||
If you are not interested in OSC bundles, just ignore this method.
|
||||
The default implementation provided here will simply do nothing.
|
||||
*/
|
||||
virtual void oscBundleReceived (const OSCBundle& /*bundle*/) {}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A class for receiving only those OSC messages from an OSCReceiver that match a
|
||||
given OSC address.
|
||||
|
||||
Use this class if your app receives OSC messages with different address patterns
|
||||
(for example "/juce/fader1", /juce/knob2" etc.) and you want to route those to
|
||||
different objects. This class contains pre-build functionality for that OSC
|
||||
address routing, including wildcard pattern matching (e.g. "/juce/fader[0-9]").
|
||||
|
||||
This class implements the concept of an "OSC Method" from the OpenSoundControl 1.0
|
||||
specification.
|
||||
|
||||
The template argument CallbackType determines how the callback will be called
|
||||
and has to be either MessageLoopCallback or RealtimeCallback. If not specified,
|
||||
MessageLoopCallback will be used by default.
|
||||
|
||||
Note: This type of listener will ignore OSC bundles.
|
||||
|
||||
@see OSCReceiver::addListener, OSCReceiver::Listener,
|
||||
OSCReceiver::MessageLoopCallback, OSCReceiver::RealtimeCallback
|
||||
*/
|
||||
template <typename CallbackType = MessageLoopCallback>
|
||||
class JUCE_API ListenerWithOSCAddress
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ListenerWithOSCAddress() = default;
|
||||
|
||||
/** Called when the OSCReceiver receives an OSC message with an OSC address
|
||||
pattern that matches the OSC address with which this listener was added.
|
||||
*/
|
||||
virtual void oscMessageReceived (const OSCMessage& message) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener that listens to OSC messages and bundles.
|
||||
This listener will be called on the application's message loop.
|
||||
*/
|
||||
void addListener (Listener<MessageLoopCallback>* listenerToAdd);
|
||||
|
||||
/** Adds a listener that listens to OSC messages and bundles.
|
||||
This listener will be called in real-time directly on the network thread
|
||||
that receives OSC data.
|
||||
*/
|
||||
void addListener (Listener<RealtimeCallback>* listenerToAdd);
|
||||
|
||||
/** Adds a filtered listener that listens to OSC messages matching the address
|
||||
used to register the listener here.
|
||||
The listener will be called on the application's message loop.
|
||||
*/
|
||||
void addListener (ListenerWithOSCAddress<MessageLoopCallback>* listenerToAdd,
|
||||
OSCAddress addressToMatch);
|
||||
|
||||
/** Adds a filtered listener that listens to OSC messages matching the address
|
||||
used to register the listener here.
|
||||
The listener will be called on the application's message loop.
|
||||
*/
|
||||
void addListener (ListenerWithOSCAddress<RealtimeCallback>* listenerToAdd,
|
||||
OSCAddress addressToMatch);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (Listener<MessageLoopCallback>* listenerToRemove);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (Listener<RealtimeCallback>* listenerToRemove);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (ListenerWithOSCAddress<MessageLoopCallback>* listenerToRemove);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (ListenerWithOSCAddress<RealtimeCallback>* listenerToRemove);
|
||||
|
||||
//==============================================================================
|
||||
/** An error handler function for OSC format errors that can be called by the
|
||||
OSCReceiver.
|
||||
|
||||
The arguments passed are the pointer to and the data of the buffer that
|
||||
the OSCReceiver has failed to parse.
|
||||
*/
|
||||
using FormatErrorHandler = std::function<void (const char* data, int dataSize)>;
|
||||
|
||||
/** Installs a custom error handler which is called in case the receiver
|
||||
encounters a stream it cannot parse as an OSC bundle or OSC message.
|
||||
|
||||
By default (i.e. if you never use this method), in case of a parsing error
|
||||
nothing happens and the invalid packet is simply discarded.
|
||||
*/
|
||||
void registerFormatErrorHandler (FormatErrorHandler handler);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
friend struct OSCReceiverCallbackMessage;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCReceiver)
|
||||
};
|
||||
|
||||
} // namespace juce
|
876
deps/juce/modules/juce_osc/osc/juce_OSCSender.cpp
vendored
Normal file
876
deps/juce/modules/juce_osc/osc/juce_OSCSender.cpp
vendored
Normal file
@ -0,0 +1,876 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
//==============================================================================
|
||||
/** Writes OSC data to an internal memory buffer, which grows as required.
|
||||
|
||||
The data that was written into the stream can then be accessed later as
|
||||
a contiguous block of memory.
|
||||
|
||||
This class implements the Open Sound Control 1.0 Specification for
|
||||
the format in which the OSC data will be written into the buffer.
|
||||
*/
|
||||
struct OSCOutputStream
|
||||
{
|
||||
OSCOutputStream() noexcept {}
|
||||
|
||||
/** Returns a pointer to the data that has been written to the stream. */
|
||||
const void* getData() const noexcept { return output.getData(); }
|
||||
|
||||
/** Returns the number of bytes of data that have been written to the stream. */
|
||||
size_t getDataSize() const noexcept { return output.getDataSize(); }
|
||||
|
||||
//==============================================================================
|
||||
bool writeInt32 (int32 value)
|
||||
{
|
||||
return output.writeIntBigEndian (value);
|
||||
}
|
||||
|
||||
bool writeUint64 (uint64 value)
|
||||
{
|
||||
return output.writeInt64BigEndian (int64 (value));
|
||||
}
|
||||
|
||||
bool writeFloat32 (float value)
|
||||
{
|
||||
return output.writeFloatBigEndian (value);
|
||||
}
|
||||
|
||||
bool writeString (const String& value)
|
||||
{
|
||||
if (! output.writeString (value))
|
||||
return false;
|
||||
|
||||
const size_t numPaddingZeros = ~value.getNumBytesAsUTF8() & 3;
|
||||
|
||||
return output.writeRepeatedByte ('\0', numPaddingZeros);
|
||||
}
|
||||
|
||||
bool writeBlob (const MemoryBlock& blob)
|
||||
{
|
||||
if (! (output.writeIntBigEndian ((int) blob.getSize())
|
||||
&& output.write (blob.getData(), blob.getSize())))
|
||||
return false;
|
||||
|
||||
const size_t numPaddingZeros = ~(blob.getSize() - 1) & 3;
|
||||
|
||||
return output.writeRepeatedByte (0, numPaddingZeros);
|
||||
}
|
||||
|
||||
bool writeColour (OSCColour colour)
|
||||
{
|
||||
return output.writeIntBigEndian ((int32) colour.toInt32());
|
||||
}
|
||||
|
||||
bool writeTimeTag (OSCTimeTag timeTag)
|
||||
{
|
||||
return output.writeInt64BigEndian (int64 (timeTag.getRawTimeTag()));
|
||||
}
|
||||
|
||||
bool writeAddress (const OSCAddress& address)
|
||||
{
|
||||
return writeString (address.toString());
|
||||
}
|
||||
|
||||
bool writeAddressPattern (const OSCAddressPattern& ap)
|
||||
{
|
||||
return writeString (ap.toString());
|
||||
}
|
||||
|
||||
bool writeTypeTagString (const OSCTypeList& typeList)
|
||||
{
|
||||
output.writeByte (',');
|
||||
|
||||
if (typeList.size() > 0)
|
||||
output.write (typeList.begin(), (size_t) typeList.size());
|
||||
|
||||
output.writeByte ('\0');
|
||||
|
||||
size_t bytesWritten = (size_t) typeList.size() + 1;
|
||||
size_t numPaddingZeros = ~bytesWritten & 0x03;
|
||||
|
||||
return output.writeRepeatedByte ('\0', numPaddingZeros);
|
||||
}
|
||||
|
||||
bool writeArgument (const OSCArgument& arg)
|
||||
{
|
||||
switch (arg.getType())
|
||||
{
|
||||
case OSCTypes::int32: return writeInt32 (arg.getInt32());
|
||||
case OSCTypes::float32: return writeFloat32 (arg.getFloat32());
|
||||
case OSCTypes::string: return writeString (arg.getString());
|
||||
case OSCTypes::blob: return writeBlob (arg.getBlob());
|
||||
case OSCTypes::colour: return writeColour (arg.getColour());
|
||||
|
||||
default:
|
||||
// In this very unlikely case you supplied an invalid OSCType!
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool writeMessage (const OSCMessage& msg)
|
||||
{
|
||||
if (! writeAddressPattern (msg.getAddressPattern()))
|
||||
return false;
|
||||
|
||||
OSCTypeList typeList;
|
||||
|
||||
for (auto& arg : msg)
|
||||
typeList.add (arg.getType());
|
||||
|
||||
if (! writeTypeTagString (typeList))
|
||||
return false;
|
||||
|
||||
for (auto& arg : msg)
|
||||
if (! writeArgument (arg))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeBundle (const OSCBundle& bundle)
|
||||
{
|
||||
if (! writeString ("#bundle"))
|
||||
return false;
|
||||
|
||||
if (! writeTimeTag (bundle.getTimeTag()))
|
||||
return false;
|
||||
|
||||
for (auto& element : bundle)
|
||||
if (! writeBundleElement (element))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool writeBundleElement (const OSCBundle::Element& element)
|
||||
{
|
||||
const int64 startPos = output.getPosition();
|
||||
|
||||
if (! writeInt32 (0)) // writing dummy value for element size
|
||||
return false;
|
||||
|
||||
if (element.isBundle())
|
||||
{
|
||||
if (! writeBundle (element.getBundle()))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! writeMessage (element.getMessage()))
|
||||
return false;
|
||||
}
|
||||
|
||||
const int64 endPos = output.getPosition();
|
||||
const int64 elementSize = endPos - (startPos + 4);
|
||||
|
||||
return output.setPosition (startPos)
|
||||
&& writeInt32 ((int32) elementSize)
|
||||
&& output.setPosition (endPos);
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryOutputStream output;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCOutputStream)
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct OSCSender::Pimpl
|
||||
{
|
||||
Pimpl() noexcept {}
|
||||
~Pimpl() noexcept { disconnect(); }
|
||||
|
||||
//==============================================================================
|
||||
bool connect (const String& newTargetHost, int newTargetPort)
|
||||
{
|
||||
if (! disconnect())
|
||||
return false;
|
||||
|
||||
socket.setOwned (new DatagramSocket (true));
|
||||
targetHostName = newTargetHost;
|
||||
targetPortNumber = newTargetPort;
|
||||
|
||||
if (socket->bindToPort (0)) // 0 = use any local port assigned by the OS.
|
||||
return true;
|
||||
|
||||
socket.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool connectToSocket (DatagramSocket& newSocket, const String& newTargetHost, int newTargetPort)
|
||||
{
|
||||
if (! disconnect())
|
||||
return false;
|
||||
|
||||
socket.setNonOwned (&newSocket);
|
||||
targetHostName = newTargetHost;
|
||||
targetPortNumber = newTargetPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool disconnect()
|
||||
{
|
||||
socket.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool send (const OSCMessage& message, const String& hostName, int portNumber)
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
|
||||
return outStream.writeMessage (message)
|
||||
&& sendOutputStream (outStream, hostName, portNumber);
|
||||
}
|
||||
|
||||
bool send (const OSCBundle& bundle, const String& hostName, int portNumber)
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
|
||||
return outStream.writeBundle (bundle)
|
||||
&& sendOutputStream (outStream, hostName, portNumber);
|
||||
}
|
||||
|
||||
bool send (const OSCMessage& message) { return send (message, targetHostName, targetPortNumber); }
|
||||
bool send (const OSCBundle& bundle) { return send (bundle, targetHostName, targetPortNumber); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool sendOutputStream (OSCOutputStream& outStream, const String& hostName, int portNumber)
|
||||
{
|
||||
if (socket != nullptr)
|
||||
{
|
||||
const int streamSize = (int) outStream.getDataSize();
|
||||
|
||||
const int bytesWritten = socket->write (hostName, portNumber,
|
||||
outStream.getData(), streamSize);
|
||||
return bytesWritten == streamSize;
|
||||
}
|
||||
|
||||
// if you hit this, you tried to send some OSC data without being
|
||||
// connected to a port! You should call OSCSender::connect() first.
|
||||
jassertfalse;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
OptionalScopedPointer<DatagramSocket> socket;
|
||||
String targetHostName;
|
||||
int targetPortNumber = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
OSCSender::OSCSender() : pimpl (new Pimpl())
|
||||
{
|
||||
}
|
||||
|
||||
OSCSender::~OSCSender()
|
||||
{
|
||||
pimpl->disconnect();
|
||||
pimpl.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCSender::connect (const String& targetHostName, int targetPortNumber)
|
||||
{
|
||||
return pimpl->connect (targetHostName, targetPortNumber);
|
||||
}
|
||||
|
||||
bool OSCSender::connectToSocket (DatagramSocket& socket, const String& targetHostName, int targetPortNumber)
|
||||
{
|
||||
return pimpl->connectToSocket (socket, targetHostName, targetPortNumber);
|
||||
}
|
||||
|
||||
bool OSCSender::disconnect()
|
||||
{
|
||||
return pimpl->disconnect();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCSender::send (const OSCMessage& message) { return pimpl->send (message); }
|
||||
bool OSCSender::send (const OSCBundle& bundle) { return pimpl->send (bundle); }
|
||||
|
||||
bool OSCSender::sendToIPAddress (const String& host, int port, const OSCMessage& message) { return pimpl->send (message, host, port); }
|
||||
bool OSCSender::sendToIPAddress (const String& host, int port, const OSCBundle& bundle) { return pimpl->send (bundle, host, port); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class OSCBinaryWriterTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCBinaryWriterTests()
|
||||
: UnitTest ("OSCBinaryWriter class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("writing OSC addresses");
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
const char check[16] = { '/', 't', 'e', 's', 't', '/', 'f', 'a', 'd', 'e', 'r', '7', '\0', '\0', '\0', '\0' };
|
||||
|
||||
OSCAddress address ("/test/fader7");
|
||||
expect (outStream.writeAddress (address));
|
||||
|
||||
expect (outStream.getDataSize() == sizeof (check));
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
|
||||
beginTest ("writing OSC address patterns");
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
const char check[20] = { '/', '*', '/', '*', 'p', 'u', 't', '/', 'f', 'a', 'd', 'e', 'r', '[', '0', '-', '9', ']', '\0', '\0' };
|
||||
|
||||
OSCAddressPattern ap ("/*/*put/fader[0-9]");
|
||||
expect (outStream.writeAddressPattern (ap));
|
||||
|
||||
expect (outStream.getDataSize() == sizeof (check));
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
|
||||
beginTest ("writing OSC time tags");
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
const char check[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
|
||||
OSCTimeTag tag;
|
||||
|
||||
expect (outStream.writeTimeTag (tag));
|
||||
expect (outStream.getDataSize() == 8);
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
|
||||
beginTest ("writing OSC type tag strings");
|
||||
{
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
|
||||
OSCTypeList list;
|
||||
|
||||
const char check[4] = { ',', '\0', '\0', '\0' };
|
||||
expect (outStream.writeTypeTagString (list));
|
||||
expect (outStream.getDataSize() == 4);
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
|
||||
OSCTypeList list;
|
||||
list.add (OSCTypes::int32);
|
||||
list.add (OSCTypes::float32);
|
||||
|
||||
const char check[4] = { ',', 'i', 'f', '\0' };
|
||||
expect (outStream.writeTypeTagString (list));
|
||||
expect (outStream.getDataSize() == sizeof (check));
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
OSCOutputStream outStream;
|
||||
|
||||
OSCTypeList list;
|
||||
list.add (OSCTypes::blob);
|
||||
list.add (OSCTypes::blob);
|
||||
list.add (OSCTypes::string);
|
||||
|
||||
const char check[8] = { ',', 'b', 'b', 's', '\0', '\0', '\0', '\0' };
|
||||
expect (outStream.writeTypeTagString (list));
|
||||
expect (outStream.getDataSize() == sizeof (check));
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("writing OSC arguments");
|
||||
{
|
||||
// test data:
|
||||
int testInt = -2015;
|
||||
const uint8 testIntRepresentation[] = { 0xFF, 0xFF, 0xF8, 0x21 }; // big endian two's complement
|
||||
|
||||
float testFloat = 345.6125f;
|
||||
const uint8 testFloatRepresentation[] = { 0x43, 0xAC, 0xCE, 0x66 }; // big endian IEEE 754
|
||||
|
||||
String testString = "Hello, World!";
|
||||
const char testStringRepresentation[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' }; // padded to size % 4 == 0
|
||||
|
||||
const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
|
||||
const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
|
||||
const uint8 testBlobRepresentation[] = { 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00 }; // padded to size % 4 == 0
|
||||
|
||||
// write:
|
||||
|
||||
{
|
||||
// int32:
|
||||
OSCArgument arg (testInt);
|
||||
OSCOutputStream outStream;
|
||||
|
||||
expect (outStream.writeArgument (arg));
|
||||
expect (outStream.getDataSize() == 4);
|
||||
expect (std::memcmp (outStream.getData(), testIntRepresentation, sizeof (testIntRepresentation)) == 0);
|
||||
}
|
||||
{
|
||||
// float32:
|
||||
OSCArgument arg (testFloat);
|
||||
OSCOutputStream outStream;
|
||||
|
||||
expect (outStream.writeArgument (arg));
|
||||
expect (outStream.getDataSize() == 4);
|
||||
expect (std::memcmp (outStream.getData(), testFloatRepresentation, sizeof (testFloatRepresentation)) == 0);
|
||||
|
||||
}
|
||||
{
|
||||
// string:
|
||||
expect (testString.length() % 4 != 0); // check whether we actually cover padding
|
||||
static_assert (sizeof (testStringRepresentation) % 4 == 0, "Size must be a multiple of 4");
|
||||
|
||||
OSCArgument arg (testString);
|
||||
OSCOutputStream outStream;
|
||||
|
||||
expect (outStream.writeArgument (arg));
|
||||
expect (outStream.getDataSize() == sizeof (testStringRepresentation));
|
||||
expect (std::memcmp (outStream.getData(), testStringRepresentation, sizeof (testStringRepresentation)) == 0);
|
||||
|
||||
}
|
||||
{
|
||||
// blob:
|
||||
expect (testBlob.getSize() % 4 != 0); // check whether we actually cover padding
|
||||
static_assert (sizeof (testBlobRepresentation) % 4 == 0, "Size must be a multiple of 4");
|
||||
|
||||
OSCArgument arg (testBlob);
|
||||
OSCOutputStream outStream;
|
||||
|
||||
expect (outStream.writeArgument (arg));
|
||||
expect (outStream.getDataSize() == sizeof (testBlobRepresentation));
|
||||
expect (std::memcmp (outStream.getData(), testBlobRepresentation, sizeof (testBlobRepresentation)) == 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Writing strings with correct padding");
|
||||
{
|
||||
// the only OSC-specific thing to check is the correct number of padding zeros
|
||||
|
||||
{
|
||||
OSCArgument with15Chars ("123456789012345");
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with15Chars));
|
||||
expect (outStream.getDataSize() == 16);
|
||||
}
|
||||
{
|
||||
OSCArgument with16Chars ("1234567890123456");
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with16Chars));
|
||||
expect (outStream.getDataSize() == 20);
|
||||
}
|
||||
{
|
||||
OSCArgument with17Chars ("12345678901234567");
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with17Chars));
|
||||
expect (outStream.getDataSize() == 20);
|
||||
}
|
||||
{
|
||||
|
||||
OSCArgument with18Chars ("123456789012345678");
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with18Chars));
|
||||
expect (outStream.getDataSize() == 20);
|
||||
}
|
||||
{
|
||||
|
||||
OSCArgument with19Chars ("1234567890123456789");
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with19Chars));
|
||||
expect (outStream.getDataSize() == 20);
|
||||
}
|
||||
{
|
||||
|
||||
OSCArgument with20Chars ("12345678901234567890");
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with20Chars));
|
||||
expect (outStream.getDataSize() == 24);
|
||||
}
|
||||
}
|
||||
beginTest ("Writing blobs with correct padding");
|
||||
{
|
||||
const char buffer[20] = {};
|
||||
{
|
||||
OSCArgument with15Bytes (MemoryBlock (buffer, 15));
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with15Bytes));
|
||||
expect (outStream.getDataSize() == 20);
|
||||
}
|
||||
{
|
||||
OSCArgument with16Bytes (MemoryBlock (buffer, 16));
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with16Bytes));
|
||||
expect (outStream.getDataSize() == 20);
|
||||
}
|
||||
{
|
||||
OSCArgument with17Bytes (MemoryBlock (buffer, 17));
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with17Bytes));
|
||||
expect (outStream.getDataSize() == 24);
|
||||
}
|
||||
{
|
||||
OSCArgument with18Bytes (MemoryBlock (buffer, 18));
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with18Bytes));
|
||||
expect (outStream.getDataSize() == 24);
|
||||
}
|
||||
{
|
||||
OSCArgument with19Bytes (MemoryBlock (buffer, 19));
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with19Bytes));
|
||||
expect (outStream.getDataSize() == 24);
|
||||
}
|
||||
{
|
||||
OSCArgument with20Bytes (MemoryBlock (buffer, 20));
|
||||
OSCOutputStream outStream;
|
||||
expect (outStream.writeArgument (with20Bytes));
|
||||
expect (outStream.getDataSize() == 24);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Writing OSC messages.");
|
||||
{
|
||||
{
|
||||
int32 testInt = -2015;
|
||||
float testFloat = 345.6125f;
|
||||
String testString = "Hello, World!";
|
||||
|
||||
const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
|
||||
const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
|
||||
|
||||
uint8 check[52] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0',
|
||||
',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
|
||||
0xFF, 0xFF, 0xF8, 0x21,
|
||||
0x43, 0xAC, 0xCE, 0x66,
|
||||
'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
|
||||
0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
OSCOutputStream outStream;
|
||||
|
||||
OSCMessage msg ("/test");
|
||||
|
||||
msg.addInt32 (testInt);
|
||||
msg.addFloat32 (testFloat);
|
||||
msg.addString (testString);
|
||||
msg.addBlob (testBlob);
|
||||
|
||||
expect (outStream.writeMessage (msg));
|
||||
expect (outStream.getDataSize() == sizeof (check));
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Writing OSC bundle.");
|
||||
{
|
||||
{
|
||||
int32 testInt = -2015;
|
||||
float testFloat = 345.6125f;
|
||||
String testString = "Hello, World!";
|
||||
const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
|
||||
const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
|
||||
|
||||
uint8 check[] = {
|
||||
'#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
|
||||
0x00, 0x00, 0x00, 0x34,
|
||||
|
||||
'/', 't', 'e', 's', 't', '/', '1', '\0',
|
||||
',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
|
||||
0xFF, 0xFF, 0xF8, 0x21,
|
||||
0x43, 0xAC, 0xCE, 0x66,
|
||||
'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
|
||||
0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x0C,
|
||||
|
||||
'/', 't', 'e', 's', 't', '/', '2', '\0',
|
||||
',', '\0', '\0', '\0',
|
||||
|
||||
0x00, 0x00, 0x00, 0x10,
|
||||
|
||||
'#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
};
|
||||
|
||||
OSCOutputStream outStream;
|
||||
|
||||
OSCBundle bundle;
|
||||
|
||||
OSCMessage msg1 ("/test/1");
|
||||
msg1.addInt32 (testInt);
|
||||
msg1.addFloat32 (testFloat);
|
||||
msg1.addString (testString);
|
||||
msg1.addBlob (testBlob);
|
||||
bundle.addElement (msg1);
|
||||
|
||||
OSCMessage msg2 ("/test/2");
|
||||
bundle.addElement (msg2);
|
||||
|
||||
OSCBundle subBundle;
|
||||
bundle.addElement (subBundle);
|
||||
|
||||
expect (outStream.writeBundle (bundle));
|
||||
expect (outStream.getDataSize() == sizeof (check));
|
||||
expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCBinaryWriterTests OSCBinaryWriterUnitTests;
|
||||
|
||||
//==============================================================================
|
||||
class OSCRoundTripTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCRoundTripTests()
|
||||
: UnitTest ("OSCRoundTripTests class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("Empty OSC message");
|
||||
{
|
||||
OSCMessage outMessage ("/test/empty");
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeMessage (outMessage);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCMessage inMessage = input.readMessage();
|
||||
|
||||
expectEquals (inMessage.size(), 0);
|
||||
}
|
||||
|
||||
beginTest ("OSC message with single argument");
|
||||
{
|
||||
OSCMessage outMessage ("/test/one_arg", 42);
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeMessage (outMessage);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCMessage inMessage = input.readMessage();
|
||||
|
||||
expectEquals (inMessage.size(), 1);
|
||||
expectEquals (inMessage[0].getInt32(), 42);
|
||||
}
|
||||
|
||||
beginTest ("OSC message with multiple arguments");
|
||||
{
|
||||
OSCMessage outMessage ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeMessage (outMessage);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCMessage inMessage = input.readMessage();
|
||||
|
||||
expectEquals (inMessage.size(), 4);
|
||||
expectEquals (inMessage[0].getInt32(), 42);
|
||||
expectEquals (inMessage[1].getFloat32(), 0.5f);
|
||||
expectEquals (inMessage[2].getString(), String ("foo"));
|
||||
expectEquals (inMessage[3].getString(), String ("bar"));
|
||||
}
|
||||
|
||||
beginTest ("Empty OSC bundle");
|
||||
{
|
||||
OSCBundle outBundle;
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeBundle (outBundle);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCBundle inBundle = input.readBundle();
|
||||
|
||||
expectEquals (inBundle.size(), 0);
|
||||
}
|
||||
|
||||
beginTest ("OSC bundle with single message");
|
||||
{
|
||||
OSCMessage outMessage ("/test/one_arg", 42);
|
||||
OSCBundle outBundle;
|
||||
outBundle.addElement (outMessage);
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeBundle (outBundle);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCBundle inBundle = input.readBundle();
|
||||
|
||||
expectEquals (inBundle.size(), 1);
|
||||
|
||||
OSCMessage inMessage = inBundle[0].getMessage();
|
||||
|
||||
expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
|
||||
expectEquals (inMessage.size(), 1);
|
||||
expectEquals (inMessage[0].getInt32(), 42);
|
||||
}
|
||||
|
||||
beginTest ("OSC bundle with multiple messages");
|
||||
{
|
||||
OSCMessage outMessage1 ("/test/empty");
|
||||
OSCMessage outMessage2 ("/test/one_arg", 42);
|
||||
OSCMessage outMessage3 ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
|
||||
|
||||
OSCBundle outBundle;
|
||||
outBundle.addElement (outMessage1);
|
||||
outBundle.addElement (outMessage2);
|
||||
outBundle.addElement (outMessage3);
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeBundle (outBundle);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCBundle inBundle = input.readBundle();
|
||||
|
||||
expectEquals (inBundle.size(), 3);
|
||||
|
||||
{
|
||||
OSCMessage inMessage = inBundle[0].getMessage();
|
||||
|
||||
expectEquals (inMessage.getAddressPattern().toString(), String ("/test/empty"));
|
||||
expectEquals (inMessage.size(), 0);
|
||||
}
|
||||
{
|
||||
OSCMessage inMessage = inBundle[1].getMessage();
|
||||
|
||||
expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
|
||||
expectEquals (inMessage.size(), 1);
|
||||
expectEquals (inMessage[0].getInt32(), 42);
|
||||
}
|
||||
{
|
||||
OSCMessage inMessage = inBundle[2].getMessage();
|
||||
|
||||
expectEquals (inMessage.getAddressPattern().toString(), String ("/test/four_args"));
|
||||
expectEquals (inMessage.size(), 4);
|
||||
expectEquals (inMessage[0].getInt32(), 42);
|
||||
expectEquals (inMessage[1].getFloat32(), 0.5f);
|
||||
expectEquals (inMessage[2].getString(), String ("foo"));
|
||||
expectEquals (inMessage[3].getString(), String ("bar"));
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("OSC bundle containing another bundle");
|
||||
{
|
||||
OSCBundle outBundleNested;
|
||||
outBundleNested.addElement (OSCMessage ("/test/one_arg", 42));
|
||||
|
||||
OSCBundle outBundle;
|
||||
outBundle.addElement (outBundleNested);
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeBundle (outBundle);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCBundle inBundle = input.readBundle();
|
||||
|
||||
expectEquals (inBundle.size(), 1);
|
||||
expect (inBundle[0].isBundle());
|
||||
OSCBundle inBundleNested = inBundle[0].getBundle();
|
||||
expectEquals (inBundleNested.size(), 1);
|
||||
expect (inBundleNested[0].isMessage());
|
||||
|
||||
OSCMessage msg = inBundleNested[0].getMessage();
|
||||
|
||||
expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
|
||||
expectEquals (msg.size(), 1);
|
||||
expectEquals (msg[0].getInt32(), 42);
|
||||
}
|
||||
|
||||
beginTest ("OSC bundle containing multiple other bundles");
|
||||
{
|
||||
OSCBundle outBundleNested1;
|
||||
outBundleNested1.addElement (OSCMessage ("/test/empty"));
|
||||
OSCBundle outBundleNested2;
|
||||
outBundleNested2.addElement (OSCMessage ("/test/one_arg", 42));
|
||||
|
||||
OSCBundle outBundle;
|
||||
outBundle.addElement (outBundleNested1);
|
||||
outBundle.addElement (outBundleNested2);
|
||||
|
||||
OSCOutputStream output;
|
||||
output.writeBundle (outBundle);
|
||||
|
||||
OSCInputStream input (output.getData(), output.getDataSize());
|
||||
OSCBundle inBundle = input.readBundle();
|
||||
|
||||
expectEquals (inBundle.size(), 2);
|
||||
|
||||
{
|
||||
expect (inBundle[0].isBundle());
|
||||
OSCBundle inBundleNested = inBundle[0].getBundle();
|
||||
expectEquals (inBundleNested.size(), 1);
|
||||
expect (inBundleNested[0].isMessage());
|
||||
|
||||
OSCMessage msg = inBundleNested[0].getMessage();
|
||||
|
||||
expectEquals (msg.getAddressPattern().toString(), String ("/test/empty"));
|
||||
expectEquals (msg.size(), 0);
|
||||
}
|
||||
{
|
||||
expect (inBundle[1].isBundle());
|
||||
OSCBundle inBundleNested = inBundle[1].getBundle();
|
||||
expectEquals (inBundleNested.size(), 1);
|
||||
expect (inBundleNested[0].isMessage());
|
||||
|
||||
OSCMessage msg = inBundleNested[0].getMessage();
|
||||
|
||||
expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
|
||||
expectEquals (msg.size(), 1);
|
||||
expectEquals (msg[0].getInt32(), 42);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCRoundTripTests OSCRoundTripUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
163
deps/juce/modules/juce_osc/osc/juce_OSCSender.h
vendored
Normal file
163
deps/juce/modules/juce_osc/osc/juce_OSCSender.h
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC message sender.
|
||||
|
||||
An OSCSender object can connect to a network port. It then can send OSC
|
||||
messages and bundles to a specified host over an UDP socket.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCSender
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructs a new OSCSender. */
|
||||
OSCSender();
|
||||
|
||||
/** Destructor. */
|
||||
~OSCSender();
|
||||
|
||||
//==============================================================================
|
||||
/** Connects to a datagram socket and prepares the socket for sending OSC
|
||||
packets to the specified target.
|
||||
|
||||
Note: The operating system will choose which specific network adapter(s)
|
||||
to bind your socket to, and which local port to use for the sender.
|
||||
|
||||
@param targetHostName The remote host to which messages will be send.
|
||||
@param targetPortNumber The remote UDP port number on which the host will
|
||||
receive the messages.
|
||||
|
||||
@returns true if the connection was successful; false otherwise.
|
||||
@see send, disconnect.
|
||||
*/
|
||||
bool connect (const String& targetHostName, int targetPortNumber);
|
||||
|
||||
/** Uses an existing datagram socket for sending OSC packets to the specified target.
|
||||
|
||||
@param socket An existing datagram socket. Make sure this doesn't
|
||||
get deleted while this class is still using it!
|
||||
@param targetHostName The remote host to which messages will be send.
|
||||
@param targetPortNumber The remote UDP port number on which the host will
|
||||
receive the messages.
|
||||
|
||||
@returns true if the connection was successful; false otherwise.
|
||||
@see connect, send, disconnect.
|
||||
*/
|
||||
bool connectToSocket (DatagramSocket& socket, const String& targetHostName, int targetPortNumber);
|
||||
|
||||
//==============================================================================
|
||||
/** Disconnects from the currently used UDP port.
|
||||
@returns true if the disconnection was successful; false otherwise.
|
||||
@see connect.
|
||||
*/
|
||||
bool disconnect();
|
||||
|
||||
//==============================================================================
|
||||
/** Sends an OSC message to the target.
|
||||
@param message The OSC message to send.
|
||||
@returns true if the operation was successful.
|
||||
*/
|
||||
bool send (const OSCMessage& message);
|
||||
|
||||
/** Send an OSC bundle to the target.
|
||||
@param bundle The OSC bundle to send.
|
||||
@returns true if the operation was successful.
|
||||
*/
|
||||
bool send (const OSCBundle& bundle);
|
||||
|
||||
/** Sends an OSC message to a specific IP address and port.
|
||||
This overrides the address and port that was originally set for this sender.
|
||||
@param targetIPAddress The IP address to send to
|
||||
@param targetPortNumber The target port number
|
||||
@param message The OSC message to send.
|
||||
@returns true if the operation was successful.
|
||||
*/
|
||||
bool sendToIPAddress (const String& targetIPAddress, int targetPortNumber,
|
||||
const OSCMessage& message);
|
||||
|
||||
/** Sends an OSC bundle to a specific IP address and port.
|
||||
This overrides the address and port that was originally set for this sender.
|
||||
@param targetIPAddress The IP address to send to
|
||||
@param targetPortNumber The target port number
|
||||
@param bundle The OSC bundle to send.
|
||||
@returns true if the operation was successful.
|
||||
*/
|
||||
bool sendToIPAddress (const String& targetIPAddress, int targetPortNumber,
|
||||
const OSCBundle& bundle);
|
||||
|
||||
/** Creates a new OSC message with the specified address pattern and list
|
||||
of arguments, and sends it to the target.
|
||||
|
||||
@param address The OSC address pattern of the message
|
||||
(you can use a string literal here).
|
||||
@param args The list of arguments for the message.
|
||||
*/
|
||||
template <typename... Args>
|
||||
bool send (const OSCAddressPattern& address, Args&&... args);
|
||||
|
||||
/** Creates a new OSC message with the specified address pattern and list
|
||||
of arguments, and sends it to the target.
|
||||
|
||||
@param targetIPAddress The IP address to send to
|
||||
@param targetPortNumber The target port number
|
||||
@param address The OSC address pattern of the message
|
||||
(you can use a string literal here).
|
||||
@param args The list of arguments for the message.
|
||||
*/
|
||||
template <typename... Args>
|
||||
bool sendToIPAddress (const String& targetIPAddress, int targetPortNumber,
|
||||
const OSCAddressPattern& address, Args&&... args);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCSender)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename... Args>
|
||||
bool OSCSender::send (const OSCAddressPattern& address, Args&&... args)
|
||||
{
|
||||
return send (OSCMessage (address, std::forward<Args> (args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool OSCSender::sendToIPAddress (const String& targetIPAddress, int targetPortNumber,
|
||||
const OSCAddressPattern& address, Args&&... args)
|
||||
{
|
||||
return sendToIPAddress (targetIPAddress, targetPortNumber, OSCMessage (address, std::forward<Args> (args)...));
|
||||
}
|
||||
|
||||
} // namespace juce
|
155
deps/juce/modules/juce_osc/osc/juce_OSCTimeTag.cpp
vendored
Normal file
155
deps/juce/modules/juce_osc/osc/juce_OSCTimeTag.cpp
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
const OSCTimeTag OSCTimeTag::immediately;
|
||||
|
||||
static const uint64 millisecondsBetweenOscAndJuceEpochs = 2208988800000ULL;
|
||||
static const uint64 rawTimeTagRepresentingImmediately = 0x0000000000000001ULL;
|
||||
|
||||
//==============================================================================
|
||||
OSCTimeTag::OSCTimeTag() noexcept : rawTimeTag (rawTimeTagRepresentingImmediately)
|
||||
{
|
||||
}
|
||||
|
||||
OSCTimeTag::OSCTimeTag (uint64 t) noexcept : rawTimeTag (t)
|
||||
{
|
||||
}
|
||||
|
||||
OSCTimeTag::OSCTimeTag (Time time) noexcept
|
||||
{
|
||||
const uint64 milliseconds = (uint64) time.toMilliseconds() + millisecondsBetweenOscAndJuceEpochs;
|
||||
|
||||
uint64 seconds = milliseconds / 1000;
|
||||
uint32 fractionalPart = uint32 (4294967.296 * (milliseconds % 1000));
|
||||
|
||||
rawTimeTag = (seconds << 32) + fractionalPart;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Time OSCTimeTag::toTime() const noexcept
|
||||
{
|
||||
const uint64 seconds = rawTimeTag >> 32;
|
||||
const uint32 fractionalPart = (rawTimeTag & 0x00000000FFFFFFFFULL);
|
||||
|
||||
const auto fractionalPartInMillis = (double) fractionalPart / 4294967.296;
|
||||
|
||||
// now using signed integer, because this is allowed to become negative:
|
||||
const auto juceTimeInMillis = (int64) (seconds * 1000)
|
||||
+ (int64) roundToInt (fractionalPartInMillis)
|
||||
- (int64) millisecondsBetweenOscAndJuceEpochs;
|
||||
|
||||
return Time (juceTimeInMillis);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OSCTimeTag::isImmediately() const noexcept
|
||||
{
|
||||
return rawTimeTag == rawTimeTagRepresentingImmediately;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class OSCTimeTagTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
OSCTimeTagTests()
|
||||
: UnitTest ("OSCTimeTag class", UnitTestCategories::osc)
|
||||
{}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("Basics");
|
||||
|
||||
{
|
||||
OSCTimeTag tag;
|
||||
expect (tag.isImmediately());
|
||||
}
|
||||
{
|
||||
OSCTimeTag tag (3535653);
|
||||
expect (! tag.isImmediately());
|
||||
|
||||
OSCTimeTag otherTag;
|
||||
otherTag = tag;
|
||||
expect (! otherTag.isImmediately());
|
||||
|
||||
OSCTimeTag copyTag (tag);
|
||||
expect (! copyTag.isImmediately());
|
||||
}
|
||||
|
||||
beginTest ("Conversion to/from JUCE Time");
|
||||
|
||||
{
|
||||
Time time;
|
||||
OSCTimeTag tag (time);
|
||||
expect (! tag.isImmediately());
|
||||
}
|
||||
{
|
||||
OSCTimeTag tag;
|
||||
Time time = tag.toTime();
|
||||
expect (time < Time::getCurrentTime());
|
||||
}
|
||||
{
|
||||
Time currentTime (Time::currentTimeMillis());
|
||||
double deltaInSeconds = 1.234;
|
||||
RelativeTime delta (deltaInSeconds);
|
||||
Time laterTime = currentTime + delta;
|
||||
|
||||
OSCTimeTag currentTimeTag (currentTime);
|
||||
OSCTimeTag laterTimeTag (laterTime);
|
||||
|
||||
uint64 currentTimeTagRaw = currentTimeTag.getRawTimeTag();
|
||||
uint64 laterTimeTagRaw = laterTimeTag.getRawTimeTag();
|
||||
|
||||
// in the raw time tag, the most significant 32 bits are seconds,
|
||||
// so let's verify that the difference is right:
|
||||
uint64 diff = laterTimeTagRaw - currentTimeTagRaw;
|
||||
double acceptableErrorInSeconds = 0.000001; // definitely not audible anymore.
|
||||
|
||||
expect ((float) diff / float (1ULL << 32) < deltaInSeconds + acceptableErrorInSeconds );
|
||||
expect ((float) diff / float (1ULL << 32) > deltaInSeconds - acceptableErrorInSeconds );
|
||||
|
||||
// round trip:
|
||||
|
||||
Time currentTime2 = currentTimeTag.toTime();
|
||||
Time laterTime2 = laterTimeTag.toTime();
|
||||
RelativeTime delta2 = laterTime2 - currentTime2;
|
||||
|
||||
expect (currentTime2 == currentTime);
|
||||
expect (laterTime2 == laterTime);
|
||||
expect (delta2 == delta);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OSCTimeTagTests OSCTimeTagUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
86
deps/juce/modules/juce_osc/osc/juce_OSCTimeTag.h
vendored
Normal file
86
deps/juce/modules/juce_osc/osc/juce_OSCTimeTag.h
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An OSC time tag.
|
||||
|
||||
OSC time tags are part of OSCBundle objects.
|
||||
|
||||
In accordance with the OSC 1.0 specification, the internal timestamp stored in
|
||||
OSCTimeTag uses the same binary format as NTP timestamps. The representation
|
||||
is by a 64 bit fixed point number. The first 32 bits specify the number of
|
||||
seconds since midnight on January 1, 1900, and the last 32 bits specify
|
||||
fractional parts of a second to a precision of about 200 picoseconds.
|
||||
|
||||
The time tag value consisting of 63 zero bits followed by a one in the least
|
||||
significant bit is a special case meaning "immediately".
|
||||
|
||||
For a more user-friendly time format, convert OSCTimeTag to a juce::Time object
|
||||
using toTime().
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCTimeTag
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Default constructor.
|
||||
Constructs an OSCTimeTag object with the special value representing "immediately".
|
||||
*/
|
||||
OSCTimeTag() noexcept;
|
||||
|
||||
/** Constructs an OSCTimeTag object from a raw binary OSC time tag. */
|
||||
OSCTimeTag (uint64 rawTimeTag) noexcept;
|
||||
|
||||
/** Constructs an OSCTimeTag object from a juce::Time object. */
|
||||
OSCTimeTag (Time time) noexcept;
|
||||
|
||||
/** Returns a juce::Time object representing the same time as the OSCTimeTag.
|
||||
|
||||
If the OSCTimeTag has the special value representing "immediately", the
|
||||
resulting juce::Time object will represent an arbitrary point of time (but
|
||||
guaranteed to be in the past), since juce::Time does not have such a special value.
|
||||
*/
|
||||
Time toTime() const noexcept;
|
||||
|
||||
/** Returns true if the OSCTimeTag object has the special value representing "immediately". */
|
||||
bool isImmediately() const noexcept;
|
||||
|
||||
/** Returns the raw binary OSC time tag representation. */
|
||||
uint64 getRawTimeTag() const noexcept { return rawTimeTag; }
|
||||
|
||||
/** The special value representing "immediately". */
|
||||
static const OSCTimeTag immediately;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
uint64 rawTimeTag;
|
||||
};
|
||||
|
||||
} // namespace juce
|
48
deps/juce/modules/juce_osc/osc/juce_OSCTypes.cpp
vendored
Normal file
48
deps/juce/modules/juce_osc/osc/juce_OSCTypes.cpp
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
const OSCType OSCTypes::int32 = 'i';
|
||||
const OSCType OSCTypes::float32 = 'f';
|
||||
const OSCType OSCTypes::string = 's';
|
||||
const OSCType OSCTypes::blob = 'b';
|
||||
const OSCType OSCTypes::colour = 'r';
|
||||
|
||||
uint32 OSCColour::toInt32() const
|
||||
{
|
||||
return ByteOrder::makeInt (alpha, blue, green, red);
|
||||
}
|
||||
|
||||
OSCColour OSCColour::fromInt32 (uint32 c)
|
||||
{
|
||||
return { (uint8) (c >> 24),
|
||||
(uint8) (c >> 16),
|
||||
(uint8) (c >> 8),
|
||||
(uint8) c };
|
||||
}
|
||||
|
||||
} // namespace juce
|
123
deps/juce/modules/juce_osc/osc/juce_OSCTypes.h
vendored
Normal file
123
deps/juce/modules/juce_osc/osc/juce_OSCTypes.h
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** The type used for OSC type tags. */
|
||||
using OSCType = char;
|
||||
|
||||
|
||||
/** The type used for OSC type tag strings. */
|
||||
using OSCTypeList = Array<OSCType>;
|
||||
|
||||
//==============================================================================
|
||||
/** The definitions of supported OSC types and their associated OSC type tags,
|
||||
as defined in the OpenSoundControl 1.0 specification.
|
||||
|
||||
Note: This implementation does not support any additional type tags that
|
||||
are not part of the specification.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
class JUCE_API OSCTypes
|
||||
{
|
||||
public:
|
||||
static const OSCType int32;
|
||||
static const OSCType float32;
|
||||
static const OSCType string;
|
||||
static const OSCType blob;
|
||||
static const OSCType colour;
|
||||
|
||||
static bool isSupportedType (OSCType type) noexcept
|
||||
{
|
||||
return type == OSCTypes::int32
|
||||
|| type == OSCTypes::float32
|
||||
|| type == OSCTypes::string
|
||||
|| type == OSCTypes::blob
|
||||
|| type == OSCTypes::colour;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a 32-bit RGBA colour for passing to and from an OSCArgument.
|
||||
@see OSCArgument, OSCTypes::colour
|
||||
@tags{OSC}
|
||||
*/
|
||||
struct OSCColour
|
||||
{
|
||||
uint8 red, green, blue, alpha;
|
||||
|
||||
uint32 toInt32() const;
|
||||
static OSCColour fromInt32 (uint32);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Base class for exceptions that can be thrown by methods in the OSC module.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
struct OSCException : public std::exception
|
||||
{
|
||||
OSCException (const String& desc)
|
||||
: description (desc)
|
||||
{
|
||||
#if ! JUCE_UNIT_TESTS
|
||||
DBG ("OSCFormatError: " + description);
|
||||
#endif
|
||||
}
|
||||
|
||||
String description;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Exception type thrown when the OSC module fails to parse something because
|
||||
of a data format not compatible with the OpenSoundControl 1.0 specification.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
struct OSCFormatError : public OSCException
|
||||
{
|
||||
OSCFormatError (const String& desc) : OSCException (desc) {}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Exception type thrown in cases of unexpected errors in the OSC module.
|
||||
|
||||
Note: This should never happen, and all the places where this is thrown
|
||||
should have a preceding jassertfalse to facilitate debugging.
|
||||
|
||||
@tags{OSC}
|
||||
*/
|
||||
struct OSCInternalError : public OSCException
|
||||
{
|
||||
OSCInternalError (const String& desc) : OSCException (desc) {}
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user