Skip to content

Commit

Permalink
[dmx] Input for sACN and Artnet
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Feb 22, 2024
1 parent 7039ecc commit 6616653
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 52 deletions.
7 changes: 7 additions & 0 deletions src/ossia/network/sockets/udp_socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ class udp_receive_socket
using proto = boost::asio::ip::udp;

public:
udp_receive_socket(boost::asio::io_context& ctx)
: m_context{ctx}
, m_socket{ctx}
{
}

udp_receive_socket(const socket_configuration& conf, boost::asio::io_context& ctx)
: m_context{ctx}
, m_endpoint{boost::asio::ip::make_address(conf.host), conf.port}
Expand All @@ -27,6 +33,7 @@ class udp_receive_socket

~udp_receive_socket() = default;

void assign(int sock) { m_socket.assign(boost::asio::ip::udp::v4(), sock); }
void open()
{
m_socket.open(boost::asio::ip::udp::v4());
Expand Down
93 changes: 90 additions & 3 deletions src/ossia/protocols/artnet/artnet_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
#include <ossia/detail/fmt.hpp>
#include <ossia/protocols/artnet/dmx_parameter.hpp>

#include <boost/asio/use_future.hpp>

#include <artnet/artnet.h>

#include <chrono>
#include <iostream>

namespace ossia::net
{
Expand All @@ -21,7 +24,7 @@ dmx_buffer::~dmx_buffer() = default;

artnet_protocol::artnet_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf, std::string_view host)
: dmx_protocol_base{ctx, conf}
: dmx_output_protocol_base{ctx, conf}
{
if(conf.frequency < 1 || conf.frequency > 44)
throw std::runtime_error("DMX 512 update frequency must be in the range [1, 44] Hz");
Expand All @@ -45,8 +48,10 @@ artnet_protocol::artnet_protocol(
throw std::runtime_error("Artnet new failed");

static constexpr int artnet_port_id = 0;
artnet_set_port_type(m_node, artnet_port_id, ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX);
artnet_set_port_addr(m_node, artnet_port_id, ARTNET_OUTPUT_PORT, m_conf.universe);
static constexpr int artnet_subnet_id = 0;
artnet_set_port_type(m_node, artnet_port_id, ARTNET_ENABLE_INPUT, ARTNET_PORT_DMX);
artnet_set_port_addr(m_node, artnet_port_id, ARTNET_INPUT_PORT, m_conf.universe);
artnet_set_subnet_addr(m_node, artnet_subnet_id);

artnet_set_short_name(m_node, "libossia");
artnet_set_long_name(m_node, "libossia artnet protocol");
Expand Down Expand Up @@ -80,6 +85,88 @@ void artnet_protocol::update_function()
m_buffer.dirty = false;
}
}

artnet_input_protocol::artnet_input_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf, std::string_view host)
: dmx_input_protocol_base{ctx, conf}
{
if(conf.frequency < 1 || conf.frequency > 44)
throw std::runtime_error("DMX 512 update frequency must be in the range [1, 44] Hz");

#if defined(_NDEBUG)
bool verbose = 0;
#else
bool verbose = 1;
#endif
m_node = artnet_new("192.168.0.55", verbose);

if(m_node == NULL)
throw std::runtime_error("Artnet new failed");

static constexpr int artnet_port_id = 0;
static constexpr int artnet_subnet_id = 0;
artnet_set_port_type(m_node, artnet_port_id, ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX);
artnet_set_port_addr(m_node, artnet_port_id, ARTNET_OUTPUT_PORT, 0);
artnet_set_subnet_addr(m_node, artnet_subnet_id);

artnet_set_short_name(m_node, "libossia");
artnet_set_long_name(m_node, "libossia artnet protocol");
artnet_set_node_type(m_node, ARTNET_NODE);
artnet_set_dmx_handler(
m_node,
[](artnet_node n, int port, void* d) -> int {
auto& self = *(artnet_input_protocol*)d;
self.on_packet(n, port);
return 0;
},
this);

artnet_dump_config(m_node);
std::fflush(stdout);
std::fflush(stderr);
if(artnet_start(m_node) != ARTNET_EOK)
throw std::runtime_error("Artnet Start failed");
}

artnet_input_protocol::~artnet_input_protocol()
{
m_socket->close();
std::future<void> wait
= boost::asio::post(m_context->context, boost::asio::use_future);
wait.get();
artnet_destroy(m_node);
}

void artnet_input_protocol::set_device(ossia::net::device_base& dev)
{
dmx_protocol_base::set_device(dev);

m_socket = std::make_unique<ossia::net::udp_receive_socket>(m_context->context);
m_socket->assign(artnet_get_sd(m_node));
auto& sock = m_socket->m_socket;
sock.non_blocking(true);

do_read();
}

void artnet_input_protocol::do_read()
{
auto& sock = m_socket->m_socket;
sock.async_wait(
boost::asio::ip::udp::socket::wait_read, [this](boost::system::error_code ec) {
if(ec == boost::asio::error::operation_aborted)
return;
artnet_read(m_node, 1);
do_read();
});
}

void artnet_input_protocol::on_packet(artnet_node n, int port)
{
int length = 0;
auto data = artnet_read_dmx(n, port, &length);
on_dmx(data + 1, std::min(length - 1, 512));
}
}

#endif
19 changes: 18 additions & 1 deletion src/ossia/protocols/artnet/artnet_protocol.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#pragma once
#include <ossia/detail/config.hpp>
#if defined(OSSIA_PROTOCOL_ARTNET)
#include <ossia/network/sockets/udp_socket.hpp>
#include <ossia/protocols/artnet/dmx_protocol_base.hpp>

using artnet_node = void*;

namespace ossia::net
{
struct dmx_config;
class OSSIA_EXPORT artnet_protocol final : public dmx_protocol_base
class OSSIA_EXPORT artnet_protocol final : public dmx_output_protocol_base
{
public:
artnet_protocol(
Expand All @@ -23,6 +24,22 @@ class OSSIA_EXPORT artnet_protocol final : public dmx_protocol_base
artnet_node m_node;
};

class OSSIA_EXPORT artnet_input_protocol final : public dmx_input_protocol_base
{
public:
artnet_input_protocol(
ossia::net::network_context_ptr, const dmx_config& conf, std::string_view host);
~artnet_input_protocol();

void set_device(ossia::net::device_base& dev) override;

private:
void on_packet(artnet_node n, int port);
void do_read();

artnet_node m_node;
std::unique_ptr<ossia::net::udp_receive_socket> m_socket;
};
}

#endif
113 changes: 107 additions & 6 deletions src/ossia/protocols/artnet/dmx_parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,77 @@
namespace ossia::net
{
template <std::size_t I>
struct artnet_visitor
struct artnet_in_visitor
{
static_assert(I >= 1);
static_assert(I <= 4);

const uint8_t* start{};
void apply(uint32_t& res) const noexcept
{
if constexpr(I == 1)
{
res += *start;
}
else if constexpr(I == 2)
{
res += *(start + 1);
res += *(start + 0) << 8;
}
else if constexpr(I == 3)
{
res += *(start + 2);
res += *(start + 1) << 8;
res += *(start + 0) << 16;
}
else if constexpr(I == 4)
{
res += *(start + 3);
res += *(start + 2) << 8;
res += *(start + 1) << 16;
res += *(start + 0) << 24;
}
}

void operator()(int& v) const noexcept
{
uint32_t vv = 0;
apply(vv);
v = vv;
}
void operator()(float& v) const noexcept
{
uint32_t vv = 0;
apply(vv);
v = vv;
}
void operator()(bool& v) const noexcept
{
uint32_t vv = 0;
apply(vv);
v = vv;
}
template <typename... Args>
void operator()(Args&&...) const noexcept
{
}
};

template <>
struct artnet_in_visitor<1>
{
const uint8_t* value{};
void operator()(int& v) const noexcept { v = *value; }
void operator()(float& v) const noexcept { v = *value; }
void operator()(bool& v) const noexcept { v = *value > 0; }
template <typename... Args>
void operator()(const Args&...) const noexcept
{
}
};

template <std::size_t I>
struct artnet_out_visitor
{
static_assert(I >= 1);
static_assert(I <= 4);
Expand Down Expand Up @@ -41,14 +111,15 @@ struct artnet_visitor
}
void operator()(int v) const noexcept { return apply(v); }
void operator()(float v) const noexcept { return apply(v); }
void operator()(bool v) const noexcept { return apply(v ? 0xFFFFFFFF : 0x0); }
template <typename... Args>
void operator()(Args&&...) const noexcept
{
}
};

template <>
struct artnet_visitor<1>
struct artnet_out_visitor<1>
{
dmx_parameter& self;
void operator()(int v) const noexcept
Expand All @@ -59,11 +130,16 @@ struct artnet_visitor<1>
{
self.m_buffer.data[self.m_channel] = std::clamp(v, 0.f, 255.f);
}
void operator()(bool v) const noexcept
{
self.m_buffer.data[self.m_channel] = v ? 255 : 0;
}
template <typename... Args>
void operator()(const Args&...) const noexcept
{
}
};

dmx_parameter::dmx_parameter(
net::node_base& node, dmx_buffer& buffer, const unsigned int channel, int min,
int max, int8_t bytes)
Expand All @@ -78,21 +154,46 @@ dmx_parameter::dmx_parameter(

dmx_parameter::~dmx_parameter() = default;

void dmx_parameter::set_dmx_value(const uint8_t* start, const uint8_t* buffer_end)
{
// dmx in -> ossia
if(buffer_end - start < m_bytes)
return;

switch(m_bytes)
{
case 1:
m_current_value.apply(artnet_in_visitor<1>{start});
break;
case 2:
m_current_value.apply(artnet_in_visitor<2>{start});
break;
case 3:
m_current_value.apply(artnet_in_visitor<3>{start});
break;
case 4:
m_current_value.apply(artnet_in_visitor<4>{start});
break;
}
push_value(m_current_value);
}

void dmx_parameter::device_update_value()
{
// ossia -> dmx out
switch(m_bytes)
{
case 1:
m_current_value.apply(artnet_visitor<1>{*this});
m_current_value.apply(artnet_out_visitor<1>{*this});
break;
case 2:
m_current_value.apply(artnet_visitor<2>{*this});
m_current_value.apply(artnet_out_visitor<2>{*this});
break;
case 3:
m_current_value.apply(artnet_visitor<3>{*this});
m_current_value.apply(artnet_out_visitor<3>{*this});
break;
case 4:
m_current_value.apply(artnet_visitor<4>{*this});
m_current_value.apply(artnet_out_visitor<4>{*this});
break;
}

Expand Down
8 changes: 7 additions & 1 deletion src/ossia/protocols/artnet/dmx_parameter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ class OSSIA_EXPORT dmx_parameter : public device_parameter
~dmx_parameter();
int8_t m_bytes{};

uint32_t channel() const noexcept { return m_channel; }

void set_dmx_value(const uint8_t* start, const uint8_t* buffer_end);

private:
void device_update_value() override;

dmx_buffer& m_buffer;
const uint32_t m_channel{};

template <std::size_t I>
friend struct artnet_visitor;
friend struct artnet_in_visitor;
template <std::size_t I>
friend struct artnet_out_visitor;
};

class OSSIA_EXPORT dmx_enum_parameter : public device_parameter
Expand Down
Loading

0 comments on commit 6616653

Please sign in to comment.