Skip to content

Commit

Permalink
Merge pull request hku-ect#324 from sphaero/multiosc
Browse files Browse the repository at this point in the history
Multi OSC output actor
  • Loading branch information
StudioBeneden authored Mar 8, 2024
2 parents fd8b6a2 + 99e4e40 commit 80033d7
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 0 deletions.
184 changes: 184 additions & 0 deletions ActorContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ convert_to_relative_to_wd(const char *path)
return strdup( &path[ wdpathlen ]);
}

void
s_replace_char(char *str, char from, char to)
{
ssize_t length = strlen(str);
for (int i = 0; i<length;i++)
{
if (str[i] == from )
{
str[i] = to;
}
}
}

// OpenTextEditor forward declare
void OpenTextEditor(zfile_t* txtfile);

Expand Down Expand Up @@ -337,6 +350,9 @@ struct ActorContainer {
else if ( streq(typeStr, "mediacontrol")) {
RenderMediacontrol( nameStr, data );
}
else if ( streq(typeStr, "list")) {
RenderMultilineString( nameStr, data );
}
else if ( streq(typeStr, "trigger")) {
RenderTrigger( nameStr, data );
}
Expand Down Expand Up @@ -465,6 +481,137 @@ struct ActorContainer {
*position += sizeof(T);
}

// Make the UI compact because there are so many fields
static void PushStyleCompact()
{
ImGuiStyle& style = ImGui::GetStyle();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f)));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f)));
}

static void PopStyleCompact()
{
ImGui::PopStyleVar(2);
}
void RenderList(const char *name, zconfig_t *data)
{
ImVec2 size = ImVec2(300,100); // what's a reasonable size?
ImGui::BeginChild("##", size, false, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings );
// The list capability expects a tree of zconfig data.
// Every node is a column
zconfig_t *namec = zconfig_locate(data, "name");
int colcount = 0;
zconfig_t *columnc = zconfig_child(data);
while (columnc)
{
// we only count nodes not leaves
if ( zconfig_child(columnc) )
{
colcount++;
}
columnc = zconfig_next(columnc);
}
assert(colcount);

// By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch"
// Each columns maintain a sizing weight, and they will occupy all available width.
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable ;//ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable(zconfig_value(namec), colcount, flags))
{
unsigned int dirty = 0; //track changes bitwise
columnc = zconfig_child(data);
// generate table header
while (columnc)
{
if ( zconfig_child(columnc) )
{
char *name = zconfig_name(columnc);
ImGui::TableSetupColumn(name, ImGuiTableColumnFlags_None);
}
columnc = zconfig_next(columnc);
}
ImGui::TableHeadersRow();
// generate entries

// always end with an empty entry row
char *values[10] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; // capture input
columnc = zconfig_child(data);
ImGui::TableNextRow();
int colidx = 0;
while (columnc)
{
zconfig_t *rowc = NULL;
if ( rowc = zconfig_child(columnc) )
{
char *name = zconfig_name(columnc);
ImGui::TableSetColumnIndex(colidx);
zconfig_t *typec = zconfig_locate(columnc, "type");
char *type = zconfig_value(typec);
ImGui::PushItemWidth( -1.0f );
if ( streq(type, "intbla") )
{
int value = 6200;
if ( ImGui::InputInt("##", &value, 1, 100,ImGuiInputTextFlags_EnterReturnsTrue ) )
{
dirty = (1 << colidx) | colidx;
}
}
else
{
static char bla[256] = "";
char label[256] = "";
snprintf(label, 256, "##%s", name);
if ( ImGui::InputText(&label[0], &bla[0], 256, ImGuiInputTextFlags_None) )
{
dirty = (1 << colidx) | colidx;
}
}
ImGui::PopItemWidth();
colidx++;
}
columnc = zconfig_next(columnc);
}
ImGui::EndTable();

if (dirty)
{
// get current value, append new value if all input fields contain data
}
}
//PushStyleCompact();
//ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
//ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
//ImGui::SameLine(); HelpMarker("Using the _Resizable flag automatically enables the _BordersInnerV flag as well, this is why the resize borders are still showing when unchecking this.");
//PopStyleCompact();


/*
if (ImGui::BeginTable("table1", 3, flags))
{
ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_None, 80.f);
ImGui::TableSetupColumn("ip", ImGuiTableColumnFlags_None, 80.f);
ImGui::TableSetupColumn("port", ImGuiTableColumnFlags_None, 60.f);
ImGui::TableHeadersRow();
for (int row = 0; row < 3; row++)
{
ImGui::TableNextRow();
for (int column = 0; column < 3; column++)
{
ImGui::TableSetColumnIndex(column);
static int port = 0;
static char bla[256] = "";
if (column == 2)
ImGui::InputInt("##port", &port);
else
ImGui::InputText("##Hello", &bla[0], 256);
}
}
ImGui::EndTable();
}*/
ImGui::EndChild();
}

void RenderMediacontrol(const char* name, zconfig_t *data)
{
if ( ImGui::Button(ICON_FA_BACKWARD) )
Expand Down Expand Up @@ -902,6 +1049,43 @@ struct ActorContainer {
}
}

void RenderMultilineString(const char* name, zconfig_t *data) {
int max = MAX_STR_DEFAULT;

zconfig_t * zvalue = zconfig_locate(data, "value");
zconfig_t * zapic = zconfig_locate(data, "api_call");
zconfig_t * zapiv = zconfig_locate(data, "api_value");
assert(zvalue);

zconfig_t * zmax = zconfig_locate(data, "max");

ReadInt( &max, zmax );

char buf[MAX_STR_DEFAULT];
const char* zvalueStr = zconfig_value(zvalue);
strcpy(buf, zvalueStr);
s_replace_char(buf, ',', '\n'); // replace comma with newline if needed
char *p = &buf[0];

ImGui::SetNextItemWidth(200);
ImGui::InputTextMultiline("##source", p, max, ImVec2(0, ImGui::GetTextLineHeight() * 8), ImGuiInputTextFlags_EnterReturnsTrue);
if ( ImGui::IsItemEdited() || ImGui::IsItemDeactivatedAfterEdit())
{
char *sendbuf = strdup(buf);
s_replace_char(sendbuf, '\n', ','); // We use ',' instead of newline since we can't save newlines in the stage save file
zconfig_set_value(zvalue, "%s", sendbuf);
sphactor_ask_api(this->actor, zconfig_value(zapic), zconfig_value(zapiv), sendbuf);
zstr_free(&sendbuf);
}

zconfig_t *help = zconfig_locate(data, "help");
if (help)
{
char *helpv = zconfig_value(help);
RenderTooltip(helpv);
}
}

void RenderTrigger(const char* name, zconfig_t *data) {
zconfig_t * zapic = zconfig_locate(data, "api_call");

Expand Down
121 changes: 121 additions & 0 deletions Actors/OscMultiOut.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include "OscMultiOut.h"
#include <string>
#include <time.h>

static void
s_replace_char(char *str, char from, char to)
{
ssize_t length = strlen(str);
for (int i = 0; i<length;i++)
{
if (str[i] == from )
{
str[i] = to;
}
}
}

const char *
OSCMultiOut::capabilities = "capabilities\n"
" data\n"
" name = \"host_list\"\n"
" type = \"list\"\n"
" help = \"List of hosts with port (host:port)\"\n"
" value = \"127.0.0.1:1234\"\n"
" api_call = \"SET HOSTS\"\n"
" api_value = \"s\"\n"
"inputs\n"
" input\n"
" type = \"OSC\"\n";

zmsg_t* OSCMultiOut::handleInit( sphactor_event_t * ev ) {
this->dgrams = zsock_new_dgram("udp://*:*");
return Sphactor::handleInit(ev);
}

zmsg_t* OSCMultiOut::handleStop( sphactor_event_t * ev ) {
if ( this->dgrams != NULL ) {
zsock_destroy(&this->dgrams);
this->dgrams = NULL;
}
if (this->hosts)
zlist_destroy(&this->hosts);

return Sphactor::handleStop(ev);
}

zmsg_t* OSCMultiOut::handleSocket( sphactor_event_t * ev ) {
if ( ev->msg == NULL ) return NULL;
if ( this->dgrams == NULL ) return ev->msg;

byte *msgBuffer;
zframe_t* frame;

do {
frame = zmsg_pop(ev->msg);
if ( frame ) {
msgBuffer = zframe_data(frame);
size_t len = zframe_size(frame);

char *host = (char *)zlist_first(this->hosts);
while ( host )
{
//char *url = strchr(host, ',');
//assert(url);
zstr_sendm(dgrams, host);
int rc = zsock_send(dgrams, "b", msgBuffer, len);
if ( rc != 0 ) {
zsys_info("Error sending zosc message to: %s, %i", host, rc);
}
host = (char *)zlist_next(this->hosts);
}
}
} while (frame != NULL );

return Sphactor::handleSocket(ev);
}

zmsg_t* OSCMultiOut::handleAPI( sphactor_event_t * ev ) {
//pop msg for command
char * cmd = zmsg_popstr(ev->msg);
if (cmd) {
if ( streq(cmd, "SET HOSTS") ) {
// clear the current list
if (this->hosts)
zlist_destroy(&this->hosts);
this->hosts = zlist_new();
zlist_autofree(this->hosts);

// fill with new data
char *hosts = zmsg_popstr(ev->msg);
while ( hosts != NULL )
{
ssize_t length = strlen(hosts);
// replace , with 0x0. Multiline use , as seperator
s_replace_char(hosts, ',', (char)0x0);
char *host = hosts;

// Iterate through the characters in the array
for (ssize_t i = 0; i < length; i++) {
// Check if the current character is a 0 terminator
if (hosts[i] == (char)0x0 )
{
if (strlen(host) > 6) // just a validation as the host must contain a : and port
zlist_append(this->hosts, host); //copies the string!
host = hosts+i+1;
}
else if (i == length - 1)
{
if (strlen(host) > 6)
zlist_append(this->hosts, host); //copies the string!
}
}
zstr_free(&hosts);
hosts = zmsg_popstr(ev->msg);
}
}
zstr_free(&cmd);
}

return Sphactor::handleAPI(ev);
}
23 changes: 23 additions & 0 deletions Actors/OscMultiOut.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef OSCMULTIOUT_H
#define OSCMULTIOUT_H
#include "libsphactor.hpp"

class OSCMultiOut : public Sphactor
{
public:
OSCMultiOut() : Sphactor() {}
static const char *capabilities;
zsock_t* dgrams = NULL;
zlist_t* hosts = nullptr;

zmsg_t *handleInit(sphactor_event_t *ev);

zmsg_t *handleAPI(sphactor_event_t *ev);

zmsg_t *handleSocket(sphactor_event_t *ev);

zmsg_t *handleStop(sphactor_event_t *ev);

};

#endif // OSCMULTIOUT_H
1 change: 1 addition & 0 deletions actors.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "Actors/HTTPLaunchpod.h"
#include "Actors/OSCOutputActor.h"
#include "Actors/OscMultiOut.h"
#include "Actors/Midi2OSCActor.h"
#include "Actors/NatNetActor.h"
#include "Actors/NatNet2OSCActor.h"
Expand Down
1 change: 1 addition & 0 deletions stage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ void RegisterActors() {

sphactor_register("HTTPLaunchpod", &httplaunchpodactor_handler, zconfig_str_load(httplaunchpodactorcapabilities), &httplaunchpodactor_new_helper, NULL); // https://stackoverflow.com/questions/65957511/typedef-for-a-registering-a-constructor-function-in-c
sphactor_register<OSCOutput>( "OSC Output", OSCOutput::capabilities);
sphactor_register<OSCMultiOut>( "OSC Multi Output", OSCMultiOut::capabilities);
sphactor_register<NatNet>( "NatNet", NatNet::capabilities );
sphactor_register<NatNet2OSC>( "NatNet2OSC", NatNet2OSC::capabilities );
sphactor_register<Midi2OSC>( "Midi2OSC", Midi2OSC::capabilities );
Expand Down

0 comments on commit 80033d7

Please sign in to comment.