From 6adf6a9389de725e2992c77f7945e54a98040238 Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Wed, 5 Jun 2013 16:26:36 -0700 Subject: [PATCH] OSC: Simplify OSC signal protocol. Add midi<->osc gateway program. --- FL/Fl_Text_Edit_Window.fl | 20 +- mixer/src/Chain.C | 6 + mixer/src/Chain.H | 2 + mixer/src/Controller_Module.C | 25 +- mixer/src/Controller_Module.H | 1 + mixer/src/JACK_Module.C | 10 +- mixer/src/Mixer.C | 195 +++++-- mixer/src/Mixer.H | 13 +- mixer/src/Mixer_Strip.C | 7 + mixer/src/Mixer_Strip.H | 1 + mixer/src/Module.C | 71 ++- mixer/src/Module.H | 28 +- mixer/src/Module_Parameter_Editor.C | 6 +- mixer/src/midi-to-osc.C | 768 ++++++++++++++++++++++++++++ mixer/wscript | 7 + nonlib/JACK/Client.H | 3 +- nonlib/JACK/Port.C | 36 +- nonlib/JACK/Port.H | 17 +- nonlib/MIDI/midievent.C | 215 ++++++++ nonlib/MIDI/midievent.H | 243 +++++++++ nonlib/MIDI/types.h | 28 + nonlib/OSC/Endpoint.C | 701 ++++++++++++------------- nonlib/OSC/Endpoint.H | 38 +- nonlib/wscript | 1 + timeline/src/Control_Sequence.C | 74 +-- timeline/src/Control_Sequence.H | 1 + timeline/src/Engine/Track.C | 4 +- timeline/src/OSC_Thread.C | 2 +- timeline/src/Timeline.C | 2 +- 29 files changed, 2004 insertions(+), 521 deletions(-) create mode 100644 mixer/src/midi-to-osc.C create mode 100644 nonlib/MIDI/midievent.C create mode 100644 nonlib/MIDI/midievent.H create mode 100644 nonlib/MIDI/types.h diff --git a/FL/Fl_Text_Edit_Window.fl b/FL/Fl_Text_Edit_Window.fl index 4221dd3..8e95597 100644 --- a/FL/Fl_Text_Edit_Window.fl +++ b/FL/Fl_Text_Edit_Window.fl @@ -1,20 +1,21 @@ # data file for the Fltk User Interface Designer (fluid) -version 1.0108 +version 1.0300 header_name {.H} code_name {.C} -decl {\#include } {} +decl {\#include } {private local +} -widget_class Fl_Text_Edit_Window {open selected - xywh {375 272 355 410} type Double resizable +widget_class Fl_Text_Edit_Window {open + xywh {377 295 355 410} type Double resizable code0 {this->size_range( 0, 0, 400, 400 );} - class Fl_Window modal visible + class Fl_Double_Window modal visible } { Fl_Box title_box { label {} - xywh {5 7 345 45} + xywh {5 7 345 28} } - Fl_Text_Editor text_editor { - xywh {5 58 345 320} resizable + Fl_Text_Editor text_editor {selected + xywh {5 37 345 341} resizable code0 {o->buffer( new Fl_Text_Buffer );} } Fl_Group {} {open @@ -33,10 +34,11 @@ widget_class Fl_Text_Edit_Window {open selected } } -Function {fl_text_edit( const char *title, const char *button_text, const char *initial_text )} {open C return_type {char *} +Function {fl_text_edit( const char *title, const char *button_text, const char *initial_text, int W = 355, int H = 410 )} {open C return_type {char *} } { code {Fl_Text_Edit_Window tew( 355, 410, title ); +tew.size( W, H ); tew.return_button->label( button_text ); tew.title_box->label( title ); if ( initial_text ) diff --git a/mixer/src/Chain.C b/mixer/src/Chain.C index 12514a1..b7d332a 100644 --- a/mixer/src/Chain.C +++ b/mixer/src/Chain.C @@ -313,6 +313,12 @@ Chain::remove ( Controller_Module *m ) redraw(); } +void +Chain::send_feedback ( void ) +{ + for ( int i = 0; i < modules(); i++ ) + module(i)->send_feedback(); +} /* remove a module from the chain. this isn't guaranteed to succeed, * because removing the module might result in an invalid routing */ diff --git a/mixer/src/Chain.H b/mixer/src/Chain.H index f0a094d..d95bfd4 100644 --- a/mixer/src/Chain.H +++ b/mixer/src/Chain.H @@ -104,6 +104,8 @@ public: const char *name ( void ) const { return _name; } void name ( const char *name ); + void send_feedback ( void ); + int get_module_instance_number ( Module *m ); void configure_ports ( void ); diff --git a/mixer/src/Controller_Module.C b/mixer/src/Controller_Module.C index a6ce1b8..9b4b2e9 100644 --- a/mixer/src/Controller_Module.C +++ b/mixer/src/Controller_Module.C @@ -45,8 +45,10 @@ // needed for mixer->endpoint #include "Mixer.H" -bool Controller_Module::_learn_mode = false; + +bool Controller_Module::learn_by_number = false; +bool Controller_Module::_learn_mode = false; Controller_Module::Controller_Module ( bool is_default ) : Module( is_default, 50, 100, name() ) @@ -186,7 +188,7 @@ Controller_Module::mode ( Mode m ) Port *p = control_output[0].connected_port(); - JACK::Port po( chain()->engine(), JACK::Port::Input, p->name(), 0, "CV" ); + JACK::Port po( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, p->name(), 0, "CV" ); if ( ! po.activate() ) { @@ -549,9 +551,24 @@ Controller_Module::handle ( int m ) if ( p ) { - DMESSAGE( "Will learn %s", p->osc_path() ); + if ( learn_by_number ) + { + char *path = p->osc_number_path(); - mixer->osc_endpoint->learn( p->osc_path() ); + DMESSAGE( "Will learn %s", path ); + + mixer->osc_endpoint->learn( path ); + + free(path); + } + else + { + const char *path = p->osc_path(); + + DMESSAGE( "Will learn %s", path ); + + mixer->osc_endpoint->learn( path ); + } } return 1; diff --git a/mixer/src/Controller_Module.H b/mixer/src/Controller_Module.H index ee2efb0..f1a5477 100644 --- a/mixer/src/Controller_Module.H +++ b/mixer/src/Controller_Module.H @@ -44,6 +44,7 @@ public: static bool _learn_mode; + static bool learn_by_number; static bool learn_mode ( void ) { return _learn_mode; } static void learn_mode ( bool b ) { _learn_mode = b; } diff --git a/mixer/src/JACK_Module.C b/mixer/src/JACK_Module.C index 182075c..d01d977 100644 --- a/mixer/src/JACK_Module.C +++ b/mixer/src/JACK_Module.C @@ -196,7 +196,7 @@ get_connections_for_ports ( std::vector<JACK::Port> ports ) if ( ! connections ) return names; - bool is_output = ports[i].type() == JACK::Port::Output; + bool is_output = ports[i].direction() == JACK::Port::Output; for ( const char **c = connections; *c; c++ ) { @@ -356,9 +356,9 @@ JACK_Module::configure_inputs ( int n ) JACK::Port *po = NULL; if ( !_prefix ) - po = new JACK::Port( chain()->engine(), JACK::Port::Output, i ); + po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, i ); else - po = new JACK::Port( chain()->engine(), JACK::Port::Output, _prefix, i ); + po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, _prefix, i ); if ( ! po->activate() ) { @@ -415,9 +415,9 @@ JACK_Module::configure_outputs ( int n ) JACK::Port *po = NULL; if ( !_prefix ) - po = new JACK::Port( chain()->engine(), JACK::Port::Input, i ); + po = new JACK::Port( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, i ); else - po = new JACK::Port( chain()->engine(), JACK::Port::Input, _prefix, i ); + po = new JACK::Port( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, _prefix, i ); if ( ! po->activate() ) { diff --git a/mixer/src/Mixer.C b/mixer/src/Mixer.C index 74924d2..626a6d5 100644 --- a/mixer/src/Mixer.C +++ b/mixer/src/Mixer.C @@ -37,7 +37,7 @@ #include <FL/Fl_File_Chooser.H> #include <FL/Fl_Theme_Chooser.H> #include <FL/Fl_Value_SliderX.H> - +#include "FL/Fl_Text_Edit_Window.H" #include "file.h" #include <string.h> @@ -50,6 +50,8 @@ #include "Controller_Module.H" +const double FEEDBACK_UPDATE_FREQ = 1.0f; + extern char *user_config_dir; extern char *instance_name; @@ -63,16 +65,16 @@ extern NSM_Client *nsm; -static void -mixer_show_tooltip ( const char *s ) +void +Mixer::show_tooltip ( const char *s ) { - mixer->status( s ); + mixer->_status->label( s ); } -static void -mixer_hide_tooltip ( void ) +void +Mixer::hide_tooltip ( void ) { - mixer->status( 0 ); + mixer->_status->label( 0 ); } @@ -94,6 +96,7 @@ static int osc_add_strip ( const char *path, const char *, lo_arg **, int , lo_m OSC_DMSG(); Fl::lock(); + ((Mixer*)(OSC_ENDPOINT())->owner)->command_add_strip(); Fl::unlock(); @@ -258,6 +261,10 @@ void Mixer::cb_menu(Fl_Widget* o) { { command_add_strip(); } + else if ( !strcmp( picked, "&Mixer/Send Feedback" ) ) + { + send_feedback(); + } else if ( !strcmp( picked, "&Mixer/Add &N Strips" ) ) { const char *s = fl_input( "Enter number of strips to add" ); @@ -278,18 +285,37 @@ void Mixer::cb_menu(Fl_Widget* o) { fl_alert( "%s", "Failed to import strip!" ); } } - else if ( ! strcmp( picked, "&Mixer/Start Learning" ) ) + else if ( ! strcmp( picked, "&Project/Se&ttings/Learn/By Strip Name" ) ) + { + Controller_Module::learn_by_number = false; + } + else if ( ! strcmp( picked, "&Project/Se&ttings/Learn/By Strip Number" ) ) + { + Controller_Module::learn_by_number = true; + } + else if ( ! strcmp( picked, "&Mixer/Remote Control/Start Learning" ) ) { Controller_Module::learn_mode( true ); - status( "Now in learn mode. Click on a highlighted control to teach it something." ); + tooltip( "Now in learn mode. Click on a highlighted control to teach it something." ); redraw(); } - else if ( ! strcmp( picked, "&Mixer/Stop Learning" ) ) + else if ( ! strcmp( picked, "&Mixer/Remote Control/Stop Learning" ) ) { Controller_Module::learn_mode( false ); - status( "Learning complete" ); + tooltip( "Learning complete" ); redraw(); } + else if ( ! strcmp( picked, "&Mixer/Remote Control/Clear Mappings" ) ) + { + if ( 1 == fl_ask( "This will remove all mappings, are you sure?") ) + { + command_clear_mappings(); + } + } + else if ( ! strcmp( picked, "&Mixer/Remote Control/Edit Mappings" ) ) + { + edit_translations(); + } else if ( !strcmp( picked, "&Mixer/Paste" ) ) { Fl::paste(*this); @@ -452,8 +478,8 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) : Fl_Tooltip::hoverdelay( 0 ); Fl_Tooltip::delay( 0 ); - fl_show_tooltip = mixer_show_tooltip; - fl_hide_tooltip = mixer_hide_tooltip; + fl_show_tooltip = &Mixer::show_tooltip; + fl_hide_tooltip = &Mixer::hide_tooltip; /* Fl_Tooltip::size( 11 ); */ /* Fl_Tooltip::textcolor( FL_FOREGROUND_COLOR ); */ /* Fl_Tooltip::color( fl_color_add_alpha( FL_DARK1, 0 ) ); */ @@ -471,18 +497,20 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) : o->add( "&Project/Se&ttings/&Rows/Two", '2', 0, 0, FL_MENU_RADIO ); o->add( "&Project/Se&ttings/&Rows/Three", '3', 0, 0, FL_MENU_RADIO ); o->add( "&Project/Se&ttings/Make Default", 0,0,0); + o->add( "&Project/Se&ttings/Learn/By Strip Number", 0, 0, 0, FL_MENU_RADIO ); + o->add( "&Project/Se&ttings/Learn/By Strip Name", 0, 0, 0, FL_MENU_RADIO | FL_MENU_VALUE ); o->add( "&Project/&Save", FL_CTRL + 's', 0, 0 ); o->add( "&Project/&Quit", FL_CTRL + 'q', 0, 0 ); o->add( "&Mixer/&Add Strip", 'a', 0, 0 ); o->add( "&Mixer/Add &N Strips" ); + o->add( "&Mixer/Send Feedback" ); o->add( "&Mixer/&Import Strip" ); o->add( "&Mixer/Paste", FL_CTRL + 'v', 0, 0 ); - o->add( "&Mixer/Start Learning", FL_F + 9, 0, 0 ); - o->add( "&Mixer/Stop Learning", FL_F + 10, 0, 0 ); + o->add( "&Mixer/Remote Control/Start Learning", FL_F + 9, 0, 0 ); + o->add( "&Mixer/Remote Control/Stop Learning", FL_F + 10, 0, 0 ); + o->add( "&Mixer/Remote Control/Clear Mappings", 0, 0, 0 ); + o->add( "&Mixer/Remote Control/Edit Mappings", 0, 0, 0 ); o->add( "&View/&Theme", 0, 0, 0 ); - /* o->add( "&Options/&Display/Update Frequency/60 Hz", 0, 0, 0, FL_MENU_RADIO ); */ - /* o->add( "&Options/&Display/Update Frequency/30 Hz", 0, 0, 0, FL_MENU_RADIO); */ - /* o->add( "&Options/&Display/Update Frequency/15 Hz", 0, 0, 0, FL_MENU_RADIO | FL_MENU_VALUE ); */ o->add( "&Help/&Manual" ); o->add( "&Help/&About" ); o->callback( cb_menu, this ); @@ -510,7 +538,7 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) : } // Fl_Blink_Button* sm_blinker o->end(); } - { Fl_Scroll *o = scroll = new Fl_Scroll( X, Y + 24, W, H - ( 24 + 18 ) ); + { Fl_Scroll *o = scroll = new Fl_Scroll( X, Y + 24, W, H - ( 100 ) ); o->box( FL_FLAT_BOX ); // o->type( Fl_Scroll::HORIZONTAL_ALWAYS ); // o->box( Fl_Scroll::BOTH ); @@ -538,6 +566,8 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) : update_frequency( 15 ); + Fl::add_timeout( FEEDBACK_UPDATE_FREQ, send_feedback_cb, this ); + update_menu(); load_options(); @@ -549,16 +579,13 @@ Mixer::osc_strip_by_number ( const char *path, const char *types, lo_arg **argv, { int n; char *rem; - + char *client_name; + OSC::Endpoint *ep = (OSC::Endpoint*)user_data; - DMESSAGE( "%s", path ); - - if ( 2 != sscanf( path, "/strip#/%d/%a[^\n]", &n, &rem ) ) + if ( 3 != sscanf( path, "%a[^/]/strip#/%d/%a[^\n]", &client_name, &n, &rem ) ) return -1; - DMESSAGE( "%s", rem ); - Mixer_Strip *o = mixer->track_by_number( n ); if ( ! o ) @@ -569,12 +596,10 @@ Mixer::osc_strip_by_number ( const char *path, const char *types, lo_arg **argv, char *new_path; - asprintf( &new_path, "/strip/%s/%s", o->name(), rem ); + asprintf( &new_path, "%s/strip/%s/%s", client_name, o->name(), rem ); free( rem ); - DMESSAGE( "Sending %s", new_path ); - lo_send_message( ep->address(), new_path, msg ); free( new_path ); @@ -582,6 +607,79 @@ Mixer::osc_strip_by_number ( const char *path, const char *types, lo_arg **argv, return 0; } +void +Mixer::load_translations ( void ) +{ + FILE *fp = fopen( "mappings", "r" ); + + if ( ! fp ) + { + WARNING( "Error opening mappings file for reading" ); + return; + } + + char *to; + char *from; + + while ( 2 == fscanf( fp, "%a[^|> ] |> %a[^ \n]\n", &from, &to ) ) + { + osc_endpoint->add_translation( from, to ); + free(from); + free(to); + } + + fclose( fp ); +} + +void +Mixer::save_translations ( void ) +{ + FILE *fp = fopen( "mappings", "w" ); + + if ( ! fp ) + { + WARNING( "Error opening mappings file for writing" ); + return; + } + + for ( int i = 0; i < osc_endpoint->ntranslations(); i++ ) + { + const char *to; + const char *from; + + if ( osc_endpoint->get_translation( i, &to, &from ) ) + { + fprintf( fp, "%s |> %s\n", to, from ); + } + } + + fclose( fp ); +} + +void +Mixer::edit_translations ( void ) +{ + char *file_contents = NULL; + + if ( exists( "mappings" ) ) + { + size_t l = ::size( "mappings" ); + + file_contents = (char*)malloc( l ); + + FILE *fp = fopen( "mappings", "r" ); + + fread( file_contents, l, 1, fp ); + + fclose( fp ); + } + + char *s = fl_text_edit( "Mappings", "&Save", file_contents, 800, 600 ); + + if ( file_contents ) + free(file_contents); +} + int Mixer::init_osc ( const char *osc_port ) { @@ -601,6 +699,8 @@ Mixer::init_osc ( const char *osc_port ) osc_endpoint->start(); + osc_endpoint->add_method( NULL, NULL, osc_strip_by_number, osc_endpoint, ""); + return 0; } @@ -613,7 +713,9 @@ Mixer::~Mixer ( ) Fl::remove_timeout( &Mixer::update_cb, this ); - /* FIXME: teardown */ + Fl::remove_timeout( &Mixer::send_feedback_cb, this ); + +/* FIXME: teardown */ mixer_strips->clear(); } @@ -621,9 +723,9 @@ void Mixer::resize ( int X, int Y, int W, int H ) { Fl_Group::resize( X, Y, W, H ); - mixer_strips->resize( X, Y + 24, W, H - 18 - 24 ); + mixer_strips->resize( X, Y + 24, W, H - (18*2) - 24 ); - scroll->resize( X, Y + 24, W, H - 24 ); + scroll->resize( X, Y + 24, W, H - 24 - 18 ); rows( _rows ); } @@ -841,6 +943,8 @@ Mixer::save ( void ) MESSAGE( "Saving state" ); Loggable::snapshot_callback( &Mixer::snapshot, this ); Loggable::snapshot( "snapshot" ); + + save_translations(); return true; } @@ -872,6 +976,27 @@ Mixer::update_menu ( void ) project_name->label( Project::name() ); } +void +Mixer::send_feedback_cb ( void *v ) +{ + Mixer *m = (Mixer*)v; + + m->send_feedback(); + + Fl::repeat_timeout( FEEDBACK_UPDATE_FREQ, send_feedback_cb, v ); +} + +/** unconditionally send feedback to all mapped controls. This is + * useful for updating the state of an external controller. */ +void +Mixer::send_feedback ( void ) +{ + for ( int i = 0; i < mixer_strips->children(); i++ ) + { + ((Mixer_Strip*)mixer_strips->child(i))->send_feedback(); + } +} + int @@ -916,6 +1041,12 @@ Mixer::handle ( int m ) /* Commands */ /************/ +void +Mixer::command_clear_mappings ( void ) +{ + osc_endpoint->clear_translations(); +} + bool Mixer::command_save ( void ) { @@ -950,6 +1081,8 @@ Mixer::command_load ( const char *path, const char *display_name ) load_project_settings(); + load_translations(); + update_menu(); mixer->activate(); diff --git a/mixer/src/Mixer.H b/mixer/src/Mixer.H index 7e20e0c..aaff477 100644 --- a/mixer/src/Mixer.H +++ b/mixer/src/Mixer.H @@ -46,6 +46,9 @@ private: float _update_interval; + static void show_tooltip ( const char *s ); + static void hide_tooltip ( void ); + int _rows; int _strip_height; @@ -71,7 +74,11 @@ private: void load_options ( void ); void save_options ( void ); void update_menu ( void ); - + void save_translations ( void ); + void load_translations ( void ); + + static void send_feedback_cb ( void *v ); + void send_feedback ( void ); void redraw_windows ( void ); static void handle_dirty ( int, void *v ); @@ -135,7 +142,9 @@ public: void load_project_settings ( void ); public: - + + void edit_translations ( void ); + void command_clear_mappings ( void ); void command_new ( void ); bool command_save ( void ); bool command_load ( const char *path, const char *display_name = 0 ); diff --git a/mixer/src/Mixer_Strip.C b/mixer/src/Mixer_Strip.C index cf1c6c2..fd4de36 100644 --- a/mixer/src/Mixer_Strip.C +++ b/mixer/src/Mixer_Strip.C @@ -770,6 +770,13 @@ Mixer_Strip::handle ( int m ) return 0; } +void +Mixer_Strip::send_feedback ( void ) +{ + if ( _chain ) + _chain->send_feedback(); +} + int Mixer_Strip::number ( void ) const { diff --git a/mixer/src/Mixer_Strip.H b/mixer/src/Mixer_Strip.H index 03ece52..3b6ec2a 100644 --- a/mixer/src/Mixer_Strip.H +++ b/mixer/src/Mixer_Strip.H @@ -141,6 +141,7 @@ protected: public: + void send_feedback ( void ); int number ( void ) const; static bool import_strip ( const char *filename ); diff --git a/mixer/src/Module.C b/mixer/src/Module.C index e38f2f5..8aab07a 100644 --- a/mixer/src/Module.C +++ b/mixer/src/Module.C @@ -226,33 +226,70 @@ Module::paste_before ( void ) +char * +Module::Port::osc_number_path ( void ) +{ + int n = _module->chain()->strip()->number(); + + char *rem; + char *client_name; + char *strip_name; + + if ( 3 != sscanf( _scaled_signal->path(), "%a[^/]/strip/%a[^/]/%a[^\n]", &client_name, &strip_name, &rem ) ) + return NULL; + + free( strip_name ); + + char *path; + asprintf( &path, "%s/strip#/%i/%s", client_name, n, rem ); + + free( client_name ); + free( rem ); + + return path; +} + void Module::Port::send_feedback ( void ) { + float f = control_value(); + if ( hints.ranged ) + { + // scale value to range. + + float scale = hints.maximum - hints.minimum; + float offset = hints.minimum; + + f = ( f - offset ) / scale; + } + + if ( f > 1.0 ) + f = 1.0; + else if ( f < 0.0 ) + f = 0.0; + if ( _scaled_signal ) { /* send feedback for by_name signal */ - mixer->osc_endpoint->send_feedback( _scaled_signal->path(), control_value() ); + mixer->osc_endpoint->send_feedback( _scaled_signal->path(), f ); /* send feedback for by number signal */ { - int n = _module->chain()->strip()->number(); - - char *s = strdup( _scaled_signal->path() ); - - char *suffix = index( s, '/' ); - suffix = index( suffix, '/' ); - suffix = index( suffix, '/' ); - - char *path; - asprintf( &path, "/strip#/%i%s", suffix ); - - mixer->osc_endpoint->send_feedback( path, control_value() ); - + char *path = osc_number_path(); + + mixer->osc_endpoint->send_feedback( path, f ); + + free(path); } } +} +void +Module::send_feedback ( void ) +{ + for ( int i = 0; i < ncontrol_inputs(); i++ ) + control_input[i].send_feedback(); } void @@ -273,6 +310,12 @@ Module::Port::connected_osc ( void ) const return false; } +void +Module::Port::learn_osc ( void ) +{ + _scaled_signal->learn_connection(); +} + char * Module::Port::generate_osc_path () { diff --git a/mixer/src/Module.H b/mixer/src/Module.H index 0f9248e..2a089d7 100644 --- a/mixer/src/Module.H +++ b/mixer/src/Module.H @@ -123,6 +123,8 @@ public: static int osc_control_change_exact ( float v, void *user_data ); static int osc_control_change_cv ( float v, void *user_data ); + void learn_osc ( void ); + Hints hints; Port ( Module *module, Direction direction, Type type, const char *name = 0 ) @@ -176,6 +178,8 @@ public: return NULL; } + char *osc_number_path ( void ); + void update_osc_port ( ) { // if ( INPUT == _direction ) @@ -200,6 +204,27 @@ public: { *((float*)buffer()) = f; } + + if ( _scaled_signal ) + { + + if ( hints.ranged ) + { + // scale value to range. + + float scale = hints.maximum - hints.minimum; + float offset = hints.minimum; + + f = ( f - offset ) / scale; + } + + if ( f > 1.0 ) + f = 1.0; + else if ( f < 0.0 ) + f = 0.0; + +// _scaled_signal->value( f ); + } } void control_value ( float f ) @@ -405,7 +430,8 @@ public: char *get_parameters ( void ) const; void set_parameters ( const char * ); - + + void send_feedback ( void ); virtual bool initialize ( void ) { return true; } /* for the given number of inputs, return how many outputs this diff --git a/mixer/src/Module_Parameter_Editor.C b/mixer/src/Module_Parameter_Editor.C index db08584..a9f48f9 100644 --- a/mixer/src/Module_Parameter_Editor.C +++ b/mixer/src/Module_Parameter_Editor.C @@ -382,9 +382,9 @@ Module_Parameter_Editor::cb_bound_handle ( Fl_Widget *w, void *v ) Fl_Button *fv = (Fl_Button*)w; - fv->value( 1 ); + fv->value( 1 ); - cd->base_widget->bind_control( cd->port_number[0] ); + cd->base_widget->bind_control( cd->port_number[0] ); } void @@ -392,6 +392,8 @@ Module_Parameter_Editor::bind_control ( int i ) { Module::Port *p = &_module->control_input[i]; + /* p->learn_osc(); */ + if ( p->connected() ) /* can only bind once */ return; diff --git a/mixer/src/midi-to-osc.C b/mixer/src/midi-to-osc.C new file mode 100644 index 0000000..7a04ea5 --- /dev/null +++ b/mixer/src/midi-to-osc.C @@ -0,0 +1,768 @@ + +/*******************************************************************************/ +/* Copyright (C) 2013 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#include <JACK/Client.H> +#include <JACK/Port.H> +#include <OSC/Endpoint.H> +#include <MIDI/midievent.H> +#include "debug.h" + +#include <sys/stat.h> +#include <sys/types.h> + +using namespace MIDI; + +#include <jack/ringbuffer.h> +#include <jack/thread.h> + +#include <stdlib.h> +#include <stdio.h> + +#include <map> +#include <string> + +#include <unistd.h> /* usleep */ +/* simple program to translate from MIDI<->OSC Signals using a fixed mapping */ + +#include <nsm.h> + +#undef APP_NAME +const char *APP_NAME = "non-midi-mapper"; +#undef VERSION +const char *VERSION = "1.0"; + +nsm_client_t *nsm; +char *instance_name; + + +OSC::Endpoint *osc = 0; +/* const double NSM_CHECK_INTERVAL = 0.25f; */ + + +void +handle_hello ( lo_message msg ) +{ + int argc = lo_message_get_argc( msg ); + lo_arg **argv = lo_message_get_argv( msg ); + + if ( argc >= 4 ) + { + 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 NON peer %s (%s) @ %s with ID \"%s\"", name, version, url, id ); + + /* register peer */ + osc->handle_hello( id, url ); + } +} + +void +check_nsm ( void ) +{ + nsm_check_nowait( nsm ); + // Fl::repeat_timeout( NSM_CHECK_INTERVAL, &check_nsm, v ); +} + +static int +osc_non_hello ( const char *, const char *, lo_arg **, int , lo_message msg, void * ) +{ + handle_hello( msg ); + return 0; +} + + +static void +say_hello ( void ) +{ + if ( nsm_is_active( nsm ) ) + { + lo_message m = lo_message_new(); + + lo_message_add( m, "sssss", + "/non/hello", + osc->url(), + APP_NAME, + VERSION, + instance_name ); + + nsm_send_broadcast( nsm, m ); + } +} + +class Engine : public JACK::Client +{ +public: + jack_ringbuffer_t *input_ring_buf; + jack_ringbuffer_t *output_ring_buf; + JACK::Port *midi_input_port; + JACK::Port *midi_output_port; + + Engine ( ) + { + input_ring_buf = jack_ringbuffer_create( 16 * 16 * sizeof( jack_midi_event_t )); + jack_ringbuffer_reset( input_ring_buf ); + output_ring_buf = jack_ringbuffer_create( 16 * 16 * sizeof( jack_midi_event_t )); + jack_ringbuffer_reset( output_ring_buf ); + + midi_input_port = 0; + midi_output_port = 0; + } + + + int process ( nframes_t nframes ) + { + /* process input */ + { + if ( !midi_input_port ) + return 0; + + void *buf = midi_input_port->buffer( nframes ); + + jack_midi_event_t ev; + + jack_nframes_t count = jack_midi_get_event_count( buf ); + + /* place MIDI events into ringbuffer for non-RT thread */ + + for ( uint i = 0; i < count; ++i ) + { +// MESSAGE( "Got midi input!" ); + + jack_midi_event_get( &ev, buf, i ); + + /* /\* time is frame within cycle, convert to absolute tick *\/ */ + /* e.timestamp( ph + (ev.time / transport.frames_per_tick) ); */ + /* e.status( ev.buffer[0] ); */ + /* e.lsb( ev.buffer[1] ); */ + /* if ( ev.size == 3 ) */ + /* e.msb( ev.buffer[2] ); */ + + if ( jack_ringbuffer_write( input_ring_buf, (char*)&ev, sizeof( jack_midi_event_t ) ) != sizeof( jack_midi_event_t ) ) + WARNING( "input buffer overrun" ); + } + } + + /* process output */ + { + void *buf = midi_output_port->buffer(nframes); + + jack_midi_clear_buffer( buf ); + + jack_midi_event_t ev; + + nframes_t frame = 0; + + while ( true ) + { + /* jack_ringbuffer_data_t vec[2]; */ + /* jack_ringbuffer_get_read_vector( output_ring_buf, vec ); */ + + if ( jack_ringbuffer_peek( output_ring_buf, (char*)&ev, sizeof( jack_midi_event_t )) <= 0 ) + break; + + unsigned char *buffer = jack_midi_event_reserve( buf, frame, ev.size ); + if ( !buffer ) + { + WARNING("Output buffer overrun, will send later" ); + break; + } + + memcpy( buffer, &ev, ev.size ); + + jack_ringbuffer_read_advance( output_ring_buf, sizeof( jack_midi_event_t ) ); + } + } + + return 0; + } + + + void freewheel ( bool starting ) + { + } + + int xrun ( void ) + { + return 0; + } + + int buffer_size ( nframes_t nframes ) + { + return 0; + } + + void shutdown ( void ) + { + } + + void thread_init ( void ) + { + } + +}; + +Engine *engine; + +const float MAX_NRPN = 16383.0f; + +static char +get_lsb( int i ) +{ + return i & 0x7F; +} + +static char +get_msb( int i ) +{ + return ( i >> 7 ) & 0x7F; +} + +static int +get_14bit ( char msb, char lsb ) + { + return msb * 128 + lsb; + } + +class signal_mapping +{ +public: + + bool is_nrpn; + // int nrpn; + + midievent event; + + std::string signal_name; + + OSC::Signal *signal; + + signal_mapping ( ) + { + is_nrpn = false; + signal = NULL; + } + + ~signal_mapping ( ) + { + if ( signal ) + delete signal; + signal = NULL; + } + + char *serialize ( void ) const + { + char *s; + const char *opcode = 0; + int v1 = 0; + + if ( is_nrpn ) + { + opcode = "NRPN"; + v1 = get_14bit( event.msb(), event.lsb() ); + } + else + switch ( event.opcode() ) + { + case MIDI::midievent::CONTROL_CHANGE: + opcode = "CC"; + v1 = event.lsb(); + break; + case MIDI::midievent::NOTE_ON: + opcode = "NOTE_ON"; + v1 = event.note(); + break; + default: + // unsupported + break; + } + + asprintf( &s, "%s %d %d", opcode, event.channel(), v1 ); + + return s; + } + + void deserialize ( const char *s ) + { + int channel; + char *opcode; + int control; + + if ( 3 == sscanf( s, "%as %d %d", &opcode, &channel, &control ) ) + { + event.channel( channel ); + event.opcode( MIDI::midievent::CONTROL_CHANGE ); + + is_nrpn = 0; + + if ( !strcmp( opcode, "NRPN" ) ) + { + is_nrpn = 1; + + event.lsb( get_lsb( control )); + event.msb( get_msb( control )); + } + else if ( !strcmp( opcode, "CC" ) ) + { + event.lsb( control ); + } + + free(opcode); + } + } + +}; + +int signal_handler ( float value, void *user_data ) +{ + signal_mapping *m = (signal_mapping*)user_data; + + if ( m->is_nrpn ) + { + jack_midi_event_t jev[4]; + { + midievent e; + e.opcode( MIDI::midievent::CONTROL_CHANGE ); + e.channel( m->event.channel() ); + e.lsb( 99 ); + e.msb( m->event.msb() ); + jev[0].size = e.size(); + e.raw( (byte_t*)&jev[0], e.size() ); +// e.pretty_print(); + } + + { + midievent e; + e.opcode( MIDI::midievent::CONTROL_CHANGE ); + e.channel( m->event.channel() ); + e.lsb( 98 ); + e.msb( m->event.lsb() ); + jev[1].size = e.size(); + e.raw( (byte_t*)&jev[1], e.size() ); +// e.pretty_print(); + } + + + { + midievent e; + e.opcode( MIDI::midievent::CONTROL_CHANGE ); + e.channel( m->event.channel() ); + e.lsb( 6 ); + e.msb( int(value * MAX_NRPN ) >> 7 ); + jev[2].size = e.size(); + e.raw( (byte_t*)&jev[2], e.size() ); +// e.pretty_print(); + } + + { + midievent e; + e.opcode( MIDI::midievent::CONTROL_CHANGE ); + e.channel( m->event.channel() ); + e.lsb( 38 ); + e.msb( int( value * MAX_NRPN ) & 0x7F ); + jev[3].size = e.size(); + e.raw( (byte_t*)&jev[3], e.size() ); +// e.pretty_print(); + } + + for ( int i = 0; i < 4; i++ ) + { + if ( jack_ringbuffer_write( engine->output_ring_buf, (char*)&jev[i], + sizeof( jack_midi_event_t ) ) != sizeof( jack_midi_event_t ) ) + WARNING( "output buffer overrun" ); + } + } + else + { + jack_midi_event_t ev; + + m->event.msb( value * 128.0f ); + ev.size = m->event.size(); + m->event.raw( (byte_t*)&ev, m->event.size() ); + +// m->event.pretty_print(); + + if ( jack_ringbuffer_write( engine->output_ring_buf, (char*)&ev, sizeof( jack_midi_event_t ) ) != sizeof( jack_midi_event_t ) ) + WARNING( "output buffer overrun" ); + } + + return 0; +} + + +std::map<std::string,signal_mapping> sig_map; + +bool +save_settings ( void ) +{ + FILE *fp = fopen( "signals", "w" ); + + if ( !fp ) + return false; + + for ( std::map<std::string,signal_mapping>::const_iterator i = sig_map.begin(); + i != sig_map.end(); + i++ ) + { + fprintf( fp, "[%s] %s\n", i->first.c_str(), i->second.signal_name.c_str() ); + } + + fclose(fp); + + return true; +} + + +bool +load_settings ( void ) +{ + FILE *fp = fopen( "signals", "r" ); + + if ( !fp ) + return false; + + sig_map.clear(); + + char *signal_name; + char *midi_event; + + while ( 2 == fscanf( fp, "[%a[^]]] %a[^\n]\n", &midi_event, &signal_name ) ) + { + DMESSAGE( "%s, %s", midi_event, signal_name ); + + if ( sig_map.find( midi_event ) == sig_map.end() ) + { + signal_mapping m; + + m.deserialize( midi_event ); + + sig_map[midi_event] = m; + sig_map[midi_event].signal_name = signal_name; + sig_map[midi_event].signal = osc->add_signal( signal_name, OSC::Signal::Output, 0, 1, 0, signal_handler, &sig_map[midi_event] ); + } + + free(signal_name); + free(midi_event); + + /* if ( sig_map.find( s ) == sig_map.end() ) */ + /* { */ + /* int channel, control; */ + + /* if ( 2 == sscanf( s, "/midi/%d/CC/%d", &channel, &control ) ) */ + /* { */ + /* signal_mapping m; */ + + /* m.event.channel( channel ); */ + /* m.event.opcode( MIDI::midievent::CONTROL_CHANGE ); */ + /* m.event.lsb( control ); */ + + /* MESSAGE( "creating signal %s", s ); */ + /* sig_map[s] = m; */ + + /* sig_map[s].signal = osc->add_signal( s, OSC::Signal::Output, 0, 1, 0, signal_handler, &sig_map[s] ); */ + + /* } */ + /* if ( 2 == sscanf( s, "/midi/%d/NRPN/%d", &channel, &control ) ) */ + /* { */ + /* signal_mapping m; */ + + /* m.event.channel( channel ); */ + /* m.event.opcode( MIDI::midievent::CONTROL_CHANGE ); */ + /* m.event.lsb( get_lsb( control ) ); */ + /* m.event.msb( get_msb( control ) ); */ + + /* m.is_nrpn = true; */ + + /* MESSAGE( "creating signal %s", s ); */ + /* sig_map[s] = m; */ + + /* sig_map[s].signal = osc->add_signal( s, OSC::Signal::Output, 0, 1, 0, signal_handler, &sig_map[s] ); */ + + /* } */ + /* else */ + /* WARNING( "Could not decode signal spec \"%s\"", s ); */ + /* } */ + + /* free(s); */ + } + + return true; +} + + +static int +command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg, void *userdata ) +{ + if ( instance_name ) + free( instance_name ); + + instance_name = strdup( client_id ); + + osc->name( client_id ); + + mkdir( name, 0777 ); + chdir( name ); + + load_settings(); + + say_hello(); + + return ERR_OK; +} + +static int +command_save ( char **out_msg, void *userdata ) +{ + if ( save_settings() ) + { + nsm_send_is_clean(nsm); + return ERR_OK; + } + else + return ERR_GENERAL; +} + +static int +command_broadcast ( const char *path, lo_message msg, void *userdata ) +{ + lo_message_get_argc( msg ); +// lo_arg **argv = lo_message_get_argv( msg ); + + if ( !strcmp( path, "/non/hello" ) ) + { + handle_hello( msg ); + return 0; + } + else + return -1; + +} + +struct nrpn_state +{ + char control_msb; + char control_lsb; + char value_msb; + char value_lsb; + bool decending; +}; + +static +struct nrpn_state * +decode_nrpn ( nrpn_state *state, midievent e, int *take_action ) +{ + nrpn_state *n = &state[e.channel()]; + + *take_action = 0; + + switch ( e.lsb() ) + { + case 6: + if ( e.msb() < n->value_msb ) + n->value_lsb = 127; + else if ( e.msb() > n->value_msb ) + n->value_lsb = 0; + + n->value_msb = e.msb(); + *take_action = 1; + return n; + case 38: + n->value_lsb = e.msb(); + *take_action = 1; + return n; + case 99: + n->control_msb = e.msb(); + n->control_lsb = 0; + return n; + case 98: + n->control_lsb = e.msb(); + return n; + } + + return NULL; +} + +int +main ( int argc, char **argv ) +{ + nrpn_state nrpn_state[16]; + + nsm = nsm_new(); +// set_nsm_callbacks( nsm ); + + nsm_set_open_callback( nsm, command_open, 0 ); + nsm_set_broadcast_callback( nsm, command_broadcast, 0 ); + nsm_set_save_callback( nsm, command_save, 0 ); + + char *nsm_url = getenv( "NSM_URL" ); + + if ( nsm_url ) + { + if ( ! nsm_init( nsm, nsm_url ) ) + { + nsm_send_announce( nsm, APP_NAME, ":dirty:", argv[0] ); + + /* poll so we can keep OSC handlers running in the GUI thread and avoid extra sync */ +// Fl::add_timeout( NSM_CHECK_INTERVAL, check_nsm, NULL ); + } + } + + engine = new Engine(); + + DMESSAGE( "Creating JACK engine" ); + + if ( ! engine->init("midi-to-osc" ) ) + { + WARNING( "Failed to create JACK client" ); + } + + engine->midi_input_port = new JACK::Port( engine, "midi-in", JACK::Port::Input, JACK::Port::MIDI ); + engine->midi_output_port = new JACK::Port( engine, "midi-out", JACK::Port::Output, JACK::Port::MIDI ); + + if ( !engine->midi_input_port->activate() ) + { + WARNING( "Failed to activate JACK port" ); + } + + if ( !engine->midi_output_port->activate() ) + { + WARNING( "Failed to activate JACK port" ); + } + + + WARNING( "Can fit %i events in a period", ( engine->nframes() * 4 ) / 3 ); + + osc = new OSC::Endpoint(); + + osc->init( LO_UDP, NULL ); + + osc->add_method( "/non/hello", "ssss", osc_non_hello, osc, "" ); + + MESSAGE( "OSC URL = %s", osc->url() ); + + /* now we just read from the MIDI ringbuffer and output OSC */ + + DMESSAGE( "waiting for events" ); + + static int max_signal = 1; + + jack_midi_event_t ev; + midievent e; + while ( true ) + { + while ( jack_ringbuffer_read( engine->input_ring_buf, (char *)&ev, sizeof( jack_midi_event_t ) ) ) + { + e.timestamp( ev.time ); + e.status( ev.buffer[0] ); + e.lsb( ev.buffer[1] ); + if ( ev.size == 3 ) + e.msb( ev.buffer[2] ); + + switch ( e.opcode() ) + { + case MIDI::midievent::CONTROL_CHANGE: + case MIDI::midievent::PITCH_WHEEL: + { + int is_nrpn = 0; + + struct nrpn_state *st = decode_nrpn( nrpn_state, e, &is_nrpn ); + + if ( st != NULL && !is_nrpn ) + continue; + + char *midi_event; + + if ( is_nrpn ) + { + asprintf( &midi_event, "NRPN %d %d", e.channel(), st->control_msb * 128 + st->control_lsb ); + } + else if ( e.opcode() == MIDI::midievent::CONTROL_CHANGE ) + asprintf( &midi_event, "CC %d %d", e.channel(), e.lsb() ); + /* else if ( e.opcode() == MIDI::midievent::PITCH_WHEEL ) */ + /* asprintf( &s, "/midi/%i/PB", e.channel() ); */ + else + break; + + if ( sig_map.find( midi_event ) == sig_map.end() ) + { + char *s; + + asprintf( &s, "/control/%i", max_signal++ ); + + signal_mapping m; + + m.event.opcode( e.opcode() ); + m.event.channel( e.channel() ); + + m.event.lsb( e.lsb() ); + m.event.msb( e.msb() ); + + m.is_nrpn = is_nrpn; + + if ( is_nrpn ) + { + m.event.lsb( st->control_lsb ); + m.event.msb( st->control_msb ); + } + + /* if ( is_nrpn ) */ + /* m.nrpn = nrpnc_msb * 127 + nrpnc_lsb; */ + + MESSAGE( "creating signal %s", s ); + sig_map[midi_event] = m; + sig_map[midi_event].signal_name = s; + sig_map[midi_event].signal = osc->add_signal( s, OSC::Signal::Output, 0, 1, 0, signal_handler, &sig_map[midi_event] ); + + nsm_send_is_dirty( nsm ); + + free(s); + } + + float val = 0; + + if ( is_nrpn ) + { + val = ( st->value_msb * 128 + st->value_lsb ) / ( MAX_NRPN ); + } + else if ( e.opcode() == MIDI::midievent::CONTROL_CHANGE ) + val = e.msb() / 128.0f; + else if ( e.opcode() == MIDI::midievent::PITCH_WHEEL ) + val = e.pitch() / ( MAX_NRPN ); + +// MESSAGE( "sending signal for %s = %f", s, val ); + + sig_map[midi_event].signal->value( val ); + + free( midi_event ); + + break; + } + default: + break; + } +// e.pretty_print(); + } + osc->wait(20); + check_nsm(); + +// usleep( 500 ); + } +} diff --git a/mixer/wscript b/mixer/wscript index 2dd3806..b36b636 100644 --- a/mixer/wscript +++ b/mixer/wscript @@ -72,6 +72,13 @@ src/main.C uselib = [ 'JACK', 'LIBLO', 'LRDF', 'NTK', 'NTK_IMAGES', 'PTHREAD', 'DL', 'M' ], install_path = '${BINDIR}') + bld.program( source = 'src/midi-to-osc.C', + target = 'midi-to-osc', + includes = ['.', 'src', '..', '../nonlib'], + use = ['nonlib', 'fl_widgets'], + uselib = [ 'JACK', 'LIBLO', 'LRDF', 'NTK', 'NTK_IMAGES', 'PTHREAD', 'DL', 'M' ], + install_path = '${BINDIR}') + bld( features = 'subst', source = 'non-mixer.desktop.in', target = 'non-mixer.desktop', diff --git a/nonlib/JACK/Client.H b/nonlib/JACK/Client.H index cd94fa9..38b6c47 100644 --- a/nonlib/JACK/Client.H +++ b/nonlib/JACK/Client.H @@ -20,9 +20,10 @@ #pragma once #include <jack/jack.h> +#include <jack/midiport.h> typedef jack_nframes_t nframes_t; -typedef float sample_t; +typedef jack_default_audio_sample_t sample_t; #include <list> diff --git a/nonlib/JACK/Port.C b/nonlib/JACK/Port.C index 7770318..a3afc1b 100644 --- a/nonlib/JACK/Port.C +++ b/nonlib/JACK/Port.C @@ -28,7 +28,7 @@ namespace JACK { - static const char *name_for_port ( Port::type_e dir, const char *base, int n, const char *type ); + static const char *name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ); int Port::max_name ( void ) @@ -43,6 +43,7 @@ namespace JACK _client = rhs._client; _port = rhs._port; _direction = rhs._direction; + _type = rhs._type; _name = strdup( rhs._name ); _client->port_added( this ); @@ -56,37 +57,44 @@ namespace JACK _port = port; _name = strdup( jack_port_name( port ) ); _direction = jack_port_flags( _port ) == JackPortIsOutput ? Output : Input; + const char *type = jack_port_type( _port ); + + _type = Audio; + if ( strstr( type, "MIDI") ) + _type = MIDI; } - Port::Port ( JACK::Client *client, const char *name, type_e dir ) + Port::Port ( JACK::Client *client, const char *name, direction_e dir, type_e type ) { _name = NULL; _freezer = NULL; _client = client; _direction = dir; + _type = type; _name = strdup( name ); } - Port::Port ( JACK::Client *client, type_e dir, const char *base, int n, const char *type ) + Port::Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype ) { _name = NULL; _freezer = NULL; _client = client; - _name = strdup( name_for_port( dir, base, n, type ) ); + _name = strdup( name_for_port( dir, base, n, subtype ) ); _direction = dir; + _type = type; } - Port::Port ( JACK::Client *client, type_e dir, int n, const char *type ) + Port::Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype ) { _name = NULL; _freezer = NULL; _client = client; - _name = strdup( name_for_port( dir, NULL, n, type ) ); + _name = strdup( name_for_port( dir, NULL, n, subtype ) ); _direction = dir; - + _type = type; } Port::~Port ( ) @@ -118,7 +126,7 @@ namespace JACK static const char * - name_for_port ( Port::type_e dir, const char *base, int n, const char *type ) + name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ) { static char pname[ 512 ]; @@ -145,7 +153,7 @@ namespace JACK } bool - Port::activate ( const char *name, type_e dir ) + Port::activate ( const char *name, direction_e dir ) { _name = strdup( name ); _direction = dir; @@ -157,7 +165,7 @@ namespace JACK Port::activate ( void ) { _port = jack_port_register( _client->jack_client(), _name, - JACK_DEFAULT_AUDIO_TYPE, + _type == Audio ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, _direction == Output ? JackPortIsOutput : JackPortIsInput, 0 ); @@ -217,7 +225,7 @@ namespace JACK bool Port::name ( const char *base, int n, const char *type ) { - return name( name_for_port( this->type(), base, n, type ) ); + return name( name_for_port( this->direction(), base, n, type ) ); } void @@ -252,12 +260,6 @@ namespace JACK return jack_port_get_connections( _port ); } - Port::type_e - Port::type ( void ) const - { - return _direction; - } - /** Restore the connections returned by connections() */ bool Port::connections ( const char **port_names ) diff --git a/nonlib/JACK/Port.H b/nonlib/JACK/Port.H index 3d54d1b..5de49e8 100644 --- a/nonlib/JACK/Port.H +++ b/nonlib/JACK/Port.H @@ -41,14 +41,15 @@ namespace JACK bool operator < ( const Port & rhs ) const; - enum type_e { Output, Input }; + enum direction_e { Output, Input }; + enum type_e { Audio, MIDI }; static int max_name ( void ); Port ( JACK::Client *client, jack_port_t *port ); - Port ( JACK::Client *client, const char *name, type_e dir ); - Port ( JACK::Client *client, type_e dir, const char *base, int n, const char *type=0 ); - Port ( JACK::Client *client, type_e dir, int n, const char *type=0 ); + Port ( JACK::Client *client, const char *name, direction_e dir, type_e type ); + Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype=0 ); + Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype=0 ); // Port ( ); ~Port ( ); @@ -58,7 +59,8 @@ namespace JACK bool valid ( void ) const { return _port; } bool connected ( void ) const { return jack_port_connected( _port ); } - type_e type ( void ) const; + direction_e direction ( void ) const { return _direction; } + type_e type ( void ) const { return _type; } const char * name ( void ) const { return _name; } bool name ( const char *name ); bool name ( const char *base, int n, const char *type=0 ); @@ -85,9 +87,10 @@ namespace JACK private: - type_e _direction; + direction_e _direction; + type_e _type; - bool activate ( const char *name, type_e dir ); + bool activate ( const char *name, direction_e dir ); /* holds all we need to know about a jack port to recreate it on a new client */ diff --git a/nonlib/MIDI/midievent.C b/nonlib/MIDI/midievent.C new file mode 100644 index 0000000..1b36587 --- /dev/null +++ b/nonlib/MIDI/midievent.C @@ -0,0 +1,215 @@ + +/*******************************************************************************/ +/* Copyright (C) 2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#include "midievent.H" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "debug.h" + +namespace MIDI +{ + static const char *opcode_names[] = + { + "Note Off", + "Note On", + "Aftertouch", + "Control Change", + "Program Change", + "Channel Pressure", + "Pitch Wheel" + }; + + midievent::midievent ( void ) + { + _sysex = NULL; + _timestamp = 0; + _data.status = NOTE_OFF; + _data.msb = _data.lsb = 0; + } + + midievent::~midievent ( void ) + { + if ( _sysex ) + delete _sysex; + + _sysex = NULL; + } + + int + midievent::pitch ( void ) const + { + return ((_data.msb << 7) | _data.lsb) - 0x2000; + } + + void + midievent::pitch ( int n ) + { + n += 0x2000; + + _data.lsb = n & 0x7F; + _data.msb = (n >> 7) & 0x7F; + } + + void + midievent::data ( byte_t D1, byte_t D2 ) + { + _data.lsb = D1 & 0x7F; + _data.msb = D2 & 0x7F; + } + + void + midievent::data ( byte_t *D1, byte_t *D2 ) const + { + *D1 = _data.lsb; + *D2 = _data.msb; + } + + void + midievent::raw ( byte_t *p, int l) const + { + memcpy( p, &_data, l ); + } + + int + midievent::size ( void ) const + { + return midievent::event_size( opcode() ); + } + + void + midievent::note_velocity ( int vel ) + { + _data.msb = vel & 0x7F; + } + + void + midievent::note ( char note ) + { + _data.lsb = note & 0x7F; + } + + unsigned char + midievent::note_velocity ( void ) const + { + return _data.msb; + } + + bool + midievent::is_same_note ( midievent * e ) const + { + return channel() == e->channel() && note() == e->note(); + } + +/** get name from opcode */ + const char * + midievent::name ( void ) const + { + return opcode_names[ (opcode() >> 4) - 8 ]; + } + +/** get opcode from name */ + int + midievent::name ( const char *name ) const + { + for ( unsigned int i = elementsof( opcode_names ); i--; ) + if ( ! strcmp( name, opcode_names[ i ] ) ) + return (i + 8) << 4; + + return -1; + } + +/** print event in hexadecimal */ + void + midievent::print ( void ) const + { + printf( "[%06f] %02X %02X %02X\n", + _timestamp, + _data.status, + _data.lsb, + _data.msb ); + } + +/** print event in english/decimal */ + void + midievent::pretty_print ( void ) const + { + printf( + "[%06f] %-15s c: %2d d1: %3d d2: %3d\n", + _timestamp, + name(), + channel(), + _data.lsb, + _data.msb ); + } + + +/*********/ +/* Sysex */ +/*********/ + + midievent::sysex::sysex ( void ) + { + _data = NULL; + _size = 0; + _alloc = 0; + } + + midievent::sysex::~sysex ( void ) + { + if ( _data ) + free( _data ); + + _data = NULL; + } + +/** add bytes to sysex message */ + void + midievent::sysex::append ( byte_t *data, size_t size ) + { + if ( _size + size > _alloc ) + _data = (byte_t *)realloc( _data, _alloc += 256 ); + + memcpy( data + _size, data, size ); + + _size += size; + } + +/** return SysEx data */ + const byte_t * + midievent::sysex::data ( void ) const + { + return _data; + } + + long + midievent::sysex::size ( void ) const + { + return _size; + } + + + + bool + midievent::operator== ( const midievent &rhs ) const + { + return _timestamp == rhs._timestamp && + ! bcmp( (void*)&_data, (void*)&rhs._data, size() ); + } +} diff --git a/nonlib/MIDI/midievent.H b/nonlib/MIDI/midievent.H new file mode 100644 index 0000000..61f5f7e --- /dev/null +++ b/nonlib/MIDI/midievent.H @@ -0,0 +1,243 @@ + +/*******************************************************************************/ +/* Copyright (C) 2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +/* this class represents a single raw MIDI event plus a timestamp */ + +#pragma once + +#include "types.h" +#include <stdlib.h> + +namespace MIDI +{ + class midievent + { + + public: + + class sysex { + size_t _size, _alloc; + byte_t *_data; + + public: + + sysex ( void ); + ~sysex ( void ); + + void append ( byte_t *data, size_t size ); + const byte_t * data ( void ) const; + long size ( void ) const; + + }; + + private: + + sysex *_sysex; + + tick_t _timestamp; /* in ticks */ + struct { + byte_t status, /* full status byte */ + lsb, /* data 1 */ + msb; /* data 2 */ + } _data; + + public: + + static inline int + event_size ( byte_t op ) + { + switch ( op ) + { + case NOTE_ON: case NOTE_OFF: case AFTERTOUCH: + case CONTROL_CHANGE: case PITCH_WHEEL: + return 3; + case PROGRAM_CHANGE: case CHANNEL_PRESSURE: + return 2; + default: + return 1; + } + }; + + /* define MIDI status bytes */ + enum { + STATUS_BIT = 0x80, + NOTE_OFF = 0x80, + NOTE_ON = 0x90, + AFTERTOUCH = 0xA0, + CONTROL_CHANGE = 0xB0, + PROGRAM_CHANGE = 0xC0, + CHANNEL_PRESSURE = 0xD0, + PITCH_WHEEL = 0xE0, + CLEAR_CHAN_MASK = 0xF0, + MIDI_CLOCK = 0xF8, + SYSEX = 0xF0, + SYSEX_END = 0xF7, + META = 0xFF + }; + + midievent ( void ); + virtual ~midievent ( void ); + + tick_t timestamp ( void ) const; + void timestamp ( tick_t time ); + void status ( byte_t status ); + byte_t status ( void ) const; + void channel ( byte_t channel ); + byte_t channel ( void ) const; + byte_t opcode ( void ) const; + void opcode ( byte_t o ); + void lsb ( byte_t n ); + void msb ( byte_t n ); + int lsb ( void ) const; + int msb ( void ) const; + int pitch ( void ) const; + void pitch ( int n ); + void data ( byte_t D1, byte_t D2 ); + void data ( byte_t *D1, byte_t *D2 ) const; + void raw ( byte_t *p, int l) const; + int size ( void ) const; + void note_velocity ( int vel ); + bool is_note_on ( void ) const; + bool is_note_off ( void ) const; + virtual unsigned char note ( void ) const; + virtual void note ( char note ); + unsigned char note_velocity ( void ) const; + bool is_same_note ( midievent * e ) const; + const char * name ( void ) const; + int name ( const char *name ) const; + void print ( void ) const; + void pretty_print ( void ) const; + + bool operator< ( const midievent &rhs ) const; + bool operator>= ( const midievent &rhs ) const; + + bool operator== ( const midievent &rhs ) const; + + }; + + +/**********************/ +/* Inlined accessors */ +/**********************/ + + + inline tick_t + midievent::timestamp ( void ) const + { + return _timestamp; + } + + inline void + midievent::timestamp ( tick_t time ) + { + _timestamp = time; + } + + inline void + midievent::status ( byte_t status ) + { + _data.status = status; + } + + inline byte_t + midievent::status ( void ) const + { + return _data.status; + } + + inline void + midievent::channel ( byte_t channel ) + { + _data.status = (_data.status & 0xF0) | (channel & 0x0F); + } + + inline byte_t + midievent::channel ( void ) const + { + return _data.status & 0x0F; + } + + inline byte_t + midievent::opcode ( void ) const + { + return _data.status & 0xF0; + } + + + inline void + midievent::opcode ( byte_t opcode ) + { + _data.status = (_data.status & 0x0F) | (opcode & 0xF0); + } + + inline void + midievent::lsb ( byte_t n ) + { + _data.lsb = n & 0x7F; + } + + inline void + midievent::msb ( byte_t n ) + { + _data.msb = n & 0x7F; + } + + inline int + midievent::lsb ( void ) const + { + return _data.lsb; + } + + inline int + midievent::msb ( void ) const + { + return _data.msb; + } + + inline bool + midievent::is_note_on ( void ) const + { + return (opcode() == NOTE_ON); + } + + inline bool + midievent::is_note_off ( void ) const + { + return (opcode() == NOTE_OFF); + } + + inline unsigned char + midievent::note ( void ) const + { + return _data.lsb; + } + + inline bool + midievent::operator< ( const midievent &rhs ) const + { + return _timestamp < rhs._timestamp; + } + + inline bool + midievent::operator>= ( const midievent &rhs ) const + { + return _timestamp >= rhs._timestamp; + } + +} diff --git a/nonlib/MIDI/types.h b/nonlib/MIDI/types.h new file mode 100644 index 0000000..192647f --- /dev/null +++ b/nonlib/MIDI/types.h @@ -0,0 +1,28 @@ + + +/*******************************************************************************/ +/* Copyright (C) 2007,2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#pragma once + +typedef unsigned char byte_t; +typedef double tick_t; +typedef unsigned int uint; + +#define elementsof(x) (sizeof((x)) / sizeof((x)[0])) + diff --git a/nonlib/OSC/Endpoint.C b/nonlib/OSC/Endpoint.C index a3817c2..77d4791 100644 --- a/nonlib/OSC/Endpoint.C +++ b/nonlib/OSC/Endpoint.C @@ -54,16 +54,12 @@ namespace OSC /* Signal */ /**********/ - int Signal::next_id = 0; - - Signal::Signal ( const char *path, Direction dir ) { _direction = dir; _path = NULL; if ( path ) _path = strdup( path ); - _id = ++next_id; _value = 0.0f; _endpoint = NULL; _peer = NULL; @@ -90,18 +86,28 @@ namespace OSC void Signal::rename ( const char *path ) { - DMESSAGE( "Renaming signal %s to %s", this->path(), path ); + char *new_path; + asprintf( &new_path, "%s%s", _endpoint->name(), path ); + + DMESSAGE( "Renaming signal %s to %s", this->path(), new_path ); if ( _direction == Signal::Input ) { lo_server_del_method( _endpoint->_server, _path, NULL ); - lo_server_add_method( _endpoint->_server, path, NULL, _endpoint->osc_sig_handler, this ); + lo_server_add_method( _endpoint->_server, new_path, NULL, _endpoint->osc_sig_handler, this ); } - free( _path ); - _path = strdup( path ); + for ( std::list<Peer*>::iterator i = _endpoint->_peers.begin(); + i != _endpoint->_peers.end(); + ++i ) + { + _endpoint->send( (*i)->addr, "/signal/renamed", _path, new_path ); + } - _endpoint->send_signal_rename_notifications( this ); + _endpoint->rename_translation_destination( _path, new_path ); + + free( _path ); + _path = new_path; } bool @@ -111,9 +117,9 @@ namespace OSC i != _outgoing.end(); ++i ) { - if ( (*i)->_peer == s->_peer && - (*i)->id() == s->id() ) - return true; + /* if ( (*i)->_peer == s->_peer && */ + /* (*i)->id() == s->id() ) */ + /* return true; */ } return false; @@ -122,24 +128,55 @@ namespace OSC void Signal::value ( float f ) { - for ( std::list<Signal*>::const_iterator i = _outgoing.begin(); - i != _outgoing.end(); - ++i ) - { - /* FIXME: won't work for loopback */ - if ( (*i)->_value != f ) - { - (*i)->_value = f; + if ( f == _value ) + return; + + /* for ( std::list<Signal*>::const_iterator i = _outgoing.begin(); */ + /* i != _outgoing.end(); */ + /* ++i ) */ + /* { */ + /* /\* FIXME: won't work for loopback *\/ */ + /* if ( (*i)->_value != f ) */ + /* { */ + /* (*i)->_value = f; */ + + _value = f; + + if ( direction() == Output ) + { + for ( std::list<Peer*>::iterator i = _endpoint->_peers.begin(); + i != _endpoint->_peers.end(); + ++i ) + { + _endpoint->send( (*i)->addr, + path(), + f ); + } + + // free(s); + } + else if ( direction() == Input ) + { + DMESSAGE( "Sending value feedback for signal %s...", path() ); + for ( std::list<Signal*>::iterator i = _incoming.begin(); + i != _incoming.end(); + ++i ) + { + DMESSAGE( "Sending value feedback to %s %s %f", lo_address_get_url( (*i)->_peer->addr), (*i)->path() , f); _endpoint->send( (*i)->_peer->addr, - "/signal/change", - id(), - (*i)->id(), + (*i)->path(), f ); } } } + void + Signal::learn_connection ( void ) + { + _endpoint->_learn_signal = this; + } + char * Signal::get_output_connection_peer_name_and_path ( int n ) { @@ -157,12 +194,10 @@ namespace OSC } } -// Signal *s = get_peer_signal_by_id( t->_peer, t->signal_id ); - if ( t ) { char *r; - asprintf( &r, "%s:%s", t->_peer->name, t->path() ); + asprintf( &r, "%s%s", t->_peer->name, t->path() ); return r; } @@ -187,6 +222,7 @@ namespace OSC _peer_scan_complete_userdata = 0; _server = 0; _name = 0; + _learn_signal = 0; owner = 0; } @@ -208,12 +244,11 @@ namespace OSC } add_method( "/signal/hello", "ss", &Endpoint::osc_sig_hello, this, "" ); - add_method( "/signal/connect", "ii", &Endpoint::osc_sig_connect, this, "" ); - add_method( "/signal/disconnect", "ii", &Endpoint::osc_sig_disconnect, this, "" ); - add_method( "/signal/renamed", "is", &Endpoint::osc_sig_renamed, this, "" ); - add_method( "/signal/removed", "i", &Endpoint::osc_sig_removed, this, "" ); - add_method( "/signal/created", "ssifff", &Endpoint::osc_sig_created, this, "" ); - add_method( "/signal/change", "iif", &Endpoint::osc_sig_handler, this, "" ); + add_method( "/signal/connect", "ss", &Endpoint::osc_sig_connect, this, "" ); + add_method( "/signal/disconnect", "ss", &Endpoint::osc_sig_disconnect, this, "" ); + add_method( "/signal/renamed", "ss", &Endpoint::osc_sig_renamed, this, "" ); + add_method( "/signal/removed", "s", &Endpoint::osc_sig_removed, this, "" ); + add_method( "/signal/created", "ssfff", &Endpoint::osc_sig_created, this, "" ); add_method( "/signal/list", NULL, &Endpoint::osc_signal_lister, this, "" ); add_method( "/reply", NULL, &Endpoint::osc_reply, this, "" ); add_method( NULL, NULL, &Endpoint::osc_generic, this, "" ); @@ -248,20 +283,7 @@ namespace OSC return NULL; } - - OSC::Signal * - Endpoint::find_signal_by_id ( int id ) - { - for ( std::list<Signal*>::iterator i = _signals.begin(); - i != _signals.end(); - ++i ) - { - if ( (*i)->id() == id ) - return *i; - } - - return NULL; - } + OSC::Signal * Endpoint::find_peer_signal_by_path ( Peer *p, const char *path ) @@ -276,15 +298,15 @@ namespace OSC return NULL; } - + OSC::Signal * - Endpoint::find_peer_signal_by_id ( Peer *p, int id ) + Endpoint::find_signal_by_path ( const char *path ) { - for ( std::list<Signal*>::iterator i = p->_signals.begin(); - i != p->_signals.end(); + for ( std::list<Signal*>::iterator i = _signals.begin(); + i != _signals.end(); ++i ) { - if ( id == (*i)->id() ) + if ( !strcmp( (*i)->path(), path ) ) return *i; } @@ -368,32 +390,24 @@ namespace OSC int Endpoint::osc_sig_disconnect ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) { - int their_id = argv[0]->i; - int our_id = argv[1]->i; + const char *their_name = &argv[0]->s; + const char *our_name = &argv[1]->s; Endpoint *ep = (Endpoint*)user_data; - Peer *p = ep->find_peer_by_address( lo_message_get_source( msg ) ); - - if ( ! p ) - return 0; - - Signal *ps = ep->find_peer_signal_by_id( p, their_id ); - - if ( ! ps ) - return 0; - - Signal *s = ep->find_signal_by_id( our_id ); + Signal *s = ep->find_signal_by_path( our_name ); if ( ! s ) return 0; if ( s->_direction == Signal::Input ) { - s->_incoming.remove( ps ); + /* s->_incoming.remove( ps ); */ - DMESSAGE( "Peer %s has disconnected from signal %s", p->name, ps->path() ); + DMESSAGE( "Peer %s has disconnected from signal %s", our_name, their_name ); + ep->del_translation( their_name ); + if ( s->_connection_state_callback ) s->_connection_state_callback( s, s->_connection_state_userdata ); @@ -403,66 +417,34 @@ namespace OSC return 0; } - int Endpoint::osc_sig_connect ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) { - int their_id = argv[0]->i; - int our_id = argv[1]->i; + const char *src_path = &argv[0]->s; + const char *dst_path = &argv[1]->s; Endpoint *ep = (Endpoint*)user_data; - Peer *p = ep->find_peer_by_address( lo_message_get_source( msg ) ); + Signal *dst_s = ep->find_signal_by_path( dst_path ); - if ( ! p ) + if ( ! dst_s ) { - WARNING( "Got connection signal from unknown peer" ); + WARNING( "Unknown destination signal in connection attempt: \"%s\"", dst_path ); return 0; } - Signal *ps = ep->find_peer_signal_by_id( p, their_id ); - - /* if ( ! ps ) */ - /* { */ - /* WARNING( "Unknown source signal" ); */ - /* WARNIN */ - /* return 0; */ - /* } */ - - if ( ! ps ) + if ( dst_s->_endpoint != ep ) { - /* we don't know about this signal yet */ - /* just add a stub signal and fill in the blanks later */ - ps = new Signal( NULL, Signal::Output); - ps->_id = their_id; - ps->_peer = p; - - p->_signals.push_back( ps ); - } - - Signal *s = ep->find_signal_by_id( our_id ); - - if ( ! s ) - { - WARNING( "Unknown destination signal" ); - + WARNING( "Got connection request for a destination signal we don't own" ); return 0; } - DMESSAGE( "Peer %s has connected to signal %s", p->name, s->path() ); + DMESSAGE( "Has requested signal connection %s |> %s", src_path, dst_s->path() ); - /* if ( s->_direction == Signal::Input ) */ - /* { */ - s->_incoming.push_back( ps ); + ep->add_translation( src_path, dst_s->path() ); - /* make a record of it ourselves */ - ps->_outgoing.push_back( s ); - - if ( s->_connection_state_callback ) - s->_connection_state_callback( s, s->_connection_state_userdata ); - - /* return 0; */ - /* } */ + /* if ( dst_s->_connection_state_callback ) */ + /* dst_s->_connection_state_callback( dst_s, dst_s->_connection_state_userdata ); */ return 0; } @@ -470,61 +452,61 @@ namespace OSC int Endpoint::osc_sig_removed ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) { - int id = argv[0]->i; + const char *name = &argv[0]->s; Endpoint *ep = (Endpoint*)user_data; - Peer *p = ep->find_peer_by_address( lo_message_get_source( msg ) ); + /* Peer *p = ep->find_peer_by_address( lo_message_get_source( msg ) ); */ - if ( ! p ) - { - WARNING( "Got signal remove notification from unknown peer." ); - return 0; - } + /* if ( ! p ) */ + /* { */ + /* WARNING( "Got signal remove notification from unknown peer." ); */ + /* return 0; */ + /* } */ - Signal *o = ep->find_peer_signal_by_id( p, id ); + Signal *o = ep->find_signal_by_path( name ); if ( ! o ) { - WARNING( "Unknown signal ID %i", id ); + WARNING( "Unknown signal: %s", name ); return 0; } DMESSAGE( "Signal %s:%s was removed", o->_peer->name, o->path() ); - /* disconnect it */ - if ( o->_outgoing.size() ) - { - for ( std::list<Signal*>::iterator i = o->_outgoing.begin(); - i != o->_outgoing.end(); - ) - { - Signal *s = *i; - /* avoid messing up iterator */ - ++i; + /* /\* disconnect it *\/ */ + /* if ( o->_outgoing.size() ) */ + /* { */ + /* for ( std::list<Signal*>::iterator i = o->_outgoing.begin(); */ + /* i != o->_outgoing.end(); */ + /* ) */ + /* { */ + /* Signal *s = *i; */ + /* /\* avoid messing up iterator *\/ */ + /* ++i; */ - ep->disconnect_signal( o, s ); - } - } + /* ep->disconnect_signal( o, s ); */ + /* } */ + /* } */ - if ( o->_incoming.size() ) - { - for ( std::list<Signal*>::iterator i = o->_incoming.begin(); - i != o->_incoming.end(); - ) - { - Signal *s = *i; - /* avoid messing up iterator */ - ++i; + /* if ( o->_incoming.size() ) */ + /* { */ + /* for ( std::list<Signal*>::iterator i = o->_incoming.begin(); */ + /* i != o->_incoming.end(); */ + /* ) */ + /* { */ + /* Signal *s = *i; */ + /* /\* avoid messing up iterator *\/ */ + /* ++i; */ - ep->disconnect_signal( s, o ); - } - } + /* ep->disconnect_signal( s, o ); */ + /* } */ + /* } */ if ( ep->_peer_signal_notification_callback ) ep->_peer_signal_notification_callback( o, Signal::Removed, ep->_peer_signal_notification_userdata ); - p->_signals.remove( o ); + ep->_signals.remove( o ); delete o; @@ -538,10 +520,9 @@ namespace OSC const char *name = &argv[0]->s; const char *direction = &argv[1]->s; - const int id = argv[2]->i; - const float min = argv[3]->f; - const float max = argv[4]->f; - const float default_value = argv[5]->f; + const float min = argv[2]->f; + const float max = argv[3]->f; + const float default_value = argv[4]->f; Peer *p = ep->find_peer_by_address( lo_message_get_source( msg ) ); @@ -561,13 +542,12 @@ namespace OSC Signal *s = new Signal( name, dir ); s->_peer = p; - s->_id = id; s->parameter_limits( min, max, default_value ); p->_signals.push_back( s ); - DMESSAGE( "Peer %s has created signal %s with id %i (%s %f %f %f)", p->name, - name, id, direction, min, max, default_value ); + DMESSAGE( "Peer %s has created signal %s (%s %f %f %f)", p->name, + name, direction, min, max, default_value ); if ( ep->_peer_signal_notification_callback ) ep->_peer_signal_notification_callback( s, Signal::Created, ep->_peer_signal_notification_userdata ); @@ -580,8 +560,8 @@ namespace OSC { DMESSAGE( "Got renamed message." ); - int id = argv[0]->i; - char *new_name = &argv[1]->s; + const char *old_name = &argv[0]->s; + const char *new_name = &argv[1]->s; Endpoint *ep = (Endpoint*)user_data; @@ -593,16 +573,18 @@ namespace OSC return 0; } - Signal *o = ep->find_peer_signal_by_id( p, id ); + Signal *o = ep->find_peer_signal_by_path( p, old_name ); if ( ! o ) { - WARNING( "Unknown signal id %i", id ); + WARNING( "Unknown signal: %s", old_name ); return 0; } - DMESSAGE( "Signal %s:%s was renamed to %s", o->_peer->name, o->_path, path ); + DMESSAGE( "Signal %s was renamed to %s", o->_path, new_name ); + ep->rename_translation_source( o->_path, new_name ); + free( o->_path ); o->_path = strdup( new_name ); @@ -616,26 +598,11 @@ namespace OSC float f = 0.0; Endpoint *ep = NULL; - - if ( !strcmp( path, "/signal/change" ) && !strcmp( types, "iif" ) ) - { - /* accept a value for numbered signal */ - int id = argv[1]->i; - f = argv[2]->f; - ep = (Endpoint*)user_data; - - o = ep->find_signal_by_id( id ); - - if ( ! o ) - { - WARNING( "Unknown signal id %i", id ); - return 0; - } - } - else if ( ! strcmp( types, "f" ) ) + if ( ! strcmp( types, "f" ) ) { /* accept a value for signal named in path */ o = (Signal*)user_data; + ep = o->_endpoint; f = argv[0]->f; } else if ( ! types || 0 == types[0] ) @@ -650,53 +617,18 @@ namespace OSC return -1; } - Peer *p = NULL; - - if ( ep ) - p = ep->find_peer_by_address( lo_message_get_source( msg ) ); - - - if ( !p ) - { - DMESSAGE( "Signal change initiated by an unknown peer" ); - /* message came from an unconnected peer, just set the value exactly */ - } - else - { - /* message is from a connected source, do mixing. */ - - /* remote signal */ - /* if ( t->_peer ) */ - - /* if ( 0 == o->_incoming.size() ) */ - /* return 0; */ - - for ( std::list<Signal*>::const_iterator i = o->_incoming.begin(); - i != o->_incoming.end(); - ++i ) - { - if ( (*i)->id() == argv[0]->i ) - { - (*i)->_value = f; - break; - } - } - - f = 0.0; - - for ( std::list<Signal*>::const_iterator i = o->_incoming.begin(); - i != o->_incoming.end(); - ++i ) - { - f += (*i)->_value; - } - } - o->_value = f; - o->_handler( f, o->_user_data ); + if ( o->_handler ) + o->_handler( f, o->_user_data ); - return 0; + return 1; + } + + void + Endpoint::clear_translations ( void ) + { + _translations.clear(); } void @@ -705,6 +637,68 @@ namespace OSC _translations[a].path = b; } + void + Endpoint::del_translation ( const char *a ) + { + std::map<std::string,TranslationDestination>::iterator i = _translations.find( a ); + + if ( i != _translations.end() ) + _translations.erase( i ); + } + + void + Endpoint::rename_translation_destination ( const char *a, const char *b ) + { + + for ( std::map<std::string,TranslationDestination>::iterator i = _translations.begin(); + i != _translations.end(); + i++ ) + { + if ( !strcmp( i->second.path.c_str(), a ) ) + { + i->second.path = b; + } + } + } + + void + Endpoint::rename_translation_source ( const char *a, const char *b ) + { + std::map<std::string,TranslationDestination>::iterator i = _translations.find( a ); + + if ( i != _translations.end() ) + { + _translations[b] = _translations[a]; + + _translations.erase( i ); + } + } + + int + Endpoint::ntranslations ( void ) + { + return _translations.size(); + } + + bool + Endpoint::get_translation ( int n, const char **from, const char **to ) + { + int j = 0; + for ( std::map<std::string,TranslationDestination>::const_iterator i = _translations.begin(); + i != _translations.end(); + i++, j++) + { + if ( j == n ) + { + *from = i->first.c_str(); + *to = i->second.path.c_str(); + return true; + } + } + + return false; + } + int Endpoint::osc_generic ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) { @@ -724,13 +718,22 @@ namespace OSC } { - std::map<std::string,TranslationDestination>::const_iterator i = ep->_translations.find( path ); + std::map<std::string,TranslationDestination>::iterator i = ep->_translations.find( path ); if ( i != ep->_translations.end() ) { const char *dpath = i->second.path.c_str(); - DMESSAGE( "Translating message \"%s\" to \"%s\"", path, dpath ); +// DMESSAGE( "Translating message \"%s\" to \"%s\"", path, dpath ); + + if ( !strcmp(types, "f" )) + { +// DMESSAGE( "recording value %f", argv[0]->f ); + i->second.current_value = argv[0]->f; + } + + i->second.suppress_feedback = true; + lo_send_message(ep->_addr, dpath, msg ); return 0; } @@ -782,7 +785,6 @@ namespace OSC path, o->path(), o->_direction == Signal::Input ? "in" : "out", - o->id(), o->parameter_limits().min, o->parameter_limits().max, o->parameter_limits().default_value @@ -821,7 +823,6 @@ namespace OSC j != (*i)->_signals.end(); ++j ) { -// DMESSAGE( "Running callback" ); if ( _peer_signal_notification_callback ) _peer_signal_notification_callback( *j, OSC::Signal::Created, v ); } @@ -874,20 +875,20 @@ namespace OSC bool Endpoint::disconnect_signal ( OSC::Signal *s, OSC::Signal *d ) { - if ( ! s->is_connected_to( d ) ) - return false; + /* if ( ! s->is_connected_to( d ) ) */ + /* return false; */ if ( d->_peer ) { MESSAGE( "Disconnecting signal output \"%s\" from %s:%s", s->path(), d->_peer->name, d->_path ); - send( d->_peer->addr, "/signal/disconnect", - s->_id, /* our signal id */ - d->_id /* their signal id */ ); + /* send( d->_peer->addr, "/signal/disconnect", */ + /* s->_id, /\* our signal id *\/ */ + /* d->_id /\* their signal id *\/ ); */ } else { - MESSAGE( "Disconnecting signal output \"%s\" to (unknown):%i", s->path(), d->_id ); + /* MESSAGE( "Disconnecting signal output \"%s\" to (unknown):%i", s->path(), d->_id ); */ } s->_outgoing.remove( d ); @@ -897,16 +898,16 @@ namespace OSC } bool - Endpoint::disconnect_signal ( OSC::Signal *s, const char *peer_name, const char *signal_path ) + Endpoint::disconnect_signal ( OSC::Signal *s, const char *signal_path ) { if ( s->_direction == Signal::Output ) { - Peer *p = find_peer_by_name( peer_name ); + /* Peer *p = find_peer_by_name( peer_name ); */ - if ( ! p ) - return false; + /* if ( ! p ) */ + /* return false; */ - Signal *ps = find_peer_signal_by_path( p, signal_path ); + Signal *ps = find_signal_by_path( signal_path ); if ( ! ps ) return false; @@ -920,101 +921,47 @@ namespace OSC return false; } - /** Connect to name signal. If the destination doesn't currently * - exist, but appears at some later point, the connection will be made - then */ - bool - Endpoint::connect_signal( OSC::Signal *s, const char *peer_and_path ) - { - char peer[512]; - char path[1024]; - /* FIXME: use %a */ - if ( 2 == sscanf( peer_and_path, "%[^:]:%s", peer, path ) ) - { - return connect_signal( s, peer, path ); - } - else - return false; - } + /* bool */ + /* Endpoint::connect_signal ( OSC::Signal *s, OSC::Signal *d ) */ + /* { */ + /* /\* if ( s->is_connected_to( d ) ) *\/ */ + /* /\* { *\/ */ + /* /\* return false; *\/ */ + /* /\* } *\/ */ + + /* MESSAGE( "Connecting signal output \"%s\" to %s:%s", s->path(), d->_peer->name, d->path() ); */ + + /* s->_outgoing.push_back( d ); */ - bool - Endpoint::connect_signal ( OSC::Signal *s, OSC::Signal *d ) - { - if ( s->is_connected_to( d ) ) - { - return false; - } + /* /\* make a record of it ourselves *\/ */ + /* d->_incoming.push_back( s ); */ - MESSAGE( "Connecting signal output \"%s\" to %s:%s", s->path(), d->_peer->name, d->path() ); + /* send( d->_peer->addr, "/signal/connect", */ + /* s->path(), */ + /* d->path() ); */ - s->_outgoing.push_back( d ); - - /* make a record of it ourselves */ - d->_incoming.push_back( s ); - - send( d->_peer->addr, "/signal/connect", - s->_id, /* our signal id */ - d->_id /* their signal id */ ); - - return true; - } + /* return true; */ + /* } */ bool - Endpoint::connect_signal( OSC::Signal *s, const char *peer_name, const char *signal_path ) + Endpoint::connect_signal( OSC::Signal *s, const char *signal_path ) { if ( s->_direction == Signal::Output ) { - Peer *p = find_peer_by_name( peer_name ); + for ( std::list<Peer*>::iterator i = _peers.begin(); + i != _peers.end(); + i++ ) + { - if ( ! p ) - return false; - - Signal *ps = find_peer_signal_by_path( p, signal_path ); - - if ( ! ps ) - return false; - - return connect_signal( s, ps ); - } - - return false; - } - - bool - Endpoint::connect_signal( OSC::Signal *s, const char *peer_name, int signal_id ) - { - if ( s->_direction == Signal::Output ) - { - Peer *p = find_peer_by_name( peer_name ); - - if ( !p ) - return false; - - Signal *ps = find_peer_signal_by_id( p, signal_id ); - - if ( !ps ) - return false; - - return connect_signal( s, ps ); - } - - return false; - } - - Signal * - Signal::get_peer_signal_by_id ( Peer *p, int signal_id ) - { - for ( std::list<Signal *>::iterator i = p->_signals.begin(); - i != p->_signals.end(); - ++i ) - { - if ( (*i)->_id == signal_id ) - return *i; + send( (*i)->addr, "/signal/connect", + s->path(), + signal_path ); + } } - return NULL; + return true; } int @@ -1041,8 +988,13 @@ namespace OSC if ( ep->_peer_scan_complete_callback ) ep->_peer_scan_complete_callback(ep->_peer_scan_complete_userdata); } - else if ( argc == 7 && p->_scanning ) + else if ( argc == 6 && p->_scanning ) { + Signal *s = ep->find_peer_signal_by_path( p, &argv[1]->s ); + + if ( s ) + return 0; + DMESSAGE( "Peer %s has signal %s (%s)", p->name, &argv[1]->s, &argv[2]->s ); int dir = 0; @@ -1052,25 +1004,16 @@ namespace OSC else if ( !strcmp( &argv[2]->s, "out" ) ) dir = Signal::Output; - Signal *s = ep->find_peer_signal_by_id( p, argv[3]->i ); - - if ( s && s->id() == argv[3]->i ) - { - /* found a stub signal, fill in the blanks */ - s->_path = strdup(&argv[1]->s); - s->parameter_limits( argv[4]->f, argv[5]->f, argv[6]->f ); - } - else - { s = new Signal( &argv[1]->s, (Signal::Direction)dir ); s->_peer = p; - s->_id = argv[3]->i; - s->parameter_limits( argv[4]->f, argv[5]->f, argv[6]->f ); + + s->parameter_limits( argv[3]->f, argv[4]->f, argv[5]->f ); p->_signals.push_back( s ); - } + + // ep->_signals.push_back(s); if ( ep->_peer_signal_notification_callback ) ep->_peer_signal_notification_callback( s, Signal::Created, ep->_peer_signal_notification_userdata ); @@ -1108,21 +1051,24 @@ namespace OSC { Signal *o = new Signal( path, dir ); - if ( path ) - o->_path = strdup( path ); + char *s; + asprintf( &s, "%s%s", name(), path ); + + if ( s ) + o->_path = s; o->_handler = handler; o->_user_data = user_data; o->_endpoint = this; + + o->parameter_limits( min, max, default_value ); _signals.push_back( o ); - if ( dir == Signal::Input ) - { - lo_server_add_method( _server, path, NULL, osc_sig_handler, o ); - } - - o->parameter_limits( min, max, default_value ); + /* if ( dir == Signal::Input ) */ + /* { */ + lo_server_add_method( _server, o->_path, NULL, osc_sig_handler, o ); + /* } */ /* tell our peers about it */ for ( std::list<Peer*>::iterator i = _peers.begin(); @@ -1133,7 +1079,6 @@ namespace OSC "/signal/created", o->path(), o->_direction == Signal::Input ? "in" : "out", - o->id(), min, max, default_value @@ -1178,21 +1123,6 @@ namespace OSC _methods.remove( meth ); } - void - Endpoint::send_signal_rename_notifications ( Signal *s ) - { - - for ( std::list<Peer*>::const_iterator i = _peers.begin(); - i != _peers.end(); - ++i ) - { - send( (*i)->addr, - "/signal/renamed", - s->id(), - s->path() ); - } - } - void Endpoint::del_signal ( Signal *o ) { @@ -1207,42 +1137,41 @@ namespace OSC { send( (*i)->addr, "/signal/removed", - o->id() ); + o->path() ); } Endpoint *ep = this; - /* disconnect it */ - if ( o->_outgoing.size() ) - { - for ( std::list<Signal*>::iterator i = o->_outgoing.begin(); - i != o->_outgoing.end(); - ) - { - Signal *s = *i; - /* avoid messing up iterator */ - ++i; + /* /\* disconnect it *\/ */ + /* if ( o->_outgoing.size() ) */ + /* { */ + /* for ( std::list<Signal*>::iterator i = o->_outgoing.begin(); */ + /* i != o->_outgoing.end(); */ + /* ) */ + /* { */ + /* Signal *s = *i; */ + /* /\* avoid messing up iterator *\/ */ + /* ++i; */ - ep->disconnect_signal( o, s ); - } - } + /* ep->disconnect_signal( o, s ); */ + /* } */ + /* } */ - if ( o->_incoming.size() ) - { - for ( std::list<Signal*>::iterator i = o->_incoming.begin(); - i != o->_incoming.end(); - ) - { - Signal *s = *i; - /* avoid messing up iterator */ - ++i; + /* if ( o->_incoming.size() ) */ + /* { */ + /* for ( std::list<Signal*>::iterator i = o->_incoming.begin(); */ + /* i != o->_incoming.end(); */ + /* ) */ + /* { */ + /* Signal *s = *i; */ + /* /\* avoid messing up iterator *\/ */ + /* ++i; */ - ep->disconnect_signal( s, o ); - } - } + /* ep->disconnect_signal( s, o ); */ + /* } */ + /* } */ /* FIXME: clear loopback connections first! */ -// delete o; _signals.remove( o ); } @@ -1271,11 +1200,11 @@ namespace OSC if ( ! strcmp( i->second.path.c_str(), path ) ) { /* found it */ - if ( i->second.current_value != v ) + if ( !i->second.suppress_feedback && i->second.current_value != v ) { const char *spath = i->first.c_str(); - DMESSAGE( "Sending feedback to \"%s\": %f", spath, v ); +// DMESSAGE( "Sending feedback to \"%s\": %f", spath, v ); /* send to all peers */ for ( std::list<Peer*>::iterator p = _peers.begin(); @@ -1288,6 +1217,10 @@ namespace OSC i->second.current_value = v; } + i->second.suppress_feedback = false; + + /* break; */ + } } } @@ -1565,6 +1498,12 @@ namespace OSC { return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sssifff", v1, v2, v3, v4, v5, v6, v7 ); } + + int + Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, float v4, float v5, float v6 ) + { + return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sssfff", v1, v2, v3, v4, v5, v6 ); + } int Endpoint::send ( lo_address to, const char *path, const char *v1, int v2, int v3 ) diff --git a/nonlib/OSC/Endpoint.H b/nonlib/OSC/Endpoint.H index d0e5d18..2bad507 100644 --- a/nonlib/OSC/Endpoint.H +++ b/nonlib/OSC/Endpoint.H @@ -131,7 +131,7 @@ namespace OSC class Signal { - static int next_id; +// static int next_id; public: @@ -152,8 +152,6 @@ namespace OSC Peer *_peer; - int _id; - char *_path; char *_documentation; @@ -180,15 +178,12 @@ namespace OSC Signal ( const char *path, Direction dir ); ~Signal ( ); - static Signal *get_peer_signal_by_id ( Peer *p, int signal_id ); int noutput_connections() { return _outgoing.size(); } bool connected ( void ) const { return _outgoing.size() + _incoming.size(); } char * get_output_connection_peer_name_and_path ( int n ); - int id ( void ) const { return _id; } - Direction direction ( void ) const { return _direction; } void parameter_limits ( float min, float max, float default_value ) @@ -218,6 +213,8 @@ namespace OSC float value ( void ) const { return _value; } bool is_connected_to ( const Signal *s ) const; + + void learn_connection ( void ); friend class Endpoint; }; @@ -243,6 +240,9 @@ namespace OSC class Endpoint { Thread _thread; + + friend class Signal; + Signal *_learn_signal; // lo_server_thread _st; lo_server _server; @@ -259,6 +259,13 @@ namespace OSC public: std::string path; float current_value; + bool suppress_feedback; + + TranslationDestination ( ) + { + suppress_feedback = false; + current_value = -1.0f; + } }; std::map<std::string,TranslationDestination> _translations; @@ -291,9 +298,8 @@ namespace OSC static void *osc_thread ( void *arg ); void osc_thread ( void ); - OSC::Signal *find_signal_by_id ( int id ); OSC::Signal *find_peer_signal_by_path ( Peer *p, const char *path ); - OSC::Signal *find_peer_signal_by_id ( Peer *p, int id ); + OSC::Signal *find_signal_by_path ( const char *path ); Peer *find_peer_by_name ( const char *name ); Peer *find_peer_by_address ( lo_address addr ); @@ -309,7 +315,7 @@ namespace OSC void *_peer_signal_notification_userdata; public: - + void send_feedback ( const char *path, float v ); void learn ( const char *path ); @@ -318,8 +324,14 @@ namespace OSC return _addr; } + void clear_translations ( void ); + void del_translation ( const char *a ); void add_translation ( const char *a, const char *b ); - + void rename_translation_destination ( const char *a, const char *b ); + void rename_translation_source ( const char *a, const char *b ); + int ntranslations ( void ); + bool get_translation ( int n, const char **from, const char **to ); + void peer_signal_notification_callback ( void (*cb)(OSC::Signal *, OSC::Signal::State, void*), void *userdata ) { _peer_signal_notification_callback = cb; @@ -338,10 +350,10 @@ namespace OSC ~Endpoint ( ); bool disconnect_signal ( OSC::Signal *s, OSC::Signal *d ); - bool disconnect_signal ( OSC::Signal *s, const char *peer_name, const char *signal_path ); + bool disconnect_signal ( OSC::Signal *s, const char *signal_path ); bool connect_signal ( OSC::Signal *s, OSC::Signal *d ); bool connect_signal ( OSC::Signal *s, const char *peer_name, const char *signal_path ); - bool connect_signal ( OSC::Signal *s, const char *peer_name, int signal_id ); +// bool connect_signal ( OSC::Signal *s, const char *peer_name, int signal_id ); bool connect_signal ( OSC::Signal *s, const char *peer_and_path ); Signal * add_signal ( const char *path, Signal::Direction dir, float min, float max, float default_value, signal_handler handler, void *user_data ); @@ -395,6 +407,8 @@ namespace OSC 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, const char *v3, float v4, float v5, float v6 ); + int send ( lo_address to, const char *path, const char *v1, const char *v2, int v3, float v4, float v5, float v6 ); int send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, int v4, float v5, float v6, float v7 ); diff --git a/nonlib/wscript b/nonlib/wscript index 6c5d234..0683074 100644 --- a/nonlib/wscript +++ b/nonlib/wscript @@ -19,6 +19,7 @@ Thread.C debug.C dsp.C file.C +MIDI/midievent.C string_util.C ''', includes = '.', diff --git a/timeline/src/Control_Sequence.C b/timeline/src/Control_Sequence.C index 6575ede..560b629 100644 --- a/timeline/src/Control_Sequence.C +++ b/timeline/src/Control_Sequence.C @@ -133,17 +133,45 @@ Control_Sequence::name ( const char *s ) if ( mode() == CV ) update_port_name(); + else + update_osc_path(); redraw(); } +void +Control_Sequence::update_osc_path ( void ) +{ + char *path; + asprintf( &path, "/track/%s/%s", track()->name(), name() ); + + char *s = escape_url( path ); + + free( path ); + + path = s; + + if ( !_osc_output() ) + { + OSC::Signal *t = timeline->osc->add_signal( path, OSC::Signal::Output, 0, 1, 0, NULL, NULL ); + + _osc_output( t ); + } + else + { + _osc_output()->rename( path ); + } + + free(path); +} + void Control_Sequence::update_port_name ( void ) { bool needs_activation = false; if ( ! _output ) { - _output = new JACK::Port( engine, JACK::Port::Output, track()->name(), track()->ncontrols(), "cv" ); + _output = new JACK::Port( engine, JACK::Port::Output, JACK::Port::Audio, track()->name(), track()->ncontrols(), "cv" ); needs_activation = true; } @@ -332,23 +360,7 @@ Control_Sequence::mode ( Mode m ) } else if ( OSC == m && mode() != OSC ) { - /* FIXME: use name here... */ - char *path; - asprintf( &path, "/track/%s/%s", track()->name(), name() ); - - char *s = escape_url( path ); - - free( path ); - - path = s; - - OSC::Signal *t = timeline->osc->add_signal( path, OSC::Signal::Output, 0, 1, 0, NULL, NULL ); - - free( path ); - - _osc_output( t ); - - DMESSAGE( "osc_output: %p", _osc_output() ); + update_osc_path(); header()->outputs_indicator->label( "osc" ); } @@ -524,24 +536,21 @@ Control_Sequence::menu_cb ( const Fl_Menu_ *m ) const char *path = ((OSC::Signal*)m->mvalue()->user_data())->path(); - char *peer_and_path; - asprintf( &peer_and_path, "%s:%s", peer_name, path ); - if ( ! _osc_output()->is_connected_to( ((OSC::Signal*)m->mvalue()->user_data()) ) ) { - _persistent_osc_connections.push_back( peer_and_path ); + _persistent_osc_connections.push_back( strdup(path) ); connect_osc(); } else { - timeline->osc->disconnect_signal( _osc_output(), peer_name, path ); + timeline->osc->disconnect_signal( _osc_output(), path ); for ( std::list<char*>::iterator i = _persistent_osc_connections.begin(); i != _persistent_osc_connections.end(); ++i ) { - if ( !strcmp( *i, peer_and_path ) ) + if ( !strcmp( *i, path ) ) { free( *i ); i = _persistent_osc_connections.erase( i ); @@ -549,7 +558,7 @@ Control_Sequence::menu_cb ( const Fl_Menu_ *m ) } } - free( peer_and_path ); + //free( path ); } } @@ -619,7 +628,7 @@ Control_Sequence::process_osc ( void ) if ( mode() != OSC ) return; - if ( _osc_output() && _osc_output()->connected() ) + if ( _osc_output() ) { sample_t buf[1]; @@ -646,10 +655,12 @@ Control_Sequence::peer_callback( OSC::Signal *sig, OSC::Signal::State state, vo if ( sig->direction() != OSC::Signal::Input ) return; +// DMESSAGE( "Paramter limits: %f %f", sig->parameter_limits().min, sig->parameter_limits().max ); + /* only list CV signals for now */ if ( ! ( sig->parameter_limits().min == 0.0 && sig->parameter_limits().max == 1.0 ) ) - return; + return; if ( ! v ) { @@ -669,11 +680,12 @@ Control_Sequence::peer_callback( OSC::Signal *sig, OSC::Signal::State state, vo unescape_url( path ); - asprintf( &s, "%s/%s%s", peer_prefix, name, path ); + asprintf( &s, "%s/%s", peer_prefix, path ); - peer_menu->add( s, 0, NULL, (void*)( sig ), - FL_MENU_TOGGLE | - ( ((Control_Sequence*)v)->_osc_output()->is_connected_to( sig ) ? FL_MENU_VALUE : 0 ) ); + peer_menu->add( s, 0, NULL, (void*)( sig ), 0 ); + + /* FL_MENU_TOGGLE | */ + /* ( ((Control_Sequence*)v)->_osc_output()->is_connected_to( sig ) ? FL_MENU_VALUE : 0 ) ); */ free( path ); diff --git a/timeline/src/Control_Sequence.H b/timeline/src/Control_Sequence.H index ac0bc72..38e9822 100644 --- a/timeline/src/Control_Sequence.H +++ b/timeline/src/Control_Sequence.H @@ -102,6 +102,7 @@ protected: void draw ( void ); int handle ( int m ); + void update_osc_path ( void ); void update_port_name ( void ); diff --git a/timeline/src/Engine/Track.C b/timeline/src/Engine/Track.C index 56b692a..d4ec167 100644 --- a/timeline/src/Engine/Track.C +++ b/timeline/src/Engine/Track.C @@ -86,7 +86,7 @@ Track::configure_outputs ( int n ) { for ( int i = on; i < n; ++i ) { - JACK::Port p( engine, JACK::Port::Output, name(), i ); + JACK::Port p( engine, JACK::Port::Output, JACK::Port::Audio, name(), i ); if ( !p.activate() ) { @@ -139,7 +139,7 @@ Track::configure_inputs ( int n ) { for ( int i = on; i < n; ++i ) { - JACK::Port p( engine, JACK::Port::Input, name(), i ); + JACK::Port p( engine, JACK::Port::Input, JACK::Port::Audio, name(), i ); if ( !p.activate() ) { diff --git a/timeline/src/OSC_Thread.C b/timeline/src/OSC_Thread.C index 50b1153..2f2acf0 100644 --- a/timeline/src/OSC_Thread.C +++ b/timeline/src/OSC_Thread.C @@ -73,7 +73,7 @@ OSC_Thread::process ( void ) unlock(); } - usleep( 100 * 1000 ); + usleep( 50 * 1000 ); } DMESSAGE( "OSC Thread stopping." ); diff --git a/timeline/src/Timeline.C b/timeline/src/Timeline.C index b708081..f1e46de 100644 --- a/timeline/src/Timeline.C +++ b/timeline/src/Timeline.C @@ -74,7 +74,7 @@ bool Timeline::snap_magnetic = true; bool Timeline::follow_playhead = true; bool Timeline::center_playhead = true; -const float UPDATE_FREQ = 1.0 / 18.0f; +const float UPDATE_FREQ = 1.0f / 18.0f; extern const char *instance_name; extern TLE *tle;