Coder Social home page Coder Social logo

shidephen / oscpp Goto Github PK

View Code? Open in Web Editor NEW

This project forked from kaoskorobase/oscpp

0.0 1.0 0.0 139 KB

Lightweight C++ header-only library for processing OpenSoundControl (OSC) packets

License: Other

C++ 96.25% Shell 0.60% Ruby 0.56% CMake 1.98% Makefile 0.61%

oscpp's Introduction

Build Status Build status

oscpp is a header-only C++11 library for constructing and parsing OpenSoundControl packets. Supported platforms are MacOS X, iOS, Linux, Android and Windows; the code should be easily portable to any platform with a C++11 compiler. oscpp intends to be a minimal, high-performance solution for working with OSC data. The library doesn't perform memory allocation (except when throwing exceptions) or other system calls and is suitable for use in realtime sensitive contexts such as audio driver callbacks.

oscpp conforms to the OpenSoundControl 1.0 specification. Except for arrays, non-standard message argument types are currently not supported and there is no direct support for message address patterns or bundle scheduling; it is up to the user of the library to implement (a subset of) the semantics according to the spec.

Installation

Since oscpp only consists of header files, the library doesn't need to be compiled or installed. Simply put the include directory into a location that is searched by your compiler and you're set.

Usage

oscpp places everything in the OSCPP namespace, with the two most important subnamespaces Client for constructing packets and Server for parsing packets.

First let's have a look at how to build OSC packets in memory: Assuming you have allocated a buffer you can construct a client packet on the stack and start filling the buffer with data. When all the data has been written, the size method returns the actual size in bytes of the resulting OSC packet.

#include <oscpp/client.hpp>

size_t makePacket(void* buffer, size_t size)
{
    // Construct a packet
    OSCPP::Client::Packet packet(buffer, size);
    packet
        // Open a bundle with a timetag
        .openBundle(1234ULL)
            // Add a message with two arguments and an array with 6 elements;
            // for efficiency this needs to be known in advance.
            .openMessage("/s_new", 2 + OSCPP::Tags::array(6))
                // Write the arguments
                .string("sinesweep")
                .int32(2)
                .openArray()
                    .string("start-freq")
                    .float32(330.0f)
                    .string("end-freq")
                    .float32(990.0f)
                    .string("amp")
                    .float32(0.4f)
                .closeArray()
            // Every `open` needs a corresponding `close`
            .closeMessage()
            // Add another message with one argument
            .openMessage("/n_free", 1)
                .int32(1)
            .closeMessage()
            // And nother one
            .openMessage("/n_set", 3)
                .int32(1)
                .string("wobble")
                // Numeric arguments are converted automatically
                // (see below)
                .int32(31)
            .closeMessage()
        .closeBundle();
    return packet.size();
}

Now given a suitable packet transport (e.g. a UDP socket or an in-memory FIFO, see below for a dummy implementation), a packet can be constructed and sent as follows:

class Transport;

size_t send(Transport* t, const void* buffer, size_t size);

void sendPacket(Transport* t, void* buffer, size_t bufferSize)
{
    const size_t packetSize = makePacket(buffer, bufferSize);
    send(t, buffer, packetSize);
}

When parsing data from OSC packets you have to handle the two distinct cases of bundles and messages:

#include <oscpp/server.hpp>
#include <oscpp/print.hpp>
#include <iostream>

void handlePacket(const OSCPP::Server::Packet& packet)
{
    if (packet.isBundle()) {
        // Convert to bundle
        OSCPP::Server::Bundle bundle(packet);

        // Print the time
        std::cout << "#bundle " << bundle.time() << std::endl;

        // Get packet stream
        OSCPP::Server::PacketStream packets(bundle.packets());

        // Iterate over all the packets and call handlePacket recursively.
        // Cuidado: Might lead to stack overflow!
        while (!packets.atEnd()) {
            handlePacket(packets.next());
        }
    } else {
        // Convert to message
        OSCPP::Server::Message msg(packet);

        // Get argument stream
        OSCPP::Server::ArgStream args(msg.args());

        // Directly compare message address to string with operator==.
        // For handling larger address spaces you could use e.g. a
        // dispatch table based on std::unordered_map.
        if (msg == "/s_new") {
            const char* name = args.string();
            const int32_t id = args.int32();
            std::cout << "/s_new" << " "
                      << name << " "
                      << id << " ";
            // Get the params array as an ArgStream
            OSCPP::Server::ArgStream params(args.array());
            while (!params.atEnd()) {
                const char* param = params.string();
                const float value = params.float32();
                std::cout << param << ":" << value << " ";
            }
            std::cout << std::endl;
        } else if (msg == "/n_set") {
            const int32_t id = args.int32();
            const char* key = args.string();
            // Numeric arguments are converted automatically
            // to float32 (e.g. from int32).
            const float value = args.float32();
            std::cout << "/n_set" << " "
                      << id << " "
                      << key << " "
                      << value << std::endl;
        } else {
            // Simply print unknown messages
            std::cout << "Unknown message: " << msg << std::endl;
        }
    }
}

Now we can receive data from a message based transport and pass it to our packet handling function:

#include <array>

const size_t kMaxPacketSize = 8192;

size_t recv(Transport* t, void* buffer, size_t size);

void recvPacket(Transport* t)
{
    std::array<char,kMaxPacketSize> buffer;
    size_t size = recv(t, buffer.data(), buffer.size());
    handlePacket(OSCPP::Server::Packet(buffer.data(), size));
}

Here's our code in an example main function:

#include <memory>
#include <stdexcept>

Transport* newTransport();

int main(int, char**)
{
    std::unique_ptr<Transport> t(newTransport());
    std::array<char,kMaxPacketSize> sendBuffer;
    try {
        sendPacket(t.get(), sendBuffer.data(), sendBuffer.size());
        recvPacket(t.get());
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

Compiling and running the example produces the following output:

#bundle 1234
/s_new sinesweep 2 start-freq:330 end-freq:990 amp:0.4
Unknown message: /n_free i:1
/n_set 1 wobble 31

How to run the example

You can build and run the example by executing

make README

You'll need to install the Haskell Platform and the Pandoc library:

cabal install pandoc

Appendix: Support code

Here's the code for a trivial transport that has a single packet buffer:

#include <cstring>

class Transport
{
public:
    size_t send(const void* buffer, size_t size)
    {
        size_t n = std::min(m_buffer.size(), size);
        std::memcpy(m_buffer.data(), buffer, n);
        m_message = n;
        return n;
    }

    size_t recv(void* buffer, size_t size)
    {
        if (m_message > 0) {
            size_t n = std::min(m_message, size);
            std::memcpy(buffer, m_buffer.data(), n);
            m_message = 0;
            return n;
        }
        return 0;
    }

private:
    std::array<char,kMaxPacketSize> m_buffer;
    size_t m_message;
};

Transport* newTransport()
{
    return new Transport;
}

size_t send(Transport* t, const void* buffer, size_t size)
{
    return t->send(buffer, size);
}

size_t recv(Transport* t, void* buffer, size_t size)
{
    return t->recv(buffer, size);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.