Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions lib/omc-osc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
CXX ?= c++
AR ?= ar
CMAKE ?= cmake
CXXFLAGS ?= -std=c++23 -Wall -Wextra -Wpedantic -Iinclude

BUILD_DIR := build
LIB := $(BUILD_DIR)/libomc_osc.a
SRCS := $(sort $(wildcard src/*.cpp))
OBJS := $(patsubst src/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))
LIBLO_SRC_DIR := ../liblo/cmake
LIBLO_BUILD_DIR := $(BUILD_DIR)/liblo
LIBLO_LIB := $(LIBLO_BUILD_DIR)/liblo.a
LIBLO_CXXFLAGS := -I../liblo -I$(LIBLO_BUILD_DIR) -Wno-variadic-macros
LIBLO_LDFLAGS := $(LIBLO_LIB) -lm

.PHONY: all clean examples

all: examples

$(BUILD_DIR):
mkdir -p $(BUILD_DIR)

$(LIBLO_LIB): | $(BUILD_DIR)
$(CMAKE) -S $(LIBLO_SRC_DIR) -B $(LIBLO_BUILD_DIR) \
-DWITH_STATIC=ON \
-DWITH_TOOLS=OFF \
-DWITH_TESTS=OFF \
-DWITH_EXAMPLES=OFF \
-DWITH_CPP_TESTS=OFF \
-DTHREADING=OFF
$(CMAKE) --build $(LIBLO_BUILD_DIR) --target liblo_static

$(BUILD_DIR)/%.o: src/%.cpp include/omc-osc/omc_osc.hpp $(LIBLO_LIB) | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) $(LIBLO_CXXFLAGS) -c $< -o $@

$(LIB): $(OBJS)
$(AR) rcs $@ $(OBJS)

examples: $(LIB)
$(CXX) $(CXXFLAGS) $(LIBLO_CXXFLAGS) -Iexamples examples/oms_server.cpp examples/example_complex_message.cpp $(LIB) $(LIBLO_LDFLAGS) -o $(BUILD_DIR)/oms_server
$(CXX) $(CXXFLAGS) $(LIBLO_CXXFLAGS) -Iexamples examples/omc_client.cpp examples/example_complex_message.cpp $(LIB) $(LIBLO_LDFLAGS) -o $(BUILD_DIR)/omc_client

clean:
rm -rf $(BUILD_DIR)
45 changes: 45 additions & 0 deletions lib/omc-osc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# omc-osc

tiny c++ wrapper around liblo for omc/oms osc stuff.

the point is: dont type paths as strings all over the app.

```cpp
using namespace omc::osc;

Peer omc(9001);
Endpoint oms{"127.0.0.1", 9000};

auto fader = OMC.faders.getValue(faderNumber(1));

omc.route(fader, [fader](Peer&, const Message& msg) {
auto value = msg.arg(fader);
});

omc.send(oms, fader, {0.75f});
```

complex values inherit from `Serializable`.

```cpp
class MyThing : public Serializable {
public:
Blob serialize() const override;
bool deserialize(const Blob& blob) override;
};
```

build:

```sh
make
```

examples:

```sh
./build/oms_server
./build/omc_client
```

not fancy yet. just enough to keep osc paths and values sane while the real oms api settles.
28 changes: 28 additions & 0 deletions lib/omc-osc/examples/example_complex_message.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "example_complex_message.hpp"

#include <cstring>

struct Payload {
float fader;
bool mute;
};

omc::osc::Blob ExampleComplexMessage::serialize() const
{
Payload payload{fader, mute};
omc::osc::Blob blob;
blob.bytes.resize(sizeof(payload));
std::memcpy(blob.bytes.data(), &payload, sizeof(payload));
return blob;
}

bool ExampleComplexMessage::deserialize(const omc::osc::Blob& blob)
{
if (blob.bytes.size() != sizeof(Payload)) return false;

Payload payload{};
std::memcpy(&payload, blob.bytes.data(), sizeof(payload));
fader = payload.fader;
mute = payload.mute;
return true;
}
12 changes: 12 additions & 0 deletions lib/omc-osc/examples/example_complex_message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "omc-osc/omc_osc.hpp"

class ExampleComplexMessage : public omc::osc::Serializable {
public:
float fader = 0.0f;
bool mute = false;

omc::osc::Blob serialize() const override;
bool deserialize(const omc::osc::Blob& blob) override;
};
40 changes: 40 additions & 0 deletions lib/omc-osc/examples/omc_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "omc-osc/omc_osc.hpp"

#include "example_complex_message.hpp"

#include <chrono>
#include <iostream>

using namespace omc::osc;

int main()
{
Endpoint oms{"127.0.0.1", 9000};
Peer omc(9001);

auto fader = OMC.faders.getValue(faderNumber(1));
Path complexPath{"/complex"};

omc.route(fader, [fader](Peer&, const Message& msg) {
if (auto value = msg.arg(fader)) {
std::cout << "rx fader 1 = " << *value << "\n";
}
});

omc.route(complexPath, [](Peer&, const Message& msg) {
ExampleComplexMessage value;
if (!msg.args.empty() && readValue(msg.args[0], value)) {
std::cout << "rx complex fader=" << value.fader << " mute=" << value.mute << "\n";
}
});

ExampleComplexMessage value;
value.fader = 0.42f;
value.mute = true;

omc.send(oms, fader, {0.75f});
omc.send(oms, complexPath, {valueOf(value)});

auto end = std::chrono::steady_clock::now() + std::chrono::seconds(1);
while (std::chrono::steady_clock::now() < end) omc.poll(50);
}
39 changes: 39 additions & 0 deletions lib/omc-osc/examples/oms_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "omc-osc/omc_osc.hpp"

#include "example_complex_message.hpp"

#include <atomic>
#include <csignal>
#include <iostream>

using namespace omc::osc;

static std::atomic<bool> running{true};

int main()
{
std::signal(SIGINT, [](int) { running = false; });

Peer oms(9000);
auto fader = OMC.faders.getValue(faderNumber(1));
Path complex{"/complex"};

oms.route(fader, [&](Peer& peer, const Message& msg) {
auto value = msg.arg(fader);
if (!value) return;

std::cout << "fader 1 = " << *value << "\n";
peer.send(msg.remote, fader, {*value});
});

oms.route(complex, [&](Peer& peer, const Message& msg) {
ExampleComplexMessage value;
if (msg.args.empty() || !readValue(msg.args[0], value)) return;

std::cout << "complex fader=" << value.fader << " mute=" << value.mute << "\n";
peer.send(msg.remote, complex, {valueOf(value)});
});

std::cout << "oms on 9000\n";
while (running) oms.poll(100);
}
13 changes: 13 additions & 0 deletions lib/omc-osc/include/omc-osc/endpoint.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <cstdint>
#include <string>

namespace omc::osc {

struct Endpoint {
std::string host = "127.0.0.1";
uint16_t port = 0;
};

} // namespace omc::osc
31 changes: 31 additions & 0 deletions lib/omc-osc/include/omc-osc/message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "omc-osc/endpoint.hpp"
#include "omc-osc/path.hpp"
#include "omc-osc/value.hpp"

#include <cstddef>
#include <type_traits>
#include <vector>

namespace omc::osc {

struct Message {
Path path;
std::vector<Value> args;
Endpoint remote;

template <class T>
const T* arg(size_t index) const
{
return index < args.size() ? std::get_if<T>(&args[index]) : nullptr;
}

template <class TPath>
auto arg(const TPath&, size_t index = 0) const -> const typename std::remove_cvref_t<TPath>::Return*
{
return arg<typename std::remove_cvref_t<TPath>::Return>(index);
}
};

} // namespace omc::osc
8 changes: 8 additions & 0 deletions lib/omc-osc/include/omc-osc/omc_osc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include "omc-osc/endpoint.hpp"
#include "omc-osc/message.hpp"
#include "omc-osc/path.hpp"
#include "omc-osc/peer.hpp"
#include "omc-osc/result.hpp"
#include "omc-osc/value.hpp"
40 changes: 40 additions & 0 deletions lib/omc-osc/include/omc-osc/path.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include <cstdint>
#include <string>
#include <utility>

namespace omc::osc {

struct Path {
std::string value;

Path() = default;
explicit Path(std::string value) : value(std::move(value)) {}
};

template <class TReturn>
struct TypedPath : Path {
using Return = TReturn;

TypedPath() = default;
explicit TypedPath(std::string value) : Path(std::move(value)) {}
};

struct FaderNumber {
uint16_t value = 0;
};

FaderNumber faderNumber(uint16_t number);

struct FaderPaths {
TypedPath<float> getValue(FaderNumber fader) const;
};

struct Paths {
FaderPaths faders;
};

extern const Paths OMC;

} // namespace omc::osc
34 changes: 34 additions & 0 deletions lib/omc-osc/include/omc-osc/peer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include "omc-osc/endpoint.hpp"
#include "omc-osc/message.hpp"
#include "omc-osc/path.hpp"
#include "omc-osc/result.hpp"
#include "omc-osc/value.hpp"

#include <functional>
#include <memory>
#include <vector>

namespace omc::osc {

class Peer {
public:
using Handler = std::function<void(Peer&, const Message&)>;

explicit Peer(uint16_t listenPort);
~Peer();

Peer(const Peer&) = delete;
Peer& operator=(const Peer&) = delete;

Result route(Path path, Handler handler);
Result send(const Endpoint& endpoint, Path path, std::vector<Value> args = {});
Result poll(int timeoutMs);

private:
struct Impl;
std::unique_ptr<Impl> impl_;
};

} // namespace omc::osc
10 changes: 10 additions & 0 deletions lib/omc-osc/include/omc-osc/result.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

namespace omc::osc {

enum class Result {
ok,
error,
};

} // namespace omc::osc
33 changes: 33 additions & 0 deletions lib/omc-osc/include/omc-osc/value.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "omc-osc/result.hpp"

#include <cstdint>
#include <string>
#include <variant>
#include <vector>

namespace omc::osc {

struct Blob {
std::vector<uint8_t> bytes;
};

class Serializable {
public:
virtual ~Serializable() = default;
virtual Blob serialize() const = 0;
virtual bool deserialize(const Blob& blob) = 0;
};

struct SerializableRef {
const Serializable& object;
};

using Value = std::variant<int32_t, float, std::string, bool, Blob, SerializableRef>;

Value valueOf(const Serializable& object);
bool readValue(const Value& value, Serializable& object);
std::string toString(const Value& value);

} // namespace omc::osc
Loading