mirror of
https://github.com/mapmapteam/mapmap.git
synced 2026-06-16 12:33:19 +02:00
Fix Merge conflict branch 'develop' of https://github.com/mapmapteam/mapmap into develop
This commit is contained in:
+16
-5
@@ -1,4 +1,6 @@
|
||||
CONFIG += qt debug c++11
|
||||
CONFIG += qt
|
||||
CONFIG += debug
|
||||
CONFIG += c++11
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
@@ -20,6 +22,7 @@ TRANSLATIONS += \
|
||||
translations/mapmap_fr.ts \
|
||||
translations/mapmap_zh_CN.ts \
|
||||
translations/mapmap_zh_TW.ts
|
||||
|
||||
RESOURCES = \
|
||||
translations/translation.qrc \
|
||||
docs/documentation.qrc \
|
||||
@@ -27,8 +30,11 @@ RESOURCES = \
|
||||
|
||||
# Manage lrelease (for translations)
|
||||
isEmpty(QMAKE_LRELEASE) {
|
||||
win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe
|
||||
else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
|
||||
win32: {
|
||||
QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe
|
||||
}
|
||||
!win32:
|
||||
QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
|
||||
}
|
||||
updateqm.input = TRANSLATIONS
|
||||
updateqm.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.qm
|
||||
@@ -45,9 +51,14 @@ system($$QMAKE_LRELEASE mapmap.pro) # Run lrelease
|
||||
|
||||
# Linux-specific:
|
||||
unix:!macx {
|
||||
QMAKE_CXXFLAGS += -D_GLIBCXX_USE_CXX11_ABI=0
|
||||
QMAKE_CXXFLAGS += -Wno-expansion-to-defined
|
||||
QMAKE_CXXFLAGS += -D_GLIBCXX_USE_CXX11_ABI=0
|
||||
QMAKE_CXXFLAGS += -Wno-expansion-to-defined
|
||||
}
|
||||
|
||||
CONFIG -= qtquickcompiler
|
||||
|
||||
unix:macx {
|
||||
CONFIG += sdk_no_version_check
|
||||
}
|
||||
|
||||
# Adds the tarball target
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2010 Tristan Matthews <le.businessman@gmail.com>
|
||||
* (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com
|
||||
* (c) 2013 Alexandre Quessy -- alexandre(@)quessy(.)net
|
||||
* (c) 2020 Alexandre Quessy -- alexandre(@)quessy(.)net
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,72 +21,42 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_OSC
|
||||
#include "OscInterface.h"
|
||||
#include "MainWindow.h"
|
||||
#include <QVariant>
|
||||
|
||||
namespace mmp {
|
||||
|
||||
const QString OscInterface::OSC_ROOT("mapmap");
|
||||
const QString OscInterface::OSC_PAINT("paint");
|
||||
const QString OscInterface::OSC_MAPPING("mapping");
|
||||
const QString OscInterface::OSC_QUIT("quit");
|
||||
const QString OscInterface::OSC_PLAY("play");
|
||||
const QString OscInterface::OSC_PAUSE("pause");
|
||||
const QString OscInterface::OSC_REWIND("rewind");
|
||||
static const QString OSC_ROOT("mapmap");
|
||||
static const QString OSC_PAINT("paint");
|
||||
static const QString OSC_MAPPING("mapping");
|
||||
static const QString OSC_QUIT("quit");
|
||||
static const QString OSC_PLAY("play");
|
||||
static const QString OSC_PAUSE("pause");
|
||||
static const QString OSC_REWIND("rewind");
|
||||
|
||||
const QString OscInterface::OSC_PAINT_MEDIA("media");
|
||||
const QString OscInterface::OSC_PAINT_COLOR("color");
|
||||
static const QString OSC_PAINT_MEDIA("media");
|
||||
static const QString OSC_PAINT_COLOR("color");
|
||||
|
||||
OscInterface::OscInterface(
|
||||
// MainWindow* owner,
|
||||
const std::string &listen_port) :
|
||||
int listen_port) :
|
||||
receiver_(listen_port),
|
||||
// owner_(owner),
|
||||
messaging_queue_() {
|
||||
//if (listen_port != OSC_PORT_NONE)
|
||||
receiving_enabled_ = true;
|
||||
if (receiving_enabled_) {
|
||||
qDebug() << "Listening osc.udp://localhost:" << listen_port.c_str();
|
||||
// receiver_.addHandler("/ping", "", ping_cb, this);
|
||||
// receiver_.addHandler("/pong", "", pong_cb, this);
|
||||
//receiver_.addHandler("/image/path", "ss", image_path_cb, this);
|
||||
receiver_.addHandler(NULL, NULL, genericHandler, this);
|
||||
qDebug() << "Listening osc.udp://localhost:" << listen_port;
|
||||
// setup handler
|
||||
QObject::connect(&receiver_, &OscReceiver::messageReceived, [=](const QString& oscAddress, const QVariantList& arguments) {
|
||||
this->messageReceivedCb(oscAddress, arguments);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OscInterface::~OscInterface() {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles /pong. Does nothing.
|
||||
*/
|
||||
int OscInterface::pong_cb(const char *path, const char * /*types*/,
|
||||
lo_arg ** /*argv*/, int /*argc*/, void * /*data*/, void *user_data) {
|
||||
OscInterface* context = static_cast<OscInterface*>(user_data);
|
||||
if (context->is_verbose())
|
||||
{
|
||||
qDebug() << "Got " << path;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles /ping. Does nothing.
|
||||
*/
|
||||
int OscInterface::ping_cb(const char *path, const char * /*types*/,
|
||||
lo_arg ** /*argv*/, int /*argc*/, void * /*data*/, void *user_data)
|
||||
{
|
||||
OscInterface* context = static_cast<OscInterface*>(user_data);
|
||||
if (context->is_verbose())
|
||||
{
|
||||
qDebug() << "Got " << path;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OscInterface::push_command(QVariantList command)
|
||||
{
|
||||
messaging_queue_.push(command);
|
||||
@@ -110,52 +81,45 @@ void OscInterface::consume_commands(MainWindow &main_window)
|
||||
|
||||
void OscInterface::start()
|
||||
{
|
||||
if (receiving_enabled_)
|
||||
{
|
||||
// start a thread to try and subscribe us
|
||||
receiver_.listen(); // start listening in separate thread
|
||||
}
|
||||
}
|
||||
|
||||
// catch any incoming messages and display them. returning 1 means that the
|
||||
// message has not been fully handled and the server should try other methods
|
||||
int OscInterface::genericHandler(const char *path, const char *types,
|
||||
lo_arg **argv, int argc, void * /*data*/, void * user_data)
|
||||
{
|
||||
OscInterface* context = static_cast<OscInterface*>(user_data);
|
||||
QVariantList message;
|
||||
void OscInterface::messageReceivedCb(const QString& oscAddress, const QVariantList& arguments) {
|
||||
QVariantList command;
|
||||
command.append(QVariant(oscAddress));
|
||||
|
||||
message.append(QVariant(QString(path)));
|
||||
message.append(QVariant(QString(types)));
|
||||
QString types = "";
|
||||
for (int i = 0; i < arguments.count(); ++ i) {
|
||||
QVariant argument = arguments[i];
|
||||
QMetaType::Type type = static_cast<QMetaType::Type>(argument.type());
|
||||
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
switch (types[i])
|
||||
{
|
||||
case 'i':
|
||||
message.append(QVariant(argv[i]->i));
|
||||
break;
|
||||
case 'f':
|
||||
message.append(QVariant((double) argv[i]->f));
|
||||
break;
|
||||
case 's':
|
||||
message.append(QVariant(QString(static_cast<const char *>(&argv[i]->s))));
|
||||
break;
|
||||
case 'd':
|
||||
message.append(QVariant((double) argv[i]->d));
|
||||
break;
|
||||
case 'T':
|
||||
message.append(QVariant(true));
|
||||
break;
|
||||
case 'F':
|
||||
message.append(QVariant(false));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (type == QMetaType::Int) {
|
||||
types += "i";
|
||||
} else if (type == QMetaType::Float) {
|
||||
types += "f";
|
||||
} else if (type == QMetaType::Double) {
|
||||
types += "f";
|
||||
} else if (type == QMetaType::QString) {
|
||||
types += "s";
|
||||
} else if (type == QMetaType::Bool) {
|
||||
if (argument.toBool()) {
|
||||
types += "T";
|
||||
} else {
|
||||
types += "F";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Unhandled OSC argument type " << argument.typeName();
|
||||
}
|
||||
// TODO: implement other OSC types
|
||||
}
|
||||
context->push_command(message);
|
||||
return 0; // handled
|
||||
command.append(QVariant(types));
|
||||
|
||||
for (int i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
QVariant argument = arguments[i];
|
||||
command.append(argument);
|
||||
}
|
||||
|
||||
this->push_command(command);
|
||||
}
|
||||
|
||||
static void printCommand(QVariantList &command)
|
||||
@@ -206,14 +170,14 @@ void OscInterface::applyOscCommand(MainWindow &main_window, QVariantList & comma
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = command.at(0).toString();
|
||||
QString path = command.at(0).toString();
|
||||
QString typetags = command.at(1).toString();
|
||||
|
||||
bool pathIsValid = false;
|
||||
// Walks through each token in the form /mapmap/paint/color - The first token is "mapmap", and then "paint"
|
||||
QPair<QString,QString> iterator = next(path);
|
||||
|
||||
if (iterator.first.isEmpty())
|
||||
{
|
||||
if (iterator.first.isEmpty()) {
|
||||
// Check root tag.
|
||||
iterator = next(iterator.second);
|
||||
if (iterator.first == OSC_ROOT)
|
||||
@@ -224,7 +188,7 @@ void OscInterface::applyOscCommand(MainWindow &main_window, QVariantList & comma
|
||||
// Paint.
|
||||
if (iterator.first == OSC_PAINT)
|
||||
{
|
||||
// Find mapping (or mappings).
|
||||
// Find paint (or paints).
|
||||
if (command.size() >= 3)
|
||||
{
|
||||
// Find paint (or paints).
|
||||
@@ -247,8 +211,10 @@ void OscInterface::applyOscCommand(MainWindow &main_window, QVariantList & comma
|
||||
pathIsValid = true;
|
||||
}
|
||||
// Property setting (eg. opacity)
|
||||
else if (command.size() >= 4)
|
||||
else if (command.size() >= 4) {
|
||||
qDebug() << "Attempt to set a paint property" << iterator.first << command.at(3);
|
||||
pathIsValid |= setElementProperty(elem, iterator.first, command.at(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,7 +281,7 @@ QPair<QString,QString> OscInterface::next(const QString& path)
|
||||
int idx = path.indexOf('/');
|
||||
if (idx >= 0)
|
||||
{
|
||||
return QPair<QString,QString>(path.left(idx), path.right(path.size()-idx-1));
|
||||
return QPair<QString,QString>(path.left(idx), path.right(path.size() - idx - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -337,4 +303,3 @@ bool OscInterface::setElementProperty(const QSharedPointer<Element>& elem, const
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_OSC
|
||||
|
||||
@@ -20,17 +20,14 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OSC_INTERFACE_H_
|
||||
#define OSC_INTERFACE_H_
|
||||
|
||||
// Compiler flag (?)
|
||||
#ifdef HAVE_OSC
|
||||
#pragma once
|
||||
|
||||
#include <QVariant>
|
||||
#include <QObject>
|
||||
#include <QMessageLogger>
|
||||
|
||||
#include "ConcurrentQueue.h"
|
||||
#include "OscReceiver.h"
|
||||
#include "oscreceiver.h"
|
||||
|
||||
namespace mmp {
|
||||
|
||||
@@ -44,18 +41,8 @@ class OscInterface {
|
||||
public:
|
||||
typedef QSharedPointer<OscInterface> ptr;
|
||||
|
||||
static const QString OSC_ROOT;
|
||||
static const QString OSC_PAINT;
|
||||
static const QString OSC_MAPPING;
|
||||
static const QString OSC_QUIT;
|
||||
static const QString OSC_PLAY;
|
||||
static const QString OSC_PAUSE;
|
||||
static const QString OSC_REWIND;
|
||||
|
||||
static const QString OSC_PAINT_MEDIA;
|
||||
static const QString OSC_PAINT_COLOR;
|
||||
|
||||
OscInterface(const std::string &listen_port);
|
||||
// FIXME: change listen_port to a int
|
||||
OscInterface(int listen_port);
|
||||
~OscInterface();
|
||||
|
||||
/// Starts listening if receiving is enabled.
|
||||
@@ -69,24 +56,14 @@ public:
|
||||
* <path> <typeTags> [args]
|
||||
*/
|
||||
void consume_commands(MainWindow &main_window);
|
||||
// FIXME use QObject signals instead of polling
|
||||
|
||||
private:
|
||||
bool is_verbose() const { return false; }
|
||||
|
||||
void push_command(QVariantList command);
|
||||
|
||||
// OSC callbacks
|
||||
static int ping_cb(const char *path, const char *types, lo_arg **argv,
|
||||
int argc, void *data, void *user_data);
|
||||
static int pong_cb(const char *path, const char *types, lo_arg **argv,
|
||||
int argc, void *data, void *user_data);
|
||||
static int genericHandler(const char *path, const char *types, lo_arg **argv,
|
||||
int argc, void *data, void *user_data);
|
||||
|
||||
bool receiving_enabled_;
|
||||
OscReceiver receiver_;
|
||||
//MainWindow* owner_;
|
||||
|
||||
ConcurrentQueue<QVariantList> messaging_queue_;
|
||||
|
||||
// In the main thread, handles the messages.
|
||||
@@ -95,12 +72,11 @@ private:
|
||||
// For path = "path_item/rest_of_path" returns (path_item, rest_of_path).
|
||||
static QPair<QString,QString> next(const QString& path);
|
||||
|
||||
void messageReceivedCb(const QString& oscAddress, const QVariantList& arguments);
|
||||
|
||||
// Sets property on element with given value.
|
||||
bool setElementProperty(const QSharedPointer<Element>& elem, const QString& property, const QVariant& value);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_OSC
|
||||
|
||||
#endif /* include guard */
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifdef HAVE_OSC
|
||||
#include "OscReceiver.h"
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
|
||||
namespace mmp {
|
||||
|
||||
bool OscReceiver::server_is_ok_ = true;
|
||||
|
||||
OscReceiver::OscReceiver(const std::string &port) : port_(port), server_(0) {
|
||||
server_ = lo_server_thread_new(port_.c_str(), error);
|
||||
server_is_ok_ = (server_ != NULL);
|
||||
// #ifdef CONFIG_DEBUG
|
||||
// /* add method that will match any path and args */
|
||||
// lo_server_thread_add_method(server_, NULL, NULL, genericHandler, this);
|
||||
// #endif
|
||||
}
|
||||
|
||||
OscReceiver::~OscReceiver() {
|
||||
// std::cout << "Freeing OSC server thread\n";
|
||||
lo_server_thread_free(server_);
|
||||
}
|
||||
|
||||
void OscReceiver::addHandler(const char *path, const char *types,
|
||||
lo_method_handler handler, void *userData) {
|
||||
if (server_is_ok_)
|
||||
lo_server_thread_add_method(server_, path, types, handler, userData);
|
||||
else
|
||||
std::cout << "Could not add OSC handler " << path << std::endl;
|
||||
}
|
||||
|
||||
void OscReceiver::listen() {
|
||||
if (server_is_ok_) {
|
||||
int lo_fd = lo_server_get_socket_fd(lo_server_thread_get_server(server_));
|
||||
if (lo_fd == 0) {
|
||||
std::cout << "OSC port " << port_ << " is already in use." << std::endl;
|
||||
} else {
|
||||
std::cout << "Listening on port " << port_ << std::endl;
|
||||
lo_server_thread_start(server_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "Could not start OSC receiver. Maybe that ";
|
||||
std::cout << "OSC port " << port_ << " is already in use." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void OscReceiver::error(int num, const char *msg, const char *path) {
|
||||
std::cerr << "liblo server error " << num << " in path " << path << ": " << msg << std::endl;
|
||||
OscReceiver::server_is_ok_ = false;
|
||||
}
|
||||
|
||||
// #ifdef CONFIG_DEBUG
|
||||
// /* catch any incoming messages and display them. returning 1 means that the
|
||||
// * * message has not been fully handled and the server should try other methods */
|
||||
// int OscReceiver::genericHandler(const char *path,
|
||||
// const char *types, lo_arg **argv,
|
||||
// int argc, void * /*data*/, void * /*user_data*/)
|
||||
// {
|
||||
// //OscReceiver *context = static_cast<OscReceiver*>(user_data);
|
||||
// printf("path: <%s>\n", path);
|
||||
// for (int i = 0; i < argc; ++i)
|
||||
// {
|
||||
// printf("arg %d '%c' ", i, types[i]);
|
||||
// lo_arg_pp(static_cast<lo_type>(types[i]), argv[i]);
|
||||
// printf("\n");
|
||||
// }
|
||||
// printf("\n");
|
||||
// fflush(stdout);
|
||||
//
|
||||
// return 1;
|
||||
// }
|
||||
// #endif // CONFIG_DEBUG
|
||||
|
||||
std::string OscReceiver::toString() const {
|
||||
return "port:" + port_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_OSC
|
||||
@@ -1,48 +0,0 @@
|
||||
#ifndef _OSC_RECEIVER_H_
|
||||
#define _OSC_RECEIVER_H_
|
||||
#ifdef HAVE_OSC
|
||||
|
||||
#include "lo/lo.h"
|
||||
#include <string>
|
||||
#include "MM.h"
|
||||
#define CONFIG_DEBUG
|
||||
|
||||
namespace mmp {
|
||||
|
||||
/**
|
||||
* General-purpose wrapper around liblo to receive OSC messages.
|
||||
*/
|
||||
class OscReceiver {
|
||||
public:
|
||||
OscReceiver(const std::string &port);
|
||||
~OscReceiver();
|
||||
|
||||
void listen();
|
||||
|
||||
const char * port() const {
|
||||
return port_.c_str();
|
||||
}
|
||||
|
||||
void addHandler(const char *path, const char *types, lo_method_handler handler, void *user_data);
|
||||
|
||||
public:
|
||||
static bool server_is_ok_;
|
||||
|
||||
private:
|
||||
std::string toString() const;
|
||||
std::string port_;
|
||||
lo_server_thread server_;
|
||||
|
||||
#ifdef CONFIG_DEBUG
|
||||
static int genericHandler(const char *path, const char *types, lo_arg **argv,
|
||||
int argc, void *data, void *user_data);
|
||||
#endif
|
||||
|
||||
static void error(int num, const char *msg, const char *path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_OSC
|
||||
|
||||
#endif // _OSC_RECEIVER_H_
|
||||
+27
-5
@@ -1,9 +1,31 @@
|
||||
|
||||
include(../src.pri)
|
||||
|
||||
HEADERS += $$PWD/ConcurrentQueue.h \
|
||||
$$PWD/OscInterface.h \
|
||||
$$PWD/OscReceiver.h
|
||||
$$PWD/OscInterface.h
|
||||
|
||||
SOURCES += $$PWD/OscInterface.cpp
|
||||
|
||||
# OSC support:
|
||||
INCLUDEPATH += $$PWD/qosc
|
||||
INCLUDEPATH += $$PWD/qosc/contrib/packosc
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qosc/contrib/oscpack/OscOutboundPacketStream.cpp \
|
||||
$$PWD/qosc/contrib/oscpack/OscPrintReceivedElements.cpp \
|
||||
$$PWD/qosc/contrib/oscpack/OscReceivedElements.cpp \
|
||||
$$PWD/qosc/contrib/oscpack/OscTypes.cpp \
|
||||
$$PWD/qosc/oscreceiver.cpp \
|
||||
$$PWD/qosc/oscsender.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qosc/contrib/oscpack/MessageMappingOscPacketListener.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscException.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscHostEndianness.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscOutboundPacketStream.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscPacketListener.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscPrintReceivedElements.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscReceivedElements.h \
|
||||
$$PWD/qosc/contrib/oscpack/OscTypes.h \
|
||||
$$PWD/qosc/oscreceiver.h \
|
||||
$$PWD/qosc/oscsender.h
|
||||
|
||||
SOURCES += $$PWD/OscInterface.cpp \
|
||||
$$PWD/OscReceiver.cpp
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_MESSAGEMAPPINGOSCPACKETLISTENER_H
|
||||
#define INCLUDED_OSCPACK_MESSAGEMAPPINGOSCPACKETLISTENER_H
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
|
||||
#include "OscPacketListener.h"
|
||||
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
template< class T >
|
||||
class MessageMappingOscPacketListener : public OscPacketListener{
|
||||
public:
|
||||
typedef void (T::*function_type)(const osc::ReceivedMessage&, const IpEndpointName&);
|
||||
|
||||
protected:
|
||||
void RegisterMessageFunction( const char *addressPattern, function_type f )
|
||||
{
|
||||
functions_.insert( std::make_pair( addressPattern, f ) );
|
||||
}
|
||||
|
||||
virtual void ProcessMessage( const osc::ReceivedMessage& m,
|
||||
const IpEndpointName& remoteEndpoint )
|
||||
{
|
||||
typename function_map_type::iterator i = functions_.find( m.AddressPattern() );
|
||||
if( i != functions_.end() )
|
||||
(dynamic_cast<T*>(this)->*(i->second))( m, remoteEndpoint );
|
||||
}
|
||||
|
||||
private:
|
||||
struct cstr_compare{
|
||||
bool operator()( const char *lhs, const char *rhs ) const
|
||||
{ return std::strcmp( lhs, rhs ) < 0; }
|
||||
};
|
||||
|
||||
typedef std::map<const char*, function_type, cstr_compare> function_map_type;
|
||||
function_map_type functions_;
|
||||
};
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_MESSAGEMAPPINGOSCPACKETLISTENER_H */
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCEXCEPTION_H
|
||||
#define INCLUDED_OSCPACK_OSCEXCEPTION_H
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace osc{
|
||||
|
||||
class Exception : public std::exception {
|
||||
const char *what_;
|
||||
|
||||
public:
|
||||
Exception() throw() {}
|
||||
Exception( const Exception& src ) throw()
|
||||
: std::exception( src )
|
||||
, what_( src.what_ ) {}
|
||||
Exception( const char *w ) throw()
|
||||
: what_( w ) {}
|
||||
Exception& operator=( const Exception& src ) throw()
|
||||
{ what_ = src.what_; return *this; }
|
||||
virtual ~Exception() throw() {}
|
||||
virtual const char* what() const throw() { return what_; }
|
||||
};
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCEXCEPTION_H */
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H
|
||||
#define INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H
|
||||
|
||||
/*
|
||||
Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined
|
||||
|
||||
We try to use preprocessor symbols to deduce the host endianness.
|
||||
|
||||
Alternatively you can define one of the above symbols from the command line.
|
||||
Usually you do this with the -D flag to the compiler. e.g.:
|
||||
|
||||
$ g++ -DOSC_HOST_LITTLE_ENDIAN ...
|
||||
*/
|
||||
|
||||
#if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN)
|
||||
|
||||
// endianness defined on the command line. nothing to do here.
|
||||
|
||||
#elif defined(__WIN32__) || defined(WIN32) || defined(WINCE)
|
||||
|
||||
// assume that __WIN32__ is only defined on little endian systems
|
||||
|
||||
#define OSC_HOST_LITTLE_ENDIAN 1
|
||||
#undef OSC_HOST_BIG_ENDIAN
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
|
||||
#define OSC_HOST_LITTLE_ENDIAN 1
|
||||
#undef OSC_HOST_BIG_ENDIAN
|
||||
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
|
||||
#define OSC_HOST_BIG_ENDIAN 1
|
||||
#undef OSC_HOST_LITTLE_ENDIAN
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
|
||||
|
||||
// should cover gcc and clang
|
||||
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
|
||||
#define OSC_HOST_LITTLE_ENDIAN 1
|
||||
#undef OSC_HOST_BIG_ENDIAN
|
||||
|
||||
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
|
||||
#define OSC_HOST_BIG_ENDIAN 1
|
||||
#undef OSC_HOST_LITTLE_ENDIAN
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// gcc defines __LITTLE_ENDIAN__ and __BIG_ENDIAN__
|
||||
// for others used here see http://sourceforge.net/p/predef/wiki/Endianness/
|
||||
#if (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \
|
||||
|| (defined(__ARMEL__) && !defined(__ARMEB__)) \
|
||||
|| (defined(__AARCH64EL__) && !defined(__AARCH64EB__)) \
|
||||
|| (defined(_MIPSEL) && !defined(_MIPSEB)) \
|
||||
|| (defined(__MIPSEL) && !defined(__MIPSEB)) \
|
||||
|| (defined(__MIPSEL__) && !defined(__MIPSEB__))
|
||||
|
||||
#define OSC_HOST_LITTLE_ENDIAN 1
|
||||
#undef OSC_HOST_BIG_ENDIAN
|
||||
|
||||
#elif (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \
|
||||
|| (defined(__ARMEB__) && !defined(__ARMEL__)) \
|
||||
|| (defined(__AARCH64EB__) && !defined(__AARCH64EL__)) \
|
||||
|| (defined(_MIPSEB) && !defined(_MIPSEL)) \
|
||||
|| (defined(__MIPSEB) && !defined(__MIPSEL)) \
|
||||
|| (defined(__MIPSEB__) && !defined(__MIPSEL__))
|
||||
|
||||
#define OSC_HOST_BIG_ENDIAN 1
|
||||
#undef OSC_HOST_LITTLE_ENDIAN
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(OSC_HOST_LITTLE_ENDIAN) && !defined(OSC_HOST_BIG_ENDIAN)
|
||||
|
||||
#error please edit OSCHostEndianness.h or define one of {OSC_HOST_LITTLE_ENDIAN, OSC_HOST_BIG_ENDIAN} to configure endianness
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H */
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#include "OscOutboundPacketStream.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(WIN32) || defined(_WIN32)
|
||||
#include <malloc.h> // for alloca
|
||||
#else
|
||||
//#include <alloca.h> // alloca on Linux (also OSX)
|
||||
#include <stdlib.h> // alloca on OSX and FreeBSD (and Linux?)
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring> // memcpy, memmove, strcpy, strlen
|
||||
#include <cstddef> // ptrdiff_t
|
||||
|
||||
#include "OscHostEndianness.h"
|
||||
|
||||
#if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug
|
||||
namespace std {
|
||||
using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'.
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace osc{
|
||||
|
||||
static void FromInt32( char *p, int32 x )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::int32 i;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.i = x;
|
||||
|
||||
p[3] = u.c[0];
|
||||
p[2] = u.c[1];
|
||||
p[1] = u.c[2];
|
||||
p[0] = u.c[3];
|
||||
#else
|
||||
*reinterpret_cast<int32*>(p) = x;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void FromUInt32( char *p, uint32 x )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::uint32 i;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.i = x;
|
||||
|
||||
p[3] = u.c[0];
|
||||
p[2] = u.c[1];
|
||||
p[1] = u.c[2];
|
||||
p[0] = u.c[3];
|
||||
#else
|
||||
*reinterpret_cast<uint32*>(p) = x;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void FromInt64( char *p, int64 x )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::int64 i;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.i = x;
|
||||
|
||||
p[7] = u.c[0];
|
||||
p[6] = u.c[1];
|
||||
p[5] = u.c[2];
|
||||
p[4] = u.c[3];
|
||||
p[3] = u.c[4];
|
||||
p[2] = u.c[5];
|
||||
p[1] = u.c[6];
|
||||
p[0] = u.c[7];
|
||||
#else
|
||||
*reinterpret_cast<int64*>(p) = x;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void FromUInt64( char *p, uint64 x )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::uint64 i;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.i = x;
|
||||
|
||||
p[7] = u.c[0];
|
||||
p[6] = u.c[1];
|
||||
p[5] = u.c[2];
|
||||
p[4] = u.c[3];
|
||||
p[3] = u.c[4];
|
||||
p[2] = u.c[5];
|
||||
p[1] = u.c[6];
|
||||
p[0] = u.c[7];
|
||||
#else
|
||||
*reinterpret_cast<uint64*>(p) = x;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// round up to the next highest multiple of 4. unless x is already a multiple of 4
|
||||
static inline std::size_t RoundUp4( std::size_t x )
|
||||
{
|
||||
return (x + 3) & ~((std::size_t)0x03);
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream::OutboundPacketStream( char *buffer, std::size_t capacity )
|
||||
: data_( buffer )
|
||||
, end_( data_ + capacity )
|
||||
, typeTagsCurrent_( end_ )
|
||||
, messageCursor_( data_ )
|
||||
, argumentCurrent_( data_ )
|
||||
, elementSizePtr_( 0 )
|
||||
, messageIsInProgress_( false )
|
||||
{
|
||||
// sanity check integer types declared in OscTypes.h
|
||||
// you'll need to fix OscTypes.h if any of these asserts fail
|
||||
|
||||
#if 0
|
||||
assert( sizeof(osc::int32) == 4 );
|
||||
assert( sizeof(osc::uint32) == 4 );
|
||||
assert( sizeof(osc::int64) == 8 );
|
||||
assert( sizeof(osc::uint64) == 8 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream::~OutboundPacketStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
char *OutboundPacketStream::BeginElement( char *beginPtr )
|
||||
{
|
||||
if( elementSizePtr_ == 0 ){
|
||||
|
||||
elementSizePtr_ = reinterpret_cast<uint32*>(data_);
|
||||
|
||||
return beginPtr;
|
||||
|
||||
}else{
|
||||
// store an offset to the old element size ptr in the element size slot
|
||||
// we store an offset rather than the actual pointer to be 64 bit clean.
|
||||
*reinterpret_cast<uint32*>(beginPtr) =
|
||||
(uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_);
|
||||
|
||||
elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
|
||||
|
||||
return beginPtr + 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::EndElement( char *endPtr )
|
||||
{
|
||||
assert( elementSizePtr_ != 0 );
|
||||
|
||||
if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){
|
||||
|
||||
elementSizePtr_ = 0;
|
||||
|
||||
}else{
|
||||
// while building an element, an offset to the containing element's
|
||||
// size slot is stored in the elements size slot (or a ptr to data_
|
||||
// if there is no containing element). We retrieve that here
|
||||
uint32 *previousElementSizePtr =
|
||||
reinterpret_cast<uint32*>(data_ + *elementSizePtr_);
|
||||
|
||||
// then we store the element size in the slot. note that the element
|
||||
// size does not include the size slot, hence the - 4 below.
|
||||
|
||||
std::ptrdiff_t d = endPtr - reinterpret_cast<char*>(elementSizePtr_);
|
||||
// assert( d >= 4 && d <= 0x7FFFFFFF ); // assume packets smaller than 2Gb
|
||||
|
||||
uint32 elementSize = static_cast<uint32>(d - 4);
|
||||
FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
|
||||
|
||||
// finally, we reset the element size ptr to the containing element
|
||||
elementSizePtr_ = previousElementSizePtr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::ElementSizeSlotRequired() const
|
||||
{
|
||||
return (elementSizePtr_ != 0);
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::CheckForAvailableBundleSpace()
|
||||
{
|
||||
std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
|
||||
|
||||
if( required > Capacity() )
|
||||
throw OutOfBufferMemoryException();
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
|
||||
{
|
||||
// plus 4 for at least four bytes of type tag
|
||||
std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0)
|
||||
+ RoundUp4(std::strlen(addressPattern) + 1) + 4;
|
||||
|
||||
if( required > Capacity() )
|
||||
throw OutOfBufferMemoryException();
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::CheckForAvailableArgumentSpace( std::size_t argumentLength )
|
||||
{
|
||||
// plus three for extra type tag, comma and null terminator
|
||||
std::size_t required = (argumentCurrent_ - data_) + argumentLength
|
||||
+ RoundUp4( (end_ - typeTagsCurrent_) + 3 );
|
||||
|
||||
if( required > Capacity() )
|
||||
throw OutOfBufferMemoryException();
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::Clear()
|
||||
{
|
||||
typeTagsCurrent_ = end_;
|
||||
messageCursor_ = data_;
|
||||
argumentCurrent_ = data_;
|
||||
elementSizePtr_ = 0;
|
||||
messageIsInProgress_ = false;
|
||||
}
|
||||
|
||||
|
||||
std::size_t OutboundPacketStream::Capacity() const
|
||||
{
|
||||
return end_ - data_;
|
||||
}
|
||||
|
||||
|
||||
std::size_t OutboundPacketStream::Size() const
|
||||
{
|
||||
std::size_t result = argumentCurrent_ - data_;
|
||||
if( IsMessageInProgress() ){
|
||||
// account for the length of the type tag string. the total type tag
|
||||
// includes an initial comma, plus at least one terminating \0
|
||||
result += RoundUp4( (end_ - typeTagsCurrent_) + 2 );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const char *OutboundPacketStream::Data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::IsReady() const
|
||||
{
|
||||
return (!IsMessageInProgress() && !IsBundleInProgress());
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::IsMessageInProgress() const
|
||||
{
|
||||
return messageIsInProgress_;
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::IsBundleInProgress() const
|
||||
{
|
||||
return (elementSizePtr_ != 0);
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
|
||||
{
|
||||
if( IsMessageInProgress() )
|
||||
throw MessageInProgressException();
|
||||
|
||||
CheckForAvailableBundleSpace();
|
||||
|
||||
messageCursor_ = BeginElement( messageCursor_ );
|
||||
|
||||
std::memcpy( messageCursor_, "#bundle\0", 8 );
|
||||
FromUInt64( messageCursor_ + 8, rhs.timeTag );
|
||||
|
||||
messageCursor_ += 16;
|
||||
argumentCurrent_ = messageCursor_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
|
||||
if( !IsBundleInProgress() )
|
||||
throw BundleNotInProgressException();
|
||||
if( IsMessageInProgress() )
|
||||
throw MessageInProgressException();
|
||||
|
||||
EndElement( messageCursor_ );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
|
||||
{
|
||||
if( IsMessageInProgress() )
|
||||
throw MessageInProgressException();
|
||||
|
||||
CheckForAvailableMessageSpace( rhs.addressPattern );
|
||||
|
||||
messageCursor_ = BeginElement( messageCursor_ );
|
||||
|
||||
std::strcpy( messageCursor_, rhs.addressPattern );
|
||||
std::size_t rhsLength = std::strlen(rhs.addressPattern);
|
||||
messageCursor_ += rhsLength + 1;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
std::size_t i = rhsLength + 1;
|
||||
while( i & 0x3 ){
|
||||
*messageCursor_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
argumentCurrent_ = messageCursor_;
|
||||
typeTagsCurrent_ = end_;
|
||||
|
||||
messageIsInProgress_ = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
|
||||
if( !IsMessageInProgress() )
|
||||
throw MessageNotInProgressException();
|
||||
|
||||
std::size_t typeTagsCount = end_ - typeTagsCurrent_;
|
||||
|
||||
if( typeTagsCount ){
|
||||
|
||||
char *tempTypeTags = (char*)alloca(typeTagsCount);
|
||||
std::memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
|
||||
|
||||
// slot size includes comma and null terminator
|
||||
std::size_t typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
|
||||
|
||||
std::size_t argumentsSize = argumentCurrent_ - messageCursor_;
|
||||
|
||||
std::memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
|
||||
|
||||
messageCursor_[0] = ',';
|
||||
// copy type tags in reverse (really forward) order
|
||||
for( std::size_t i=0; i < typeTagsCount; ++i )
|
||||
messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
|
||||
|
||||
char *p = messageCursor_ + 1 + typeTagsCount;
|
||||
for( std::size_t i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
|
||||
*p++ = '\0';
|
||||
|
||||
typeTagsCurrent_ = end_;
|
||||
|
||||
// advance messageCursor_ for next message
|
||||
messageCursor_ += typeTagSlotSize + argumentsSize;
|
||||
|
||||
}else{
|
||||
// send an empty type tags string
|
||||
std::memcpy( messageCursor_, ",\0\0\0", 4 );
|
||||
|
||||
// advance messageCursor_ for next message
|
||||
messageCursor_ += 4;
|
||||
}
|
||||
|
||||
argumentCurrent_ = messageCursor_;
|
||||
|
||||
EndElement( messageCursor_ );
|
||||
|
||||
messageIsInProgress_ = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = NIL_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = INT32_TYPE_TAG;
|
||||
FromInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
|
||||
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
float f;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.f = rhs;
|
||||
|
||||
argumentCurrent_[3] = u.c[0];
|
||||
argumentCurrent_[2] = u.c[1];
|
||||
argumentCurrent_[1] = u.c[2];
|
||||
argumentCurrent_[0] = u.c[3];
|
||||
#else
|
||||
*reinterpret_cast<float*>(argumentCurrent_) = rhs;
|
||||
#endif
|
||||
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = CHAR_TYPE_TAG;
|
||||
FromInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(8);
|
||||
|
||||
*(--typeTagsCurrent_) = INT64_TYPE_TAG;
|
||||
FromInt64( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 8;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(8);
|
||||
|
||||
*(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
|
||||
FromUInt64( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 8;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(8);
|
||||
|
||||
*(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
|
||||
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
double f;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.f = rhs;
|
||||
|
||||
argumentCurrent_[7] = u.c[0];
|
||||
argumentCurrent_[6] = u.c[1];
|
||||
argumentCurrent_[5] = u.c[2];
|
||||
argumentCurrent_[4] = u.c[3];
|
||||
argumentCurrent_[3] = u.c[4];
|
||||
argumentCurrent_[2] = u.c[5];
|
||||
argumentCurrent_[1] = u.c[6];
|
||||
argumentCurrent_[0] = u.c[7];
|
||||
#else
|
||||
*reinterpret_cast<double*>(argumentCurrent_) = rhs;
|
||||
#endif
|
||||
|
||||
argumentCurrent_ += 8;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
|
||||
|
||||
*(--typeTagsCurrent_) = STRING_TYPE_TAG;
|
||||
std::strcpy( argumentCurrent_, rhs );
|
||||
std::size_t rhsLength = std::strlen(rhs);
|
||||
argumentCurrent_ += rhsLength + 1;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
std::size_t i = rhsLength + 1;
|
||||
while( i & 0x3 ){
|
||||
*argumentCurrent_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
|
||||
|
||||
*(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
|
||||
std::strcpy( argumentCurrent_, rhs );
|
||||
std::size_t rhsLength = std::strlen(rhs);
|
||||
argumentCurrent_ += rhsLength + 1;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
std::size_t i = rhsLength + 1;
|
||||
while( i & 0x3 ){
|
||||
*argumentCurrent_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
|
||||
|
||||
*(--typeTagsCurrent_) = BLOB_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs.size );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
std::memcpy( argumentCurrent_, rhs.data, rhs.size );
|
||||
argumentCurrent_ += rhs.size;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
unsigned long i = rhs.size;
|
||||
while( i & 0x3 ){
|
||||
*argumentCurrent_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayInitiator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H
|
||||
#define INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H
|
||||
|
||||
#include <cstring> // size_t
|
||||
|
||||
#include "OscTypes.h"
|
||||
#include "OscException.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
class OutOfBufferMemoryException : public Exception{
|
||||
public:
|
||||
OutOfBufferMemoryException( const char *w="out of buffer memory" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class BundleNotInProgressException : public Exception{
|
||||
public:
|
||||
BundleNotInProgressException(
|
||||
const char *w="call to EndBundle when bundle is not in progress" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MessageInProgressException : public Exception{
|
||||
public:
|
||||
MessageInProgressException(
|
||||
const char *w="opening or closing bundle or message while message is in progress" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MessageNotInProgressException : public Exception{
|
||||
public:
|
||||
MessageNotInProgressException(
|
||||
const char *w="call to EndMessage when message is not in progress" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
|
||||
class OutboundPacketStream{
|
||||
public:
|
||||
OutboundPacketStream( char *buffer, std::size_t capacity );
|
||||
~OutboundPacketStream();
|
||||
|
||||
void Clear();
|
||||
|
||||
std::size_t Capacity() const;
|
||||
|
||||
// invariant: size() is valid even while building a message.
|
||||
std::size_t Size() const;
|
||||
|
||||
const char *Data() const;
|
||||
|
||||
// indicates that all messages have been closed with a matching EndMessage
|
||||
// and all bundles have been closed with a matching EndBundle
|
||||
bool IsReady() const;
|
||||
|
||||
bool IsMessageInProgress() const;
|
||||
bool IsBundleInProgress() const;
|
||||
|
||||
OutboundPacketStream& operator<<( const BundleInitiator& rhs );
|
||||
OutboundPacketStream& operator<<( const BundleTerminator& rhs );
|
||||
|
||||
OutboundPacketStream& operator<<( const BeginMessage& rhs );
|
||||
OutboundPacketStream& operator<<( const MessageTerminator& rhs );
|
||||
|
||||
OutboundPacketStream& operator<<( bool rhs );
|
||||
OutboundPacketStream& operator<<( const NilType& rhs );
|
||||
OutboundPacketStream& operator<<( const InfinitumType& rhs );
|
||||
OutboundPacketStream& operator<<( int32 rhs );
|
||||
|
||||
#if !(defined(__x86_64__) || defined(_M_X64))
|
||||
OutboundPacketStream& operator<<( int rhs )
|
||||
{ *this << (int32)rhs; return *this; }
|
||||
#endif
|
||||
|
||||
OutboundPacketStream& operator<<( float rhs );
|
||||
OutboundPacketStream& operator<<( char rhs );
|
||||
OutboundPacketStream& operator<<( const RgbaColor& rhs );
|
||||
OutboundPacketStream& operator<<( const MidiMessage& rhs );
|
||||
OutboundPacketStream& operator<<( int64 rhs );
|
||||
OutboundPacketStream& operator<<( const TimeTag& rhs );
|
||||
OutboundPacketStream& operator<<( double rhs );
|
||||
OutboundPacketStream& operator<<( const char* rhs );
|
||||
OutboundPacketStream& operator<<( const Symbol& rhs );
|
||||
OutboundPacketStream& operator<<( const Blob& rhs );
|
||||
|
||||
OutboundPacketStream& operator<<( const ArrayInitiator& rhs );
|
||||
OutboundPacketStream& operator<<( const ArrayTerminator& rhs );
|
||||
|
||||
private:
|
||||
|
||||
char *BeginElement( char *beginPtr );
|
||||
void EndElement( char *endPtr );
|
||||
|
||||
bool ElementSizeSlotRequired() const;
|
||||
void CheckForAvailableBundleSpace();
|
||||
void CheckForAvailableMessageSpace( const char *addressPattern );
|
||||
void CheckForAvailableArgumentSpace( std::size_t argumentLength );
|
||||
|
||||
char *data_;
|
||||
char *end_;
|
||||
|
||||
char *typeTagsCurrent_; // stored in reverse order
|
||||
char *messageCursor_;
|
||||
char *argumentCurrent_;
|
||||
|
||||
// elementSizePtr_ has two special values: 0 indicates that a bundle
|
||||
// isn't open, and elementSizePtr_==data_ indicates that a bundle is
|
||||
// open but that it doesn't have a size slot (ie the outermost bundle)
|
||||
uint32 *elementSizePtr_;
|
||||
|
||||
bool messageIsInProgress_;
|
||||
};
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H */
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCPACKETLISTENER_H
|
||||
#define INCLUDED_OSCPACK_OSCPACKETLISTENER_H
|
||||
|
||||
#include "OscReceivedElements.h"
|
||||
#include "../ip/PacketListener.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
class OscPacketListener : public PacketListener{
|
||||
protected:
|
||||
virtual void ProcessBundle( const osc::ReceivedBundle& b,
|
||||
const IpEndpointName& remoteEndpoint )
|
||||
{
|
||||
// ignore bundle time tag for now
|
||||
|
||||
for( ReceivedBundle::const_iterator i = b.ElementsBegin();
|
||||
i != b.ElementsEnd(); ++i ){
|
||||
if( i->IsBundle() )
|
||||
ProcessBundle( ReceivedBundle(*i), remoteEndpoint );
|
||||
else
|
||||
ProcessMessage( ReceivedMessage(*i), remoteEndpoint );
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ProcessMessage( const osc::ReceivedMessage& m,
|
||||
const IpEndpointName& remoteEndpoint ) = 0;
|
||||
|
||||
public:
|
||||
virtual void ProcessPacket( const char *data, int size,
|
||||
const IpEndpointName& remoteEndpoint )
|
||||
{
|
||||
osc::ReceivedPacket p( data, size );
|
||||
if( p.IsBundle() )
|
||||
ProcessBundle( ReceivedBundle(p), remoteEndpoint );
|
||||
else
|
||||
ProcessMessage( ReceivedMessage(p), remoteEndpoint );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCPACKETLISTENER_H */
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#include "OscPrintReceivedElements.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug
|
||||
namespace std {
|
||||
using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'.
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace osc{
|
||||
|
||||
|
||||
std::ostream& operator<<( std::ostream & os,
|
||||
const ReceivedMessageArgument& arg )
|
||||
{
|
||||
switch( arg.TypeTag() ){
|
||||
case TRUE_TYPE_TAG:
|
||||
os << "bool:true";
|
||||
break;
|
||||
|
||||
case FALSE_TYPE_TAG:
|
||||
os << "bool:false";
|
||||
break;
|
||||
|
||||
case NIL_TYPE_TAG:
|
||||
os << "(Nil)";
|
||||
break;
|
||||
|
||||
case INFINITUM_TYPE_TAG:
|
||||
os << "(Infinitum)";
|
||||
break;
|
||||
|
||||
case INT32_TYPE_TAG:
|
||||
os << "int32:" << arg.AsInt32Unchecked();
|
||||
break;
|
||||
|
||||
case FLOAT_TYPE_TAG:
|
||||
os << "float32:" << arg.AsFloatUnchecked();
|
||||
break;
|
||||
|
||||
case CHAR_TYPE_TAG:
|
||||
{
|
||||
char s[2] = {0};
|
||||
s[0] = arg.AsCharUnchecked();
|
||||
os << "char:'" << s << "'";
|
||||
}
|
||||
break;
|
||||
|
||||
case RGBA_COLOR_TYPE_TAG:
|
||||
{
|
||||
uint32 color = arg.AsRgbaColorUnchecked();
|
||||
|
||||
os << "RGBA:0x"
|
||||
<< std::hex << std::setfill('0')
|
||||
<< std::setw(2) << (int)((color>>24) & 0xFF)
|
||||
<< std::setw(2) << (int)((color>>16) & 0xFF)
|
||||
<< std::setw(2) << (int)((color>>8) & 0xFF)
|
||||
<< std::setw(2) << (int)(color & 0xFF)
|
||||
<< std::setfill(' ');
|
||||
os.unsetf(std::ios::basefield);
|
||||
}
|
||||
break;
|
||||
|
||||
case MIDI_MESSAGE_TYPE_TAG:
|
||||
{
|
||||
uint32 m = arg.AsMidiMessageUnchecked();
|
||||
os << "midi (port, status, data1, data2):<<"
|
||||
<< std::hex << std::setfill('0')
|
||||
<< "0x" << std::setw(2) << (int)((m>>24) & 0xFF)
|
||||
<< " 0x" << std::setw(2) << (int)((m>>16) & 0xFF)
|
||||
<< " 0x" << std::setw(2) << (int)((m>>8) & 0xFF)
|
||||
<< " 0x" << std::setw(2) << (int)(m & 0xFF)
|
||||
<< std::setfill(' ') << ">>";
|
||||
os.unsetf(std::ios::basefield);
|
||||
}
|
||||
break;
|
||||
|
||||
case INT64_TYPE_TAG:
|
||||
os << "int64:" << arg.AsInt64Unchecked();
|
||||
break;
|
||||
|
||||
case TIME_TAG_TYPE_TAG:
|
||||
{
|
||||
os << "OSC-timetag:" << arg.AsTimeTagUnchecked() << " ";
|
||||
|
||||
std::time_t t =
|
||||
(unsigned long)( arg.AsTimeTagUnchecked() >> 32 );
|
||||
|
||||
const char *timeString = std::ctime( &t );
|
||||
size_t len = std::strlen( timeString );
|
||||
|
||||
// -1 to omit trailing newline from string returned by ctime()
|
||||
if( len > 1 )
|
||||
os.write( timeString, len - 1 );
|
||||
}
|
||||
break;
|
||||
|
||||
case DOUBLE_TYPE_TAG:
|
||||
os << "double:" << arg.AsDoubleUnchecked();
|
||||
break;
|
||||
|
||||
case STRING_TYPE_TAG:
|
||||
os << "OSC-string:`" << arg.AsStringUnchecked() << "'";
|
||||
break;
|
||||
|
||||
case SYMBOL_TYPE_TAG:
|
||||
os << "OSC-string (symbol):`" << arg.AsSymbolUnchecked() << "'";
|
||||
break;
|
||||
|
||||
case BLOB_TYPE_TAG:
|
||||
{
|
||||
const void *data;
|
||||
osc_bundle_element_size_t size;
|
||||
arg.AsBlobUnchecked( data, size );
|
||||
os << "OSC-blob:<<" << std::hex << std::setfill('0');
|
||||
unsigned char *p = (unsigned char*)data;
|
||||
for( osc_bundle_element_size_t i = 0; i < size; ++i ){
|
||||
os << "0x" << std::setw(2) << int(p[i]);
|
||||
if( i != size-1 )
|
||||
os << ' ';
|
||||
}
|
||||
os.unsetf(std::ios::basefield);
|
||||
os << ">>" << std::setfill(' ');
|
||||
}
|
||||
break;
|
||||
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
os << "[";
|
||||
break;
|
||||
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
os << "]";
|
||||
break;
|
||||
|
||||
default:
|
||||
os << "unknown";
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m )
|
||||
{
|
||||
os << "[";
|
||||
if( m.AddressPatternIsUInt32() )
|
||||
os << m.AddressPatternAsUInt32();
|
||||
else
|
||||
os << m.AddressPattern();
|
||||
|
||||
bool first = true;
|
||||
for( ReceivedMessage::const_iterator i = m.ArgumentsBegin();
|
||||
i != m.ArgumentsEnd(); ++i ){
|
||||
if( first ){
|
||||
os << " ";
|
||||
first = false;
|
||||
}else{
|
||||
os << ", ";
|
||||
}
|
||||
|
||||
os << *i;
|
||||
}
|
||||
|
||||
os << "]";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b )
|
||||
{
|
||||
static int indent = 0;
|
||||
|
||||
for( int j=0; j < indent; ++j )
|
||||
os << " ";
|
||||
os << "{ ( ";
|
||||
if( b.TimeTag() == 1 )
|
||||
os << "immediate";
|
||||
else
|
||||
os << b.TimeTag();
|
||||
os << " )\n";
|
||||
|
||||
++indent;
|
||||
|
||||
for( ReceivedBundle::const_iterator i = b.ElementsBegin();
|
||||
i != b.ElementsEnd(); ++i ){
|
||||
if( i->IsBundle() ){
|
||||
ReceivedBundle b(*i);
|
||||
os << b << "\n";
|
||||
}else{
|
||||
ReceivedMessage m(*i);
|
||||
for( int j=0; j < indent; ++j )
|
||||
os << " ";
|
||||
os << m << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
--indent;
|
||||
|
||||
for( int j=0; j < indent; ++j )
|
||||
os << " ";
|
||||
os << "}";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p )
|
||||
{
|
||||
if( p.IsBundle() ){
|
||||
ReceivedBundle b(p);
|
||||
os << b << "\n";
|
||||
}else{
|
||||
ReceivedMessage m(p);
|
||||
os << m << "\n";
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace osc
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCPRINTRECEIVEDELEMENTS_H
|
||||
#define INCLUDED_OSCPACK_OSCPRINTRECEIVEDELEMENTS_H
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include "OscReceivedElements.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p );
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedMessageArgument& arg );
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m );
|
||||
std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b );
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCPRINTRECEIVEDELEMENTS_H */
|
||||
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#include "OscReceivedElements.h"
|
||||
|
||||
#include "OscHostEndianness.h"
|
||||
|
||||
#include <cstddef> // ptrdiff_t
|
||||
|
||||
namespace osc{
|
||||
|
||||
|
||||
// return the first 4 byte boundary after the end of a str4
|
||||
// be careful about calling this version if you don't know whether
|
||||
// the string is terminated correctly.
|
||||
static inline const char* FindStr4End( const char *p )
|
||||
{
|
||||
if( p[0] == '\0' ) // special case for SuperCollider integer address pattern
|
||||
return p + 4;
|
||||
|
||||
p += 3;
|
||||
|
||||
while( *p )
|
||||
p += 4;
|
||||
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
|
||||
// return the first 4 byte boundary after the end of a str4
|
||||
// returns 0 if p == end or if the string is unterminated
|
||||
static inline const char* FindStr4End( const char *p, const char *end )
|
||||
{
|
||||
if( p >= end )
|
||||
return 0;
|
||||
|
||||
if( p[0] == '\0' ) // special case for SuperCollider integer address pattern
|
||||
return p + 4;
|
||||
|
||||
p += 3;
|
||||
end -= 1;
|
||||
|
||||
while( p < end && *p )
|
||||
p += 4;
|
||||
|
||||
if( *p )
|
||||
return 0;
|
||||
else
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
|
||||
// round up to the next highest multiple of 4. unless x is already a multiple of 4
|
||||
static inline uint32 RoundUp4( uint32 x )
|
||||
{
|
||||
return (x + 3) & ~((uint32)0x03);
|
||||
}
|
||||
|
||||
|
||||
static inline int32 ToInt32( const char *p )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::int32 i;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.c[0] = p[3];
|
||||
u.c[1] = p[2];
|
||||
u.c[2] = p[1];
|
||||
u.c[3] = p[0];
|
||||
|
||||
return u.i;
|
||||
#else
|
||||
return *(int32*)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline uint32 ToUInt32( const char *p )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::uint32 i;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.c[0] = p[3];
|
||||
u.c[1] = p[2];
|
||||
u.c[2] = p[1];
|
||||
u.c[3] = p[0];
|
||||
|
||||
return u.i;
|
||||
#else
|
||||
return *(uint32*)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline int64 ToInt64( const char *p )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::int64 i;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.c[0] = p[7];
|
||||
u.c[1] = p[6];
|
||||
u.c[2] = p[5];
|
||||
u.c[3] = p[4];
|
||||
u.c[4] = p[3];
|
||||
u.c[5] = p[2];
|
||||
u.c[6] = p[1];
|
||||
u.c[7] = p[0];
|
||||
|
||||
return u.i;
|
||||
#else
|
||||
return *(int64*)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline uint64 ToUInt64( const char *p )
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::uint64 i;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.c[0] = p[7];
|
||||
u.c[1] = p[6];
|
||||
u.c[2] = p[5];
|
||||
u.c[3] = p[4];
|
||||
u.c[4] = p[3];
|
||||
u.c[5] = p[2];
|
||||
u.c[6] = p[1];
|
||||
u.c[7] = p[0];
|
||||
|
||||
return u.i;
|
||||
#else
|
||||
return *(uint64*)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ReceivedPacket::IsBundle() const
|
||||
{
|
||||
return (Size() > 0 && Contents()[0] == '#');
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ReceivedBundleElement::IsBundle() const
|
||||
{
|
||||
return (Size() > 0 && Contents()[0] == '#');
|
||||
}
|
||||
|
||||
|
||||
osc_bundle_element_size_t ReceivedBundleElement::Size() const
|
||||
{
|
||||
return ToInt32( sizePtr_ );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ReceivedMessageArgument::AsBool() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == TRUE_TYPE_TAG )
|
||||
return true;
|
||||
else if( *typeTagPtr_ == FALSE_TYPE_TAG )
|
||||
return false;
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
bool ReceivedMessageArgument::AsBoolUnchecked() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == TRUE_TYPE_TAG )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int32 ReceivedMessageArgument::AsInt32() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == INT32_TYPE_TAG )
|
||||
return AsInt32Unchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
int32 ReceivedMessageArgument::AsInt32Unchecked() const
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::int32 i;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.c[0] = argumentPtr_[3];
|
||||
u.c[1] = argumentPtr_[2];
|
||||
u.c[2] = argumentPtr_[1];
|
||||
u.c[3] = argumentPtr_[0];
|
||||
|
||||
return u.i;
|
||||
#else
|
||||
return *(int32*)argument_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
float ReceivedMessageArgument::AsFloat() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == FLOAT_TYPE_TAG )
|
||||
return AsFloatUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
float ReceivedMessageArgument::AsFloatUnchecked() const
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
float f;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.c[0] = argumentPtr_[3];
|
||||
u.c[1] = argumentPtr_[2];
|
||||
u.c[2] = argumentPtr_[1];
|
||||
u.c[3] = argumentPtr_[0];
|
||||
|
||||
return u.f;
|
||||
#else
|
||||
return *(float*)argument_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
char ReceivedMessageArgument::AsChar() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == CHAR_TYPE_TAG )
|
||||
return AsCharUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
char ReceivedMessageArgument::AsCharUnchecked() const
|
||||
{
|
||||
return (char)ToInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsRgbaColor() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG )
|
||||
return AsRgbaColorUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const
|
||||
{
|
||||
return ToUInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsMidiMessage() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG )
|
||||
return AsMidiMessageUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const
|
||||
{
|
||||
return ToUInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
int64 ReceivedMessageArgument::AsInt64() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == INT64_TYPE_TAG )
|
||||
return AsInt64Unchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
int64 ReceivedMessageArgument::AsInt64Unchecked() const
|
||||
{
|
||||
return ToInt64( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedMessageArgument::AsTimeTag() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG )
|
||||
return AsTimeTagUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const
|
||||
{
|
||||
return ToUInt64( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
double ReceivedMessageArgument::AsDouble() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == DOUBLE_TYPE_TAG )
|
||||
return AsDoubleUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
double ReceivedMessageArgument::AsDoubleUnchecked() const
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
double d;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.c[0] = argumentPtr_[7];
|
||||
u.c[1] = argumentPtr_[6];
|
||||
u.c[2] = argumentPtr_[5];
|
||||
u.c[3] = argumentPtr_[4];
|
||||
u.c[4] = argumentPtr_[3];
|
||||
u.c[5] = argumentPtr_[2];
|
||||
u.c[6] = argumentPtr_[1];
|
||||
u.c[7] = argumentPtr_[0];
|
||||
|
||||
return u.d;
|
||||
#else
|
||||
return *(double*)argument_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
const char* ReceivedMessageArgument::AsString() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == STRING_TYPE_TAG )
|
||||
return argumentPtr_;
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
const char* ReceivedMessageArgument::AsSymbol() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == SYMBOL_TYPE_TAG )
|
||||
return argumentPtr_;
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size ) const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == BLOB_TYPE_TAG )
|
||||
AsBlobUnchecked( data, size );
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const
|
||||
{
|
||||
// read blob size as an unsigned int then validate
|
||||
osc_bundle_element_size_t sizeResult = (osc_bundle_element_size_t)ToUInt32( argumentPtr_ );
|
||||
if( !IsValidElementSizeValue(sizeResult) )
|
||||
throw MalformedMessageException("invalid blob size");
|
||||
|
||||
size = sizeResult;
|
||||
data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32);
|
||||
}
|
||||
|
||||
std::size_t ReceivedMessageArgument::ComputeArrayItemCount() const
|
||||
{
|
||||
// it is only valid to call ComputeArrayItemCount when the argument is the array start marker
|
||||
if( !IsArrayBegin() )
|
||||
throw WrongArgumentTypeException();
|
||||
|
||||
std::size_t result = 0;
|
||||
unsigned int level = 0;
|
||||
const char *typeTag = typeTagPtr_ + 1;
|
||||
|
||||
// iterate through all type tags. note that ReceivedMessage::Init
|
||||
// has already checked that the message is well formed.
|
||||
while( *typeTag ) {
|
||||
switch( *typeTag++ ) {
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
level += 1;
|
||||
break;
|
||||
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
if(level == 0)
|
||||
return result;
|
||||
level -= 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
if( level == 0 ) // only count items at level 0
|
||||
++result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ReceivedMessageArgumentIterator::Advance()
|
||||
{
|
||||
if( !value_.typeTagPtr_ )
|
||||
return;
|
||||
|
||||
switch( *value_.typeTagPtr_++ ){
|
||||
case '\0':
|
||||
// don't advance past end
|
||||
--value_.typeTagPtr_;
|
||||
break;
|
||||
|
||||
case TRUE_TYPE_TAG:
|
||||
case FALSE_TYPE_TAG:
|
||||
case NIL_TYPE_TAG:
|
||||
case INFINITUM_TYPE_TAG:
|
||||
|
||||
// zero length
|
||||
break;
|
||||
|
||||
case INT32_TYPE_TAG:
|
||||
case FLOAT_TYPE_TAG:
|
||||
case CHAR_TYPE_TAG:
|
||||
case RGBA_COLOR_TYPE_TAG:
|
||||
case MIDI_MESSAGE_TYPE_TAG:
|
||||
|
||||
value_.argumentPtr_ += 4;
|
||||
break;
|
||||
|
||||
case INT64_TYPE_TAG:
|
||||
case TIME_TAG_TYPE_TAG:
|
||||
case DOUBLE_TYPE_TAG:
|
||||
|
||||
value_.argumentPtr_ += 8;
|
||||
break;
|
||||
|
||||
case STRING_TYPE_TAG:
|
||||
case SYMBOL_TYPE_TAG:
|
||||
|
||||
// we use the unsafe function FindStr4End(char*) here because all of
|
||||
// the arguments have already been validated in
|
||||
// ReceivedMessage::Init() below.
|
||||
|
||||
value_.argumentPtr_ = FindStr4End( value_.argumentPtr_ );
|
||||
break;
|
||||
|
||||
case BLOB_TYPE_TAG:
|
||||
{
|
||||
// treat blob size as an unsigned int for the purposes of this calculation
|
||||
uint32 blobSize = ToUInt32( value_.argumentPtr_ );
|
||||
value_.argumentPtr_ = value_.argumentPtr_ + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize );
|
||||
}
|
||||
break;
|
||||
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
|
||||
// [ Indicates the beginning of an array. The tags following are for
|
||||
// data in the Array until a close brace tag is reached.
|
||||
// ] Indicates the end of an array.
|
||||
|
||||
// zero length, don't advance argument ptr
|
||||
break;
|
||||
|
||||
default: // unknown type tag
|
||||
// don't advance
|
||||
--value_.typeTagPtr_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet )
|
||||
: addressPattern_( packet.Contents() )
|
||||
{
|
||||
Init( packet.Contents(), packet.Size() );
|
||||
}
|
||||
|
||||
|
||||
ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement )
|
||||
: addressPattern_( bundleElement.Contents() )
|
||||
{
|
||||
Init( bundleElement.Contents(), bundleElement.Size() );
|
||||
}
|
||||
|
||||
|
||||
bool ReceivedMessage::AddressPatternIsUInt32() const
|
||||
{
|
||||
return (addressPattern_[0] == '\0');
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessage::AddressPatternAsUInt32() const
|
||||
{
|
||||
return ToUInt32( addressPattern_ );
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessage::Init( const char *message, osc_bundle_element_size_t size )
|
||||
{
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
throw MalformedMessageException( "invalid message size" );
|
||||
|
||||
if( size == 0 )
|
||||
throw MalformedMessageException( "zero length messages not permitted" );
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
throw MalformedMessageException( "message size must be multiple of four" );
|
||||
|
||||
const char *end = message + size;
|
||||
|
||||
typeTagsBegin_ = FindStr4End( addressPattern_, end );
|
||||
if( typeTagsBegin_ == 0 ){
|
||||
// address pattern was not terminated before end
|
||||
throw MalformedMessageException( "unterminated address pattern" );
|
||||
}
|
||||
|
||||
if( typeTagsBegin_ == end ){
|
||||
// message consists of only the address pattern - no arguments or type tags.
|
||||
typeTagsBegin_ = 0;
|
||||
typeTagsEnd_ = 0;
|
||||
arguments_ = 0;
|
||||
|
||||
}else{
|
||||
if( *typeTagsBegin_ != ',' )
|
||||
throw MalformedMessageException( "type tags not present" );
|
||||
|
||||
if( *(typeTagsBegin_ + 1) == '\0' ){
|
||||
// zero length type tags
|
||||
typeTagsBegin_ = 0;
|
||||
typeTagsEnd_ = 0;
|
||||
arguments_ = 0;
|
||||
|
||||
}else{
|
||||
// check that all arguments are present and well formed
|
||||
|
||||
arguments_ = FindStr4End( typeTagsBegin_, end );
|
||||
if( arguments_ == 0 ){
|
||||
throw MalformedMessageException( "type tags were not terminated before end of message" );
|
||||
}
|
||||
|
||||
++typeTagsBegin_; // advance past initial ','
|
||||
|
||||
const char *typeTag = typeTagsBegin_;
|
||||
const char *argument = arguments_;
|
||||
unsigned int arrayLevel = 0;
|
||||
|
||||
do{
|
||||
switch( *typeTag ){
|
||||
case TRUE_TYPE_TAG:
|
||||
case FALSE_TYPE_TAG:
|
||||
case NIL_TYPE_TAG:
|
||||
case INFINITUM_TYPE_TAG:
|
||||
// zero length
|
||||
break;
|
||||
|
||||
// [ Indicates the beginning of an array. The tags following are for
|
||||
// data in the Array until a close brace tag is reached.
|
||||
// ] Indicates the end of an array.
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
++arrayLevel;
|
||||
// (zero length argument data)
|
||||
break;
|
||||
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
--arrayLevel;
|
||||
// (zero length argument data)
|
||||
break;
|
||||
|
||||
case INT32_TYPE_TAG:
|
||||
case FLOAT_TYPE_TAG:
|
||||
case CHAR_TYPE_TAG:
|
||||
case RGBA_COLOR_TYPE_TAG:
|
||||
case MIDI_MESSAGE_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
argument += 4;
|
||||
if( argument > end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
break;
|
||||
|
||||
case INT64_TYPE_TAG:
|
||||
case TIME_TAG_TYPE_TAG:
|
||||
case DOUBLE_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
argument += 8;
|
||||
if( argument > end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
break;
|
||||
|
||||
case STRING_TYPE_TAG:
|
||||
case SYMBOL_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
argument = FindStr4End( argument, end );
|
||||
if( argument == 0 )
|
||||
throw MalformedMessageException( "unterminated string argument" );
|
||||
break;
|
||||
|
||||
case BLOB_TYPE_TAG:
|
||||
{
|
||||
if( argument + osc::OSC_SIZEOF_INT32 > end )
|
||||
MalformedMessageException( "arguments exceed message size" );
|
||||
|
||||
// treat blob size as an unsigned int for the purposes of this calculation
|
||||
uint32 blobSize = ToUInt32( argument );
|
||||
argument = argument + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize );
|
||||
if( argument > end )
|
||||
MalformedMessageException( "arguments exceed message size" );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw MalformedMessageException( "unknown type tag" );
|
||||
}
|
||||
|
||||
}while( *++typeTag != '\0' );
|
||||
typeTagsEnd_ = typeTag;
|
||||
|
||||
if( arrayLevel != 0 )
|
||||
throw MalformedMessageException( "array was not terminated before end of message (expected ']' end of array tag)" );
|
||||
}
|
||||
|
||||
// These invariants should be guaranteed by the above code.
|
||||
// we depend on them in the implementation of ArgumentCount()
|
||||
#ifndef NDEBUG
|
||||
std::ptrdiff_t argumentCount = typeTagsEnd_ - typeTagsBegin_;
|
||||
assert( argumentCount >= 0 );
|
||||
assert( argumentCount <= OSC_INT32_MAX );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet )
|
||||
: elementCount_( 0 )
|
||||
{
|
||||
Init( packet.Contents(), packet.Size() );
|
||||
}
|
||||
|
||||
|
||||
ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement )
|
||||
: elementCount_( 0 )
|
||||
{
|
||||
Init( bundleElement.Contents(), bundleElement.Size() );
|
||||
}
|
||||
|
||||
|
||||
void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size )
|
||||
{
|
||||
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
throw MalformedBundleException( "invalid bundle size" );
|
||||
|
||||
if( size < 16 )
|
||||
throw MalformedBundleException( "packet too short for bundle" );
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
throw MalformedBundleException( "bundle size must be multiple of four" );
|
||||
|
||||
if( bundle[0] != '#'
|
||||
|| bundle[1] != 'b'
|
||||
|| bundle[2] != 'u'
|
||||
|| bundle[3] != 'n'
|
||||
|| bundle[4] != 'd'
|
||||
|| bundle[5] != 'l'
|
||||
|| bundle[6] != 'e'
|
||||
|| bundle[7] != '\0' )
|
||||
throw MalformedBundleException( "bad bundle address pattern" );
|
||||
|
||||
end_ = bundle + size;
|
||||
|
||||
timeTag_ = bundle + 8;
|
||||
|
||||
const char *p = timeTag_ + 8;
|
||||
|
||||
while( p < end_ ){
|
||||
if( p + osc::OSC_SIZEOF_INT32 > end_ )
|
||||
throw MalformedBundleException( "packet too short for elementSize" );
|
||||
|
||||
// treat element size as an unsigned int for the purposes of this calculation
|
||||
uint32 elementSize = ToUInt32( p );
|
||||
if( (elementSize & ((uint32)0x03)) != 0 )
|
||||
throw MalformedBundleException( "bundle element size must be multiple of four" );
|
||||
|
||||
p += osc::OSC_SIZEOF_INT32 + elementSize;
|
||||
if( p > end_ )
|
||||
throw MalformedBundleException( "packet too short for bundle element" );
|
||||
|
||||
++elementCount_;
|
||||
}
|
||||
|
||||
if( p != end_ )
|
||||
throw MalformedBundleException( "bundle contents " );
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedBundle::TimeTag() const
|
||||
{
|
||||
return ToUInt64( timeTag_ );
|
||||
}
|
||||
|
||||
|
||||
} // namespace osc
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H
|
||||
#define INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring> // size_t
|
||||
|
||||
#include "OscTypes.h"
|
||||
#include "OscException.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
|
||||
class MalformedPacketException : public Exception{
|
||||
public:
|
||||
MalformedPacketException( const char *w="malformed packet" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MalformedMessageException : public Exception{
|
||||
public:
|
||||
MalformedMessageException( const char *w="malformed message" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MalformedBundleException : public Exception{
|
||||
public:
|
||||
MalformedBundleException( const char *w="malformed bundle" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class WrongArgumentTypeException : public Exception{
|
||||
public:
|
||||
WrongArgumentTypeException( const char *w="wrong argument type" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MissingArgumentException : public Exception{
|
||||
public:
|
||||
MissingArgumentException( const char *w="missing argument" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class ExcessArgumentException : public Exception{
|
||||
public:
|
||||
ExcessArgumentException( const char *w="too many arguments" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
|
||||
class ReceivedPacket{
|
||||
public:
|
||||
// Although the OSC spec is not entirely clear on this, we only support
|
||||
// packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value
|
||||
// representable by an int32). An exception will be raised if you pass a
|
||||
// larger value to the ReceivedPacket() constructor.
|
||||
|
||||
ReceivedPacket( const char *contents, osc_bundle_element_size_t size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize(size) ) {}
|
||||
|
||||
ReceivedPacket( const char *contents, std::size_t size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {}
|
||||
|
||||
#if !(defined(__x86_64__) || defined(_M_X64))
|
||||
ReceivedPacket( const char *contents, int size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {}
|
||||
#endif
|
||||
|
||||
bool IsMessage() const { return !IsBundle(); }
|
||||
bool IsBundle() const;
|
||||
|
||||
osc_bundle_element_size_t Size() const { return size_; }
|
||||
const char *Contents() const { return contents_; }
|
||||
|
||||
private:
|
||||
const char *contents_;
|
||||
osc_bundle_element_size_t size_;
|
||||
|
||||
static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size )
|
||||
{
|
||||
// sanity check integer types declared in OscTypes.h
|
||||
// you'll need to fix OscTypes.h if any of these asserts fail
|
||||
#if 0
|
||||
assert( sizeof(osc::int32) == 4 );
|
||||
assert( sizeof(osc::uint32) == 4 );
|
||||
assert( sizeof(osc::int64) == 8 );
|
||||
assert( sizeof(osc::uint64) == 8 );
|
||||
#endif
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
throw MalformedPacketException( "invalid packet size" );
|
||||
|
||||
if( size == 0 )
|
||||
throw MalformedPacketException( "zero length elements not permitted" );
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
throw MalformedPacketException( "element size must be multiple of four" );
|
||||
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundleElement{
|
||||
public:
|
||||
ReceivedBundleElement( const char *sizePtr )
|
||||
: sizePtr_( sizePtr ) {}
|
||||
|
||||
friend class ReceivedBundleElementIterator;
|
||||
|
||||
bool IsMessage() const { return !IsBundle(); }
|
||||
bool IsBundle() const;
|
||||
|
||||
osc_bundle_element_size_t Size() const;
|
||||
const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; }
|
||||
|
||||
private:
|
||||
const char *sizePtr_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundleElementIterator{
|
||||
public:
|
||||
ReceivedBundleElementIterator( const char *sizePtr )
|
||||
: value_( sizePtr ) {}
|
||||
|
||||
ReceivedBundleElementIterator operator++()
|
||||
{
|
||||
Advance();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedBundleElementIterator operator++(int)
|
||||
{
|
||||
ReceivedBundleElementIterator old( *this );
|
||||
Advance();
|
||||
return old;
|
||||
}
|
||||
|
||||
const ReceivedBundleElement& operator*() const { return value_; }
|
||||
|
||||
const ReceivedBundleElement* operator->() const { return &value_; }
|
||||
|
||||
friend bool operator==(const ReceivedBundleElementIterator& lhs,
|
||||
const ReceivedBundleElementIterator& rhs );
|
||||
|
||||
private:
|
||||
ReceivedBundleElement value_;
|
||||
|
||||
void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); }
|
||||
|
||||
bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const
|
||||
{
|
||||
return value_.sizePtr_ == rhs.value_.sizePtr_;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const ReceivedBundleElementIterator& lhs,
|
||||
const ReceivedBundleElementIterator& rhs )
|
||||
{
|
||||
return lhs.IsEqualTo( rhs );
|
||||
}
|
||||
|
||||
inline bool operator!=(const ReceivedBundleElementIterator& lhs,
|
||||
const ReceivedBundleElementIterator& rhs )
|
||||
{
|
||||
return !( lhs == rhs );
|
||||
}
|
||||
|
||||
|
||||
class ReceivedMessageArgument{
|
||||
public:
|
||||
ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr )
|
||||
: typeTagPtr_( typeTagPtr )
|
||||
, argumentPtr_( argumentPtr ) {}
|
||||
|
||||
friend class ReceivedMessageArgumentIterator;
|
||||
|
||||
char TypeTag() const { return *typeTagPtr_; }
|
||||
|
||||
// the unchecked methods below don't check whether the argument actually
|
||||
// is of the specified type. they should only be used if you've already
|
||||
// checked the type tag or the associated IsType() method.
|
||||
|
||||
bool IsBool() const
|
||||
{ return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; }
|
||||
bool AsBool() const;
|
||||
bool AsBoolUnchecked() const;
|
||||
|
||||
bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; }
|
||||
bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; }
|
||||
|
||||
bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; }
|
||||
int32 AsInt32() const;
|
||||
int32 AsInt32Unchecked() const;
|
||||
|
||||
bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; }
|
||||
float AsFloat() const;
|
||||
float AsFloatUnchecked() const;
|
||||
|
||||
bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; }
|
||||
char AsChar() const;
|
||||
char AsCharUnchecked() const;
|
||||
|
||||
bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; }
|
||||
uint32 AsRgbaColor() const;
|
||||
uint32 AsRgbaColorUnchecked() const;
|
||||
|
||||
bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; }
|
||||
uint32 AsMidiMessage() const;
|
||||
uint32 AsMidiMessageUnchecked() const;
|
||||
|
||||
bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; }
|
||||
int64 AsInt64() const;
|
||||
int64 AsInt64Unchecked() const;
|
||||
|
||||
bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; }
|
||||
uint64 AsTimeTag() const;
|
||||
uint64 AsTimeTagUnchecked() const;
|
||||
|
||||
bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; }
|
||||
double AsDouble() const;
|
||||
double AsDoubleUnchecked() const;
|
||||
|
||||
bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; }
|
||||
const char* AsString() const;
|
||||
const char* AsStringUnchecked() const { return argumentPtr_; }
|
||||
|
||||
bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; }
|
||||
const char* AsSymbol() const;
|
||||
const char* AsSymbolUnchecked() const { return argumentPtr_; }
|
||||
|
||||
bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; }
|
||||
void AsBlob( const void*& data, osc_bundle_element_size_t& size ) const;
|
||||
void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const;
|
||||
|
||||
bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; }
|
||||
bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; }
|
||||
// Calculate the number of top-level items in the array. Nested arrays count as one item.
|
||||
// Only valid at array start. Will throw an exception if IsArrayStart() == false.
|
||||
std::size_t ComputeArrayItemCount() const;
|
||||
|
||||
private:
|
||||
const char *typeTagPtr_;
|
||||
const char *argumentPtr_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedMessageArgumentIterator{
|
||||
public:
|
||||
ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments )
|
||||
: value_( typeTags, arguments ) {}
|
||||
|
||||
ReceivedMessageArgumentIterator operator++()
|
||||
{
|
||||
Advance();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentIterator operator++(int)
|
||||
{
|
||||
ReceivedMessageArgumentIterator old( *this );
|
||||
Advance();
|
||||
return old;
|
||||
}
|
||||
|
||||
const ReceivedMessageArgument& operator*() const { return value_; }
|
||||
|
||||
const ReceivedMessageArgument* operator->() const { return &value_; }
|
||||
|
||||
friend bool operator==(const ReceivedMessageArgumentIterator& lhs,
|
||||
const ReceivedMessageArgumentIterator& rhs );
|
||||
|
||||
private:
|
||||
ReceivedMessageArgument value_;
|
||||
|
||||
void Advance();
|
||||
|
||||
bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const
|
||||
{
|
||||
return value_.typeTagPtr_ == rhs.value_.typeTagPtr_;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const ReceivedMessageArgumentIterator& lhs,
|
||||
const ReceivedMessageArgumentIterator& rhs )
|
||||
{
|
||||
return lhs.IsEqualTo( rhs );
|
||||
}
|
||||
|
||||
inline bool operator!=(const ReceivedMessageArgumentIterator& lhs,
|
||||
const ReceivedMessageArgumentIterator& rhs )
|
||||
{
|
||||
return !( lhs == rhs );
|
||||
}
|
||||
|
||||
|
||||
class ReceivedMessageArgumentStream{
|
||||
friend class ReceivedMessage;
|
||||
ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin,
|
||||
const ReceivedMessageArgumentIterator& end )
|
||||
: p_( begin )
|
||||
, end_( end ) {}
|
||||
|
||||
ReceivedMessageArgumentIterator p_, end_;
|
||||
|
||||
public:
|
||||
|
||||
// end of stream
|
||||
bool Eos() const { return p_ == end_; }
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( bool& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsBool();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// not sure if it would be useful to stream Nil and Infinitum
|
||||
// for now it's not possible
|
||||
// same goes for array boundaries
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( int32& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsInt32();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( float& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsFloat();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( char& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsChar();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsRgbaColor();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsMidiMessage();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( int64& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsInt64();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( TimeTag& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsTimeTag();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( double& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsDouble();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( Blob& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
(*p_++).AsBlob( rhs.data, rhs.size );
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( const char*& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsString();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( Symbol& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsSymbol();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs )
|
||||
{
|
||||
(void) rhs; // suppress unused parameter warning
|
||||
|
||||
if( !Eos() )
|
||||
throw ExcessArgumentException();
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ReceivedMessage{
|
||||
void Init( const char *bundle, osc_bundle_element_size_t size );
|
||||
public:
|
||||
explicit ReceivedMessage( const ReceivedPacket& packet );
|
||||
explicit ReceivedMessage( const ReceivedBundleElement& bundleElement );
|
||||
|
||||
const char *AddressPattern() const { return addressPattern_; }
|
||||
|
||||
// Support for non-standard SuperCollider integer address patterns:
|
||||
bool AddressPatternIsUInt32() const;
|
||||
uint32 AddressPatternAsUInt32() const;
|
||||
|
||||
uint32 ArgumentCount() const { return static_cast<uint32>(typeTagsEnd_ - typeTagsBegin_); }
|
||||
|
||||
const char *TypeTags() const { return typeTagsBegin_; }
|
||||
|
||||
|
||||
typedef ReceivedMessageArgumentIterator const_iterator;
|
||||
|
||||
ReceivedMessageArgumentIterator ArgumentsBegin() const
|
||||
{
|
||||
return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ );
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentIterator ArgumentsEnd() const
|
||||
{
|
||||
return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 );
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream ArgumentStream() const
|
||||
{
|
||||
return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() );
|
||||
}
|
||||
|
||||
private:
|
||||
const char *addressPattern_;
|
||||
const char *typeTagsBegin_;
|
||||
const char *typeTagsEnd_;
|
||||
const char *arguments_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundle{
|
||||
void Init( const char *message, osc_bundle_element_size_t size );
|
||||
public:
|
||||
explicit ReceivedBundle( const ReceivedPacket& packet );
|
||||
explicit ReceivedBundle( const ReceivedBundleElement& bundleElement );
|
||||
|
||||
uint64 TimeTag() const;
|
||||
|
||||
uint32 ElementCount() const { return elementCount_; }
|
||||
|
||||
typedef ReceivedBundleElementIterator const_iterator;
|
||||
|
||||
ReceivedBundleElementIterator ElementsBegin() const
|
||||
{
|
||||
return ReceivedBundleElementIterator( timeTag_ + 8 );
|
||||
}
|
||||
|
||||
ReceivedBundleElementIterator ElementsEnd() const
|
||||
{
|
||||
return ReceivedBundleElementIterator( end_ );
|
||||
}
|
||||
|
||||
private:
|
||||
const char *timeTag_;
|
||||
const char *end_;
|
||||
uint32 elementCount_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#include "OscTypes.h"
|
||||
|
||||
namespace osc{
|
||||
|
||||
BundleInitiator BeginBundleImmediate(1);
|
||||
BundleTerminator EndBundle;
|
||||
MessageTerminator EndMessage;
|
||||
NilType OscNil;
|
||||
#ifndef _OBJC_OBJC_H_
|
||||
NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead
|
||||
#endif
|
||||
InfinitumType Infinitum;
|
||||
ArrayInitiator BeginArray;
|
||||
ArrayTerminator EndArray;
|
||||
|
||||
} // namespace osc
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The text above constitutes the entire oscpack license; however,
|
||||
the oscpack developer(s) also make the following non-binding requests:
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
requested to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. It is also
|
||||
requested that these non-binding requests be included whenever the
|
||||
above license is reproduced.
|
||||
*/
|
||||
#ifndef INCLUDED_OSCPACK_OSCTYPES_H
|
||||
#define INCLUDED_OSCPACK_OSCTYPES_H
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
// basic types
|
||||
|
||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
||||
|
||||
typedef __int64 int64;
|
||||
typedef unsigned __int64 uint64;
|
||||
|
||||
#elif defined(__x86_64__) || defined(_M_X64)
|
||||
|
||||
typedef long int64;
|
||||
typedef unsigned long uint64;
|
||||
|
||||
#else
|
||||
|
||||
typedef long long int64;
|
||||
typedef unsigned long long uint64;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
|
||||
typedef signed int int32;
|
||||
typedef unsigned int uint32;
|
||||
|
||||
#else
|
||||
|
||||
typedef signed long int32;
|
||||
typedef unsigned long uint32;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
enum ValueTypeSizes{
|
||||
OSC_SIZEOF_INT32 = 4,
|
||||
OSC_SIZEOF_UINT32 = 4,
|
||||
OSC_SIZEOF_INT64 = 8,
|
||||
OSC_SIZEOF_UINT64 = 8,
|
||||
};
|
||||
|
||||
|
||||
// osc_bundle_element_size_t is used for the size of bundle elements and blobs
|
||||
// the OSC spec specifies these as int32 (signed) but we ensure that they
|
||||
// are always positive since negative field sizes make no sense.
|
||||
|
||||
typedef int32 osc_bundle_element_size_t;
|
||||
|
||||
enum {
|
||||
OSC_INT32_MAX = 0x7FFFFFFF,
|
||||
|
||||
// Element sizes are specified to be int32, and are always rounded up to nearest
|
||||
// multiple of 4. Therefore their values can't be greater than 0x7FFFFFFC.
|
||||
OSC_BUNDLE_ELEMENT_SIZE_MAX = 0x7FFFFFFC
|
||||
};
|
||||
|
||||
|
||||
inline bool IsValidElementSizeValue( osc_bundle_element_size_t x )
|
||||
{
|
||||
// sizes may not be negative or exceed OSC_BUNDLE_ELEMENT_SIZE_MAX
|
||||
return x >= 0 && x <= OSC_BUNDLE_ELEMENT_SIZE_MAX;
|
||||
}
|
||||
|
||||
|
||||
inline bool IsMultipleOf4( osc_bundle_element_size_t x )
|
||||
{
|
||||
return (x & ((osc_bundle_element_size_t)0x03)) == 0;
|
||||
}
|
||||
|
||||
|
||||
enum TypeTagValues {
|
||||
TRUE_TYPE_TAG = 'T',
|
||||
FALSE_TYPE_TAG = 'F',
|
||||
NIL_TYPE_TAG = 'N',
|
||||
INFINITUM_TYPE_TAG = 'I',
|
||||
INT32_TYPE_TAG = 'i',
|
||||
FLOAT_TYPE_TAG = 'f',
|
||||
CHAR_TYPE_TAG = 'c',
|
||||
RGBA_COLOR_TYPE_TAG = 'r',
|
||||
MIDI_MESSAGE_TYPE_TAG = 'm',
|
||||
INT64_TYPE_TAG = 'h',
|
||||
TIME_TAG_TYPE_TAG = 't',
|
||||
DOUBLE_TYPE_TAG = 'd',
|
||||
STRING_TYPE_TAG = 's',
|
||||
SYMBOL_TYPE_TAG = 'S',
|
||||
BLOB_TYPE_TAG = 'b',
|
||||
ARRAY_BEGIN_TYPE_TAG = '[',
|
||||
ARRAY_END_TYPE_TAG = ']'
|
||||
};
|
||||
|
||||
|
||||
|
||||
// i/o manipulators used for streaming interfaces
|
||||
|
||||
struct BundleInitiator{
|
||||
explicit BundleInitiator( uint64 timeTag_ ) : timeTag( timeTag_ ) {}
|
||||
uint64 timeTag;
|
||||
};
|
||||
|
||||
extern BundleInitiator BeginBundleImmediate;
|
||||
|
||||
inline BundleInitiator BeginBundle( uint64 timeTag=1 )
|
||||
{
|
||||
return BundleInitiator(timeTag);
|
||||
}
|
||||
|
||||
|
||||
struct BundleTerminator{
|
||||
};
|
||||
|
||||
extern BundleTerminator EndBundle;
|
||||
|
||||
struct BeginMessage{
|
||||
explicit BeginMessage( const char *addressPattern_ ) : addressPattern( addressPattern_ ) {}
|
||||
const char *addressPattern;
|
||||
};
|
||||
|
||||
struct MessageTerminator{
|
||||
};
|
||||
|
||||
extern MessageTerminator EndMessage;
|
||||
|
||||
|
||||
// osc specific types. they are defined as structs so they can be used
|
||||
// as separately identifiable types with the streaming operators.
|
||||
|
||||
struct NilType{
|
||||
};
|
||||
|
||||
extern NilType OscNil;
|
||||
|
||||
#ifndef _OBJC_OBJC_H_
|
||||
extern NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead
|
||||
#endif
|
||||
|
||||
struct InfinitumType{
|
||||
};
|
||||
|
||||
extern InfinitumType Infinitum;
|
||||
|
||||
struct RgbaColor{
|
||||
RgbaColor() {}
|
||||
explicit RgbaColor( uint32 value_ ) : value( value_ ) {}
|
||||
uint32 value;
|
||||
|
||||
operator uint32() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
struct MidiMessage{
|
||||
MidiMessage() {}
|
||||
explicit MidiMessage( uint32 value_ ) : value( value_ ) {}
|
||||
uint32 value;
|
||||
|
||||
operator uint32() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
struct TimeTag{
|
||||
TimeTag() {}
|
||||
explicit TimeTag( uint64 value_ ) : value( value_ ) {}
|
||||
uint64 value;
|
||||
|
||||
operator uint64() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
struct Symbol{
|
||||
Symbol() {}
|
||||
explicit Symbol( const char* value_ ) : value( value_ ) {}
|
||||
const char* value;
|
||||
|
||||
operator const char *() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
struct Blob{
|
||||
Blob() {}
|
||||
explicit Blob( const void* data_, osc_bundle_element_size_t size_ )
|
||||
: data( data_ ), size( size_ ) {}
|
||||
const void* data;
|
||||
osc_bundle_element_size_t size;
|
||||
};
|
||||
|
||||
struct ArrayInitiator{
|
||||
};
|
||||
|
||||
extern ArrayInitiator BeginArray;
|
||||
|
||||
struct ArrayTerminator{
|
||||
};
|
||||
|
||||
extern ArrayTerminator EndArray;
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCTYPES_H */
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "oscreceiver.h"
|
||||
#include "contrib/oscpack/OscTypes.h"
|
||||
#include "contrib/oscpack/OscReceivedElements.h"
|
||||
|
||||
OscReceiver::OscReceiver(quint16 receivePort, QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_udpSocket = new QUdpSocket(this);
|
||||
// m_udpSocket->bind(QHostAddress::LocalHost, receivePort);
|
||||
qDebug() << "Listening for OSC on " << receivePort;
|
||||
m_udpSocket->bind(QHostAddress::Any, receivePort);
|
||||
connect(m_udpSocket, &QUdpSocket::readyRead, this, &OscReceiver::readyReadCb);
|
||||
}
|
||||
|
||||
void OscReceiver::readyReadCb() {
|
||||
while (this->m_udpSocket->hasPendingDatagrams()) {
|
||||
QNetworkDatagram datagram = this->m_udpSocket->receiveDatagram();
|
||||
// XXX: we could also retrieve the sender host and port
|
||||
QByteArray data = datagram.data();
|
||||
QVariantList arguments;
|
||||
QString oscAddress;
|
||||
this->byteArrayToVariantList(arguments, oscAddress, data);
|
||||
emit messageReceived(oscAddress, arguments);
|
||||
qDebug() << "C++OscReceiver Received: " << oscAddress << arguments;
|
||||
}
|
||||
}
|
||||
|
||||
void OscReceiver::byteArrayToVariantList(QVariantList& outputVariantList, QString& outputOscAddress, const QByteArray& inputByteArray) {
|
||||
osc::ReceivedPacket packet(inputByteArray.data(), inputByteArray.size());
|
||||
// TODO: catch parsing exceptions
|
||||
if (packet.IsMessage()) {
|
||||
osc::ReceivedMessage message(packet);
|
||||
// Get address pattern
|
||||
QString address(message.AddressPattern());
|
||||
outputOscAddress.replace(0, address.size(), address);
|
||||
|
||||
for (auto iter = message.ArgumentsBegin(); iter != message.ArgumentsEnd(); ++ iter) {
|
||||
osc::ReceivedMessageArgument argument = (*iter);
|
||||
if (argument.IsBool()) {
|
||||
outputVariantList.append(QVariant(argument.AsBool()));
|
||||
} else if (argument.IsString()) {
|
||||
outputVariantList.append(QVariant(argument.AsString()));
|
||||
} else if (argument.IsInt32()) {
|
||||
outputVariantList.append(QVariant(argument.AsInt32()));
|
||||
} else if (argument.IsFloat()) {
|
||||
outputVariantList.append(QVariant(argument.AsFloat()));
|
||||
} else if (argument.IsChar()) {
|
||||
outputVariantList.append(QVariant(argument.AsChar()));
|
||||
//} else if (argument.IsInt64()) {
|
||||
// outputVariantList.append(QVariant(argument.AsInt64()));
|
||||
} else if (argument.IsDouble()) {
|
||||
outputVariantList.append(QVariant(argument.AsDouble()));
|
||||
}
|
||||
// TODO: support Array, Midi, Blob, Symbol, TimeTag, RGBA, Nil
|
||||
}
|
||||
} // TODO: also parse bundles
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef OSCRECEIVER_H
|
||||
#define OSCRECEIVER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QtNetwork>
|
||||
#include <QHostAddress>
|
||||
#include "qosc_global.h"
|
||||
|
||||
/**
|
||||
* @brief Receives OSC on a given port number.
|
||||
*
|
||||
* Currently only supports unicast UDP.
|
||||
*/
|
||||
class QOSC_EXPORT OscReceiver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
// TODO: Support multicast UDP
|
||||
// TODO: Support TCP
|
||||
// TODO: Support broadcast UDP
|
||||
// TODO: Support OSC bundles
|
||||
// TODO: Provide a class to route messages.
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param receivePort Port number to listen to.
|
||||
*/
|
||||
explicit OscReceiver(quint16 receivePort, QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal triggered each time we receive a message.
|
||||
* @param oscAddress
|
||||
* @param message
|
||||
*/
|
||||
void messageReceived(const QString& oscAddress, const QVariantList& message);
|
||||
|
||||
public slots:
|
||||
void readyReadCb();
|
||||
|
||||
private:
|
||||
QUdpSocket* m_udpSocket;
|
||||
void byteArrayToVariantList(QVariantList& outputVariantList, QString& outputOscAddress, const QByteArray& inputByteArray);
|
||||
};
|
||||
|
||||
#endif // OSCRECEIVER_H
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "oscsender.h"
|
||||
#include "contrib/oscpack/OscOutboundPacketStream.h"
|
||||
#include "contrib/oscpack/OscTypes.h"
|
||||
#include <iostream>
|
||||
|
||||
// FIXME: we should also allow addresses such as "localhost", or "example.com"
|
||||
|
||||
OscSender::OscSender(const QString& hostAddress, quint16 port, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_udpSocket(new QUdpSocket(this)),
|
||||
m_hostAddress(hostAddress),
|
||||
m_port(port)
|
||||
{
|
||||
m_udpSocket->connectToHost(QHostAddress(m_hostAddress) , m_port);
|
||||
}
|
||||
|
||||
void OscSender::send(const QString& oscAddress, const QVariantList& arguments) {
|
||||
QByteArray datagram;
|
||||
this->variantListToByteArray(datagram, oscAddress, arguments);
|
||||
|
||||
qint64 written = m_udpSocket->write(datagram);
|
||||
|
||||
qDebug() << "C++OscSender Send" << datagram.size() << "bytes:" << datagram.toHex() <<
|
||||
"to " << m_hostAddress << "on port" << m_port;
|
||||
|
||||
if (written == -1) {
|
||||
qCritical() << "Failed to send OSC. (write bytes to the send socket)";
|
||||
}
|
||||
m_udpSocket->flush();
|
||||
m_udpSocket->waitForBytesWritten();
|
||||
}
|
||||
|
||||
|
||||
void OscSender::variantListToByteArray(QByteArray& outputResult, const QString& oscAddress, const QVariantList& arguments) {
|
||||
char buffer[1024];
|
||||
osc::OutboundPacketStream packet(buffer, 1024);
|
||||
// FIXME: Sending datagrams larger than 512 bytes is in general disadvised, as even if they are sent successfully,
|
||||
// they are likely to be fragmented by the IP layer before arriving at their final destination.
|
||||
// packet << osc::BeginBundleImmediate << osc::BeginMessage(oscAddress.toStdString().c_str());
|
||||
packet << osc::BeginMessage(oscAddress.toStdString().c_str());
|
||||
|
||||
for (int i = 0; i < arguments.count(); ++ i) {
|
||||
QVariant argument = arguments[i];
|
||||
QMetaType::Type type = static_cast<QMetaType::Type>(argument.type());
|
||||
|
||||
if (type == QMetaType::Int) {
|
||||
packet << argument.toInt();
|
||||
} else if (type == QMetaType::Float) {
|
||||
packet << argument.toFloat();
|
||||
} else if (type == QMetaType::Double) {
|
||||
packet << argument.toDouble();
|
||||
} else if (type == QMetaType::QString) {
|
||||
packet << argument.toString().toStdString().c_str();
|
||||
//} else if (type == QMetaType::QByteArray) {
|
||||
// osc::Blob b(argument.toByteArray().constData());
|
||||
// packet << b;
|
||||
} else if (type == QMetaType::Bool) {
|
||||
packet << argument.toBool();
|
||||
} else {
|
||||
qDebug() << "Unhandled OSC argument type " << argument.typeName();
|
||||
}
|
||||
// TODO: implement other OSC types
|
||||
}
|
||||
|
||||
// packet << osc::EndMessage << osc::EndBundle;
|
||||
packet << osc::EndMessage;
|
||||
outputResult.append(packet.Data(), packet.Size());
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef OSCSENDER_H
|
||||
#define OSCSENDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QtNetwork>
|
||||
#include <QHostAddress>
|
||||
#include "qosc_global.h"
|
||||
|
||||
/**
|
||||
* @brief Sends OSC messages to a given host and port.
|
||||
*
|
||||
* Currently only supports unicast UDP.
|
||||
*/
|
||||
class QOSC_EXPORT OscSender : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
// TODO: Add portNumber property (and allow users to change it)
|
||||
// TODO: Add hostAddress property (and allow users to change it)
|
||||
// TODO: Support TCP
|
||||
// TODO: Support multicast
|
||||
// TODO: Support broadcast
|
||||
// TODO: Support OSC bundles
|
||||
// TODO: Support DNS resolution
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param hostAddress
|
||||
* @param port
|
||||
* @param parent
|
||||
*/
|
||||
explicit OscSender(const QString& hostAddress, quint16 port, QObject* parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Sends an OSC message to the host and address that this sender is configured to send to.
|
||||
* @param oscAddress OSC path /like/this
|
||||
* @param arguments List of QVariant arguments of any type
|
||||
*/
|
||||
Q_INVOKABLE void send(const QString& oscAddress, const QVariantList& arguments);
|
||||
|
||||
signals:
|
||||
// TODO: Add messageSent signal
|
||||
// TODO: Add connected signal for TCP sender.
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
QUdpSocket* m_udpSocket;
|
||||
QHostAddress m_hostAddress;
|
||||
quint16 m_port;
|
||||
|
||||
void variantListToByteArray(QByteArray& outputResult, const QString& oscAddress, const QVariantList& arguments);
|
||||
};
|
||||
|
||||
#endif // OSCSENDER_H
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <QtCore/QtGlobal>
|
||||
|
||||
#if defined(QOSC_LIBRARY)
|
||||
# define QOSC_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define QOSC_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
+6
-12
@@ -3553,24 +3553,21 @@ void MainWindow::removeCurrentMapping() {
|
||||
|
||||
void MainWindow::startOscReceiver()
|
||||
{
|
||||
#ifdef HAVE_OSC
|
||||
int port = oscListeningPort;
|
||||
std::ostringstream os;
|
||||
os << port;
|
||||
os << oscListeningPort;
|
||||
#if QT_VERSION >= 0x050500
|
||||
QMessageLogger(__FILE__, __LINE__, 0).info() << "OSC port: " << port ;
|
||||
QMessageLogger(__FILE__, __LINE__, 0).info() << "OSC port: " << oscListeningPort;
|
||||
#else
|
||||
QMessageLogger(__FILE__, __LINE__, 0).debug() << "OSC port: " << port ;
|
||||
QMessageLogger(__FILE__, __LINE__, 0).debug() << "OSC port: " << oscListeningPort;
|
||||
#endif
|
||||
osc_interface.reset(new OscInterface(os.str()));
|
||||
if (port != 0)
|
||||
osc_interface.reset(new OscInterface(oscListeningPort));
|
||||
if (oscListeningPort != 0)
|
||||
{
|
||||
osc_interface->start();
|
||||
}
|
||||
osc_timer = new QTimer(this); // FIXME: memleak?
|
||||
connect(osc_timer, SIGNAL(timeout()), this, SLOT(pollOscInterface()));
|
||||
osc_timer->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MainWindow::setOscPort(int port)
|
||||
@@ -3580,10 +3577,8 @@ bool MainWindow::setOscPort(int port)
|
||||
qWarning() << "OSC port is out of range: " << port << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
oscListeningPort = port;
|
||||
startOscReceiver();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3610,9 +3605,8 @@ bool MainWindow::setOscPort(QString portNumber)
|
||||
|
||||
void MainWindow::pollOscInterface()
|
||||
{
|
||||
#ifdef HAVE_OSC
|
||||
// FIXME: we should now use its QObject signals instead of polling it
|
||||
osc_interface->consume_commands(*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::exitFullScreen()
|
||||
|
||||
@@ -37,9 +37,7 @@
|
||||
|
||||
#include "MapperGLCanvas.h"
|
||||
#include "MapperGLCanvasToolbar.h"
|
||||
#ifdef HAVE_OSC
|
||||
#include "OscInterface.h"
|
||||
#endif
|
||||
|
||||
#include "OutputGLWindow.h"
|
||||
#include "ConsoleWindow.h"
|
||||
@@ -504,9 +502,7 @@ private:
|
||||
MappingItemDelegate *mappingItemDelegate;
|
||||
|
||||
// OSC.
|
||||
#ifdef HAVE_OSC
|
||||
OscInterface::ptr osc_interface;
|
||||
#endif
|
||||
int oscListeningPort;
|
||||
QTimer *osc_timer;
|
||||
|
||||
|
||||
+8
-13
@@ -1,7 +1,13 @@
|
||||
QT += gui opengl xml core network
|
||||
QT += gui
|
||||
QT += opengl
|
||||
QT += xml
|
||||
QT += core
|
||||
QT += network
|
||||
QT += multimedia
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
QT -= gui # using widgets instead gui in Qt5
|
||||
QT += widgets multimedia webenginewidgets
|
||||
QT += widgets webenginewidgets
|
||||
}
|
||||
|
||||
#Includes common configuration for all subdirectory .pro files.
|
||||
@@ -18,12 +24,10 @@ unix:!macx {
|
||||
INCLUDE_PATH +=
|
||||
PKGCONFIG += \
|
||||
gstreamer-1.0 gstreamer-base-1.0 gstreamer-app-1.0 gstreamer-pbutils-1.0 \
|
||||
liblo \
|
||||
gl x11
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-result -Wno-unused-parameter \
|
||||
-Wno-unused-variable -Wno-switch -Wno-comment \
|
||||
-Wno-unused-but-set-variable
|
||||
QMAKE_CXXFLAGS += -DHAVE_OSC
|
||||
}
|
||||
|
||||
# macOS-specific:
|
||||
@@ -40,15 +44,6 @@ macx {
|
||||
# This tells qmake not to put the executable inside a bundle.
|
||||
# just for reference. Do not uncomment.
|
||||
# CONFIG-=app_bundle
|
||||
|
||||
# For OSC support: (if pkg-config was installed)
|
||||
# CONFIG += link_pkgconfig
|
||||
# PKGCONFIG += lo
|
||||
|
||||
# FIXME: No OSC for now:
|
||||
# LIBS += -L/usr/local/lib -llo
|
||||
# INCLUDEPATH += /usr/local/include
|
||||
# QMAKE_CXXFLAGS += -DHAVE_OSC
|
||||
ICON = resources/app_icons/mapmap.icns
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user