Frm Stephan Huber, osc plugin that supports send and recieving events via UDP packets.

This commit is contained in:
Robert Osfield 2012-11-19 10:48:37 +00:00
parent c005e6997c
commit e2ee774705
29 changed files with 5350 additions and 0 deletions

View File

@ -255,6 +255,7 @@ IF(LIBVNCSERVER_FOUND)
ENDIF()
ADD_SUBDIRECTORY(pvr)
ADD_SUBDIRECTORY(osc)
####################################################
#

View File

@ -0,0 +1,49 @@
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR})
SET(TARGET_SRC
ip/IpEndpointName.cpp
osc/OscOutboundPacketStream.cpp
osc/OscPrintReceivedElements.cpp
osc/OscReceivedElements.cpp
osc/OscTypes.cpp
OscDevice.cpp
OscProxyEventHandler.cpp
ReaderWriterOscDevice.cpp
)
SET(TARGET_H
ip/IpEndpointName.h
ip/NetworkingUtils.h
ip/PacketListener.h
ip/TimerListener.h
ip/UdpSocket.h
osc/MessageMappingOscPacketListener.h
osc/OscException.h
osc/OscHostEndianness.h
osc/OscOutboundPacketStream.h
osc/OscPacketListener.h
osc/OscPrintReceivedElements.h
osc/OscReceivedElements.h
osc/OscTypes.h
OscProxyEventHandler.hpp
OscDevice.hpp
)
if(WIN32)
SET(TARGET_SRC
${TARGET_SRC}
ip/win32/NetworkingUtils.cpp
ip/win32/UdpSocket.cpp
)
ELSE()
SET(TARGET_SRC
${TARGET_SRC}
ip/posix/NetworkingUtils.cpp
ip/posix/UdpSocket.cpp
)
ENDIF()
SET(TARGET_ADDED_LIBRARIES osgPresentation )
#### end var setup ###
SETUP_PLUGIN(osc)

422
src/osgPlugins/osc/OscDevice.cpp Executable file
View File

@ -0,0 +1,422 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include "OscDevice.hpp"
#include <OpenThreads/Thread>
#include <osgDB/FileUtils>
#include "osc/OscPrintReceivedElements.h"
class StandardRequestHandler : public OscDevice::RequestHandler {
public:
StandardRequestHandler() : OscDevice::RequestHandler("") {}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
OSG_NOTICE << "OscDevice :: unhandled request: " << full_request_path << std::endl;
for(osc::ReceivedMessageArgumentIterator itr = m.ArgumentsBegin(); itr != m.ArgumentsEnd(); ++itr)
{
OSG_NOTICE << " " << (*itr) << std::endl;
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << ": fall-through request-handler, catches all requests w/o registered handler and report them to the console";
}
};
class SetMouseInputRangeRequestHandler : public OscDevice::RequestHandler {
public:
SetMouseInputRangeRequestHandler()
: OscDevice::RequestHandler("/osgga/mouse/set_input_range")
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
try {
float x_min(-1.0f), y_min(-1.0f), x_max(1.0f), y_max(1.0f);
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >> x_min >> y_min >> x_max >> y_max >> osc::EndMessage;
getDevice()->getEventQueue()->setMouseInputRange(x_min, y_min, x_max, y_max);
return true;
}
catch(osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(float x_min, float y_min, float x_max, float y_max): sets the mouse-input-range" << std::dec;
}
};
class SetMouseOrientationRequestHandler : public OscDevice::RequestHandler {
public:
SetMouseOrientationRequestHandler()
: OscDevice::RequestHandler("/osgga/mouse/y_orientation_increasing_upwards")
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
try {
bool increasing_upwards(false);
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >>increasing_upwards >> osc::EndMessage;
getDevice()->getEventQueue()->getCurrentEventState()->setMouseYOrientation(
increasing_upwards ? osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS : osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS);
return true;
}
catch(osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(float x_min, float y_min, float x_max, float y_max): sets the mouse-input-range" << std::dec;
}
};
class KeyCodeRequestHandler : public OscDevice::RequestHandler {
public:
KeyCodeRequestHandler(bool handle_key_press)
: OscDevice::RequestHandler(std::string("/osgga/key/") + ((handle_key_press) ? "press" : "release"))
, _handleKeyPress(handle_key_press)
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
try {
osc::int32 keycode(0);
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >> keycode >> osc::EndMessage;
if (_handleKeyPress)
getDevice()->getEventQueue()->keyPress(keycode, getLocalTime());
else
getDevice()->getEventQueue()->keyRelease(keycode, getLocalTime());
return true;
}
catch(osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(int keycode): send KEY_" << (_handleKeyPress ? "DOWN" : "UP");
}
private:
bool _handleKeyPress;
};
class KeyPressAndReleaseRequestHandler : public OscDevice::RequestHandler {
public:
KeyPressAndReleaseRequestHandler()
: OscDevice::RequestHandler("/osgga/key/press_and_release")
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
try {
osc::int32 keycode(0);
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >> keycode >> osc::EndMessage;
getDevice()->getEventQueue()->keyPress(keycode, getLocalTime());
getDevice()->getEventQueue()->keyRelease(keycode, getLocalTime());
return true;
}
catch(osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(int keycode): send KEY_DOWN and KEY_UP";
}
private:
bool _handleKeyPress;
};
class MouseMotionRequestHandler : public OscDevice::RequestHandler {
public:
MouseMotionRequestHandler()
: OscDevice::RequestHandler("/osgga/mouse/motion")
, _lastX(0.0f)
, _lastY(0.0f)
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
try {
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >> _lastX >> _lastY >> osc::EndMessage;
getDevice()->getEventQueue()->mouseMotion(_lastX, _lastY, getLocalTime());
return true;
}
catch (osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(float x, float y): send mouse motion";
}
float getLastX() const { return _lastX; }
float getLastY() const { return _lastY; }
private:
float _lastX, _lastY;
};
class MouseButtonToggleRequestHandler : public OscDevice::RequestHandler {
public:
MouseButtonToggleRequestHandler(const std::string& btn_name, MouseMotionRequestHandler* mm_handler)
: OscDevice::RequestHandler("/osgga/mouse/toggle/"+btn_name)
, _mmHandler(mm_handler)
, _btnNum(atoi(btn_name.c_str()))
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
float down(0.0f);
try {
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >> down >> osc::EndMessage;
if (down > 0)
getDevice()->getEventQueue()->mouseButtonPress(_mmHandler->getLastX(), _mmHandler->getLastY(), _btnNum, getLocalTime());
else
getDevice()->getEventQueue()->mouseButtonRelease(_mmHandler->getLastX(), _mmHandler->getLastY(), _btnNum, getLocalTime());
return true;
}
catch (osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(float down): toggle mouse button";
}
private:
osg::observer_ptr<MouseMotionRequestHandler> _mmHandler;
int _btnNum;
};
class MouseButtonRequestHandler : public OscDevice::RequestHandler {
public:
enum Mode { PRESS, RELEASE, DOUBLE_PRESS};
MouseButtonRequestHandler(Mode mode)
: OscDevice::RequestHandler("")
, _mode(mode)
{
switch(mode) {
case PRESS:
setRequestPath("/osgga/mouse/press");
break;
case RELEASE:
setRequestPath("/osgga/mouse/release");
break;
case DOUBLE_PRESS:
setRequestPath("/osgga/mouse/doublepress");
break;
}
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m)
{
float x(0.0f), y(0.0f);
osc::int32 btn(0);
try {
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
args >> x >> y >> btn >> osc::EndMessage;
switch (_mode) {
case PRESS:
getDevice()->getEventQueue()->mouseButtonPress(x,y, btn, getLocalTime());
break;
case RELEASE:
getDevice()->getEventQueue()->mouseButtonRelease(x,y, btn, getLocalTime());
break;
case DOUBLE_PRESS:
getDevice()->getEventQueue()->mouseDoubleButtonPress(x,y, btn, getLocalTime());
break;
}
return true;
}
catch (osc::Exception e) {
handleException(e);
}
return false;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << "(float x, float y, int btn): send mouse ";
switch (_mode) {
case PRESS:
out << "press"; break;
case RELEASE:
out << "release"; break;
case DOUBLE_PRESS:
out << "double press"; break;
}
}
private:
Mode _mode;
};
OscDevice::OscDevice(const std::string& server_address, int listening_port)
: osgGA::Device()
, OpenThreads::Thread()
, osc::OscPacketListener()
, _listeningAddress(server_address)
, _listeningPort(listening_port)
, _socket(NULL)
, _map()
{
_socket = new UdpListeningReceiveSocket(IpEndpointName( server_address.c_str(), listening_port ), this);
addRequestHandler(new KeyCodeRequestHandler(false));
addRequestHandler(new KeyCodeRequestHandler(true));
addRequestHandler(new KeyPressAndReleaseRequestHandler());
addRequestHandler(new SetMouseInputRangeRequestHandler());
addRequestHandler(new SetMouseOrientationRequestHandler());
MouseMotionRequestHandler* mm_handler = new MouseMotionRequestHandler();
addRequestHandler(mm_handler);
addRequestHandler(new MouseButtonRequestHandler(MouseButtonRequestHandler::PRESS));
addRequestHandler(new MouseButtonRequestHandler(MouseButtonRequestHandler::RELEASE));
addRequestHandler(new MouseButtonRequestHandler(MouseButtonRequestHandler::DOUBLE_PRESS));
addRequestHandler(new MouseButtonToggleRequestHandler("1", mm_handler));
addRequestHandler(new MouseButtonToggleRequestHandler("2", mm_handler));
addRequestHandler(new MouseButtonToggleRequestHandler("3", mm_handler));
addRequestHandler(new StandardRequestHandler());
start();
}
OscDevice::~OscDevice()
{
_socket->AsynchronousBreak();
join();
delete _socket;
}
void OscDevice::run()
{
_socket->Run();
}
void OscDevice::ProcessMessage( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint )
{
std::string in_request_path(m.AddressPattern());
std::string request_path = in_request_path + "/";
std::size_t pos(std::string::npos);
bool handled(false);
do {
pos = request_path.find_last_of('/', pos-1);
if (pos != std::string::npos)
{
std::string mangled_path = request_path.substr(0, pos);
std::pair<RequestHandlerMap::iterator,RequestHandlerMap::iterator> range = _map.equal_range(mangled_path);
for(RequestHandlerMap::iterator i = range.first; i != range.second; ++i)
{
OSG_INFO << "OscDevice :: handling " << mangled_path << " with " << i->second << std::endl;
if (i->second->operator()(mangled_path, in_request_path, m) && !handled)
handled = true;
}
}
} while ((pos != std::string::npos) && (pos > 0) && !handled);
}
void OscDevice::addRequestHandler(RequestHandler* handler)
{
if (handler)
{
_map.insert(std::make_pair(handler->getRequestPath(), handler));
handler->setDevice(this);
}
}
void OscDevice::describeTo(std::ostream& out) const
{
out << "OscDevice :: listening on " << _listeningAddress << ":" << _listeningPort << std::endl;
out << std::endl;
for(RequestHandlerMap::const_iterator i = _map.begin(); i != _map.end(); ++i)
{
const RequestHandler* handler(i->second.get());
out << "OscDevice :: ";
handler->describeTo(out);
out << std::endl;
}
}

111
src/osgPlugins/osc/OscDevice.hpp Executable file
View File

@ -0,0 +1,111 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#pragma once
#include <osg/Referenced>
#include <OpenThreads/Thread>
#include <osgGA/Device>
#include <osc/OscPacketListener.h>
#include <ip/UdpSocket.h>
class OscDevice : public osgGA::Device, OpenThreads::Thread, osc::OscPacketListener {
public:
class RequestHandler : public osg::Referenced {
public:
RequestHandler(const std::string& request_path)
: osg::Referenced()
, _requestPath(request_path)
, _device(NULL)
{
}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) = 0;
const std::string& getRequestPath() const { return _requestPath; }
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << ": no description available";
}
protected:
void setDevice(OscDevice* device) { _device = device; }
OscDevice* getDevice() const { return _device; }
/// set the request-path, works only from the constructor
void setRequestPath(const std::string& request_path) { _requestPath = request_path; }
void handleException(const osc::Exception& e)
{
OSG_WARN << "OscDevice :: error while handling " << getRequestPath() << ": " << e.what() << std::endl;
}
double getLocalTime() const { return getDevice()->getEventQueue()->getTime(); }
private:
std::string _requestPath;
OscDevice* _device;
friend class OscDevice;
};
typedef std::multimap<std::string, osg::ref_ptr<RequestHandler> > RequestHandlerMap;
OscDevice(const std::string& server_address, int listening_port);
~OscDevice();
virtual void checkEvents() {}
virtual void run();
virtual void ProcessMessage( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint );
void addRequestHandler(RequestHandler* handler);
void describeTo(std::ostream& out) const;
friend std::ostream& operator<<(std::ostream& out, const OscDevice& device)
{
device.describeTo(out);
return out;
}
private:
std::string _listeningAddress;
unsigned int _listeningPort;
UdpListeningReceiveSocket* _socket;
RequestHandlerMap _map;
};
class SendKeystrokeRequestHandler : public OscDevice::RequestHandler {
public:
SendKeystrokeRequestHandler(const std::string& request_path, int key) : OscDevice::RequestHandler(request_path), _key(key) {}
virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& arguments)
{
getDevice()->getEventQueue()->keyPress(_key);
getDevice()->getEventQueue()->keyRelease(_key);
return true;
}
virtual void describeTo(std::ostream& out) const
{
out << getRequestPath() << ": send KEY_DOWN + KEY_UP, code: 0x" << std::hex << _key << std::dec;
}
private:
int _key;
};

View File

@ -0,0 +1,107 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include "OscProxyEventHandler.hpp"
static const unsigned long BUFFER_SIZE = 2048;
OscProxyEventHandler::OscProxyEventHandler(const std::string& address, int port)
: osgGA::GUIEventHandler()
, _transmitSocket(IpEndpointName(address.c_str(), port))
, _buffer(new char[BUFFER_SIZE])
, _oscStream(_buffer, BUFFER_SIZE)
, _firstRun(true)
{
OSG_INFO << "OscDevice :: sending events to " << address << ":" << port << std::endl;
}
OscProxyEventHandler::~OscProxyEventHandler()
{
delete[] (_buffer);
}
bool OscProxyEventHandler::handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, osg::Object *, osg::NodeVisitor *)
{
bool do_send(false);
switch(ea.getEventType())
{
case osgGA::GUIEventAdapter::FRAME:
if (_firstRun)
{
_firstRun = false;
sendInit(ea);
do_send = true;
}
break;
case osgGA::GUIEventAdapter::RESIZE:
sendInit(ea);
do_send = true;
break;
case osgGA::GUIEventAdapter::PUSH:
_oscStream << osc::BeginMessage("/osgga/mouse/press") << ea.getX() << ea.getY() << ea.getButton() << osc::EndMessage;
do_send = true;
break;
case osgGA::GUIEventAdapter::RELEASE:
_oscStream << osc::BeginMessage("/osgga/mouse/release") << ea.getX() << ea.getY() << ea.getButton() << osc::EndMessage;
do_send = true;
break;
case osgGA::GUIEventAdapter::DOUBLECLICK:
_oscStream << osc::BeginMessage("/osgga/mouse/doublepress") << ea.getX() << ea.getY() << ea.getButton() << osc::EndMessage;
do_send = true;
break;
case osgGA::GUIEventAdapter::MOVE:
case osgGA::GUIEventAdapter::DRAG:
_oscStream << osc::BeginMessage("/osgga/mouse/motion") << ea.getX() << ea.getY() << osc::EndMessage;
do_send = true;
break;
case osgGA::GUIEventAdapter::KEYDOWN:
_oscStream << osc::BeginMessage("/osgga/key/press") << ea.getKey() << osc::EndMessage;
do_send = true;
break;
case osgGA::GUIEventAdapter::KEYUP:
_oscStream << osc::BeginMessage("/osgga/key/release") << ea.getKey() << osc::EndMessage;
do_send = true;
break;
default:
break;
}
if (do_send)
{
OSG_INFO << "OscDevice :: sending event per OSC " << std::endl;
_transmitSocket.Send( _oscStream.Data(), _oscStream.Size() );
_oscStream.Clear();
}
return false;
}
void OscProxyEventHandler::sendInit(const osgGA::GUIEventAdapter &ea)
{
_oscStream << osc::BeginBundle();
_oscStream << osc::BeginMessage("/osgga/resize") << ea.getWindowX() << ea.getWindowY() << ea.getWindowWidth() << ea.getWindowHeight() << osc::EndMessage;
_oscStream << osc::BeginMessage("/osgga/mouse/set_input_range") << ea.getXmin() << ea.getYmin() << ea.getXmax() << ea.getYmax() << osc::EndMessage;
_oscStream << osc::BeginMessage("/osgga/mouse/y_orientation_increasing_upwards") << (bool)(ea.getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS) << osc::EndMessage;
_oscStream << osc::EndBundle;
}

View File

@ -0,0 +1,37 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#pragma once
#include <osgGA/GUIEventHandler>
#include <ip/UdpSocket.h>
#include <osc/OscOutboundPacketStream.h>
class OscProxyEventHandler : public osgGA::GUIEventHandler {
public:
OscProxyEventHandler(const std::string& address, int port);
~OscProxyEventHandler();
virtual bool handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, osg::Object *, osg::NodeVisitor *);
private:
void sendInit(const osgGA::GUIEventAdapter& ea);
UdpTransmitSocket _transmitSocket;
char* _buffer;
osc::OutboundPacketStream _oscStream;
bool _firstRun;
};

View File

@ -0,0 +1,160 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2008 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
/*
* ReadMe
*
* the osc-plugin can return an osgGA::Device which handles various osc-messages
* and puts them into the event-queue of the app
* To open the osc-device for receiving do something like this:
*
* std::string filename = "<your-port-number-to-listen-on>.receiver.osc";
* osgGA::Device* device = dynamic_cast<osgGA::Device*>(osgDB::readObjectFile(filename));
*
* and add that device to your viewer
* The plugin supports the following option: documentRegisteredHandlers, which will
* dump all registered handlers to the console. The device registers some convenient
* handlers to remote control a p3d-presentation.
*
*
* The plugin supports forwarding most of the events per osc to another host.
* It uses a special event-handler, which forwards the events. To get this
* event-handler, do something like this:
*
* std::string filename = "<target-address>:<target-port>.sender.osc";
* osgGA::GUIEventHandler* event_handler = dynamic_cast<osgGA::GUIEventHandler*>(osgDB::readObjectFile(filename));
*
* and add that event handler as the first event handler to your viewer/app
*
*
* TODO:
* - add multitouch support, preferably using the TUIO-protocol
* - be more tolerant with given filenames
*/
#include <osgDB/Registry>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include "OscDevice.hpp"
#include "OscProxyEventHandler.hpp"
class ReaderWriterOsc : public osgDB::ReaderWriter
{
public:
ReaderWriterOsc()
{
supportsExtension("osc", "Virtual Device Integration via a OSC_receiver");
supportsOption("documentRegisteredHandlers", "dump a documentation of all registered REST-handler to the console");
}
virtual const char* className() const { return "OSC Virtual Device Integration plugin"; }
virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
{
if (osgDB::getFileExtension(file) == "osc")
{
std::string file_name = osgDB::getNameLessExtension(file);
if (osgDB::getFileExtension(file_name) == "sender")
{
file_name = osgDB::getNameLessExtension(file_name);
std::string server_address = file_name.substr(0,file_name.find(':'));
std::string server_port = file_name.substr(file_name.find(':') + 1);
return new OscProxyEventHandler(server_address, atoi(server_port.c_str()));
}
else
{
// defaults to receiver
file_name = osgDB::getNameLessExtension(file_name);
if (file_name.find(':') == std::string::npos) {
file_name = "0.0.0.0:" + file_name;
}
std::string server_address = file_name.substr(0,file_name.find(':'));
std::string server_port = file_name.substr(file_name.find(':') + 1);
int port = atoi(server_port.c_str());
if (port <= 0)
{
OSG_WARN << "ReaderWriterOsc :: can't get valid port from " << osgDB::getNameLessAllExtensions(file) << std::endl;
port = 8000;
}
try {
osg::ref_ptr<OscDevice> device = new OscDevice(server_address, port);
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/slide/first", osgGA::GUIEventAdapter::KEY_Home));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/slide/last", osgGA::GUIEventAdapter::KEY_End));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/slide/next", osgGA::GUIEventAdapter::KEY_Right));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/slide/previous", osgGA::GUIEventAdapter::KEY_Left));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/layer/next", osgGA::GUIEventAdapter::KEY_Down));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/layer/previous", osgGA::GUIEventAdapter::KEY_Up));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/slideorlayer/next", osgGA::GUIEventAdapter::KEY_Page_Down));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/slideorlayer/previous", osgGA::GUIEventAdapter::KEY_Page_Up));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/unpause", 'o'));
device->addRequestHandler(new SendKeystrokeRequestHandler("/p3d/pause", 'p'));
device->addRequestHandler(new SendKeystrokeRequestHandler("/osgviewer/home", ' '));
device->addRequestHandler(new SendKeystrokeRequestHandler("/osgviewer/stats", 's'));
if ((options && (options->getPluginStringData("documentRegisteredHandlers") == "true")))
{
std::cout << *device << std::endl;
}
return device.release();
}
catch(const osc::Exception& e)
{
OSG_WARN << "OscDevice :: could not register UDP listener : " << e.what() << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
catch(const std::exception& e)
{
OSG_WARN << "OscDevice :: could not register UDP listener : " << e.what() << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
catch(...)
{
OSG_WARN << "OscDevice :: could not register UDP listener" << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
}
}
return ReadResult::FILE_NOT_HANDLED;
}
};
// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(osc, ReaderWriterOsc)

View File

@ -0,0 +1,81 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "IpEndpointName.h"
#include <cstdio>
#include "NetworkingUtils.h"
unsigned long IpEndpointName::GetHostByName( const char *s )
{
return ::GetHostByName(s);
}
void IpEndpointName::AddressAsString( char *s ) const
{
if( address == ANY_ADDRESS ){
sprintf( s, "<any>" );
}else{
sprintf( s, "%d.%d.%d.%d",
(int)((address >> 24) & 0xFF),
(int)((address >> 16) & 0xFF),
(int)((address >> 8) & 0xFF),
(int)(address & 0xFF) );
}
}
void IpEndpointName::AddressAndPortAsString( char *s ) const
{
if( port == ANY_PORT ){
if( address == ANY_ADDRESS ){
sprintf( s, "<any>:<any>" );
}else{
sprintf( s, "%d.%d.%d.%d:<any>",
(int)((address >> 24) & 0xFF),
(int)((address >> 16) & 0xFF),
(int)((address >> 8) & 0xFF),
(int)(address & 0xFF) );
}
}else{
if( address == ANY_ADDRESS ){
sprintf( s, "<any>:%d", port );
}else{
sprintf( s, "%d.%d.%d.%d:%d",
(int)((address >> 24) & 0xFF),
(int)((address >> 16) & 0xFF),
(int)((address >> 8) & 0xFF),
(int)(address & 0xFF),
(int)port );
}
}
}

View File

@ -0,0 +1,76 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_IPENDPOINTNAME_H
#define INCLUDED_IPENDPOINTNAME_H
class IpEndpointName{
static unsigned long GetHostByName( const char *s );
public:
static const unsigned long ANY_ADDRESS = 0xFFFFFFFF;
static const int ANY_PORT = -1;
IpEndpointName()
: address( ANY_ADDRESS ), port( ANY_PORT ) {}
IpEndpointName( int port_ )
: address( ANY_ADDRESS ), port( port_ ) {}
IpEndpointName( unsigned long ipAddress_, int port_ )
: address( ipAddress_ ), port( port_ ) {}
IpEndpointName( const char *addressName, int port_=ANY_PORT )
: address( GetHostByName( addressName ) )
, port( port_ ) {}
IpEndpointName( int addressA, int addressB, int addressC, int addressD, int port_=ANY_PORT )
: address( ( (addressA << 24) | (addressB << 16) | (addressC << 8) | addressD ) )
, port( port_ ) {}
// address and port are maintained in host byte order here
unsigned long address;
int port;
bool IsMulticastAddress() const { return ((address >> 24) & 0xFF) >= 224 && ((address >> 24) & 0xFF) <= 239; }
enum { ADDRESS_STRING_LENGTH=17 };
void AddressAsString( char *s ) const;
enum { ADDRESS_AND_PORT_STRING_LENGTH=23};
void AddressAndPortAsString( char *s ) const;
};
inline bool operator==( const IpEndpointName& lhs, const IpEndpointName& rhs )
{
return (lhs.address == rhs.address && lhs.port == rhs.port );
}
inline bool operator!=( const IpEndpointName& lhs, const IpEndpointName& rhs )
{
return !(lhs == rhs);
}
#endif /* INCLUDED_IPENDPOINTNAME_H */

View File

@ -0,0 +1,49 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_NETWORKINGUTILS_H
#define INCLUDED_NETWORKINGUTILS_H
// in general NetworkInitializer is only used internally, but if you're
// application creates multiple sockets from different threads at runtime you
// should instantiate one of these in main just to make sure the networking
// layer is initialized.
class NetworkInitializer{
public:
NetworkInitializer();
~NetworkInitializer();
};
// return ip address of host name in host byte order
unsigned long GetHostByName( const char *name );
#endif /* INCLUDED_NETWORKINGUTILS_H */

View File

@ -0,0 +1,43 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_PACKETLISTENER_H
#define INCLUDED_PACKETLISTENER_H
class IpEndpointName;
class PacketListener{
public:
virtual ~PacketListener() {}
virtual void ProcessPacket( const char *data, int size,
const IpEndpointName& remoteEndpoint ) = 0;
};
#endif /* INCLUDED_PACKETLISTENER_H */

View File

@ -0,0 +1,40 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_TIMERLISTENER_H
#define INCLUDED_TIMERLISTENER_H
class TimerListener{
public:
virtual ~TimerListener() {}
virtual void TimerExpired() = 0;
};
#endif /* INCLUDED_TIMERLISTENER_H */

View File

@ -0,0 +1,158 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_UDPSOCKET_H
#define INCLUDED_UDPSOCKET_H
#ifndef INCLUDED_NETWORKINGUTILITIES_H
#include "NetworkingUtils.h"
#endif /* INCLUDED_NETWORKINGUTILITIES_H */
#ifndef INCLUDED_IPENDPOINTNAME_H
#include "IpEndpointName.h"
#endif /* INCLUDED_IPENDPOINTNAME_H */
class PacketListener;
class TimerListener;
class UdpSocket;
class SocketReceiveMultiplexer{
class Implementation;
Implementation *impl_;
friend class UdpSocket;
public:
SocketReceiveMultiplexer();
~SocketReceiveMultiplexer();
// only call the attach/detach methods _before_ calling Run
// only one listener per socket, each socket at most once
void AttachSocketListener( UdpSocket *socket, PacketListener *listener );
void DetachSocketListener( UdpSocket *socket, PacketListener *listener );
void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener );
void AttachPeriodicTimerListener(
int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener );
void DetachPeriodicTimerListener( TimerListener *listener );
void Run(); // loop and block processing messages indefinitely
void RunUntilSigInt();
void Break(); // call this from a listener to exit once the listener returns
void AsynchronousBreak(); // call this from another thread or signal handler to exit the Run() state
};
class UdpSocket{
class Implementation;
Implementation *impl_;
friend class SocketReceiveMultiplexer::Implementation;
public:
// ctor throws std::runtime_error if there's a problem
// initializing the socket.
UdpSocket();
virtual ~UdpSocket();
// the socket is created in an unbound, unconnected state
// such a socket can only be used to send to an arbitrary
// address using SendTo(). To use Send() you need to first
// connect to a remote endpoint using Connect(). To use
// ReceiveFrom you need to first bind to a local endpoint
// using Bind().
// retrieve the local endpoint name when sending to 'to'
IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const;
// Connect to a remote endpoint which is used as the target
// for calls to Send()
void Connect( const IpEndpointName& remoteEndpoint );
void Send( const char *data, int size );
void SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size );
// Bind a local endpoint to receive incoming data. Endpoint
// can be 'any' for the system to choose an endpoint
void Bind( const IpEndpointName& localEndpoint );
bool IsBound() const;
int ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size );
};
// convenience classes for transmitting and receiving
// they just call Connect and/or Bind in the ctor.
// note that you can still use a receive socket
// for transmitting etc
class UdpTransmitSocket : public UdpSocket{
public:
UdpTransmitSocket( const IpEndpointName& remoteEndpoint )
{ Connect( remoteEndpoint ); }
};
class UdpReceiveSocket : public UdpSocket{
public:
UdpReceiveSocket( const IpEndpointName& localEndpoint )
{ Bind( localEndpoint ); }
};
// UdpListeningReceiveSocket provides a simple way to bind one listener
// to a single socket without having to manually set up a SocketReceiveMultiplexer
class UdpListeningReceiveSocket : public UdpSocket{
SocketReceiveMultiplexer mux_;
PacketListener *listener_;
public:
UdpListeningReceiveSocket( const IpEndpointName& localEndpoint, PacketListener *listener )
: listener_( listener )
{
Bind( localEndpoint );
mux_.AttachSocketListener( this, listener_ );
}
~UdpListeningReceiveSocket()
{ mux_.DetachSocketListener( this, listener_ ); }
// see SocketReceiveMultiplexer above for the behaviour of these methods...
void Run() { mux_.Run(); }
void RunUntilSigInt() { mux_.RunUntilSigInt(); }
void Break() { mux_.Break(); }
void AsynchronousBreak() { mux_.AsynchronousBreak(); }
};
#endif /* INCLUDED_UDPSOCKET_H */

View File

@ -0,0 +1,57 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "ip/NetworkingUtils.h"
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
NetworkInitializer::NetworkInitializer() {}
NetworkInitializer::~NetworkInitializer() {}
unsigned long GetHostByName( const char *name )
{
unsigned long result = 0;
struct hostent *h = gethostbyname( name );
if( h ){
struct in_addr a;
memcpy( &a, h->h_addr_list[0], h->h_length );
result = ntohl(a.s_addr);
}
return result;
}

View File

@ -0,0 +1,546 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "ip/UdpSocket.h"
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <assert.h>
#include <signal.h>
#include <math.h>
#include <errno.h>
#include <string.h> // for memset
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h> // for sockaddr_in
#include "ip/PacketListener.h"
#include "ip/TimerListener.h"
#if defined(__APPLE__) && !defined(_SOCKLEN_T)
// pre system 10.3 didn have socklen_t
typedef ssize_t socklen_t;
#endif
static void SockaddrFromIpEndpointName( struct sockaddr_in& sockAddr, const IpEndpointName& endpoint )
{
memset( (char *)&sockAddr, 0, sizeof(sockAddr ) );
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr =
(endpoint.address == IpEndpointName::ANY_ADDRESS)
? INADDR_ANY
: htonl( endpoint.address );
sockAddr.sin_port =
(endpoint.port == IpEndpointName::ANY_PORT)
? 0
: htons( endpoint.port );
}
static IpEndpointName IpEndpointNameFromSockaddr( const struct sockaddr_in& sockAddr )
{
return IpEndpointName(
(sockAddr.sin_addr.s_addr == INADDR_ANY)
? IpEndpointName::ANY_ADDRESS
: ntohl( sockAddr.sin_addr.s_addr ),
(sockAddr.sin_port == 0)
? IpEndpointName::ANY_PORT
: ntohs( sockAddr.sin_port )
);
}
class UdpSocket::Implementation{
bool isBound_;
bool isConnected_;
int socket_;
struct sockaddr_in connectedAddr_;
struct sockaddr_in sendToAddr_;
public:
Implementation()
: isBound_( false )
, isConnected_( false )
, socket_( -1 )
{
if( (socket_ = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 ){
throw std::runtime_error("unable to create udp socket\n");
}
memset( &sendToAddr_, 0, sizeof(sendToAddr_) );
sendToAddr_.sin_family = AF_INET;
}
~Implementation()
{
if (socket_ != -1) close(socket_);
}
IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const
{
assert( isBound_ );
// first connect the socket to the remote server
struct sockaddr_in connectSockAddr;
SockaddrFromIpEndpointName( connectSockAddr, remoteEndpoint );
if (connect(socket_, (struct sockaddr *)&connectSockAddr, sizeof(connectSockAddr)) < 0) {
throw std::runtime_error("unable to connect udp socket\n");
}
// get the address
struct sockaddr_in sockAddr;
memset( (char *)&sockAddr, 0, sizeof(sockAddr ) );
socklen_t length = sizeof(sockAddr);
if (getsockname(socket_, (struct sockaddr *)&sockAddr, &length) < 0) {
throw std::runtime_error("unable to getsockname\n");
}
if( isConnected_ ){
// reconnect to the connected address
if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) {
throw std::runtime_error("unable to connect udp socket\n");
}
}else{
// unconnect from the remote address
struct sockaddr_in unconnectSockAddr;
memset( (char *)&unconnectSockAddr, 0, sizeof(unconnectSockAddr ) );
unconnectSockAddr.sin_family = AF_UNSPEC;
// address fields are zero
int connectResult = connect(socket_, (struct sockaddr *)&unconnectSockAddr, sizeof(unconnectSockAddr));
if ( connectResult < 0 && errno != EAFNOSUPPORT ) {
throw std::runtime_error("unable to un-connect udp socket\n");
}
}
return IpEndpointNameFromSockaddr( sockAddr );
}
void Connect( const IpEndpointName& remoteEndpoint )
{
SockaddrFromIpEndpointName( connectedAddr_, remoteEndpoint );
if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) {
throw std::runtime_error("unable to connect udp socket\n");
}
isConnected_ = true;
}
void Send( const char *data, int size )
{
assert( isConnected_ );
send( socket_, data, size, 0 );
}
void SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size )
{
sendToAddr_.sin_addr.s_addr = htonl( remoteEndpoint.address );
sendToAddr_.sin_port = htons( remoteEndpoint.port );
sendto( socket_, data, size, 0, (sockaddr*)&sendToAddr_, sizeof(sendToAddr_) );
}
void Bind( const IpEndpointName& localEndpoint )
{
struct sockaddr_in bindSockAddr;
SockaddrFromIpEndpointName( bindSockAddr, localEndpoint );
if (bind(socket_, (struct sockaddr *)&bindSockAddr, sizeof(bindSockAddr)) < 0) {
throw std::runtime_error("unable to bind udp socket\n");
}
isBound_ = true;
}
bool IsBound() const { return isBound_; }
int ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size )
{
assert( isBound_ );
struct sockaddr_in fromAddr;
socklen_t fromAddrLen = sizeof(fromAddr);
int result = recvfrom(socket_, data, size, 0,
(struct sockaddr *) &fromAddr, (socklen_t*)&fromAddrLen);
if( result < 0 )
return 0;
remoteEndpoint.address = ntohl(fromAddr.sin_addr.s_addr);
remoteEndpoint.port = ntohs(fromAddr.sin_port);
return result;
}
int Socket() { return socket_; }
};
UdpSocket::UdpSocket()
{
impl_ = new Implementation();
}
UdpSocket::~UdpSocket()
{
delete impl_;
}
IpEndpointName UdpSocket::LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const
{
return impl_->LocalEndpointFor( remoteEndpoint );
}
void UdpSocket::Connect( const IpEndpointName& remoteEndpoint )
{
impl_->Connect( remoteEndpoint );
}
void UdpSocket::Send( const char *data, int size )
{
impl_->Send( data, size );
}
void UdpSocket::SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size )
{
impl_->SendTo( remoteEndpoint, data, size );
}
void UdpSocket::Bind( const IpEndpointName& localEndpoint )
{
impl_->Bind( localEndpoint );
}
bool UdpSocket::IsBound() const
{
return impl_->IsBound();
}
int UdpSocket::ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size )
{
return impl_->ReceiveFrom( remoteEndpoint, data, size );
}
struct AttachedTimerListener{
AttachedTimerListener( int id, int p, TimerListener *tl )
: initialDelayMs( id )
, periodMs( p )
, listener( tl ) {}
int initialDelayMs;
int periodMs;
TimerListener *listener;
};
static bool CompareScheduledTimerCalls(
const std::pair< double, AttachedTimerListener > & lhs, const std::pair< double, AttachedTimerListener > & rhs )
{
return lhs.first < rhs.first;
}
SocketReceiveMultiplexer *multiplexerInstanceToAbortWithSigInt_ = 0;
extern "C" /*static*/ void InterruptSignalHandler( int );
/*static*/ void InterruptSignalHandler( int )
{
multiplexerInstanceToAbortWithSigInt_->AsynchronousBreak();
signal( SIGINT, SIG_DFL );
}
class SocketReceiveMultiplexer::Implementation{
std::vector< std::pair< PacketListener*, UdpSocket* > > socketListeners_;
std::vector< AttachedTimerListener > timerListeners_;
volatile bool break_;
int breakPipe_[2]; // [0] is the reader descriptor and [1] the writer
double GetCurrentTimeMs() const
{
struct timeval t;
gettimeofday( &t, 0 );
return ((double)t.tv_sec*1000.) + ((double)t.tv_usec / 1000.);
}
public:
Implementation()
{
if( pipe(breakPipe_) != 0 )
throw std::runtime_error( "creation of asynchronous break pipes failed\n" );
}
~Implementation()
{
close( breakPipe_[0] );
close( breakPipe_[1] );
}
void AttachSocketListener( UdpSocket *socket, PacketListener *listener )
{
assert( std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) ) == socketListeners_.end() );
// we don't check that the same socket has been added multiple times, even though this is an error
socketListeners_.push_back( std::make_pair( listener, socket ) );
}
void DetachSocketListener( UdpSocket *socket, PacketListener *listener )
{
std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i =
std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) );
assert( i != socketListeners_.end() );
socketListeners_.erase( i );
}
void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener )
{
timerListeners_.push_back( AttachedTimerListener( periodMilliseconds, periodMilliseconds, listener ) );
}
void AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener )
{
timerListeners_.push_back( AttachedTimerListener( initialDelayMilliseconds, periodMilliseconds, listener ) );
}
void DetachPeriodicTimerListener( TimerListener *listener )
{
std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin();
while( i != timerListeners_.end() ){
if( i->listener == listener )
break;
++i;
}
assert( i != timerListeners_.end() );
timerListeners_.erase( i );
}
void Run()
{
break_ = false;
// configure the master fd_set for select()
fd_set masterfds, tempfds;
FD_ZERO( &masterfds );
FD_ZERO( &tempfds );
// in addition to listening to the inbound sockets we
// also listen to the asynchronous break pipe, so that AsynchronousBreak()
// can break us out of select() from another thread.
FD_SET( breakPipe_[0], &masterfds );
int fdmax = breakPipe_[0];
for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin();
i != socketListeners_.end(); ++i ){
if( fdmax < i->second->impl_->Socket() )
fdmax = i->second->impl_->Socket();
FD_SET( i->second->impl_->Socket(), &masterfds );
}
// configure the timer queue
double currentTimeMs = GetCurrentTimeMs();
// expiry time ms, listener
std::vector< std::pair< double, AttachedTimerListener > > timerQueue_;
for( std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin();
i != timerListeners_.end(); ++i )
timerQueue_.push_back( std::make_pair( currentTimeMs + i->initialDelayMs, *i ) );
std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls );
const int MAX_BUFFER_SIZE = 4098;
char *data = new char[ MAX_BUFFER_SIZE ];
IpEndpointName remoteEndpoint;
struct timeval timeout;
while( !break_ ){
tempfds = masterfds;
struct timeval *timeoutPtr = 0;
if( !timerQueue_.empty() ){
double timeoutMs = timerQueue_.front().first - GetCurrentTimeMs();
if( timeoutMs < 0 )
timeoutMs = 0;
// 1000000 microseconds in a second
timeout.tv_sec = (long)(timeoutMs * .001);
timeout.tv_usec = (long)((timeoutMs - (timeout.tv_sec * 1000)) * 1000);
timeoutPtr = &timeout;
}
if( select( fdmax + 1, &tempfds, 0, 0, timeoutPtr ) < 0 && errno != EINTR ){
throw std::runtime_error("select failed\n");
}
if ( FD_ISSET( breakPipe_[0], &tempfds ) ){
// clear pending data from the asynchronous break pipe
char c;
read( breakPipe_[0], &c, 1 );
}
if( break_ )
break;
for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin();
i != socketListeners_.end(); ++i ){
if( FD_ISSET( i->second->impl_->Socket(), &tempfds ) ){
int size = i->second->ReceiveFrom( remoteEndpoint, data, MAX_BUFFER_SIZE );
if( size > 0 ){
i->first->ProcessPacket( data, size, remoteEndpoint );
if( break_ )
break;
}
}
}
// execute any expired timers
currentTimeMs = GetCurrentTimeMs();
bool resort = false;
for( std::vector< std::pair< double, AttachedTimerListener > >::iterator i = timerQueue_.begin();
i != timerQueue_.end() && i->first <= currentTimeMs; ++i ){
i->second.listener->TimerExpired();
if( break_ )
break;
i->first += i->second.periodMs;
resort = true;
}
if( resort )
std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls );
}
delete [] data;
}
void Break()
{
break_ = true;
}
void AsynchronousBreak()
{
break_ = true;
// Send a termination message to the asynchronous break pipe, so select() will return
write( breakPipe_[1], "!", 1 );
}
};
SocketReceiveMultiplexer::SocketReceiveMultiplexer()
{
impl_ = new Implementation();
}
SocketReceiveMultiplexer::~SocketReceiveMultiplexer()
{
delete impl_;
}
void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket *socket, PacketListener *listener )
{
impl_->AttachSocketListener( socket, listener );
}
void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket *socket, PacketListener *listener )
{
impl_->DetachSocketListener( socket, listener );
}
void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener )
{
impl_->AttachPeriodicTimerListener( periodMilliseconds, listener );
}
void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener )
{
impl_->AttachPeriodicTimerListener( initialDelayMilliseconds, periodMilliseconds, listener );
}
void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener *listener )
{
impl_->DetachPeriodicTimerListener( listener );
}
void SocketReceiveMultiplexer::Run()
{
impl_->Run();
}
void SocketReceiveMultiplexer::RunUntilSigInt()
{
assert( multiplexerInstanceToAbortWithSigInt_ == 0 ); /* at present we support only one multiplexer instance running until sig int */
multiplexerInstanceToAbortWithSigInt_ = this;
signal( SIGINT, InterruptSignalHandler );
impl_->Run();
signal( SIGINT, SIG_DFL );
multiplexerInstanceToAbortWithSigInt_ = 0;
}
void SocketReceiveMultiplexer::Break()
{
impl_->Break();
}
void SocketReceiveMultiplexer::AsynchronousBreak()
{
impl_->AsynchronousBreak();
}

View File

@ -0,0 +1,88 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "ip/NetworkingUtils.h"
#include <winsock2.h> // this must come first to prevent errors with MSVC7
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
static LONG initCount_ = 0;
static bool winsockInitialized_ = false;
NetworkInitializer::NetworkInitializer()
{
if( InterlockedIncrement( &initCount_ ) == 1 ){
// there is a race condition here if one thread tries to access
// the library while another is still initializing it.
// i can't think of an easy way to fix it so i'm telling you here
// incase you need to init the library from two threads at once.
// this is why the header file advises to instantiate one of these
// in main() so that the initialization happens globally
// initialize winsock
WSAData wsaData;
int nCode = WSAStartup(MAKEWORD(1, 1), &wsaData);
if( nCode != 0 ){
//std::cout << "WSAStartup() failed with error code " << nCode << "\n";
}else{
winsockInitialized_ = true;
}
}
}
NetworkInitializer::~NetworkInitializer()
{
if( InterlockedDecrement( &initCount_ ) == 0 ){
if( winsockInitialized_ ){
WSACleanup();
winsockInitialized_ = false;
}
}
}
unsigned long GetHostByName( const char *name )
{
NetworkInitializer networkInitializer;
unsigned long result = 0;
struct hostent *h = gethostbyname( name );
if( h ){
struct in_addr a;
memcpy( &a, h->h_addr_list[0], h->h_length );
result = ntohl(a.s_addr);
}
return result;
}

View File

@ -0,0 +1,534 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "ip/UdpSocket.h"
#include <winsock2.h> // this must come first to prevent errors with MSVC7
#include <windows.h>
#include <mmsystem.h> // for timeGetTime()
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <assert.h>
#ifndef WINCE
#include <signal.h>
#endif
#include "ip/NetworkingUtils.h"
#include "ip/PacketListener.h"
#include "ip/TimerListener.h"
typedef int socklen_t;
static void SockaddrFromIpEndpointName( struct sockaddr_in& sockAddr, const IpEndpointName& endpoint )
{
memset( (char *)&sockAddr, 0, sizeof(sockAddr ) );
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr =
(endpoint.address == IpEndpointName::ANY_ADDRESS)
? INADDR_ANY
: htonl( endpoint.address );
sockAddr.sin_port =
(endpoint.port == IpEndpointName::ANY_PORT)
? (short)0
: htons( (short)endpoint.port );
}
static IpEndpointName IpEndpointNameFromSockaddr( const struct sockaddr_in& sockAddr )
{
return IpEndpointName(
(sockAddr.sin_addr.s_addr == INADDR_ANY)
? IpEndpointName::ANY_ADDRESS
: ntohl( sockAddr.sin_addr.s_addr ),
(sockAddr.sin_port == 0)
? IpEndpointName::ANY_PORT
: ntohs( sockAddr.sin_port )
);
}
class UdpSocket::Implementation{
NetworkInitializer networkInitializer_;
bool isBound_;
bool isConnected_;
SOCKET socket_;
struct sockaddr_in connectedAddr_;
struct sockaddr_in sendToAddr_;
public:
Implementation()
: isBound_( false )
, isConnected_( false )
, socket_( INVALID_SOCKET )
{
if( (socket_ = socket( AF_INET, SOCK_DGRAM, 0 )) == INVALID_SOCKET ){
throw std::runtime_error("unable to create udp socket\n");
}
memset( &sendToAddr_, 0, sizeof(sendToAddr_) );
sendToAddr_.sin_family = AF_INET;
}
~Implementation()
{
if (socket_ != INVALID_SOCKET) closesocket(socket_);
}
IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const
{
assert( isBound_ );
// first connect the socket to the remote server
struct sockaddr_in connectSockAddr;
SockaddrFromIpEndpointName( connectSockAddr, remoteEndpoint );
if (connect(socket_, (struct sockaddr *)&connectSockAddr, sizeof(connectSockAddr)) < 0) {
throw std::runtime_error("unable to connect udp socket\n");
}
// get the address
struct sockaddr_in sockAddr;
memset( (char *)&sockAddr, 0, sizeof(sockAddr ) );
socklen_t length = sizeof(sockAddr);
if (getsockname(socket_, (struct sockaddr *)&sockAddr, &length) < 0) {
throw std::runtime_error("unable to getsockname\n");
}
if( isConnected_ ){
// reconnect to the connected address
if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) {
throw std::runtime_error("unable to connect udp socket\n");
}
}else{
// unconnect from the remote address
struct sockaddr_in unconnectSockAddr;
SockaddrFromIpEndpointName( unconnectSockAddr, IpEndpointName() );
if( connect(socket_, (struct sockaddr *)&unconnectSockAddr, sizeof(unconnectSockAddr)) < 0
&& WSAGetLastError() != WSAEADDRNOTAVAIL ){
throw std::runtime_error("unable to un-connect udp socket\n");
}
}
return IpEndpointNameFromSockaddr( sockAddr );
}
void Connect( const IpEndpointName& remoteEndpoint )
{
SockaddrFromIpEndpointName( connectedAddr_, remoteEndpoint );
if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) {
throw std::runtime_error("unable to connect udp socket\n");
}
isConnected_ = true;
}
void Send( const char *data, int size )
{
assert( isConnected_ );
send( socket_, data, size, 0 );
}
void SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size )
{
sendToAddr_.sin_addr.s_addr = htonl( remoteEndpoint.address );
sendToAddr_.sin_port = htons( (short)remoteEndpoint.port );
sendto( socket_, data, size, 0, (sockaddr*)&sendToAddr_, sizeof(sendToAddr_) );
}
void Bind( const IpEndpointName& localEndpoint )
{
struct sockaddr_in bindSockAddr;
SockaddrFromIpEndpointName( bindSockAddr, localEndpoint );
if (bind(socket_, (struct sockaddr *)&bindSockAddr, sizeof(bindSockAddr)) < 0) {
throw std::runtime_error("unable to bind udp socket\n");
}
isBound_ = true;
}
bool IsBound() const { return isBound_; }
int ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size )
{
assert( isBound_ );
struct sockaddr_in fromAddr;
socklen_t fromAddrLen = sizeof(fromAddr);
int result = recvfrom(socket_, data, size, 0,
(struct sockaddr *) &fromAddr, (socklen_t*)&fromAddrLen);
if( result < 0 )
return 0;
remoteEndpoint.address = ntohl(fromAddr.sin_addr.s_addr);
remoteEndpoint.port = ntohs(fromAddr.sin_port);
return result;
}
SOCKET& Socket() { return socket_; }
};
UdpSocket::UdpSocket()
{
impl_ = new Implementation();
}
UdpSocket::~UdpSocket()
{
delete impl_;
}
IpEndpointName UdpSocket::LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const
{
return impl_->LocalEndpointFor( remoteEndpoint );
}
void UdpSocket::Connect( const IpEndpointName& remoteEndpoint )
{
impl_->Connect( remoteEndpoint );
}
void UdpSocket::Send( const char *data, int size )
{
impl_->Send( data, size );
}
void UdpSocket::SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size )
{
impl_->SendTo( remoteEndpoint, data, size );
}
void UdpSocket::Bind( const IpEndpointName& localEndpoint )
{
impl_->Bind( localEndpoint );
}
bool UdpSocket::IsBound() const
{
return impl_->IsBound();
}
int UdpSocket::ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size )
{
return impl_->ReceiveFrom( remoteEndpoint, data, size );
}
struct AttachedTimerListener{
AttachedTimerListener( int id, int p, TimerListener *tl )
: initialDelayMs( id )
, periodMs( p )
, listener( tl ) {}
int initialDelayMs;
int periodMs;
TimerListener *listener;
};
static bool CompareScheduledTimerCalls(
const std::pair< double, AttachedTimerListener > & lhs, const std::pair< double, AttachedTimerListener > & rhs )
{
return lhs.first < rhs.first;
}
SocketReceiveMultiplexer *multiplexerInstanceToAbortWithSigInt_ = 0;
extern "C" /*static*/ void InterruptSignalHandler( int );
/*static*/ void InterruptSignalHandler( int )
{
multiplexerInstanceToAbortWithSigInt_->AsynchronousBreak();
#ifndef WINCE
signal( SIGINT, SIG_DFL );
#endif
}
class SocketReceiveMultiplexer::Implementation{
NetworkInitializer networkInitializer_;
std::vector< std::pair< PacketListener*, UdpSocket* > > socketListeners_;
std::vector< AttachedTimerListener > timerListeners_;
volatile bool break_;
HANDLE breakEvent_;
double GetCurrentTimeMs() const
{
#ifndef WINCE
return timeGetTime(); // FIXME: bad choice if you want to run for more than 40 days
#else
return 0;
#endif
}
public:
Implementation()
{
breakEvent_ = CreateEvent( NULL, FALSE, FALSE, NULL );
}
~Implementation()
{
CloseHandle( breakEvent_ );
}
void AttachSocketListener( UdpSocket *socket, PacketListener *listener )
{
assert( std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) ) == socketListeners_.end() );
// we don't check that the same socket has been added multiple times, even though this is an error
socketListeners_.push_back( std::make_pair( listener, socket ) );
}
void DetachSocketListener( UdpSocket *socket, PacketListener *listener )
{
std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i =
std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) );
assert( i != socketListeners_.end() );
socketListeners_.erase( i );
}
void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener )
{
timerListeners_.push_back( AttachedTimerListener( periodMilliseconds, periodMilliseconds, listener ) );
}
void AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener )
{
timerListeners_.push_back( AttachedTimerListener( initialDelayMilliseconds, periodMilliseconds, listener ) );
}
void DetachPeriodicTimerListener( TimerListener *listener )
{
std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin();
while( i != timerListeners_.end() ){
if( i->listener == listener )
break;
++i;
}
assert( i != timerListeners_.end() );
timerListeners_.erase( i );
}
void Run()
{
break_ = false;
// prepare the window events which we use to wake up on incoming data
// we use this instead of select() primarily to support the AsyncBreak()
// mechanism.
std::vector<HANDLE> events( socketListeners_.size() + 1, 0 );
int j=0;
for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin();
i != socketListeners_.end(); ++i, ++j ){
HANDLE event = CreateEvent( NULL, FALSE, FALSE, NULL );
WSAEventSelect( i->second->impl_->Socket(), event, FD_READ ); // note that this makes the socket non-blocking which is why we can safely call RecieveFrom() on all sockets below
events[j] = event;
}
events[ socketListeners_.size() ] = breakEvent_; // last event in the collection is the break event
// configure the timer queue
double currentTimeMs = GetCurrentTimeMs();
// expiry time ms, listener
std::vector< std::pair< double, AttachedTimerListener > > timerQueue_;
for( std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin();
i != timerListeners_.end(); ++i )
timerQueue_.push_back( std::make_pair( currentTimeMs + i->initialDelayMs, *i ) );
std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls );
const int MAX_BUFFER_SIZE = 4098;
char *data = new char[ MAX_BUFFER_SIZE ];
IpEndpointName remoteEndpoint;
while( !break_ ){
double currentTimeMs = GetCurrentTimeMs();
DWORD waitTime = INFINITE;
if( !timerQueue_.empty() ){
waitTime = (DWORD)( timerQueue_.front().first >= currentTimeMs
? timerQueue_.front().first - currentTimeMs
: 0 );
}
DWORD waitResult = WaitForMultipleObjects( (DWORD)socketListeners_.size() + 1, &events[0], FALSE, waitTime );
if( break_ )
break;
if( waitResult != WAIT_TIMEOUT ){
for( int i = waitResult - WAIT_OBJECT_0; i < (int)socketListeners_.size(); ++i ){
int size = socketListeners_[i].second->ReceiveFrom( remoteEndpoint, data, MAX_BUFFER_SIZE );
if( size > 0 ){
socketListeners_[i].first->ProcessPacket( data, size, remoteEndpoint );
if( break_ )
break;
}
}
}
// execute any expired timers
currentTimeMs = GetCurrentTimeMs();
bool resort = false;
for( std::vector< std::pair< double, AttachedTimerListener > >::iterator i = timerQueue_.begin();
i != timerQueue_.end() && i->first <= currentTimeMs; ++i ){
i->second.listener->TimerExpired();
if( break_ )
break;
i->first += i->second.periodMs;
resort = true;
}
if( resort )
std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls );
}
delete [] data;
// free events
j = 0;
for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin();
i != socketListeners_.end(); ++i, ++j ){
WSAEventSelect( i->second->impl_->Socket(), events[j], 0 ); // remove association between socket and event
CloseHandle( events[j] );
unsigned long enableNonblocking = 0;
ioctlsocket( i->second->impl_->Socket(), FIONBIO, &enableNonblocking ); // make the socket blocking again
}
}
void Break()
{
break_ = true;
}
void AsynchronousBreak()
{
break_ = true;
SetEvent( breakEvent_ );
}
};
SocketReceiveMultiplexer::SocketReceiveMultiplexer()
{
impl_ = new Implementation();
}
SocketReceiveMultiplexer::~SocketReceiveMultiplexer()
{
delete impl_;
}
void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket *socket, PacketListener *listener )
{
impl_->AttachSocketListener( socket, listener );
}
void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket *socket, PacketListener *listener )
{
impl_->DetachSocketListener( socket, listener );
}
void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener )
{
impl_->AttachPeriodicTimerListener( periodMilliseconds, listener );
}
void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener )
{
impl_->AttachPeriodicTimerListener( initialDelayMilliseconds, periodMilliseconds, listener );
}
void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener *listener )
{
impl_->DetachPeriodicTimerListener( listener );
}
void SocketReceiveMultiplexer::Run()
{
impl_->Run();
}
void SocketReceiveMultiplexer::RunUntilSigInt()
{
assert( multiplexerInstanceToAbortWithSigInt_ == 0 ); /* at present we support only one multiplexer instance running until sig int */
multiplexerInstanceToAbortWithSigInt_ = this;
#ifndef WINCE
signal( SIGINT, InterruptSignalHandler );
#endif
impl_->Run();
#ifndef WINCE
signal( SIGINT, SIG_DFL );
#endif
multiplexerInstanceToAbortWithSigInt_ = 0;
}
void SocketReceiveMultiplexer::Break()
{
impl_->Break();
}
void SocketReceiveMultiplexer::AsynchronousBreak()
{
impl_->AsynchronousBreak();
}

View File

@ -0,0 +1,73 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H
#define INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H
#include <string.h>
#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 strcmp( lhs, rhs ) < 0; }
};
typedef std::map<const char*, function_type, cstr_compare> function_map_type;
function_map_type functions_;
};
} // namespace osc
#endif /* INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H */

View File

@ -0,0 +1,54 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_OSC_EXCEPTION_H
#define INCLUDED_OSC_EXCEPTION_H
#include <exception>
namespace osc{
class Exception : public std::exception {
const char *what_;
public:
Exception() throw() {}
Exception( const Exception& src ) throw()
: 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_OSC_EXCEPTION_H */

View File

@ -0,0 +1,85 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef OSC_HOSTENDIANNESS_H
#define OSC_HOSTENDIANNESS_H
/*
Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined
If you know a way to enhance the detection below for Linux and/or MacOSX
please let me know! I've tried a few things which don't work.
*/
#if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN)
// you can define one of the above symbols from the command line
// then you don't have to edit this file.
#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(__GLIBC__)
#include <endian.h>
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define __LITTLE_ENDIAN__
#elif (__BYTE_ORDER == __BIG_ENDIAN)
#define __BIG_ENDIAN__
#else
#error Unknown machine endianness detected.
#endif
#endif
#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
#else
#error please edit OSCHostEndianness.h to configure endianness
#endif
#endif
#endif /* OSC_HOSTENDIANNESS_H */

View File

@ -0,0 +1,639 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "OscOutboundPacketStream.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#if defined(__WIN32__) || defined(WIN32)
#include <malloc.h> // for alloca
#endif
#include "OscHostEndianness.h"
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
}
static inline long RoundUp4( long x )
{
return ((x-1) & (~0x03L)) + 4;
}
OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity )
: data_( buffer )
, end_( data_ + capacity )
, typeTagsCurrent_( end_ )
, messageCursor_( data_ )
, argumentCurrent_( data_ )
, elementSizePtr_( 0 )
, messageIsInProgress_( false )
{
}
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 =
(uint32*)(data_ + *reinterpret_cast<uint32*>(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.
uint32 elementSize =
(endPtr - reinterpret_cast<char*>(elementSizePtr_)) - 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()
{
unsigned long 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
unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0)
+ RoundUp4(strlen(addressPattern) + 1) + 4;
if( required > Capacity() )
throw OutOfBufferMemoryException();
}
void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength )
{
// plus three for extra type tag, comma and null terminator
unsigned long 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;
}
unsigned int OutboundPacketStream::Capacity() const
{
return end_ - data_;
}
unsigned int OutboundPacketStream::Size() const
{
unsigned int 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_ );
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_ );
strcpy( messageCursor_, rhs.addressPattern );
unsigned long rhsLength = strlen(rhs.addressPattern);
messageCursor_ += rhsLength + 1;
// zero pad to 4-byte boundary
unsigned long 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();
int typeTagsCount = end_ - typeTagsCurrent_;
if( typeTagsCount ){
char *tempTypeTags = (char*)alloca(typeTagsCount);
memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
// slot size includes comma and null terminator
int typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
uint32 argumentsSize = argumentCurrent_ - messageCursor_;
memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
messageCursor_[0] = ',';
// copy type tags in reverse (really forward) order
for( int i=0; i < typeTagsCount; ++i )
messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
char *p = messageCursor_ + 1 + typeTagsCount;
for( int 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
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(strlen(rhs) + 1) );
*(--typeTagsCurrent_) = STRING_TYPE_TAG;
strcpy( argumentCurrent_, rhs );
unsigned long rhsLength = strlen(rhs);
argumentCurrent_ += rhsLength + 1;
// zero pad to 4-byte boundary
unsigned long i = rhsLength + 1;
while( i & 0x3 ){
*argumentCurrent_++ = '\0';
++i;
}
return *this;
}
OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
{
CheckForAvailableArgumentSpace( RoundUp4(strlen(rhs) + 1) );
*(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
strcpy( argumentCurrent_, rhs );
unsigned long rhsLength = strlen(rhs);
argumentCurrent_ += rhsLength + 1;
// zero pad to 4-byte boundary
unsigned long 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;
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;
}
} // namespace osc

View File

@ -0,0 +1,142 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_OSCOUTBOUNDPACKET_H
#define INCLUDED_OSCOUTBOUNDPACKET_H
#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, unsigned long capacity );
~OutboundPacketStream();
void Clear();
unsigned int Capacity() const;
// invariant: size() is valid even while building a message.
unsigned int 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 );
#ifndef __x86_64__
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 );
private:
char *BeginElement( char *beginPtr );
void EndElement( char *endPtr );
bool ElementSizeSlotRequired() const;
void CheckForAvailableBundleSpace();
void CheckForAvailableMessageSpace( const char *addressPattern );
void CheckForAvailableArgumentSpace( long 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_OSC_OUTBOUND_PACKET_H */

View File

@ -0,0 +1,72 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_OSCPACKETLISTENER_H
#define INCLUDED_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_OSCPACKETLISTENER_H */

View File

@ -0,0 +1,244 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "OscPrintReceivedElements.h"
#include <iostream>
#include <iomanip>
#include <ctime>
#include <cstring>
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 );
// strip trailing newline from string returned by ctime
const char *timeString = std::ctime( &t );
size_t len = strlen( timeString );
char *s = new char[ len + 1 ];
strcpy( s, timeString );
if( len )
s[ len - 1 ] = '\0';
os << " " << s;
}
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:
{
unsigned long size;
const void *data;
arg.AsBlobUnchecked( data, size );
os << "OSC-blob:<<" << std::hex << std::setfill('0');
unsigned char *p = (unsigned char*)data;
for( unsigned long 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;
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

View File

@ -0,0 +1,49 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_OSCPRINTRECEIVEDELEMENTS_H
#define INCLUDED_OSCPRINTRECEIVEDELEMENTS_H
#include <iosfwd>
#ifndef INCLUDED_OSCRECEIVEDELEMENTS_H
#include "OscReceivedElements.h"
#endif /* INCLUDED_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_OSCPRINTRECEIVEDELEMENTS_H */

View File

@ -0,0 +1,722 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "OscReceivedElements.h"
#include <cassert>
#include "OscHostEndianness.h"
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;
}
static inline unsigned long RoundUp4( unsigned long x )
{
unsigned long remainder = x & 0x3UL;
if( remainder )
return x + (4 - remainder);
else
return x;
}
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
}
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
}
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] == '#');
}
int32 ReceivedBundleElement::Size() const
{
return ToUInt32( size_ );
}
//------------------------------------------------------------------------------
bool ReceivedMessageArgument::AsBool() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == TRUE_TYPE_TAG )
return true;
else if( *typeTag_ == FALSE_TYPE_TAG )
return false;
else
throw WrongArgumentTypeException();
}
bool ReceivedMessageArgument::AsBoolUnchecked() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == TRUE_TYPE_TAG )
return true;
else
return false;
}
int32 ReceivedMessageArgument::AsInt32() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == 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] = argument_[3];
u.c[1] = argument_[2];
u.c[2] = argument_[1];
u.c[3] = argument_[0];
return u.i;
#else
return *(int32*)argument_;
#endif
}
float ReceivedMessageArgument::AsFloat() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == 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] = argument_[3];
u.c[1] = argument_[2];
u.c[2] = argument_[1];
u.c[3] = argument_[0];
return u.f;
#else
return *(float*)argument_;
#endif
}
char ReceivedMessageArgument::AsChar() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == CHAR_TYPE_TAG )
return AsCharUnchecked();
else
throw WrongArgumentTypeException();
}
char ReceivedMessageArgument::AsCharUnchecked() const
{
return (char)ToInt32( argument_ );
}
uint32 ReceivedMessageArgument::AsRgbaColor() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == RGBA_COLOR_TYPE_TAG )
return AsRgbaColorUnchecked();
else
throw WrongArgumentTypeException();
}
uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const
{
return ToUInt32( argument_ );
}
uint32 ReceivedMessageArgument::AsMidiMessage() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == MIDI_MESSAGE_TYPE_TAG )
return AsMidiMessageUnchecked();
else
throw WrongArgumentTypeException();
}
uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const
{
return ToUInt32( argument_ );
}
int64 ReceivedMessageArgument::AsInt64() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == INT64_TYPE_TAG )
return AsInt64Unchecked();
else
throw WrongArgumentTypeException();
}
int64 ReceivedMessageArgument::AsInt64Unchecked() const
{
return ToInt64( argument_ );
}
uint64 ReceivedMessageArgument::AsTimeTag() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == TIME_TAG_TYPE_TAG )
return AsTimeTagUnchecked();
else
throw WrongArgumentTypeException();
}
uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const
{
return ToUInt64( argument_ );
}
double ReceivedMessageArgument::AsDouble() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == 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] = argument_[7];
u.c[1] = argument_[6];
u.c[2] = argument_[5];
u.c[3] = argument_[4];
u.c[4] = argument_[3];
u.c[5] = argument_[2];
u.c[6] = argument_[1];
u.c[7] = argument_[0];
return u.d;
#else
return *(double*)argument_;
#endif
}
const char* ReceivedMessageArgument::AsString() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == STRING_TYPE_TAG )
return argument_;
else
throw WrongArgumentTypeException();
}
const char* ReceivedMessageArgument::AsSymbol() const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == SYMBOL_TYPE_TAG )
return argument_;
else
throw WrongArgumentTypeException();
}
void ReceivedMessageArgument::AsBlob( const void*& data, unsigned long& size ) const
{
if( !typeTag_ )
throw MissingArgumentException();
else if( *typeTag_ == BLOB_TYPE_TAG )
AsBlobUnchecked( data, size );
else
throw WrongArgumentTypeException();
}
void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, unsigned long& size ) const
{
size = ToUInt32( argument_ );
data = (void*)(argument_+4);
}
//------------------------------------------------------------------------------
void ReceivedMessageArgumentIterator::Advance()
{
if( !value_.typeTag_ )
return;
switch( *value_.typeTag_++ ){
case '\0':
// don't advance past end
--value_.typeTag_;
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_.argument_ += 4;
break;
case INT64_TYPE_TAG:
case TIME_TAG_TYPE_TAG:
case DOUBLE_TYPE_TAG:
value_.argument_ += 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_.argument_ = FindStr4End( value_.argument_ );
break;
case BLOB_TYPE_TAG:
{
uint32 blobSize = ToUInt32( value_.argument_ );
value_.argument_ = value_.argument_ + 4 + RoundUp4( (unsigned long)blobSize );
}
break;
default: // unknown type tag
// don't advance
--value_.typeTag_;
break;
// not handled:
// [ 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.
}
}
//------------------------------------------------------------------------------
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, unsigned long size )
{
if( size == 0 )
throw MalformedMessageException( "zero length messages not permitted" );
if( (size & 0x03L) != 0 )
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_;
do{
switch( *typeTag ){
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:
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 + 4 > end )
MalformedMessageException( "arguments exceed message size" );
uint32 blobSize = ToUInt32( argument );
argument = argument + 4 + RoundUp4( (unsigned long)blobSize );
if( argument > end )
MalformedMessageException( "arguments exceed message size" );
}
break;
default:
throw MalformedMessageException( "unknown type tag" );
// not handled:
// [ 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.
}
}while( *++typeTag != '\0' );
typeTagsEnd_ = typeTag;
}
}
}
//------------------------------------------------------------------------------
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, unsigned long size )
{
if( size < 16 )
throw MalformedBundleException( "packet too short for bundle" );
if( (size & 0x03L) != 0 )
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 + 4 > end_ )
throw MalformedBundleException( "packet too short for elementSize" );
uint32 elementSize = ToUInt32( p );
if( (elementSize & 0x03L) != 0 )
throw MalformedBundleException( "bundle element size must be multiple of four" );
p += 4 + 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

View File

@ -0,0 +1,488 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_OSCRECEIVEDELEMENTS_H
#define INCLUDED_OSCRECEIVEDELEMENTS_H
#include <cstddef>
#include "OscTypes.h"
#include "OscException.h"
namespace osc{
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:
ReceivedPacket( const char *contents, int32 size )
: contents_( contents )
, size_( size ) {}
bool IsMessage() const { return !IsBundle(); }
bool IsBundle() const;
int32 Size() const { return size_; }
const char *Contents() const { return contents_; }
private:
const char *contents_;
int32 size_;
};
class ReceivedBundleElement{
public:
ReceivedBundleElement( const char *size )
: size_( size ) {}
friend class ReceivedBundleElementIterator;
bool IsMessage() const { return !IsBundle(); }
bool IsBundle() const;
int32 Size() const;
const char *Contents() const { return size_ + 4; }
private:
const char *size_;
};
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_.size_ = value_.Contents() + value_.Size(); }
bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const
{
return value_.size_ == rhs.value_.size_;
}
};
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 *typeTag, const char *argument )
: typeTag_( typeTag )
, argument_( argument ) {}
friend class ReceivedMessageArgumentIterator;
const char TypeTag() const { return *typeTag_; }
// 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 *typeTag_ == TRUE_TYPE_TAG || *typeTag_ == FALSE_TYPE_TAG; }
bool AsBool() const;
bool AsBoolUnchecked() const;
bool IsNil() const { return *typeTag_ == NIL_TYPE_TAG; }
bool IsInfinitum() const { return *typeTag_ == INFINITUM_TYPE_TAG; }
bool IsInt32() const { return *typeTag_ == INT32_TYPE_TAG; }
int32 AsInt32() const;
int32 AsInt32Unchecked() const;
bool IsFloat() const { return *typeTag_ == FLOAT_TYPE_TAG; }
float AsFloat() const;
float AsFloatUnchecked() const;
bool IsChar() const { return *typeTag_ == CHAR_TYPE_TAG; }
char AsChar() const;
char AsCharUnchecked() const;
bool IsRgbaColor() const { return *typeTag_ == RGBA_COLOR_TYPE_TAG; }
uint32 AsRgbaColor() const;
uint32 AsRgbaColorUnchecked() const;
bool IsMidiMessage() const { return *typeTag_ == MIDI_MESSAGE_TYPE_TAG; }
uint32 AsMidiMessage() const;
uint32 AsMidiMessageUnchecked() const;
bool IsInt64() const { return *typeTag_ == INT64_TYPE_TAG; }
int64 AsInt64() const;
int64 AsInt64Unchecked() const;
bool IsTimeTag() const { return *typeTag_ == TIME_TAG_TYPE_TAG; }
uint64 AsTimeTag() const;
uint64 AsTimeTagUnchecked() const;
bool IsDouble() const { return *typeTag_ == DOUBLE_TYPE_TAG; }
double AsDouble() const;
double AsDoubleUnchecked() const;
bool IsString() const { return *typeTag_ == STRING_TYPE_TAG; }
const char* AsString() const;
const char* AsStringUnchecked() const { return argument_; }
bool IsSymbol() const { return *typeTag_ == SYMBOL_TYPE_TAG; }
const char* AsSymbol() const;
const char* AsSymbolUnchecked() const { return argument_; }
bool IsBlob() const { return *typeTag_ == BLOB_TYPE_TAG; }
void AsBlob( const void*& data, unsigned long& size ) const;
void AsBlobUnchecked( const void*& data, unsigned long& size ) const;
private:
const char *typeTag_;
const char *argument_;
};
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_.typeTag_ == rhs.value_.typeTag_;
}
};
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
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 )
{
if( !Eos() )
throw ExcessArgumentException();
return *this;
}
};
class ReceivedMessage{
void Init( const char *bundle, unsigned long size );
public:
explicit ReceivedMessage( const ReceivedPacket& packet );
explicit ReceivedMessage( const ReceivedBundleElement& bundleElement );
const char *AddressPattern() const { return addressPattern_; }
// Support for non-standad SuperCollider integer address patterns:
bool AddressPatternIsUInt32() const;
uint32 AddressPatternAsUInt32() const;
unsigned long ArgumentCount() const { return static_cast<unsigned long>(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, unsigned long size );
public:
explicit ReceivedBundle( const ReceivedPacket& packet );
explicit ReceivedBundle( const ReceivedBundleElement& bundleElement );
uint64 TimeTag() const;
unsigned long 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_;
unsigned long elementCount_;
};
} // namespace osc
#endif /* INCLUDED_OSCRECEIVEDELEMENTS_H */

View File

@ -0,0 +1,40 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#include "OscTypes.h"
namespace osc{
BundleInitiator BeginBundleImmediate(1);
BundleTerminator EndBundle;
MessageTerminator EndMessage;
NilType Nil;
InfinitumType Infinitum;
} // namespace osc

View File

@ -0,0 +1,183 @@
/*
oscpack -- Open Sound Control packet manipulation library
http://www.audiomulch.com/~rossb/oscpack
Copyright (c) 2004-2005 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.
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.
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.
*/
#ifndef INCLUDED_OSCTYPES_H
#define INCLUDED_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 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'
};
// 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 Nil;
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_, unsigned long size_ )
: data( data_ ), size( size_ ) {}
const void* data;
unsigned long size;
};
} // namespace osc
#endif /* INCLUDED_OSCTYPES_H */