Skip to content

Commit

Permalink
complete OSC to LSL converter plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
gisogrimm committed Oct 9, 2024
1 parent a842755 commit f38ea83
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 50 deletions.
1 change: 1 addition & 0 deletions manual/documentation.tsc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
<granularsynth/>
<oscactor path="/path" channels="1 2 3" influence="1 1 1"/>
<ltcgen/>
<osc2lsl/>
</modules>
<connect src="a" dest="b"/>
<range/>
Expand Down
29 changes: 29 additions & 0 deletions manual/modosc2lsl.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Convert OSC messages into an LSL stream.

\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade}
{\footnotesize
\label{attrtab:osc2lsl}
Attributes of element {\bf osc2lsl}\nopagebreak

\begin{tabularx}{\textwidth}{lXl}
\hline
name & description (type, unit) & def.\\
\hline
\hline
\indattr{first\_row\_is\_timestamp} & Use data of first row as LSL time stamp (bool) & false\\
\hline
\indattr{lslname} & LSL name (string) & osc2lsl\\
\hline
\indattr{lsltype} & LSL type (string) & osc2lsl\\
\hline
\indattr{path} & OSC path name (string) & /osc2lsl\\
\hline
\indattr{retval} & OSC return value: 0 = handle messages also locally, non-0 = mark message as handled, do not handle locally (int32) & 1\\
\hline
\indattr{size} & Dimension of variable (uint32) & 1\\
\hline
\indattr{source\_id} & LSL source ID (string) & osc2lsl29\\
\hline
\end{tabularx}
}
\end{snugshade}
1 change: 1 addition & 0 deletions packaging/deb/tascarcli.csv
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"plugins/build/tascar_mididispatch.so","usr/lib/"
"plugins/build/tascar_motionpath.so","usr/lib/"
"plugins/build/tascar_nearsensor.so","usr/lib/"
"plugins/build/tascar_osc2lsl.so","usr/lib/"
"plugins/build/tascar_oscactor.so","usr/lib/"
"plugins/build/tascar_osceog.so","usr/lib/"
"plugins/build/tascar_oscevents.so","usr/lib/"
Expand Down
4 changes: 2 additions & 2 deletions plugins/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ endif

ifeq "$(HAS_LSL)" "yes"
CXXFLAGS += -DHAS_LSL
TASCARMODS += lsljacktime lslactor levels2osc pos2lsl
TASCARMODS += lsljacktime lslactor levels2osc pos2lsl osc2lsl
AUDIOPLUGINS += speechactivity periodogram
TASCARMODSGUI += glabsensors waitforlslstream lsl2osc
GLABSENSORS += smiley eog emergency espheadtracker \
Expand Down Expand Up @@ -151,7 +151,7 @@ endif
build/$(PLUGINPREFIX)tascar_datalogging$(DLLEXT): build/datalogging_glade.h
build/$(PLUGINPREFIX)tascar_hossustain$(DLLEXT): EXTERNALS += fftw3f
build/$(PLUGINPREFIX)tascar_lightctl$(DLLEXT) build/$(PLUGINPREFIX)tascar_ovheadtracker$(DLLEXT): LDLIBS+=$(TASCARDMXLIB)
build/$(PLUGINPREFIX)tascar_lsljacktime$(DLLEXT) build/$(PLUGINPREFIX)tascar_pos2lsl$(DLLEXT) build/$(PLUGINPREFIX)tascar_levels2osc$(DLLEXT) build/$(PLUGINPREFIX)tascar_lslactor$(DLLEXT): LDLIBS+=-llsl
build/$(PLUGINPREFIX)tascar_lsljacktime$(DLLEXT) build/$(PLUGINPREFIX)tascar_pos2lsl$(DLLEXT) build/$(PLUGINPREFIX)tascar_levels2osc$(DLLEXT) build/$(PLUGINPREFIX)tascar_lslactor$(DLLEXT) build/$(PLUGINPREFIX)tascar_osc2lsl$(DLLEXT): LDLIBS+=-llsl
build/$(PLUGINPREFIX)tascar_ltcgen$(DLLEXT): EXTERNALS+=ltc
build/$(PLUGINPREFIX)tascar_simplecontroller$(DLLEXT): build/simplecontroller_glade.h
build/$(PLUGINPREFIX)tascar_timedisplay$(DLLEXT): build/timedisplay_glade.h
Expand Down
91 changes: 43 additions & 48 deletions plugins/src/tascarmod_osc2lsl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,80 +18,75 @@
*/

#include "session.h"

// stream map:
typedef std::map<std::string, lsl::stream_outlet*> stream_map_t;

// use first row as timestamp, and the rest as data:
bool timestamp(false);
#include <lsl_cpp.h>

class osc2lsl_t : public TASCAR::module_base_t {
public:
osc2lsl_t(const TASCAR::module_cfg_t& cfg);
~osc2lsl_t();
static int osc_recv(const char* path, const char* types, lo_arg** argv,
int argc, lo_message msg, void* user_data);
int osc_recv(const char* path, lo_message msg);
int osc_recv(lo_arg** argv, int argc);

private:
std::string path;
std::string url;
std::string newpath;
std::string startswith;
bool trimstart = false;
lo_address target;
std::string path = "/osc2lsl";
uint32_t size = 1u;
bool first_row_is_timestamp = false;
std::string lslname = "osc2lsl";
std::string lsltype = "osc2lsl";
std::string source_id = "osc2lsl" + TASCAR::get_tuid();
int retval = 1;
lsl::stream_outlet* sop = NULL;
std::vector<double> data;
};

int osc2lsl_t::osc_recv(const char* path, const char*, lo_arg**, int,
lo_message msg, void* user_data)
int osc2lsl_t::osc_recv(const char*, const char*, lo_arg** argv, int argc,
lo_message, void* user_data)
{
return ((osc2lsl_t*)user_data)->osc_recv(path, msg);
return ((osc2lsl_t*)user_data)->osc_recv(argv, argc);
}

int osc2lsl_t::osc_recv(const char* lpath, lo_message msg)
int osc2lsl_t::osc_recv(lo_arg** argv, int argc)
{
bool start_matched =
(startswith.size() > 0) && (strlen(lpath) >= startswith.size()) &&
(strncmp(lpath, startswith.c_str(), startswith.size()) == 0);
if(startswith.size() && (!start_matched))
return retval;
if(newpath.size()) {
lo_send_message(target, newpath.c_str(), msg);
if(argc != (int)size)
return retval;
}
const char* trimmedpath = lpath;
if(start_matched && trimstart)
trimmedpath = lpath + startswith.size();
lo_send_message(target, trimmedpath, msg);
std::uint32_t startchannel(first_row_is_timestamp);
for(std::uint32_t k = startchannel; k < size; ++k)
data[k] = argv[k]->d;
if(first_row_is_timestamp)
sop->push_sample(data, argv[0]->d);
else
sop->push_sample(data);
return retval;
}

osc2lsl_t::osc2lsl_t(const TASCAR::module_cfg_t& cfg)
: module_base_t(cfg), path(""), url("osc.udp://localhost:9000/"),
target(NULL)
osc2lsl_t::osc2lsl_t(const TASCAR::module_cfg_t& cfg) : module_base_t(cfg)
{
GET_ATTRIBUTE(path, "", "Path filter, or empty to match any path");
GET_ATTRIBUTE(url, "", "Target OSC URL");
GET_ATTRIBUTE(
newpath, "",
"Replace incoming path with this path, or empty for no replacement");
GET_ATTRIBUTE(startswith, "",
"Forward only messags which start with this path");
GET_ATTRIBUTE_BOOL(trimstart,
"Trim startswith part of the path before forwarding");
GET_ATTRIBUTE(path, "", "OSC path name");
GET_ATTRIBUTE(size, "", "Dimension of variable");
GET_ATTRIBUTE_BOOL(first_row_is_timestamp,
"Use data of first row as LSL time stamp");
GET_ATTRIBUTE(lslname, "", "LSL name");
GET_ATTRIBUTE(lsltype, "", "LSL type");
GET_ATTRIBUTE(source_id, "", "LSL source ID");
GET_ATTRIBUTE(retval, "",
"Return value: 0 = handle messages also locally, non-0 = do "
"not handle locally");
target = lo_address_new_from_url(url.c_str());
if(!target)
throw TASCAR::ErrMsg("Unable to create OSC target client \"" + url + "\".");
session->add_method(path, NULL, &osc2lsl_t::osc_recv, this);
"OSC return value: 0 = handle messages also locally, non-0 = "
"mark message as handled, do not handle locally");
int numchannels(size);
if(first_row_is_timestamp && (size > 1))
--numchannels;
lsl::stream_info info(lslname, lsltype, numchannels, lsl::IRREGULAR_RATE,
lsl::cf_double64, source_id);
sop = new lsl::stream_outlet(info);
data.resize(numchannels);
session->add_method(path, std::string(size, 'd').c_str(),
&osc2lsl_t::osc_recv, this);
}

osc2lsl_t::~osc2lsl_t()
{
lo_address_free(target);
// lo_address_free(target);
delete sop;
}

REGISTER_MODULE(osc2lsl_t);
Expand Down

0 comments on commit f38ea83

Please sign in to comment.