32,
license_<"SPDX-License-Identifier: LGPL-2.1-or-later">
34,
description_<"Open Sound Control bindings using the liblo library">
38 ,
"The UDP port on which to receive incoming messages."
42 ,
"The UDP port on which to send outgoing messages."
46 ,
"The IP address to send outgoing messages to."
52 toggle<
"server running"> server_running;
53 toggle<
"output running"> output_running;
59 template<
typename T>
static void
60 set_input(
const char *path,
const char *types
61 , lo_arg **argv,
int argc, lo_message msg
65 if constexpr (
Bang<T>) in =
true;
66 else if constexpr (std::integral<value_t<T>>)
70 fprintf(stderr,
"liblo: wrong type; expected 'i', got '%c'\n", types[0]);
73 set_value(in, argv[0]->i);
75 else if constexpr (std::floating_point<value_t<T>>)
79 fprintf(stderr,
"liblo: wrong type; expected 'f', got '%c'\n", types[0]);
82 set_value(in, argv[0]->f);
84 else if constexpr (string_like<value_t<T>>)
88 fprintf(stderr,
"liblo: wrong type; expected 's', got '%c'\n", types[0]);
91 set_value(in, &argv[0]->s);
93 else if constexpr (array_like<value_t<T>>)
95 for (std::size_t i = 0; i < size<value_t<T>>(); ++i)
97 auto& element = value_of(in)[i];
98 if constexpr (std::integral<element_t<T>>)
102 fprintf(stderr,
"liblo: wrong type; expected 'i', got '%c'\n", types[i]);
105 element = argv[i]->i;
107 else if constexpr (std::floating_point<element_t<T>>)
111 fprintf(stderr,
"liblo: wrong type; expected 'f', got '%c'\n", types[i]);
114 element = argv[i]->f;
116 else if constexpr (string_like<element_t<T>>)
120 fprintf(stderr,
"liblo: wrong type; expected 's', got '%c'\n", types[i]);
123 element = &argv[i]->s;
126 if constexpr (UpdatedFlag<T>) in.updated =
true;
128 if constexpr (has_name<T>) fprintf(stdout,
"liblo: set input %s\n", name_of(in));
129 else fprintf(stdout,
"liblo: set unnamed input\n");
132 bool port_is_valid(
auto& port)
135 auto [ _, ec ] = std::from_chars(port->c_str(), port->c_str() + port->length(), port_num);
136 bool ret = ec == std::errc{} && (1024 <= port_num && port_num <= 65535);
137 if (not ret) fprintf(stderr,
"liblo: invalid port %s (parsed as %d)\n", port->c_str(), port_num);
140 void set_server(
auto& components)
142 bool valid_user_src_port = port_is_valid(inputs.src_port);
145 if (outputs.server_running && not (inputs.src_port.updated && valid_user_src_port))
return;
147 fprintf(stdout,
"liblo: setting up server\n");
148 if (server) lo_server_free(server);
150 if (not valid_user_src_port)
152 fprintf(stdout,
"liblo: searching for unused port\n");
153 server = lo_server_new(NULL, &LibloOsc::server_error_handler);
157 fprintf(stdout,
"liblo: using given port %s\n", inputs.src_port->c_str());
158 server = lo_server_new(inputs.src_port->c_str(), &LibloOsc::server_error_handler);
163 fprintf(stderr,
"liblo: server setup failed\n");
164 outputs.server_running = 0;
168 fprintf(stderr,
"liblo: server setup successful\n");
170 if (not valid_user_src_port)
172 char port_str[6] = {0};
173 int port_num = lo_server_get_port(server);
174 snprintf(port_str, 6,
"%d", port_num);
175 inputs.src_port.value() = port_str;
176 clear_flag(inputs.src_port);
177 fprintf(stdout,
"liblo: connected on port %s\n", inputs.src_port->c_str());
180 fprintf(stdout,
"liblo: connected on port %s\n", inputs.src_port->c_str());
182 fprintf(stderr,
"liblo: registering callbacks\n");
183 for_each_input(components, [&]<
typename T>(T& in)
185 fprintf(stdout,
"liblo: registering callback for %s ... ", name_of(in));
186 constexpr auto handler = +[](
const char *path,
const char *types
187 , lo_arg **argv,
int argc, lo_message msg
192 fprintf(stdout,
"liblo: got message %s", path);
195 T& in = *(T*)user_data;
196 LibloOsc::set_input(path, types, argv, argc, msg, in);
199 lo_server_add_method( server
200 , osc_path_v<T, Components>, osc_type_string_v<T>+1
201 , handler, (
void*)&in
203 fprintf(stdout,
"done\n");
205 fprintf(stderr,
"liblo: done registering callbacks\n");
207 outputs.server_running = 1;
211 bool ip_addr_is_valid(
auto& ip)
214 bool ret = ip->length() >= 7;
215 if (not ret) fprintf(stderr,
"liblo: invalid IP address %s\n", ip->c_str());
219 bool dst_inputs_are_valid()
221 return ip_addr_is_valid(inputs.dst_addr) and port_is_valid(inputs.dst_port);
225 bool dst_updated = (inputs.dst_port.updated || inputs.dst_addr.updated);
226 if (not (dst_updated && dst_inputs_are_valid()) )
return;
227 fprintf(stdout,
"liblo: setting destination address to %s:%s\n"
228 , inputs.dst_addr->c_str(), inputs.dst_port->c_str()
230 if (dst) lo_address_free(dst);
231 dst = lo_address_new(inputs.dst_addr->c_str(), inputs.dst_port->c_str());
232 if (dst) outputs.output_running = 1;
235 outputs.output_running = 0;
236 fprintf(stderr,
"liblo: unable to set destination address\n");
240 static void server_error_handler(
int num,
const char *msg,
const char *where)
242 fprintf(stderr,
"liblo error: %s %s\n", msg, where);
245 void init(Components& components)
247 fprintf(stdout,
"liblo: initializing\n");
248 outputs.server_running = 0;
249 set_server(components);
250 outputs.output_running = 0;
254 void external_sources()
256 if (outputs.server_running) lo_server_recv_noblock(server, 0);
258 void main(Components& components)
260 set_server(components);
263 void external_destinations(Components& components)
265 if (outputs.output_running)
267 lo_bundle bundle = lo_bundle_new(LO_TT_IMMEDIATE);
268 for_each_output(components, [&]<
typename T>(T& output)
270 if constexpr (OccasionalValue<T> || Bang<T>)
272 if (not flag_state_of(output))
275 else if constexpr (
requires (T t) {t == output;})
283 lo_message message = lo_message_new();
286 perror(
"liblo: unable to malloc new message. perror reports: \n");
289 if constexpr (has_value<T> && not Bang<T>)
293 constexpr auto type = std::integral<element_t<T>> ?
"i"
294 : std::floating_point<element_t<T>> ?
"f"
295 : string_like<element_t<T>> ?
"s" :
"" ;
297 if constexpr (string_like<value_t<T>>)
298 ret = lo_message_add_string(message, value_of(output).c_str());
299 else if constexpr (array_like<value_t<T>>)
301 for (
auto& element : value_of(output))
303 ret = lo_message_add(message, type, element);
307 else ret = lo_message_add(message, type, value_of(output));
311 lo_message_free(message);
316 int ret = lo_bundle_add_message(bundle, osc_path_v<T, Components>, message);
317 if (ret < 0) fprintf(stderr,
"liblo: unable to add message to bundle.\n");
321 int ret = lo_send_bundle(dst, bundle);
326 lo_bundle_free_recursive(bundle);
A text string endpoint with occasional message semantics.
Definition sygah-endpoints.hpp:320