@@ -50,7 +50,7 @@ | |||||
const double STATUS_UPDATE_FREQ = 0.2f; | const double STATUS_UPDATE_FREQ = 0.2f; | ||||
const double OSC_INTERVAL = 0.2f; | |||||
const double OSC_INTERVAL = 1.0 / 20.0; /* 20 hz */ | |||||
extern char *user_config_dir; | extern char *user_config_dir; | ||||
extern char *instance_name; | extern char *instance_name; | ||||
@@ -98,14 +98,13 @@ Mixer::reply_to_finger ( lo_message msg ) | |||||
int argc = lo_message_get_argc( msg ); | int argc = lo_message_get_argc( msg ); | ||||
lo_arg **argv = lo_message_get_argv( msg ); | lo_arg **argv = lo_message_get_argv( msg ); | ||||
if ( argc < 2 ) | |||||
if ( argc < 1 ) | |||||
return; | return; | ||||
lo_address to = lo_address_new_from_url( &argv[1]->s ); | |||||
lo_address to = lo_address_new_from_url( &argv[0]->s ); | |||||
osc_endpoint->send( to, | osc_endpoint->send( to, | ||||
"/reply", | |||||
"/non/finger", | |||||
"/non/hello", | |||||
osc_endpoint->url(), | osc_endpoint->url(), | ||||
APP_NAME, | APP_NAME, | ||||
VERSION, | VERSION, | ||||
@@ -114,6 +113,24 @@ Mixer::reply_to_finger ( lo_message msg ) | |||||
lo_address_free( to ); | lo_address_free( to ); | ||||
} | } | ||||
void | |||||
Mixer::say_hello ( void ) | |||||
{ | |||||
lo_message m = lo_message_new(); | |||||
lo_message_add( m, "sssss", | |||||
"/non/hello", | |||||
osc_endpoint->url(), | |||||
APP_NAME, | |||||
VERSION, | |||||
instance_name ); | |||||
nsm->broadcast( m ); | |||||
lo_message_free( m ); | |||||
} | |||||
static | static | ||||
@@ -103,6 +103,8 @@ public: | |||||
void sm_active ( bool b ); | void sm_active ( bool b ); | ||||
void say_hello ( void ); | |||||
public: | public: | ||||
bool command_save ( void ); | bool command_save ( void ); | ||||
@@ -218,14 +218,14 @@ Module::Port::generate_osc_path () | |||||
char *path = NULL; | char *path = NULL; | ||||
// /mixer/strip/STRIPNAME/control/MODULENAME/CONTROLNAME | |||||
// /mixer/STRIPNAME/MODULENAME/CONTROLNAME | |||||
int n = module()->chain()->get_module_instance_number( module() ); | int n = module()->chain()->get_module_instance_number( module() ); | ||||
if ( n > 0 ) | if ( n > 0 ) | ||||
asprintf( &path, "/non/mixer/strip/%s/control/%s.%i/%s", module()->chain()->name(), p->module()->label(), n, p->name() ); | |||||
asprintf( &path, "/mixer/%s/%s.%i/%s", module()->chain()->name(), p->module()->label(), n, p->name() ); | |||||
else | else | ||||
asprintf( &path, "/non/mixer/strip/%s/control/%s/%s", module()->chain()->name(), p->module()->label(), p->name() ); | |||||
asprintf( &path, "/mixer/%s/%s/%s", module()->chain()->name(), p->module()->label(), p->name() ); | |||||
// Hack to keep spaces out of OSC URL... Probably need to handle other special characters similarly. | // Hack to keep spaces out of OSC URL... Probably need to handle other special characters similarly. | ||||
for ( int i = strlen( path ); i--; ) | for ( int i = strlen( path ); i--; ) | ||||
@@ -240,36 +240,34 @@ Module::Port::generate_osc_path () | |||||
void | void | ||||
Module::Port::change_osc_path ( char *path ) | Module::Port::change_osc_path ( char *path ) | ||||
{ | { | ||||
if ( _osc_path ) | |||||
if ( _scaled_signal && _unscaled_signal ) | |||||
{ | { | ||||
mixer->osc_endpoint->del_method( _osc_path, "f" ); | |||||
mixer->osc_endpoint->del_method( _osc_path_unscaled, "f" ); | |||||
free( _osc_path ); | |||||
free( _osc_path_unscaled ); | |||||
_osc_path = NULL; | |||||
_osc_path_unscaled = NULL; | |||||
mixer->osc_endpoint->del_signal( _scaled_signal ); | |||||
mixer->osc_endpoint->del_signal( _unscaled_signal ); | |||||
_scaled_signal = _unscaled_signal = NULL; | |||||
} | } | ||||
if ( path ) | if ( path ) | ||||
{ | { | ||||
_osc_path_unscaled = NULL; | |||||
_osc_path = path; | |||||
char *scaled_path = path; | |||||
char *unscaled_path = NULL; | |||||
asprintf( &unscaled_path, "%s/unscaled", path ); | |||||
asprintf( &_osc_path_unscaled, "%s/unscaled", path ); | |||||
_scaled_signal = mixer->osc_endpoint->add_signal( scaled_path, OSC::Signal::Input, &Module::Port::osc_control_change_cv, this ); | |||||
mixer->osc_endpoint->add_method( _osc_path, "f", &Module::Port::osc_control_change_cv, this, "value" ); | |||||
_unscaled_signal = mixer->osc_endpoint->add_signal( unscaled_path, OSC::Signal::Input, &Module::Port::osc_control_change_exact, this ); | |||||
mixer->osc_endpoint->add_method( _osc_path_unscaled, "f", &Module::Port::osc_control_change_exact, this, "value" ); | |||||
free( unscaled_path ); | |||||
free( scaled_path ); | |||||
if ( hints.ranged ) | if ( hints.ranged ) | ||||
{ | { | ||||
mixer->osc_endpoint->set_parameter_limits( _osc_path_unscaled, "f", 0, | |||||
hints.minimum, | |||||
hints.maximum, | |||||
hints.default_value ); | |||||
_unscaled_signal->parameter_limits( | |||||
hints.minimum, | |||||
hints.maximum, | |||||
hints.default_value ); | |||||
} | } | ||||
float scaled_default = 0.5f; | float scaled_default = 0.5f; | ||||
@@ -277,25 +275,25 @@ Module::Port::change_osc_path ( char *path ) | |||||
if ( hints.ranged ) | if ( hints.ranged ) | ||||
{ | { | ||||
float scale = hints.maximum - hints.minimum; | float scale = hints.maximum - hints.minimum; | ||||
// float offset = hints.minimum; | |||||
float offset = hints.minimum; | |||||
scaled_default = ( hints.default_value / scale ); | |||||
scaled_default = ( hints.default_value - offset ) / scale; | |||||
} | } | ||||
mixer->osc_endpoint->set_parameter_limits( _osc_path, "f", 0, | |||||
0.0f, | |||||
1.0f, | |||||
scaled_default ); | |||||
_scaled_signal->parameter_limits( | |||||
0.0f, | |||||
1.0f, | |||||
scaled_default ); | |||||
} | } | ||||
} | } | ||||
int | int | ||||
Module::Port::osc_control_change_exact ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
Module::Port::osc_control_change_exact ( float v, void *user_data ) | |||||
{ | { | ||||
Module::Port *p = (Module::Port*)user_data; | Module::Port *p = (Module::Port*)user_data; | ||||
float f = argv[0]->f; | |||||
float f = v; | |||||
if ( p->hints.ranged ) | if ( p->hints.ranged ) | ||||
{ | { | ||||
@@ -307,17 +305,17 @@ Module::Port::osc_control_change_exact ( const char *path, const char *types, lo | |||||
p->control_value( f ); | p->control_value( f ); | ||||
mixer->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, f ); | |||||
// mixer->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, f ); | |||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
Module::Port::osc_control_change_cv ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
Module::Port::osc_control_change_cv ( float v, void *user_data ) | |||||
{ | { | ||||
Module::Port *p = (Module::Port*)user_data; | Module::Port *p = (Module::Port*)user_data; | ||||
float f = argv[0]->f; | |||||
float f = v; | |||||
// clamp value to control voltage range. | // clamp value to control voltage range. | ||||
if ( f > 1.0 ) | if ( f > 1.0 ) | ||||
@@ -337,7 +335,7 @@ Module::Port::osc_control_change_cv ( const char *path, const char *types, lo_ar | |||||
p->control_value( f ); | p->control_value( f ); | ||||
mixer->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, f ); | |||||
// mixer->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, f ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -110,8 +110,8 @@ public: | |||||
} | } | ||||
}; | }; | ||||
static int osc_control_change_exact ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
static int osc_control_change_cv ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
static int osc_control_change_exact ( float v, void *user_data ); | |||||
static int osc_control_change_cv ( float v, void *user_data ); | |||||
Hints hints; | Hints hints; | ||||
@@ -124,8 +124,8 @@ public: | |||||
_nframes = 0; | _nframes = 0; | ||||
_connected = 0; | _connected = 0; | ||||
_module = module; | _module = module; | ||||
_osc_path = 0; | |||||
_osc_path_unscaled = 0; | |||||
_scaled_signal = 0; | |||||
_unscaled_signal = 0; | |||||
} | } | ||||
Port ( const Port& p ) | Port ( const Port& p ) | ||||
@@ -138,8 +138,8 @@ public: | |||||
_connected = p._connected; | _connected = p._connected; | ||||
_module = p._module; | _module = p._module; | ||||
hints = p.hints; | hints = p.hints; | ||||
_osc_path = p._osc_path; | |||||
_osc_path_unscaled = p._osc_path_unscaled; | |||||
_scaled_signal = p._scaled_signal; | |||||
_unscaled_signal = p._unscaled_signal; | |||||
} | } | ||||
virtual ~Port ( ) | virtual ~Port ( ) | ||||
@@ -160,7 +160,7 @@ public: | |||||
const char *osc_path ( ) | const char *osc_path ( ) | ||||
{ | { | ||||
return _osc_path; | |||||
return _scaled_signal->path(); | |||||
} | } | ||||
void update_osc_port ( ) | void update_osc_port ( ) | ||||
@@ -246,8 +246,8 @@ public: | |||||
nframes_t _nframes; | nframes_t _nframes; | ||||
Module *_module; | Module *_module; | ||||
char *_osc_path; | |||||
char *_osc_path_unscaled; | |||||
OSC::Signal *_scaled_signal; | |||||
OSC::Signal *_unscaled_signal; | |||||
}; | }; | ||||
void bbox ( int &X, int &Y, int &W, int &H ) | void bbox ( int &X, int &Y, int &W, int &H ) | ||||
@@ -36,12 +36,12 @@ int command_open ( const char *name, const char *display_name, const char *clien | |||||
int command_save ( char **out_msg ); | int command_save ( char **out_msg ); | ||||
int | int | ||||
NSM_Client::command_broadcast ( lo_message msg ) | |||||
NSM_Client::command_broadcast ( const char *path, lo_message msg ) | |||||
{ | { | ||||
int argc = lo_message_get_argc( msg ); | int argc = lo_message_get_argc( msg ); | ||||
lo_arg **argv = lo_message_get_argv( msg ); | lo_arg **argv = lo_message_get_argv( msg ); | ||||
if ( argc > 1 && !strcmp( &argv[0]->s, "/non/finger" ) ) | |||||
if ( argc == 1 && !strcmp( path, "/non/finger" ) ) | |||||
{ | { | ||||
mixer->reply_to_finger( msg ); | mixer->reply_to_finger( msg ); | ||||
return 0; | return 0; | ||||
@@ -36,5 +36,5 @@ protected: | |||||
void command_active ( bool active ); | void command_active ( bool active ); | ||||
int command_broadcast ( lo_message msg ); | |||||
int command_broadcast ( const char *path, lo_message msg ); | |||||
}; | }; |
@@ -123,11 +123,11 @@ namespace NSM | |||||
void | void | ||||
Client::broadcast ( const char *path, const char *v1 ) | |||||
Client::broadcast ( lo_message msg ) | |||||
{ | { | ||||
if ( nsm_is_active ) | if ( nsm_is_active ) | ||||
{ | { | ||||
lo_send_from( nsm_addr, _server, LO_TT_IMMEDIATE, "/nsm/server/broadcast", "ss", path, v1 ); | |||||
lo_send_message_from( nsm_addr, _server, "/nsm/server/broadcast", msg ); | |||||
} | } | ||||
} | } | ||||
@@ -164,7 +164,7 @@ namespace NSM | |||||
lo_server_add_method( _server, "/nsm/client/open", "sss", &Client::osc_open, this ); | lo_server_add_method( _server, "/nsm/client/open", "sss", &Client::osc_open, this ); | ||||
lo_server_add_method( _server, "/nsm/client/save", "", &Client::osc_save, this ); | lo_server_add_method( _server, "/nsm/client/save", "", &Client::osc_save, this ); | ||||
lo_server_add_method( _server, "/nsm/client/session_is_loaded", "", &Client::osc_session_is_loaded, this ); | lo_server_add_method( _server, "/nsm/client/session_is_loaded", "", &Client::osc_session_is_loaded, this ); | ||||
lo_server_add_method( _server, "/nsm/client/broadcast", NULL, &Client::osc_broadcast, this ); | |||||
lo_server_add_method( _server, NULL, NULL, &Client::osc_broadcast, this ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -183,7 +183,7 @@ namespace NSM | |||||
lo_server_thread_add_method( _st, "/nsm/client/open", "sss", &Client::osc_open, this ); | lo_server_thread_add_method( _st, "/nsm/client/open", "sss", &Client::osc_open, this ); | ||||
lo_server_thread_add_method( _st, "/nsm/client/save", "", &Client::osc_save, this ); | lo_server_thread_add_method( _st, "/nsm/client/save", "", &Client::osc_save, this ); | ||||
lo_server_thread_add_method( _st, "/nsm/client/session_is_loaded", "", &Client::osc_session_is_loaded, this ); | lo_server_thread_add_method( _st, "/nsm/client/session_is_loaded", "", &Client::osc_session_is_loaded, this ); | ||||
lo_server_thread_add_method( _st, "/nsm/client/broadcast", NULL, &Client::osc_broadcast, this ); | |||||
lo_server_thread_add_method( _st, NULL, NULL, &Client::osc_broadcast, this ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -195,7 +195,7 @@ namespace NSM | |||||
int | int | ||||
Client::osc_broadcast ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | Client::osc_broadcast ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | ||||
{ | { | ||||
return ((NSM::Client*)user_data)->command_broadcast( msg ); | |||||
return ((NSM::Client*)user_data)->command_broadcast( path, msg ); | |||||
} | } | ||||
int | int | ||||
@@ -66,7 +66,7 @@ namespace NSM | |||||
void message( int priority, const char *msg ); | void message( int priority, const char *msg ); | ||||
void announce ( const char *nsm_url, const char *appliction_name, const char *capabilities, const char *process_name ); | void announce ( const char *nsm_url, const char *appliction_name, const char *capabilities, const char *process_name ); | ||||
void broadcast ( const char *path, const char *v1 ); | |||||
void broadcast ( lo_message msg ); | |||||
/* init without threading */ | /* init without threading */ | ||||
int init ( void ); | int init ( void ); | ||||
@@ -91,7 +91,7 @@ namespace NSM | |||||
virtual void command_session_is_loaded ( void ) { } | virtual void command_session_is_loaded ( void ) { } | ||||
/* invoked when an unrecognized message is received. Should return 0 if you handled it, -1 otherwise. */ | /* invoked when an unrecognized message is received. Should return 0 if you handled it, -1 otherwise. */ | ||||
virtual int command_broadcast ( lo_message msg ) { return -1; } | |||||
virtual int command_broadcast ( const char *path, lo_message msg ) { return -1; } | |||||
private: | private: | ||||
@@ -31,7 +31,51 @@ | |||||
namespace OSC | namespace OSC | ||||
{ | { | ||||
/**********/ | |||||
/* Method */ | |||||
/**********/ | |||||
Method::Method ( ) | |||||
{ | |||||
_path = _typespec = _documentation = 0; | |||||
} | |||||
Method::~Method ( ) | |||||
{ | |||||
if ( _path ) | |||||
free( _path ); | |||||
if ( _typespec ) | |||||
free( _typespec ); | |||||
} | |||||
/**********/ | |||||
/* Signal */ | |||||
/**********/ | |||||
int Signal::next_id = 0; | |||||
void | |||||
Signal::value ( float f ) | |||||
{ | |||||
for ( std::list<Target*>::const_iterator i = _outgoing.begin(); | |||||
i != _outgoing.end(); | |||||
++i ) | |||||
{ | |||||
// DMESSAGE( "Sending signal value %i %f", (*i)->signal_id, f ); | |||||
if ( (*i)->value != f ) | |||||
{ | |||||
(*i)->value = f; | |||||
_endpoint->send( (*i)->peer->addr, | |||||
"/signal/change", | |||||
(*i)->signal_id, | |||||
f ); | |||||
} | |||||
} | |||||
} | |||||
void | void | ||||
Endpoint::error_handler(int num, const char *msg, const char *path) | Endpoint::error_handler(int num, const char *msg, const char *path) | ||||
{ | { | ||||
@@ -55,13 +99,10 @@ namespace OSC | |||||
return -1; | return -1; | ||||
} | } | ||||
// char *url = lo_server_get_url(_server); | |||||
// printf("OSC: %s\n",url); | |||||
// free(url); | |||||
// add generic handler for path reporting. | |||||
add_method( "/osc/query/parameters", "s", osc_query_parameters, this, "" ); | |||||
add_method( "/signal/change", "if", &Endpoint::osc_sig_handler, this, "" ); | |||||
add_method( NULL, "", &Endpoint::osc_generic, this, "" ); | add_method( NULL, "", &Endpoint::osc_generic, this, "" ); | ||||
add_method( NULL, NULL, &Endpoint::osc_signal_lister, this, "" ); | |||||
add_method( "/reply", NULL, &Endpoint::osc_reply, this, "" ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -73,62 +114,97 @@ namespace OSC | |||||
lo_server_free( _server ); | lo_server_free( _server ); | ||||
} | } | ||||
int | |||||
Endpoint::osc_query_parameters ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
OSC::Target * | |||||
Endpoint::find_target_by_peer_address ( std::list<Target*> *l, lo_address addr ) | |||||
{ | { | ||||
OSC_DMSG(); | |||||
Endpoint *ep = (Endpoint*)user_data; | |||||
const char *qpath = &argv[0]->s; | |||||
Method_Data *md = NULL; | |||||
for ( std::list<Method_Data *>::iterator i = ep->_methods.begin(); i != ep->_methods.end(); ++i ) | |||||
for ( std::list<Target*>::iterator i = l->begin(); | |||||
i != l->end(); | |||||
++i ) | |||||
{ | { | ||||
if ( ! (*i)->path ) | |||||
continue; | |||||
if ( ! strcmp( qpath, (*i)->path ) && (*i)->typespec ) | |||||
if ( address_matches( addr, (*i)->peer->addr ) ) | |||||
{ | { | ||||
md = *i; | |||||
/* FIXME: what about the fact that there could be multiple messages with the same path but | |||||
different typespecs ? */ | |||||
break; | |||||
return *i; | |||||
} | } | ||||
} | } | ||||
if ( ! md ) | |||||
return NULL; | |||||
} | |||||
OSC::Signal * | |||||
Endpoint::find_signal_by_id ( int id ) | |||||
{ | |||||
for ( std::list<Signal*>::iterator i = _signals.begin(); | |||||
i != _signals.end(); | |||||
++i ) | |||||
{ | { | ||||
ep->send( lo_message_get_source( msg ), "/error", path, | |||||
"Could not find specified path" ); | |||||
if ( (*i)->id() == id ) | |||||
return *i; | |||||
} | |||||
} | |||||
return 0; | |||||
int | |||||
Endpoint::osc_sig_handler ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
{ | |||||
Signal *o; | |||||
float f = 0.0; | |||||
if ( !strcmp( path, "/signal/change" ) && !strcmp( types, "if" ) ) | |||||
{ | |||||
/* accept a value for numbered signal */ | |||||
int id = argv[0]->i; | |||||
f = argv[1]->f; | |||||
o = ((Endpoint*)user_data)->find_signal_by_id( id ); | |||||
if ( ! o ) | |||||
{ | |||||
WARNING( "Unknown signal id %i", id ); | |||||
return 0; | |||||
} | |||||
} | |||||
else if ( ! strcmp( types, "f" ) ) | |||||
{ | |||||
/* accept a value for signal named in path */ | |||||
o = (Signal*)user_data; | |||||
f = argv[0]->f; | |||||
} | |||||
else if ( ! types || 0 == types[0] ) | |||||
{ | |||||
/* reply with current value */ | |||||
o = (Signal*)user_data; | |||||
o->_endpoint->send( lo_message_get_source( msg ), "/reply", path, o->value() ); | |||||
} | |||||
else | |||||
{ | |||||
return -1; | |||||
} | } | ||||
char *r = (char*) malloc( 256 ); | |||||
r[0] = 0; | |||||
Target *t = NULL; | |||||
for ( int i = 0; i < strlen( md->typespec ); ++i ) | |||||
if ( 0 == o->_incoming.size() || | |||||
! ( t = find_target_by_peer_address( &o->_incoming, lo_message_get_source( msg ) ) ) ) | |||||
{ | |||||
/* message came from an unconnected peer, just set the value exactly */ | |||||
} | |||||
else | |||||
{ | { | ||||
char desc[50]; | |||||
/* message is from a connected source, do mixing. */ | |||||
snprintf( desc, sizeof(desc), "f:%f:%f:%f\n", | |||||
md->parameter_limits[i].min, | |||||
md->parameter_limits[i].max, | |||||
md->parameter_limits[i].default_value ); | |||||
t->value = f; | |||||
r = (char*)realloc( r, strlen( r ) + strlen( desc ) + 2 ); | |||||
f = 0.0; | |||||
strcat( r, desc ); | |||||
strcat( r, "\n" ); | |||||
for ( std::list<Target*>::const_iterator i = o->_incoming.begin(); | |||||
i != o->_incoming.end(); | |||||
++i ) | |||||
{ | |||||
f += (*i)->value; | |||||
} | |||||
} | } | ||||
ep->send( lo_message_get_source( msg ), "/reply", path, | |||||
qpath, | |||||
r ); | |||||
o->_value = f; | |||||
o->_handler( f, o->_user_data ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -140,93 +216,269 @@ namespace OSC | |||||
if ( path[ strlen(path) - 1 ] != '/' ) | if ( path[ strlen(path) - 1 ] != '/' ) | ||||
return -1; | return -1; | ||||
char *paths = ((Endpoint*)user_data)->get_paths( path ); | |||||
Endpoint *ep = (Endpoint*)user_data; | |||||
((Endpoint*)user_data)->send( lo_message_get_source( msg ), "/reply", path, paths ); | |||||
for ( std::list<Method*>::const_iterator i = ep->_methods.begin(); i != ep->_methods.end(); ++i ) | |||||
{ | |||||
if ( ! (*i)->path() ) | |||||
continue; | |||||
free(paths); | |||||
if (! strncmp( (*i)->path(), path, strlen(path) ) ) | |||||
{ | |||||
/* asprintf( &stored_path, "%s (%s); %s", path, typespec, argument_description ); */ | |||||
((Endpoint*)user_data)->send( lo_message_get_source( msg ), "/reply", path, (*i)->path() ); | |||||
} | |||||
} | |||||
((Endpoint*)user_data)->send( lo_message_get_source( msg ), "/reply", path ); | |||||
return 0; | return 0; | ||||
} | } | ||||
// returns a malloc()'d string containing path names beginning with /prefix/, newline separated | |||||
char * | |||||
Endpoint::get_paths ( const char *prefix ) | |||||
int | |||||
Endpoint::osc_signal_lister ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
{ | { | ||||
char *r = (char*)malloc( 1024 ); | |||||
r[0] = 0; | |||||
for ( std::list<Method_Data*>::const_iterator i = _methods.begin(); i != _methods.end(); ++i ) | |||||
// OSC_DMSG(); | |||||
Signal::Direction dir; | |||||
if ( ! strcmp( path, "/signal/list_inputs" ) ) | |||||
{ | { | ||||
if ( ! (*i)->path ) | |||||
continue; | |||||
dir = Signal::Input; | |||||
} | |||||
else if ( ! strcmp( path, "/signal/list_outputs" ) ) | |||||
{ | |||||
dir = Signal::Output; | |||||
} | |||||
else | |||||
return -1; | |||||
const char *prefix = NULL; | |||||
if ( argc ) | |||||
prefix = &argv[0]->s; | |||||
if (! strncmp( (*i)->path, prefix, strlen(prefix) ) ) | |||||
Endpoint *ep = (Endpoint*)user_data; | |||||
for ( std::list<Signal*>::const_iterator i = ep->_signals.begin(); i != ep->_signals.end(); ++i ) | |||||
{ | |||||
Signal *o = *i; | |||||
if ( dir == Signal::Bidirectional || | |||||
dir == o->_direction ) | |||||
{ | { | ||||
r = (char*)realloc( r, strlen( r ) + strlen( (*i)->path ) + 2 ); | |||||
if ( ! prefix || ! strncmp( o->path(), prefix, strlen(prefix) ) ) | |||||
{ | |||||
ep->send( lo_message_get_source( msg ), | |||||
"/reply", | |||||
path, | |||||
o->path(), | |||||
o->id(), | |||||
o->parameter_limits().min, | |||||
o->parameter_limits().max, | |||||
o->parameter_limits().default_value | |||||
); | |||||
} | |||||
} | |||||
} | |||||
/* asprintf( &stored_path, "%s (%s); %s", path, typespec, argument_description ); */ | |||||
ep->send( lo_message_get_source( msg ), "/reply", path ); | |||||
return 0; | |||||
} | |||||
bool | |||||
Endpoint::address_matches ( lo_address addr1, lo_address addr2 ) | |||||
{ | |||||
char *purl = strdup( lo_address_get_port( addr1 ) ); | |||||
char *url = strdup( lo_address_get_port( addr2 ) ); | |||||
bool r = !strcmp( purl, url ); | |||||
free( purl ); | |||||
free( url ); | |||||
strcat( r, (*i)->path ); | |||||
strcat( r, "\n" ); | |||||
return r; | |||||
} | |||||
void | |||||
Endpoint::list_peers ( void (*callback) (const char *, const char *, int, void * ), void *v ) | |||||
{ | |||||
for ( std::list<Peer*>::iterator i = _peers.begin(); | |||||
i != _peers.end(); | |||||
++i ) | |||||
{ | |||||
for ( std::list<Signal*>::iterator j = (*i)->_signals.begin(); | |||||
j != (*i)->_signals.end(); | |||||
++j ) | |||||
{ | |||||
// DMESSAGE( "Running callback" ); | |||||
callback( (*i)->name, (*j)->path(), (*j)->id(), v ); | |||||
} | } | ||||
} | } | ||||
} | |||||
return r; | |||||
Peer * | |||||
Endpoint::find_peer_by_address ( lo_address addr ) | |||||
{ | |||||
char *url = strdup( lo_address_get_port( addr ) ); | |||||
Peer *p = NULL; | |||||
for ( std::list<Peer*>::iterator i = _peers.begin(); | |||||
i != _peers.end(); | |||||
++i ) | |||||
{ | |||||
char *purl = strdup( lo_address_get_port( (*i)->addr ) ); | |||||
if ( !strcmp( purl, url ) ) | |||||
{ | |||||
free( purl ); | |||||
p = *i; | |||||
break; | |||||
} | |||||
free(purl); | |||||
} | |||||
free( url ); | |||||
return p; | |||||
} | } | ||||
void | |||||
Endpoint::set_parameter_limits ( const char *path, const char *typespec, | |||||
int index, | |||||
float min, float max, float default_value ) | |||||
Peer * | |||||
Endpoint::find_peer_by_name ( const char *name ) | |||||
{ | { | ||||
assert( typespec ); | |||||
for ( std::list<Peer*>::iterator i = _peers.begin(); | |||||
i != _peers.end(); | |||||
++i ) | |||||
{ | |||||
if ( !strcmp( name, (*i)->name ) ) | |||||
{ | |||||
return *i; | |||||
} | |||||
} | |||||
assert( index < strlen( typespec ) ); | |||||
return NULL; | |||||
} | |||||
for ( std::list<Method_Data *>::iterator i = _methods.begin(); i != _methods.end(); ++i ) | |||||
/* First part of 'to' is a peer name */ | |||||
bool | |||||
Endpoint::connect_signal( OSC::Signal *s, const char *peer_name, int signal_id ) | |||||
{ | |||||
if ( s->_direction == Signal::Output ) | |||||
{ | { | ||||
if ( ! (*i)->path ) | |||||
continue; | |||||
Peer *p = find_peer_by_name( peer_name ); | |||||
if ( ! strcmp( path, (*i)->path ) && | |||||
! strcmp( typespec, (*i)->typespec ) ) | |||||
MESSAGE( "Connecting signal output \"%s\" to %s:%i", s->path(), peer_name, signal_id ); | |||||
if ( p ) | |||||
{ | { | ||||
(*i)->parameter_limits[index].min = min; | |||||
(*i)->parameter_limits[index].max = max; | |||||
(*i)->parameter_limits[index].default_value = default_value; | |||||
Target *t = new Target(); | |||||
t->peer = p; | |||||
t->signal_id = signal_id; | |||||
s->_outgoing.push_back( t ); | |||||
send( p->addr, "/signal/connect", | |||||
0, /* FIXME: our signal id */ | |||||
0 /* FIXME: their signal id */ ); | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
break; | |||||
int | |||||
Endpoint::osc_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
{ | |||||
Endpoint *ep = (Endpoint*)user_data; | |||||
if ( argc && !strcmp( &argv[0]->s, "/signal/list_inputs" ) ) | |||||
{ | |||||
Peer *p = ep->find_peer_by_address( lo_message_get_source( msg ) ); | |||||
if ( ! p ) | |||||
{ | |||||
WARNING( "Got input list reply from unknown peer." ); | |||||
return 0; | |||||
} | |||||
if ( argc == 1 ) | |||||
{ | |||||
p->_scanning = false; | |||||
DMESSAGE( "Done scanning %s", p->name ); | |||||
} | } | ||||
else if ( p->_scanning ) | |||||
{ | |||||
DMESSAGE( "Peer %s has signal %s", p->name, &argv[1]->s ); | |||||
Signal *s = new Signal( &argv[1]->s, Signal::Input ); | |||||
s->_id = argv[2]->i; | |||||
s->parameter_limits().min = argv[3]->f; | |||||
s->parameter_limits().max = argv[4]->f; | |||||
s->parameter_limits().default_value = argv[4]->f; | |||||
p->_signals.push_back( s ); | |||||
} | |||||
return 0; | |||||
} | } | ||||
else | |||||
return -1; | |||||
} | } | ||||
method_handle | |||||
Method * | |||||
Endpoint::add_method ( const char *path, const char *typespec, lo_method_handler handler, void *user_data, const char *argument_description ) | Endpoint::add_method ( const char *path, const char *typespec, lo_method_handler handler, void *user_data, const char *argument_description ) | ||||
{ | { | ||||
// DMESSAGE( "Added OSC method %s (%s)", path, typespec ); | // DMESSAGE( "Added OSC method %s (%s)", path, typespec ); | ||||
lo_server_add_method( _server, path, typespec, handler, user_data ); | lo_server_add_method( _server, path, typespec, handler, user_data ); | ||||
Method_Data *md = new Method_Data; | |||||
Method *md = new Method; | |||||
if ( path ) | if ( path ) | ||||
md->path = strdup( path ); | |||||
md->_path = strdup( path ); | |||||
if ( typespec ) | if ( typespec ) | ||||
md->typespec = strdup( typespec ); | |||||
md->_typespec = strdup( typespec ); | |||||
if ( argument_description ) | if ( argument_description ) | ||||
md->documentation = strdup( argument_description ); | |||||
md->_documentation = strdup( argument_description ); | |||||
if ( typespec ) | if ( typespec ) | ||||
md->parameter_limits = new Parameter_Limits[strlen(typespec)]; | |||||
md->_parameter_limits = new Parameter_Limits[strlen(typespec)]; | |||||
_methods.push_back( md ); | _methods.push_back( md ); | ||||
return md; | return md; | ||||
/* asprintf( &stored_path, "%s (%s); %s", path, typespec, argument_description ); */ | |||||
} | |||||
Signal * | |||||
Endpoint::add_signal ( const char *path, Signal::Direction dir, signal_handler handler, void *user_data ) | |||||
{ | |||||
Signal *md = new Signal( path, dir ); | |||||
/* _path_names.push_back( stored_path ); */ | |||||
if ( path ) | |||||
md->_path = strdup( path ); | |||||
md->_handler = handler; | |||||
md->_user_data = user_data; | |||||
md->_endpoint = this; | |||||
_signals.push_back( md ); | |||||
if ( dir == Signal::Input ) | |||||
{ | |||||
lo_server_add_method( _server, path, NULL, osc_sig_handler, md ); | |||||
} | |||||
return md; | |||||
} | } | ||||
void | void | ||||
@@ -236,13 +488,13 @@ namespace OSC | |||||
lo_server_del_method( _server, path, typespec ); | lo_server_del_method( _server, path, typespec ); | ||||
for ( std::list<Method_Data *>::iterator i = _methods.begin(); i != _methods.end(); ++i ) | |||||
for ( std::list<Method *>::iterator i = _methods.begin(); i != _methods.end(); ++i ) | |||||
{ | { | ||||
if ( ! (*i)->path ) | |||||
if ( ! (*i)->path() ) | |||||
continue; | continue; | ||||
if ( ! strcmp( path, (*i)->path ) && | |||||
! strcmp( typespec, (*i)->typespec ) ) | |||||
if ( ! strcmp( path, (*i)->path() ) && | |||||
! strcmp( typespec, (*i)->typespec() ) ) | |||||
{ | { | ||||
delete *i; | delete *i; | ||||
i = _methods.erase( i ); | i = _methods.erase( i ); | ||||
@@ -253,36 +505,42 @@ namespace OSC | |||||
} | } | ||||
void | void | ||||
Endpoint::del_method ( const method_handle mh ) | |||||
Endpoint::del_method ( Method *meth ) | |||||
{ | { | ||||
// DMESSAGE( "Deleted OSC method %s (%s)", path, typespec ); | // DMESSAGE( "Deleted OSC method %s (%s)", path, typespec ); | ||||
Method_Data *meth = const_cast<Method_Data*>( (const Method_Data*)mh ); | |||||
lo_server_del_method( _server, meth->path, meth->typespec ); | |||||
lo_server_del_method( _server, meth->path(), meth->typespec() ); | |||||
delete meth; | delete meth; | ||||
_methods.remove( meth ); | _methods.remove( meth ); | ||||
} | |||||
void | |||||
Endpoint::del_signal ( Signal *o ) | |||||
{ | |||||
// DMESSAGE( "Deleted OSC method %s (%s)", path, typespec ); | |||||
/* for ( std::list<Method_Data *>::iterator i = _methods.begin(); i != _methods.end(); ++i ) */ | |||||
/* { */ | |||||
/* if ( ! (*i)->path ) */ | |||||
/* continue; */ | |||||
lo_server_del_method( _server, o->path(), "f" ); | |||||
/* if ( ! strcmp( path, (*i)->path ) && */ | |||||
/* ! strcmp( typespec, (*i)->typespec ) ) */ | |||||
/* { */ | |||||
/* delete *i; */ | |||||
/* i = _methods.erase( i ); */ | |||||
delete o; | |||||
/* break; */ | |||||
/* } */ | |||||
/* } */ | |||||
_signals.remove( o ); | |||||
} | } | ||||
void | |||||
Endpoint::scan_peer ( const char *name, const char *url ) | |||||
{ | |||||
Peer *p = new Peer; | |||||
p->name = strdup( name ); | |||||
p->addr = lo_address_new_from_url( url ); | |||||
p->_scanning = true; | |||||
_peers.push_back( p ); | |||||
send( p->addr, "/signal/list_inputs" ); | |||||
} | |||||
void * | void * | ||||
Endpoint::osc_thread ( void * arg ) | Endpoint::osc_thread ( void * arg ) | ||||
@@ -342,7 +600,7 @@ namespace OSC | |||||
/** Process any waiting events and return after timeout */ | /** Process any waiting events and return after timeout */ | ||||
void | void | ||||
Endpoint::wait ( int timeout ) const | Endpoint::wait ( int timeout ) const | ||||
{ | |||||
{ | |||||
if ( lo_server_wait( _server, timeout ) ) | if ( lo_server_wait( _server, timeout ) ) | ||||
while ( lo_server_recv_noblock( _server, 0 ) ) { } | while ( lo_server_recv_noblock( _server, 0 ) ) { } | ||||
} | } | ||||
@@ -372,15 +630,15 @@ namespace OSC | |||||
switch ( ov->type() ) | switch ( ov->type() ) | ||||
{ | { | ||||
case 'f': | case 'f': | ||||
// DMESSAGE( "Adding float %f", ((OSC_Float*)ov)->value() ); | |||||
DMESSAGE( "Adding float %f", ((OSC_Float*)ov)->value() ); | |||||
lo_message_add_float( m, ((OSC_Float*)ov)->value() ); | lo_message_add_float( m, ((OSC_Float*)ov)->value() ); | ||||
break; | break; | ||||
case 'i': | case 'i': | ||||
// DMESSAGE( "Adding int %i", ((OSC_Int*)ov)->value() ); | |||||
DMESSAGE( "Adding int %i", ((OSC_Int*)ov)->value() ); | |||||
lo_message_add_int32( m, ((OSC_Int*)ov)->value() ); | lo_message_add_int32( m, ((OSC_Int*)ov)->value() ); | ||||
break; | break; | ||||
case 's': | case 's': | ||||
// DMESSAGE( "Adding string %s", ((OSC_String*)ov)->value() ); | |||||
DMESSAGE( "Adding string %s", ((OSC_String*)ov)->value() ); | |||||
lo_message_add_string( m, ((OSC_String*)ov)->value() ); | lo_message_add_string( m, ((OSC_String*)ov)->value() ); | ||||
break; | break; | ||||
default: | default: | ||||
@@ -389,7 +647,7 @@ namespace OSC | |||||
} | } | ||||
} | } | ||||
// DMESSAGE( "Path: %s", path ); | |||||
DMESSAGE( "Path: %s", path ); | |||||
lo_bundle b = lo_bundle_new( LO_TT_IMMEDIATE ); | lo_bundle b = lo_bundle_new( LO_TT_IMMEDIATE ); | ||||
@@ -460,7 +718,7 @@ namespace OSC | |||||
int | int | ||||
Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, int v3, int v4, int v5 ) | Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, int v3, int v4, int v5 ) | ||||
{ | |||||
{ | |||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "ssiii", v1, v2, v3, v4, v5 ); | return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "ssiii", v1, v2, v3, v4, v5 ); | ||||
} | } | ||||
@@ -519,4 +777,27 @@ namespace OSC | |||||
return lo_send_message_from( to, _server, path, msg ); | return lo_send_message_from( to, _server, path, msg ); | ||||
} | } | ||||
int | |||||
Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, int v3, float v4, float v5, float v6 ) | |||||
{ | |||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "ssifff", v1, v2, v3, v4, v5, v6 ); | |||||
} | |||||
int | |||||
Endpoint::send ( lo_address to, const char *path, const char *v1, int v2, int v3 ) | |||||
{ | |||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sii", v1, v2, v3 ); | |||||
} | |||||
int | |||||
Endpoint::send ( lo_address to, const char *path, int v1, int v2 ) | |||||
{ | |||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "ii", v1, v2 ); | |||||
} | |||||
int | |||||
Endpoint::send ( lo_address to, const char *path, int v1, float v2 ) | |||||
{ | |||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "if", v1, v2 ); | |||||
} | |||||
} | } |
@@ -23,11 +23,10 @@ | |||||
#include "Thread.H" | #include "Thread.H" | ||||
#include <list> | #include <list> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | |||||
namespace OSC | namespace OSC | ||||
{ | { | ||||
typedef void * method_handle; | |||||
class OSC_Value | class OSC_Value | ||||
{ | { | ||||
@@ -107,83 +106,189 @@ namespace OSC | |||||
} | } | ||||
}; | }; | ||||
struct Parameter_Limits | |||||
{ | |||||
float min; | |||||
float max; | |||||
float default_value; | |||||
}; | |||||
class Endpoint; | |||||
class Signal; | |||||
struct Peer | |||||
{ | |||||
bool _scanning; | |||||
char *name; | |||||
lo_address addr; | |||||
std::list<Signal*> _signals; | |||||
}; | |||||
struct Target | |||||
{ | |||||
Peer *peer; | |||||
// char *path; | |||||
int signal_id; | |||||
float value; | |||||
}; | |||||
typedef int (*signal_handler) ( float value, void *user_data ); | |||||
class Signal | |||||
{ | |||||
static int next_id; | |||||
public: | |||||
enum Direction { | |||||
Input, | |||||
Output, | |||||
Bidirectional | |||||
}; | |||||
private: | |||||
Endpoint *_endpoint; | |||||
int _id; | |||||
char *_path; | |||||
char *_documentation; | |||||
float _value; | |||||
std::list<Target*> _outgoing; | |||||
Direction _direction; | |||||
/* FIXME: | |||||
In order to support signal mixing, the receiver must track | |||||
each connected signal channel separately, and add the | |||||
values together before invoking the handler. | |||||
*/ | |||||
std::list<Target*> _incoming; | |||||
signal_handler _handler; | |||||
void *_user_data; | |||||
public: | |||||
Parameter_Limits _parameter_limits; | |||||
Signal ( const char *path, Direction dir ) | |||||
{ | |||||
_direction = dir; | |||||
_path = strdup( path ); | |||||
_id = ++next_id; | |||||
_value = 0.0f; | |||||
} | |||||
~Signal ( ) | |||||
{ | |||||
free( _path ); | |||||
} | |||||
bool connected ( void ) { return _outgoing.size() + _incoming.size(); } | |||||
int id ( void ) { return _id; } | |||||
Direction direction ( void ) { return _direction; } | |||||
void parameter_limits ( float min, float max, float default_value ) | |||||
{ | |||||
_parameter_limits.min = min; | |||||
_parameter_limits.max = max; | |||||
_parameter_limits.default_value = default_value; | |||||
_value = default_value; | |||||
} | |||||
Parameter_Limits& parameter_limits ( void ) { return _parameter_limits; } | |||||
const char *path ( void ) { return _path; } | |||||
/* publishes value to targets */ | |||||
void value ( float v ); | |||||
/* get current value */ | |||||
float value ( void ) { return _value; } | |||||
friend class Endpoint; | |||||
}; | |||||
class Method | |||||
{ | |||||
char *_path; | |||||
char *_typespec; | |||||
char *_documentation; | |||||
struct Parameter_Limits *_parameter_limits; | |||||
public: | |||||
const char *path ( void ) { return _path; } | |||||
const char *typespec ( void ) { return _typespec; } | |||||
Method ( ); | |||||
~Method ( ); | |||||
friend class Endpoint; | |||||
}; | |||||
class Endpoint | class Endpoint | ||||
{ | { | ||||
static void error_handler(int num, const char *msg, const char *path); | static void error_handler(int num, const char *msg, const char *path); | ||||
static int osc_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
static int osc_signal_lister ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
static int osc_generic ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | static int osc_generic ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | ||||
static int osc_query_parameters ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
static int osc_sig_handler ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
Thread _thread; | Thread _thread; | ||||
// lo_server_thread _st; | // lo_server_thread _st; | ||||
lo_server _server; | lo_server _server; | ||||
struct Parameter_Limits | |||||
{ | |||||
float min; | |||||
float max; | |||||
float default_value; | |||||
}; | |||||
struct Method_Data | |||||
{ | |||||
char *path; | |||||
char *typespec; | |||||
char *documentation; | |||||
struct Parameter_Limits *parameter_limits; | |||||
std::list<lo_address> subscribers; | |||||
Method_Data ( ) | |||||
{ | |||||
path = typespec = documentation = 0; | |||||
parameter_limits = 0; | |||||
} | |||||
~Method_Data ( ) | |||||
{ | |||||
if ( path ) | |||||
free( path ); | |||||
if ( typespec ) | |||||
free( typespec ); | |||||
if ( parameter_limits ) | |||||
free( parameter_limits ); | |||||
for ( std::list<lo_address>::iterator i = subscribers.begin(); | |||||
i != subscribers.end(); | |||||
++i ) | |||||
{ | |||||
lo_address_free( *i ); | |||||
} | |||||
subscribers.clear(); | |||||
} | |||||
}; | |||||
std::list<Method_Data*> _methods; | |||||
std::list<Peer*> _peers; | |||||
std::list<Signal*> _signals; | |||||
std::list<Method*> _methods; | |||||
static void *osc_thread ( void *arg ); | static void *osc_thread ( void *arg ); | ||||
void osc_thread ( void ); | void osc_thread ( void ); | ||||
OSC::Signal *find_signal_by_id ( int id ); | |||||
Peer *find_peer_by_name ( const char *name ); | |||||
Peer *find_peer_by_address ( lo_address addr ); | |||||
static bool address_matches ( lo_address addr1, lo_address addr2 ); | |||||
static Target *find_target_by_peer_address ( std::list<Target*> *l, lo_address addr ); | |||||
public: | public: | ||||
void list_peers ( void (*callback) (const char *, const char *, int, void * ), void *v ); | |||||
int init ( const char *port = 0 ); | int init ( const char *port = 0 ); | ||||
Endpoint ( ); | Endpoint ( ); | ||||
~Endpoint ( ); | ~Endpoint ( ); | ||||
char *get_paths ( const char *prefix ); | |||||
method_handle add_method ( const char *path, const char *typespec, lo_method_handler handler, void *user_data, const char *argument_description ); | |||||
bool connect_signal( OSC::Signal *s, const char *peer_name, int signal_id ); | |||||
Signal *add_signal ( const char *path, Signal::Direction dir, signal_handler handler, void *user_data ); | |||||
Method *add_method ( const char *path, const char *typespec, lo_method_handler handler, void *user_data, const char *argument_description ); | |||||
void del_method ( const char *path, const char *typespec ); | void del_method ( const char *path, const char *typespec ); | ||||
void del_method ( const method_handle ); | |||||
void del_method ( Method* method ); | |||||
void del_signal ( Signal *signal ); | |||||
void start ( void ); | void start ( void ); | ||||
void stop ( void ); | void stop ( void ); | ||||
int port ( void ) const; | int port ( void ) const; | ||||
char * url ( void ) const; | char * url ( void ) const; | ||||
void set_parameter_limits ( const char *path, const char *typespec, int index, float min, float max, float default_value ); | |||||
void scan_peer ( const char *name, const char *url ); | |||||
void check ( void ) const; | void check ( void ) const; | ||||
void wait ( int timeout ) const; | void wait ( int timeout ) const; | ||||
@@ -197,8 +302,11 @@ namespace OSC | |||||
int send ( lo_address to, const char *path, double v ); | int send ( lo_address to, const char *path, double v ); | ||||
int send ( lo_address to, const char *path, int v ); | int send ( lo_address to, const char *path, int v ); | ||||
int send ( lo_address to, const char *path, long v ); | int send ( lo_address to, const char *path, long v ); | ||||
int send ( lo_address to, const char *path, const char * v1, float v2 ); | |||||
int send ( lo_address to, const char *path, int v1, int v2 ); | |||||
int send ( lo_address to, const char *path, int v1, float v2 ); | |||||
int send ( lo_address to, const char *path, const char *v ); | int send ( lo_address to, const char *path, const char *v ); | ||||
int send ( lo_address to, const char *path, const char *v1, float v2 ); | |||||
int send ( lo_address to, const char *path, const char *v1, int v2, int v3 ); | |||||
int send ( lo_address to, const char *path, const char *v1, const char *v2 ); | int send ( lo_address to, const char *path, const char *v1, const char *v2 ); | ||||
int send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3 ); | int send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3 ); | ||||
int send ( lo_address to, const char *path, const char *v1, int v2, int v3, int v4 ); | int send ( lo_address to, const char *path, const char *v1, int v2, int v3, int v4 ); | ||||
@@ -216,6 +324,8 @@ namespace OSC | |||||
int send ( lo_address to, const char *path, lo_message msg ); | int send ( lo_address to, const char *path, lo_message msg ); | ||||
int send ( lo_address to, const char *path, const char *v1, const char *v2, int v3, float v4, float v5, float v6 ); | |||||
// can be used to point back to owning object. | // can be used to point back to owning object. | ||||
void *owner; | void *owner; | ||||
}; | }; | ||||
@@ -349,8 +349,11 @@ set_name ( const char *name ) | |||||
bool | bool | ||||
address_matches ( lo_address addr1, lo_address addr2 ) | address_matches ( lo_address addr1, lo_address addr2 ) | ||||
{ | { | ||||
char *url1 = lo_address_get_url( addr1 ); | |||||
char *url2 = lo_address_get_url( addr2 ); | |||||
/* char *url1 = lo_address_get_url( addr1 ); */ | |||||
/* char *url2 = lo_address_get_url( addr2 ); */ | |||||
char *url1 = strdup( lo_address_get_port( addr1 ) ); | |||||
char *url2 = strdup(lo_address_get_port( addr2 ) ); | |||||
bool r = !strcmp( url1, url2 ); | bool r = !strcmp( url1, url2 ); | ||||
@@ -1464,7 +1467,7 @@ OSC_HANDLER( broadcast ) | |||||
if ( strcmp( sender_url, url ) ) | if ( strcmp( sender_url, url ) ) | ||||
{ | { | ||||
osc_server->send( (*i)->addr, "/nsm/client/broadcast", new_args ); | |||||
osc_server->send( (*i)->addr, to_path, new_args ); | |||||
} | } | ||||
free( url ); | free( url ); | ||||
@@ -30,6 +30,8 @@ | |||||
#include <list> | #include <list> | ||||
using std::list; | using std::list; | ||||
#include "Transport.H" | |||||
bool Control_Sequence::draw_with_gradient = true; | bool Control_Sequence::draw_with_gradient = true; | ||||
@@ -38,6 +40,8 @@ bool Control_Sequence::draw_with_grid = true; | |||||
const double OSC_INTERVAL = 1.0f / 20.0f; | |||||
Control_Sequence::Control_Sequence ( Track *track ) : Sequence( 0 ) | Control_Sequence::Control_Sequence ( Track *track ) : Sequence( 0 ) | ||||
{ | { | ||||
init(); | init(); | ||||
@@ -51,10 +55,20 @@ Control_Sequence::Control_Sequence ( Track *track ) : Sequence( 0 ) | |||||
FATAL( "could not create JACK port" ); | FATAL( "could not create JACK port" ); | ||||
} | } | ||||
{ | |||||
char *path; | |||||
asprintf( &path, "/non/daw/%s/control/%i", track->name(), track->ncontrols() ); | |||||
_osc_output = timeline->osc->add_signal( path, OSC::Signal::Output, NULL, NULL ); | |||||
free( path ); | |||||
} | |||||
if ( track ) | if ( track ) | ||||
track->add( this ); | track->add( this ); | ||||
log_create(); | log_create(); | ||||
} | } | ||||
@@ -88,8 +102,10 @@ Control_Sequence::init ( void ) | |||||
_track = NULL; | _track = NULL; | ||||
_highlighted = false; | _highlighted = false; | ||||
_output = NULL; | _output = NULL; | ||||
_osc_output = NULL; | |||||
color( fl_darker( FL_YELLOW ) ); | color( fl_darker( FL_YELLOW ) ); | ||||
Fl::add_timeout( OSC_INTERVAL, &Control_Sequence::process_osc, this ); | |||||
} | } | ||||
@@ -282,6 +298,69 @@ Control_Sequence::draw ( void ) | |||||
fl_pop_clip(); | fl_pop_clip(); | ||||
} | } | ||||
#include "FL/menu_popup.H" | |||||
void | |||||
Control_Sequence::menu_cb ( Fl_Widget *w, void *v ) | |||||
{ | |||||
((Control_Sequence*)v)->menu_cb( (const Fl_Menu_*)w ); | |||||
} | |||||
void | |||||
Control_Sequence::menu_cb ( const Fl_Menu_ *m ) | |||||
{ | |||||
char picked[1024]; | |||||
if ( ! m->mvalue() ) // || m->mvalue()->flags & FL_SUBMENU_POINTER || m->mvalue()->flags & FL_SUBMENU ) | |||||
return; | |||||
m->item_pathname( picked, sizeof( picked ), m->mvalue() ); | |||||
// DMESSAGE( "Picked: %s (%s)", picked, m->mvalue()->label() ); | |||||
if ( ! _osc_output ) | |||||
{ | |||||
char *path; | |||||
asprintf( &path, "/non/daw/%s/control/%i", track()->name(), track()->ncontrols() ); | |||||
_osc_output = timeline->osc->add_signal( path, OSC::Signal::Output, NULL, NULL ); | |||||
free( path ); | |||||
} | |||||
/* FIXME: somebody has to free these unsigned longs */ | |||||
unsigned long id = *(unsigned long*)m->mvalue()->user_data(); | |||||
char *peer_name = index( picked, '/' ) + 1; | |||||
*index( peer_name, '/' ) = 0; | |||||
timeline->osc->connect_signal( _osc_output, peer_name, id ); | |||||
} | |||||
void | |||||
Control_Sequence::process_osc ( void *v ) | |||||
{ | |||||
((Control_Sequence*)v)->process_osc(); | |||||
} | |||||
void | |||||
Control_Sequence::process_osc ( void ) | |||||
{ | |||||
Fl::add_timeout( OSC_INTERVAL, &Control_Sequence::process_osc, this ); | |||||
if ( _osc_output && _osc_output->connected() ) | |||||
{ | |||||
sample_t buf[1]; | |||||
play( buf, (nframes_t)transport->frame, (nframes_t) 1 ); | |||||
_osc_output->value( (float)buf[0] ); | |||||
} | |||||
} | |||||
int | int | ||||
Control_Sequence::handle ( int m ) | Control_Sequence::handle ( int m ) | ||||
{ | { | ||||
@@ -319,33 +398,55 @@ Control_Sequence::handle ( int m ) | |||||
} | } | ||||
else if ( Fl::event_button3() && ! ( Fl::event_state() & ( FL_ALT | FL_SHIFT | FL_CTRL ) ) ) | else if ( Fl::event_button3() && ! ( Fl::event_state() & ( FL_ALT | FL_SHIFT | FL_CTRL ) ) ) | ||||
{ | { | ||||
timeline->discover_peers(); | |||||
Fl_Menu_Item menu[] = | |||||
{ | |||||
{ "Rename" }, | |||||
{ "Remove" }, | |||||
{ 0 } | |||||
}; | |||||
Fl_Menu_Button menu( 0, 0, 0, 0, "Control Sequence" ); | |||||
const Fl_Menu_Item *r = menu->popup( Fl::event_x(), Fl::event_y(), "Control Sequence" ); | |||||
/* Fl_Menu_Button *con = new Fl_Menu_Button( 0, 0, 0, 0 ); */ | |||||
if ( r ) | |||||
{ | |||||
if ( r == &menu[ 0 ] ) | |||||
{ | |||||
const char *s = fl_input( "Input new name for control sequence:", name() ); | |||||
// con->callback( &Control_Sequence::menu_cb, (void*)this ); | |||||
if ( s ) | |||||
name( s ); | |||||
menu.clear(); | |||||
redraw(); | |||||
} | |||||
else if ( r == &menu[ 1 ] ) | |||||
{ | |||||
Fl::delete_widget( this ); | |||||
} | |||||
timeline->add_osc_peers_to_menu( &menu, "Connect To" ); | |||||
/* menu.add( "Connect To", 0, 0, 0); */ | |||||
/* menu.add( "Connect To", 0, 0, const_cast< Fl_Menu_Item *>( con->menu() ), FL_SUBMENU_POINTER ); */ | |||||
menu.add( "Rename", 0, 0, 0 ); | |||||
menu.add( "Remove", 0, 0, 0 ); | |||||
} | |||||
menu.callback( &Control_Sequence::menu_cb, (void*)this); | |||||
/* Fl_Menu_Item menu[] = */ | |||||
/* { */ | |||||
/* { "Rename" }, */ | |||||
/* { "Remove" }, */ | |||||
/* { "Connect To" }, */ | |||||
/* { 0 } */ | |||||
/* }; */ | |||||
menu_popup( &menu, x(), y() ); | |||||
// const Fl_Menu_Item *r = menu.popup( Fl::event_x(), Fl::event_y(), "Control Sequence" ); | |||||
/* if ( r ) */ | |||||
/* { */ | |||||
/* if ( r == &menu[ 0 ] ) */ | |||||
/* { */ | |||||
/* const char *s = fl_input( "Input new name for control sequence:", name() ); */ | |||||
/* if ( s ) */ | |||||
/* name( s ); */ | |||||
/* redraw(); */ | |||||
/* } */ | |||||
/* else if ( r == &menu[ 1 ] ) */ | |||||
/* { */ | |||||
/* Fl::delete_widget( this ); */ | |||||
/* } */ | |||||
/* } */ | |||||
return 1; | return 1; | ||||
} | } | ||||
@@ -26,6 +26,7 @@ | |||||
#include "JACK/Port.H" | #include "JACK/Port.H" | ||||
// class JACK::Port; | // class JACK::Port; | ||||
#include "OSC/Endpoint.H" | |||||
class Control_Sequence : public Sequence | class Control_Sequence : public Sequence | ||||
{ | { | ||||
@@ -41,6 +42,8 @@ private: | |||||
JACK::Port *_output; | JACK::Port *_output; | ||||
OSC::Signal *_osc_output; | |||||
bool _highlighted; | bool _highlighted; | ||||
curve_type_e _type; | curve_type_e _type; | ||||
@@ -49,6 +52,13 @@ private: | |||||
void draw_curve ( bool flip, bool filled ); | void draw_curve ( bool flip, bool filled ); | ||||
static void menu_cb ( Fl_Widget *w, void *v ); | |||||
void menu_cb ( const Fl_Menu_ *m ); | |||||
static void process_osc ( void *v ); | |||||
void process_osc ( void ); | |||||
protected: | protected: | ||||
@@ -54,7 +54,7 @@ sigmoid_interpolate ( float y1, float y2, float mu ) | |||||
nframes_t | nframes_t | ||||
Control_Sequence::play ( sample_t *buf, nframes_t frame, nframes_t nframes ) | Control_Sequence::play ( sample_t *buf, nframes_t frame, nframes_t nframes ) | ||||
{ | { | ||||
THREAD_ASSERT( RT ); | |||||
// THREAD_ASSERT( RT ); | |||||
Control_Point *p2, *p1 = (Control_Point*)&_widgets.front(); | Control_Point *p2, *p1 = (Control_Point*)&_widgets.front(); | ||||
@@ -91,12 +91,12 @@ NSM_Client::command_session_is_loaded ( void ) | |||||
} | } | ||||
int | int | ||||
NSM_Client::command_broadcast ( lo_message msg ) | |||||
NSM_Client::command_broadcast ( const char *path, lo_message msg ) | |||||
{ | { | ||||
int argc = lo_message_get_argc( msg ); | int argc = lo_message_get_argc( msg ); | ||||
lo_arg **argv = lo_message_get_argv( msg ); | lo_arg **argv = lo_message_get_argv( msg ); | ||||
if ( argc > 1 && !strcmp( &argv[0]->s, "/non/finger" ) ) | |||||
if ( argc == 1 && !strcmp( path, "/non/finger" ) ) | |||||
{ | { | ||||
timeline->reply_to_finger( msg ); | timeline->reply_to_finger( msg ); | ||||
return 0; | return 0; | ||||
@@ -35,5 +35,5 @@ protected: | |||||
int command_save ( char **out_msg ); | int command_save ( char **out_msg ); | ||||
void command_session_is_loaded ( void ); | void command_session_is_loaded ( void ); | ||||
int command_broadcast ( lo_message msg ); | |||||
int command_broadcast ( const char *path, lo_message msg ); | |||||
}; | }; |
@@ -1517,6 +1517,8 @@ Timeline::command_load ( const char *name, const char *display_name ) | |||||
Project::set_name ( display_name ? display_name : name ); | Project::set_name ( display_name ? display_name : name ); | ||||
discover_peers(); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -1539,6 +1541,9 @@ Timeline::command_new ( const char *name, const char *display_name ) | |||||
/* tle->update_menu(); */ | /* tle->update_menu(); */ | ||||
/* tle->main_window->redraw(); */ | /* tle->main_window->redraw(); */ | ||||
discover_peers(); | |||||
} | } | ||||
const char * | const char * | ||||
@@ -1573,7 +1578,7 @@ Timeline::init_osc ( const char *osc_port ) | |||||
printf( "OSC=%s\n", osc->url() ); | printf( "OSC=%s\n", osc->url() ); | ||||
osc->add_method( "/reply", NULL, &Timeline::osc_reply, osc, "" ); | |||||
osc->add_method( "/non/hello", "ssss", &Timeline::osc_non_hello, osc, "" ); | |||||
// osc->start(); | // osc->start(); | ||||
@@ -1584,19 +1589,23 @@ Timeline::init_osc ( const char *osc_port ) | |||||
} | } | ||||
int | int | ||||
Timeline::osc_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
Timeline::osc_non_hello ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) | |||||
{ | { | ||||
OSC_DMSG(); | OSC_DMSG(); | ||||
if ( argc >= 5 && !strcmp( &argv[0]->s, "/non/finger" ) ) | |||||
if ( argc >= 4 ) | |||||
{ | { | ||||
const char *url = &argv[1]->s; | |||||
const char *name = &argv[2]->s; | |||||
const char *version = &argv[3]->s; | |||||
const char *id = &argv[4]->s; | |||||
const char *url = &argv[0]->s; | |||||
const char *name = &argv[1]->s; | |||||
const char *version = &argv[2]->s; | |||||
const char *id = &argv[3]->s; | |||||
MESSAGE( "Discovered OSC peer %s (%s) @ %s with ID \"%s\"", name, version, url, id ); | MESSAGE( "Discovered OSC peer %s (%s) @ %s with ID \"%s\"", name, version, url, id ); | ||||
MESSAGE( "Scanning..." ); | |||||
timeline->osc->scan_peer( id, url ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -1609,14 +1618,13 @@ Timeline::reply_to_finger ( lo_message msg ) | |||||
int argc = lo_message_get_argc( msg ); | int argc = lo_message_get_argc( msg ); | ||||
lo_arg **argv = lo_message_get_argv( msg ); | lo_arg **argv = lo_message_get_argv( msg ); | ||||
if ( argc < 2 ) | |||||
if ( argc < 1 ) | |||||
return; | return; | ||||
lo_address reply = lo_address_new_from_url( &argv[1]->s ); | |||||
lo_address reply = lo_address_new_from_url( &argv[0]->s ); | |||||
osc->send( reply, | osc->send( reply, | ||||
"/reply", | |||||
"/non/finger", | |||||
"/non/hello", | |||||
osc->url(), | osc->url(), | ||||
APP_NAME, | APP_NAME, | ||||
VERSION, | VERSION, | ||||
@@ -1625,10 +1633,49 @@ Timeline::reply_to_finger ( lo_message msg ) | |||||
lo_address_free( reply ); | lo_address_free( reply ); | ||||
} | } | ||||
void | void | ||||
Timeline::discover_peers ( void ) | Timeline::discover_peers ( void ) | ||||
{ | { | ||||
nsm->broadcast( "/non/finger", osc->url() ); | |||||
lo_message m = lo_message_new(); | |||||
lo_message_add_string( m, "/non/finger" ); | |||||
lo_message_add_string( m, osc->url() ); | |||||
nsm->broadcast( m ); | |||||
lo_message_free( m ); | |||||
} | |||||
void | |||||
Timeline::peer_callback( const char *name, const char *path, int id, void *v ) | |||||
{ | |||||
((Timeline*)v)->peer_callback2( name, path, id ); | |||||
} | |||||
static Fl_Menu_Button *peer_menu; | |||||
static const char *peer_prefix; | |||||
void | |||||
Timeline::peer_callback2( const char *name, const char *path, int id ) | |||||
{ | |||||
char *s; | |||||
asprintf( &s, "%s/%s/%s", peer_prefix, name, path ); | |||||
/* FIXME: Somebody has to free these unsigned longs! */ | |||||
peer_menu->add( s, 0, NULL, new unsigned long( id ) ); | |||||
free( s ); | |||||
} | |||||
void | |||||
Timeline::add_osc_peers_to_menu ( Fl_Menu_Button *m, const char *prefix ) | |||||
{ | |||||
peer_menu = m; | |||||
peer_prefix = prefix; | |||||
osc->list_peers( &Timeline::peer_callback, this ); | |||||
} | } | ||||
@@ -87,8 +87,7 @@ class Timeline : public Fl_Single_Window, public RWLock | |||||
{ | { | ||||
static void draw_clip ( void * v, int X, int Y, int W, int H ); | static void draw_clip ( void * v, int X, int Y, int W, int H ); | ||||
OSC::Endpoint *osc; | |||||
int _old_xposition; | int _old_xposition; | ||||
int _old_yposition; | int _old_yposition; | ||||
@@ -120,6 +119,8 @@ class Timeline : public Fl_Single_Window, public RWLock | |||||
public: | public: | ||||
OSC::Endpoint *osc; | |||||
#undef Bars | #undef Bars | ||||
#undef Beats | #undef Beats | ||||
#undef None | #undef None | ||||
@@ -232,6 +233,12 @@ public: | |||||
/* OSC */ | /* OSC */ | ||||
static void peer_callback( const char *name, const char *path, int id, void *v ); | |||||
void peer_callback2( const char *name, const char *path, int id ); | |||||
void add_osc_peers_to_menu ( Fl_Menu_Button *m, const char *prefix ); | |||||
void discover_peers ( void ); | void discover_peers ( void ); | ||||
static void check_osc ( void * v ); | static void check_osc ( void * v ); | ||||
@@ -239,7 +246,9 @@ public: | |||||
int init_osc ( const char *osc_port ); | int init_osc ( const char *osc_port ); | ||||
static int osc_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | static int osc_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | ||||
static int osc_non_hello ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ); | |||||
void reply_to_finger ( lo_message msg ); | void reply_to_finger ( lo_message msg ); | ||||
private: | private: | ||||