Implement the Non-Session-Manager (NSM)
This commit is contained in:
parent
cff2c15583
commit
4a22d675e2
2
Makefile
2
Makefile
|
@ -17,7 +17,7 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
|
||||
###############################################################################
|
||||
|
||||
SUBDIRS=nonlib FL timeline mixer
|
||||
SUBDIRS=nonlib FL timeline mixer session
|
||||
|
||||
all:
|
||||
@ for dir in $(SUBDIRS); do $(MAKE) -s -C $$dir; done
|
||||
|
|
|
@ -422,7 +422,7 @@ Chain::can_configure_outputs ( Module *m, int n ) const
|
|||
unsigned int
|
||||
Chain::maximum_name_length ( void )
|
||||
{
|
||||
return JACK::Client::maximum_name_length() - ( strlen( APP_NAME ) + 1 + ( instance_name ? strlen( instance_name ) + 1 : 0 ) );
|
||||
return JACK::Client::maximum_name_length() - ( strlen( instance_name ) + 1 );
|
||||
}
|
||||
|
||||
/* rename chain... we have to let our modules know our name has
|
||||
|
@ -432,7 +432,7 @@ void
|
|||
Chain::name ( const char *name )
|
||||
{
|
||||
char ename[512];
|
||||
snprintf( ename, sizeof(ename), "%s%s%s/%s", APP_NAME, instance_name ? "." : "", instance_name ? instance_name : "", name );
|
||||
snprintf( ename, sizeof(ename), "%s/%s", instance_name, name );
|
||||
|
||||
if ( ! _engine )
|
||||
{
|
||||
|
|
|
@ -71,14 +71,14 @@ Controller_Module::Controller_Module ( bool is_default ) : Module( is_default, 5
|
|||
|
||||
end();
|
||||
|
||||
Fl::add_timeout( CONTROL_UPDATE_FREQ, update_cb, this );
|
||||
// Fl::add_timeout( CONTROL_UPDATE_FREQ, update_cb, this );
|
||||
|
||||
log_create();
|
||||
}
|
||||
|
||||
Controller_Module::~Controller_Module ( )
|
||||
{
|
||||
Fl::remove_timeout( update_cb, this );
|
||||
// Fl::remove_timeout( update_cb, this );
|
||||
|
||||
log_destroy();
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
|
||||
|
||||
const float METER_UPDATE_FREQ = 0.1f;
|
||||
const float METER_UPDATE_FREQ = 0.2f;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -28,31 +28,39 @@
|
|||
#include <FL/Fl_Menu_Bar.H>
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include "New_Project_Dialog.H"
|
||||
#include "Engine/Engine.H"
|
||||
#include "FL/Fl_Flowpack.H"
|
||||
#include "Project.H"
|
||||
#include "FL/Fl_Menu_Settings.H"
|
||||
#include "About_Dialog.H"
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
|
||||
#include "file.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "FL/color_scheme.H"
|
||||
#include "OSC/Endpoint.H"
|
||||
#include <lo/lo.h>
|
||||
#include "FL/Fl_Blinker.H"
|
||||
|
||||
const double STATUS_UPDATE_FREQ = 0.2f;
|
||||
|
||||
const double OSC_INTERVAL = 0.1f;
|
||||
const double OSC_INTERVAL = 0.2f;
|
||||
|
||||
extern char *user_config_dir;
|
||||
extern char *instance_name;
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "NSM.H"
|
||||
|
||||
extern NSM_Client *nsm;
|
||||
|
||||
/* static void update_cb( void *v ) { */
|
||||
/* Fl::repeat_timeout( STATUS_UPDATE_FREQ, update_cb, v ); */
|
||||
|
||||
|
@ -65,69 +73,19 @@ extern char *user_config_dir;
|
|||
/* OSC Message Handlers */
|
||||
/************************/
|
||||
|
||||
#undef OSC_REPLY_OK
|
||||
#undef OSC_REPLY_ERR
|
||||
#undef OSC_REPLY
|
||||
|
||||
OSC_HANDLER( quit )
|
||||
{
|
||||
OSC_DMSG();
|
||||
|
||||
((Mixer*)user_data)->command_quit();
|
||||
|
||||
OSC_REPLY_OK();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSC_HANDLER( save )
|
||||
{
|
||||
OSC_DMSG();
|
||||
|
||||
if ( ((Mixer*)user_data)->command_save() )
|
||||
OSC_REPLY_OK();
|
||||
else
|
||||
OSC_REPLY_ERR();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSC_HANDLER( load )
|
||||
{
|
||||
OSC_DMSG();
|
||||
|
||||
const char *project_path = &argv[0]->s;
|
||||
const char *project_display_name = &argv[1]->s;
|
||||
|
||||
if ( ((Mixer*)user_data)->command_load( project_path, project_display_name ) )
|
||||
OSC_REPLY_OK();
|
||||
else
|
||||
OSC_REPLY_ERR();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSC_HANDLER( new )
|
||||
{
|
||||
OSC_DMSG();
|
||||
|
||||
const char *project_path = &argv[0]->s;
|
||||
const char *project_display_name = &argv[1]->s;
|
||||
|
||||
if ( ((Mixer*)user_data)->command_new( project_path, project_display_name ) )
|
||||
OSC_REPLY_OK();
|
||||
else
|
||||
OSC_REPLY_ERR();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// OSC_HANDLER( root )
|
||||
// {
|
||||
// }
|
||||
#define OSC_REPLY_OK() ((OSC::Endpoint*)user_data)->send( lo_message_get_source( msg ), path, 0, "OK" )
|
||||
#define OSC_REPLY( value ) ((OSC::Endpoint*)user_data)->send( lo_message_get_source( msg ), path, value )
|
||||
#define OSC_REPLY_ERR(errcode, value) ((OSC::Endpoint*)user_data)->send( lo_message_get_source( msg ), path,errcode, value )
|
||||
|
||||
OSC_HANDLER( add_strip )
|
||||
{
|
||||
OSC_DMSG();
|
||||
|
||||
((Mixer*)user_data)->command_add_strip();
|
||||
((Mixer*)(OSC_ENDPOINT())->owner)->command_add_strip();
|
||||
|
||||
OSC_REPLY_OK();
|
||||
|
||||
|
@ -138,20 +96,45 @@ OSC_HANDLER( finger )
|
|||
{
|
||||
OSC_DMSG();
|
||||
|
||||
lo_address src = lo_message_get_source( msg );
|
||||
OSC::Endpoint *ep = ((OSC::Endpoint*)user_data);
|
||||
|
||||
const char *s = "APP_TITLE\n";
|
||||
lo_address reply = lo_address_new_from_url( &argv[0]->s );
|
||||
|
||||
/* if ( 1 >= lo_send_from( src, ((Mixer*)user_data)->osc_endpoint, LO_TT_IMMEDIATE, "/finger-reply", "s", s ) ) */
|
||||
/* { */
|
||||
/* DMESSAGE( "Failed to send reply" ); */
|
||||
/* } */
|
||||
ep->send( reply,
|
||||
"/reply",
|
||||
path,
|
||||
ep->url(),
|
||||
APP_NAME,
|
||||
VERSION,
|
||||
instance_name );
|
||||
|
||||
lo_address_free( reply );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static
|
||||
Fl_Menu_Item *
|
||||
find_item( Fl_Menu_ *menu, const char *path )
|
||||
{
|
||||
return const_cast<Fl_Menu_Item*>(menu->find_item( path ));
|
||||
}
|
||||
|
||||
void
|
||||
Mixer::sm_active ( bool b )
|
||||
{
|
||||
sm_blinker->value( b );
|
||||
sm_blinker->tooltip( nsm->session_manager_name() );
|
||||
|
||||
if ( b )
|
||||
{
|
||||
find_item( menubar, "&Project/&Open" )->deactivate();
|
||||
find_item( menubar, "&Project/&New" )->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::cb_menu(Fl_Widget* o) {
|
||||
Fl_Menu_Bar *menu = (Fl_Menu_Bar*)o;
|
||||
|
@ -347,6 +330,19 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) :
|
|||
o->align( FL_ALIGN_INSIDE | FL_ALIGN_CENTER );
|
||||
o->labeltype( FL_SHADOW_LABEL );
|
||||
}
|
||||
{ sm_blinker = new Fl_Blinker( ( X + W) - 52, Y + 4, 50, 15, "SM");
|
||||
sm_blinker->box(FL_ROUNDED_BOX);
|
||||
sm_blinker->down_box(FL_ROUNDED_BOX);
|
||||
sm_blinker->color((Fl_Color)75);
|
||||
sm_blinker->selection_color((Fl_Color)86);
|
||||
sm_blinker->labeltype(FL_NORMAL_LABEL);
|
||||
sm_blinker->labelfont(2);
|
||||
sm_blinker->labelsize(14);
|
||||
sm_blinker->labelcolor(FL_DARK3);
|
||||
sm_blinker->align(Fl_Align(FL_ALIGN_CENTER));
|
||||
sm_blinker->when(FL_WHEN_RELEASE);
|
||||
sm_blinker->deactivate();
|
||||
} // Fl_Blinker* sm_blinker
|
||||
{ Fl_Scroll *o = scroll = new Fl_Scroll( X, Y + 24, W, H - 24 );
|
||||
o->box( FL_NO_BOX );
|
||||
// o->type( Fl_Scroll::HORIZONTAL_ALWAYS );
|
||||
|
@ -375,22 +371,20 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) :
|
|||
load_options();
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
Mixer::init_osc ( const char *osc_port )
|
||||
{
|
||||
osc_endpoint = new OSC::Endpoint(osc_port);
|
||||
osc_endpoint = new OSC::Endpoint();
|
||||
|
||||
osc_endpoint->url();
|
||||
if ( int r = osc_endpoint->init( osc_port ) )
|
||||
return r;
|
||||
|
||||
// if ( 1 >= lo_send_from( src, ((Mixer*)user_data)->osc_endpoint, LO_TT_IMMEDIATE, "/finger-reply", "s", s ) )
|
||||
osc_endpoint->owner = this;
|
||||
|
||||
osc_endpoint->add_method( "/nsm/quit", "", OSC_NAME( quit ), this, "" );
|
||||
osc_endpoint->add_method( "/nsm/load", "ss", OSC_NAME( load ), this, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/save", "", OSC_NAME( save ), this, "" );
|
||||
osc_endpoint->add_method( "/nsm/new", "ss", OSC_NAME( new ), this, "path,display_name" );
|
||||
// osc_endpoint->add_method( "/nsm/", "", OSC_NAME( root ), this );
|
||||
osc_endpoint->add_method( "/finger", "", OSC_NAME( finger ), this, "" );
|
||||
osc_endpoint->add_method( "/mixer/add_strip", "", OSC_NAME( add_strip ), this, "" );
|
||||
printf( "OSC=%s\n", osc_endpoint->url() );
|
||||
|
||||
osc_endpoint->add_method( "/non/finger", "s", OSC_NAME( finger ), osc_endpoint, "" );
|
||||
osc_endpoint->add_method( "/non/mixer/add_strip", "", OSC_NAME( add_strip ), osc_endpoint, "" );
|
||||
|
||||
// osc_endpoint->start();
|
||||
|
||||
|
@ -680,6 +674,8 @@ Mixer::command_save ( void )
|
|||
bool
|
||||
Mixer::command_load ( const char *path, const char *display_name )
|
||||
{
|
||||
mixer->hide();
|
||||
|
||||
if ( int err = Project::open( path ) )
|
||||
{
|
||||
// fl_alert( "Error opening project specified on commandline: %s", Project::errstr( err ) );
|
||||
|
@ -691,6 +687,8 @@ Mixer::command_load ( const char *path, const char *display_name )
|
|||
|
||||
update_menu();
|
||||
|
||||
mixer->show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <FL/Fl_Pack.H>
|
||||
#include "Mixer_Strip.H"
|
||||
|
||||
class Fl_Blinker;
|
||||
class Fl_Flowpack;
|
||||
class Fl_Menu_Bar;
|
||||
|
||||
|
@ -37,6 +38,7 @@ class Mixer : public Fl_Group
|
|||
public:
|
||||
|
||||
OSC::Endpoint *osc_endpoint;
|
||||
Fl_Blinker *sm_blinker;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -93,9 +95,11 @@ public:
|
|||
|
||||
static void check_osc ( void * v );
|
||||
|
||||
void announce ( const char *nash_url );
|
||||
void announce ( const char *nash_url, const char *process_name );
|
||||
|
||||
void init_osc ( const char* osc_port );
|
||||
int init_osc ( const char* osc_port );
|
||||
|
||||
void sm_active ( bool b );
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -213,11 +213,9 @@ Module::Port::generate_osc_path ()
|
|||
int n = module()->chain()->get_module_instance_number( module() );
|
||||
|
||||
if ( n > 0 )
|
||||
asprintf( &path, "/mixer/strip/%s/control/%s.%i/%s", module()->chain()->name(), p->module()->label(), n, p->name() );
|
||||
asprintf( &path, "/non/mixer/strip/%s/control/%s.%i/%s", module()->chain()->name(), p->module()->label(), n, p->name() );
|
||||
else
|
||||
asprintf( &path, "/mixer/strip/%s/control/%s/%s", module()->chain()->name(), p->module()->label(), p->name() );
|
||||
|
||||
// asprintf( &path, "/mixer/strip/control/%s/%s", p->module()->label(), p->name() );
|
||||
asprintf( &path, "/non/mixer/strip/%s/control/%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.
|
||||
for ( int i = strlen( path ); i--; )
|
||||
|
@ -254,6 +252,30 @@ Module::Port::change_osc_path ( char *path )
|
|||
mixer->osc_endpoint->add_method( _osc_path, "f", &Module::Port::osc_control_change_cv, this, "value" );
|
||||
|
||||
mixer->osc_endpoint->add_method( _osc_path_unscaled, "f", &Module::Port::osc_control_change_exact, this, "value" );
|
||||
|
||||
if ( hints.ranged )
|
||||
{
|
||||
mixer->osc_endpoint->set_parameter_limits( _osc_path_unscaled, "f", 0,
|
||||
hints.minimum,
|
||||
hints.maximum,
|
||||
hints.default_value );
|
||||
|
||||
}
|
||||
|
||||
float scaled_default = 0.5f;
|
||||
|
||||
if ( hints.ranged )
|
||||
{
|
||||
float scale = hints.maximum - hints.minimum;
|
||||
// float offset = hints.minimum;
|
||||
|
||||
scaled_default = ( hints.default_value / scale );
|
||||
}
|
||||
|
||||
mixer->osc_endpoint->set_parameter_limits( _osc_path, "f", 0,
|
||||
0.0f,
|
||||
1.0f,
|
||||
scaled_default );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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 "const.h"
|
||||
#include "debug.h"
|
||||
#include "Mixer.H"
|
||||
#include "NSM.H"
|
||||
#include "Project.H"
|
||||
|
||||
extern char *instance_name;
|
||||
extern Mixer *mixer;
|
||||
|
||||
extern NSM_Client *nsm;
|
||||
|
||||
NSM_Client::NSM_Client ( )
|
||||
{
|
||||
}
|
||||
|
||||
int command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg );
|
||||
int command_save ( char **out_msg );
|
||||
|
||||
|
||||
int
|
||||
NSM_Client::command_save ( char **out_msg )
|
||||
{
|
||||
Fl::lock();
|
||||
|
||||
int r = ERR_OK;
|
||||
|
||||
if ( ! mixer->command_save() )
|
||||
{
|
||||
*out_msg = strdup( "Failed to save for unknown reason");
|
||||
return r = ERR_GENERAL;
|
||||
}
|
||||
|
||||
Fl::unlock();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
NSM_Client::command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg )
|
||||
{
|
||||
Fl::lock();
|
||||
|
||||
if ( instance_name )
|
||||
free( instance_name );
|
||||
|
||||
instance_name = strdup( client_id );
|
||||
|
||||
int r = ERR_OK;
|
||||
|
||||
if ( Project::validate( name ) )
|
||||
{
|
||||
if ( ! mixer->command_load( name, display_name ) )
|
||||
{
|
||||
*out_msg = strdup( "Failed to load for unknown reason" );
|
||||
r = ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! mixer->command_new( name, display_name ) )
|
||||
{
|
||||
*out_msg = strdup( "Failed to load for unknown reason" );
|
||||
r = ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
|
||||
Fl::unlock();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
NSM_Client::command_active ( bool active )
|
||||
{
|
||||
Fl::lock();
|
||||
|
||||
mixer->sm_active( active );
|
||||
|
||||
Fl::unlock();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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
|
||||
|
||||
#include "NSM/Client.H"
|
||||
|
||||
class NSM_Client : public NSM::Client
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
NSM_Client ( );
|
||||
~NSM_Client ( ) { };
|
||||
|
||||
protected:
|
||||
|
||||
int command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg );
|
||||
int command_save ( char **out_msg );
|
||||
|
||||
void command_active ( bool active );
|
||||
};
|
|
@ -304,7 +304,7 @@ Project::create ( const char *name, const char *template_name )
|
|||
|
||||
if ( mkdir( name, 0777 ) )
|
||||
{
|
||||
WARNING( "Cannot create project directory" );
|
||||
WARNING( "Cannot create project directory: %s", name );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,14 +52,21 @@
|
|||
#include "Chain.H"
|
||||
#include "Mixer_Strip.H"
|
||||
|
||||
#include "NSM.H"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/* TODO: put these in a header */
|
||||
#define USER_CONFIG_DIR ".non-mixer/"
|
||||
|
||||
|
||||
const double OSC_INTERVAL = 0.1f;
|
||||
|
||||
char *user_config_dir;
|
||||
Mixer *mixer;
|
||||
NSM_Client *nsm;
|
||||
|
||||
const char *instance_name;
|
||||
char *instance_name;
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -83,6 +90,32 @@ static void cb_main ( Fl_Double_Window *o, void *)
|
|||
mixer->command_quit();
|
||||
}
|
||||
|
||||
void
|
||||
check_osc ( void * v )
|
||||
{
|
||||
nsm->check();
|
||||
Fl::repeat_timeout( OSC_INTERVAL, check_osc, v );
|
||||
}
|
||||
|
||||
static int got_sigterm = 0;
|
||||
|
||||
void
|
||||
sigterm_handler ( int )
|
||||
{
|
||||
got_sigterm = 1;
|
||||
Fl::awake();
|
||||
}
|
||||
|
||||
void
|
||||
check_sigterm ( void * )
|
||||
{
|
||||
if ( got_sigterm )
|
||||
{
|
||||
MESSAGE( "Got SIGTERM, quitting..." );
|
||||
mixer->command_quit();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main ( int argc, char **argv )
|
||||
{
|
||||
|
@ -93,6 +126,8 @@ main ( int argc, char **argv )
|
|||
|
||||
ensure_dirs();
|
||||
|
||||
signal( SIGTERM, sigterm_handler );
|
||||
|
||||
Fl_Tooltip::color( FL_BLACK );
|
||||
Fl_Tooltip::textcolor( FL_YELLOW );
|
||||
Fl_Tooltip::size( 14 );
|
||||
|
@ -119,6 +154,8 @@ main ( int argc, char **argv )
|
|||
Fl::get_system_colors();
|
||||
Fl::scheme( "plastic" );
|
||||
|
||||
Fl::lock();
|
||||
|
||||
Plugin_Module::spawn_discover_thread();
|
||||
|
||||
Fl_Double_Window *main_window;
|
||||
|
@ -142,6 +179,10 @@ main ( int argc, char **argv )
|
|||
|
||||
const char *osc_port = NULL;
|
||||
|
||||
nsm = new NSM_Client;
|
||||
|
||||
instance_name = strdup( APP_NAME );
|
||||
|
||||
{
|
||||
int r = argc - 1;
|
||||
int i = 1;
|
||||
|
@ -152,7 +193,7 @@ main ( int argc, char **argv )
|
|||
if ( r > 1 )
|
||||
{
|
||||
MESSAGE( "Using instance name \"%s\"", argv[i+1] );
|
||||
instance_name = argv[i+1];
|
||||
instance_name = strdup( argv[i+1] );
|
||||
--r;
|
||||
++i;
|
||||
}
|
||||
|
@ -185,6 +226,17 @@ main ( int argc, char **argv )
|
|||
|
||||
mixer->init_osc( osc_port );
|
||||
|
||||
char *nsm_url = getenv( "NSM_URL" );
|
||||
|
||||
if ( nsm_url )
|
||||
{
|
||||
if ( ! nsm->init() )
|
||||
{
|
||||
nsm->announce( nsm_url, APP_NAME, ":switch:dirty:", argv[0] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( r >= 1 )
|
||||
{
|
||||
MESSAGE( "Loading \"%s\"", argv[i] );
|
||||
|
@ -194,8 +246,13 @@ main ( int argc, char **argv )
|
|||
fl_alert( "Error opening project specified on commandline" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// poll so we can keep OSC handlers running in the GUI thread and avoid extra sync
|
||||
Fl::add_timeout( OSC_INTERVAL, check_osc, NULL );
|
||||
|
||||
Fl::add_check( check_sigterm );
|
||||
|
||||
Fl::run();
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace JACK
|
|||
jack_client_close( _client );
|
||||
}
|
||||
|
||||
/** Tell JACK to calling process callback. This MUST be called in
|
||||
/** Tell JACK to stop calling process callback. This MUST be called in
|
||||
* an inheriting class' destructor */
|
||||
void
|
||||
Client::deactivate ( )
|
||||
|
@ -201,6 +201,15 @@ namespace JACK
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::close ( void )
|
||||
{
|
||||
jack_deactivate( _client );
|
||||
jack_client_close( _client );
|
||||
|
||||
_client = NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
Client::name ( const char *s )
|
||||
{
|
||||
|
@ -222,4 +231,28 @@ namespace JACK
|
|||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
Client::transport_stop ( )
|
||||
{
|
||||
jack_transport_stop( _client );
|
||||
}
|
||||
|
||||
void
|
||||
Client::transport_start ( )
|
||||
{
|
||||
jack_transport_start( _client );
|
||||
}
|
||||
|
||||
void
|
||||
Client::transport_locate ( nframes_t frame )
|
||||
{
|
||||
jack_transport_locate( _client, frame );
|
||||
}
|
||||
|
||||
jack_transport_state_t
|
||||
Client::transport_query ( jack_position_t *pos )
|
||||
{
|
||||
return jack_transport_query( _client, pos );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace JACK
|
|||
const char * init ( const char *client_name, unsigned int opts = 0 );
|
||||
const char * name ( const char * );
|
||||
|
||||
void close ( void );
|
||||
nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); }
|
||||
float frame_rate ( void ) const { return jack_get_sample_rate( _client ); }
|
||||
static nframes_t sample_rate ( void ) { return _sample_rate; }
|
||||
|
@ -98,6 +99,13 @@ namespace JACK
|
|||
bool zombified ( void ) const { return _zombified; }
|
||||
float cpu_load ( void ) const { return jack_cpu_load( _client ); }
|
||||
|
||||
|
||||
void transport_stop ( void );
|
||||
void transport_start ( void );
|
||||
void transport_locate ( nframes_t frame );
|
||||
jack_transport_state_t transport_query ( jack_position_t *pos );
|
||||
|
||||
|
||||
static int maximum_name_length ( void ) { return jack_client_name_size(); }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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 "../debug.h"
|
||||
#include "Client.H"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace NSM
|
||||
{
|
||||
|
||||
/************************/
|
||||
/* OSC Message Handlers */
|
||||
/************************/
|
||||
|
||||
#undef OSC_REPLY
|
||||
#undef OSC_REPLY_ERR
|
||||
|
||||
#define OSC_REPLY( value ) lo_send_from( ((NSM::Client*)user_data)->nsm_addr, ((NSM::Client*)user_data)->_server, LO_TT_IMMEDIATE, "/reply", "ss", path, value )
|
||||
|
||||
#define OSC_REPLY_ERR( errcode, value ) lo_send_from( ((NSM::Client*)user_data)->nsm_addr, ((NSM::Client*)user_data)->_server, LO_TT_IMMEDIATE, "/error", "sis", path, errcode, value )
|
||||
|
||||
Client::Client ( )
|
||||
{
|
||||
nsm_addr = 0;
|
||||
nsm_client_id = 0;
|
||||
_session_manager_name = 0;
|
||||
nsm_is_active = false;
|
||||
_server = 0;
|
||||
_st = 0;
|
||||
}
|
||||
|
||||
Client::~Client ( )
|
||||
{
|
||||
if ( _st )
|
||||
stop();
|
||||
|
||||
if ( _st )
|
||||
lo_server_thread_free( _st );
|
||||
else
|
||||
lo_server_free ( _server );
|
||||
}
|
||||
|
||||
void
|
||||
Client::announce ( const char *nash_url, const char *application_name, const char *capabilities, const char *process_name )
|
||||
{
|
||||
MESSAGE( "Announcing to NSM" );
|
||||
|
||||
lo_address to = lo_address_new_from_url( nash_url );
|
||||
|
||||
if ( ! to )
|
||||
{
|
||||
MESSAGE( "Bad address" );
|
||||
return;
|
||||
}
|
||||
|
||||
int pid = (int)getpid();
|
||||
|
||||
lo_send_from( to, _server, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
|
||||
application_name,
|
||||
capabilities,
|
||||
process_name,
|
||||
0, /* api_major_version */
|
||||
5, /* api_minor_version */
|
||||
pid );
|
||||
|
||||
lo_address_free( to );
|
||||
}
|
||||
|
||||
void
|
||||
Client::progress ( float p )
|
||||
{
|
||||
if ( nsm_is_active )
|
||||
{
|
||||
lo_send_from( nsm_addr, _server, LO_TT_IMMEDIATE, "/nsm/client/progress", "f", p );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::is_dirty ( void )
|
||||
{
|
||||
if ( nsm_is_active )
|
||||
{
|
||||
lo_send_from( nsm_addr, _server, LO_TT_IMMEDIATE, "/nsm/client/is_dirty", "" );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::is_clean ( void )
|
||||
{
|
||||
if ( nsm_is_active )
|
||||
{
|
||||
lo_send_from( nsm_addr, _server, LO_TT_IMMEDIATE, "/nsm/client/is_clean", "" );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::message ( int priority, const char *msg )
|
||||
{
|
||||
if ( nsm_is_active )
|
||||
{
|
||||
lo_send_from( nsm_addr, _server, LO_TT_IMMEDIATE, "/nsm/client/message", "is", priority, msg );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::check ( )
|
||||
{
|
||||
lo_server_recv_noblock( _server, 0 );
|
||||
}
|
||||
|
||||
void
|
||||
Client::start ( )
|
||||
{
|
||||
lo_server_thread_start( _st );
|
||||
}
|
||||
|
||||
void
|
||||
Client::stop ( )
|
||||
{
|
||||
lo_server_thread_stop( _st );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Client::init ( )
|
||||
{
|
||||
_server = lo_server_new( NULL, NULL );
|
||||
|
||||
if ( ! _server )
|
||||
return -1;
|
||||
|
||||
lo_server_add_method( _server, "/error", "sis", &Client::osc_error, this );
|
||||
lo_server_add_method( _server, "/reply", "ssss", &Client::osc_announce_reply, 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 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Client::init_thread ( )
|
||||
{
|
||||
_st = lo_server_thread_new( NULL, NULL );
|
||||
_server = lo_server_thread_get_server( _st );
|
||||
|
||||
if ( ! _server || ! _st )
|
||||
return -1;
|
||||
|
||||
lo_server_thread_add_method( _st, "/error", "sis", &Client::osc_error, this );
|
||||
lo_server_thread_add_method( _st, "/reply", "ssss", &Client::osc_announce_reply, 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 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************/
|
||||
/* OSC Message Handlers */
|
||||
/************************/
|
||||
|
||||
int
|
||||
Client::osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
char *out_msg = NULL;
|
||||
|
||||
int r = ((NSM::Client*)user_data)->command_save(&out_msg);
|
||||
|
||||
if ( r )
|
||||
OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") );
|
||||
else
|
||||
OSC_REPLY( "OK" );
|
||||
|
||||
if ( out_msg )
|
||||
free( out_msg );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Client::osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
char *out_msg = NULL;
|
||||
|
||||
NSM::Client *nsm = (NSM::Client*)user_data;
|
||||
|
||||
nsm->nsm_client_id = strdup( &argv[2]->s );
|
||||
|
||||
int r = ((NSM::Client*)user_data)->command_open( &argv[0]->s, &argv[1]->s, &argv[2]->s, &out_msg);
|
||||
|
||||
if ( r )
|
||||
OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") );
|
||||
else
|
||||
OSC_REPLY( "OK" );
|
||||
|
||||
if ( out_msg )
|
||||
free( out_msg );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Client::osc_session_is_loaded ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
NSM::Client *nsm = (NSM::Client*)user_data;
|
||||
|
||||
nsm->command_session_is_loaded();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Client::osc_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) )
|
||||
return -1;
|
||||
|
||||
NSM::Client *nsm = (NSM::Client*)user_data;
|
||||
|
||||
|
||||
WARNING( "Failed to register with NSM: %s", &argv[2]->s );
|
||||
nsm->nsm_is_active = false;
|
||||
|
||||
nsm->command_active( nsm->nsm_is_active );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Client::osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) )
|
||||
return -1;
|
||||
|
||||
NSM::Client *nsm = (NSM::Client*)user_data;
|
||||
|
||||
MESSAGE( "Successfully registered. NSM says: %s", &argv[1]->s );
|
||||
nsm->nsm_is_active = true;
|
||||
nsm->_session_manager_name = strdup( &argv[2]->s );
|
||||
nsm->nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ));
|
||||
|
||||
nsm->command_active( nsm->nsm_is_active );
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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
|
||||
|
||||
#include <lo/lo.h>
|
||||
|
||||
namespace NSM
|
||||
{
|
||||
|
||||
class Client
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
lo_server _server;
|
||||
lo_server_thread _st;
|
||||
lo_address nsm_addr;
|
||||
|
||||
bool nsm_is_active;
|
||||
char *nsm_client_id;
|
||||
char *_session_manager_name;
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
ERR_OK = 0,
|
||||
ERR_GENERAL = -1,
|
||||
ERR_INCOMPATIBLE_API = -2,
|
||||
ERR_BLACKLISTED = -3,
|
||||
ERR_LAUNCH_FAILED = -4,
|
||||
ERR_NO_SUCH_FILE = -5,
|
||||
ERR_NO_SESSION_OPEN = -6,
|
||||
ERR_UNSAVED_CHANGES = -7,
|
||||
ERR_NOT_NOW = -8
|
||||
};
|
||||
|
||||
Client ( );
|
||||
virtual ~Client ( );
|
||||
|
||||
bool is_active ( void ) { return nsm_is_active; }
|
||||
|
||||
const char *session_manager_name ( void ) { return _session_manager_name; }
|
||||
|
||||
/* Client->Server methods */
|
||||
void is_dirty ( void );
|
||||
void is_clean ( void );
|
||||
void progress ( float f );
|
||||
void message( int priority, const char *msg );
|
||||
void announce ( const char *nsm_url, const char *appliction_name, const char *capabilities, const char *process_name );
|
||||
|
||||
/* init without threading */
|
||||
int init ( void );
|
||||
/* init with threading */
|
||||
int init_thread ( void );
|
||||
|
||||
/* call this periodically to check for new messages */
|
||||
void check ( void );
|
||||
|
||||
/* or call these to start and stop a thread (must do your own locking in handler!) */
|
||||
void start ( void );
|
||||
void stop ( void );
|
||||
|
||||
protected:
|
||||
|
||||
/* Server->Client methods */
|
||||
virtual int command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg ) = 0;
|
||||
virtual int command_save ( char **out_msg ) = 0;
|
||||
|
||||
virtual void command_active ( bool active ) { }
|
||||
|
||||
virtual void command_session_is_loaded ( void ) { }
|
||||
|
||||
private:
|
||||
|
||||
/* osc handlers */
|
||||
static int osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data );
|
||||
static int osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data );
|
||||
static int osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data );
|
||||
static int osc_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data );
|
||||
static int osc_session_is_loaded ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data );
|
||||
|
||||
};
|
||||
};
|
|
@ -22,51 +22,120 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "Endpoint.H"
|
||||
|
||||
#include "Thread.H"
|
||||
|
||||
namespace OSC
|
||||
{
|
||||
|
||||
|
||||
void
|
||||
Endpoint::error_handler(int num, const char *msg, const char *path)
|
||||
{
|
||||
WARNING( "LibLO server error %d in path %s: %s\n", num, path, msg);
|
||||
}
|
||||
|
||||
Endpoint::Endpoint ( const char *port )
|
||||
Endpoint::Endpoint ( )
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::init ( const char *port )
|
||||
{
|
||||
DMESSAGE( "Creating OSC server" );
|
||||
|
||||
// _st = lo_server_thread_new( s, error_handler );
|
||||
// _server = lo_server_thread_get_server( _st );
|
||||
|
||||
_server = lo_server_new( port, error_handler );
|
||||
|
||||
if ( ! _server )
|
||||
FATAL( "Error creating OSC server" );
|
||||
|
||||
char *url = lo_server_get_url(_server);
|
||||
printf("OSC: %s\n",url);
|
||||
free(url);
|
||||
{
|
||||
WARNING( "Error creating OSC server" );
|
||||
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( NULL, "", &Endpoint::osc_generic, this, "" );
|
||||
|
||||
// _path_names = new std::list<const char*>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Endpoint::~Endpoint ( )
|
||||
{
|
||||
// lo_server_thread_free( _st );
|
||||
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_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 )
|
||||
{
|
||||
if ( ! (*i)->path )
|
||||
continue;
|
||||
|
||||
if ( ! strcmp( qpath, (*i)->path ) && (*i)->typespec )
|
||||
{
|
||||
md = *i;
|
||||
|
||||
/* FIXME: what about the fact that there could be multiple messages with the same path but
|
||||
different typespecs ? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! md )
|
||||
{
|
||||
ep->send( lo_message_get_source( msg ), "/error", path,
|
||||
"Could not find specified path" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *r = (char*) malloc( 256 );
|
||||
r[0] = 0;
|
||||
|
||||
for ( int i = 0; i < strlen( md->typespec ); ++i )
|
||||
{
|
||||
char desc[50];
|
||||
|
||||
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 );
|
||||
|
||||
r = (char*)realloc( r, strlen( r ) + strlen( desc ) + 2 );
|
||||
|
||||
strcat( r, desc );
|
||||
strcat( r, "\n" );
|
||||
}
|
||||
|
||||
ep->send( lo_message_get_source( msg ), "/reply", path,
|
||||
qpath,
|
||||
r );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::osc_generic ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
OSC_DMSG();
|
||||
// OSC_DMSG();
|
||||
|
||||
if ( path[ strlen(path) - 1 ] != '/' )
|
||||
return -1;
|
||||
|
@ -87,16 +156,18 @@ namespace OSC
|
|||
char *r = (char*)malloc( 1024 );
|
||||
r[0] = 0;
|
||||
|
||||
for ( std::list<char*>::iterator i = _path_names.begin(); i != _path_names.end(); ++i )
|
||||
for ( std::list<Method_Data*>::const_iterator i = _methods.begin(); i != _methods.end(); ++i )
|
||||
{
|
||||
if ( ! *i )
|
||||
if ( ! (*i)->path )
|
||||
continue;
|
||||
|
||||
if (! strncmp( *i, prefix, strlen(prefix) ) )
|
||||
if (! strncmp( (*i)->path, prefix, strlen(prefix) ) )
|
||||
{
|
||||
r = (char*)realloc( r, strlen( r ) + strlen( *i ) + 2 );
|
||||
r = (char*)realloc( r, strlen( r ) + strlen( (*i)->path ) + 2 );
|
||||
|
||||
strcat( r, *i );
|
||||
/* asprintf( &stored_path, "%s (%s); %s", path, typespec, argument_description ); */
|
||||
|
||||
strcat( r, (*i)->path );
|
||||
strcat( r, "\n" );
|
||||
}
|
||||
}
|
||||
|
@ -105,66 +176,138 @@ namespace OSC
|
|||
}
|
||||
|
||||
void
|
||||
Endpoint::set_parameter_limits ( const char *path, const char *typespec,
|
||||
int index,
|
||||
float min, float max, float default_value )
|
||||
{
|
||||
assert( typespec );
|
||||
|
||||
assert( index < strlen( typespec ) );
|
||||
|
||||
for ( std::list<Method_Data *>::iterator i = _methods.begin(); i != _methods.end(); ++i )
|
||||
{
|
||||
if ( ! (*i)->path )
|
||||
continue;
|
||||
|
||||
if ( ! strcmp( path, (*i)->path ) &&
|
||||
! strcmp( typespec, (*i)->typespec ) )
|
||||
{
|
||||
(*i)->parameter_limits[index].min = min;
|
||||
(*i)->parameter_limits[index].max = max;
|
||||
(*i)->parameter_limits[index].default_value = default_value;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method_handle
|
||||
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 );
|
||||
|
||||
char *stored_path = NULL;
|
||||
Method_Data *md = new Method_Data;
|
||||
|
||||
asprintf( &stored_path, "%s (%s); %s", path, typespec, argument_description );
|
||||
if ( path )
|
||||
md->path = strdup( path );
|
||||
if ( typespec )
|
||||
md->typespec = strdup( typespec );
|
||||
if ( argument_description )
|
||||
md->documentation = strdup( argument_description );
|
||||
|
||||
_path_names.push_back( stored_path );
|
||||
if ( typespec )
|
||||
md->parameter_limits = new Parameter_Limits[strlen(typespec)];
|
||||
|
||||
_methods.push_back( md );
|
||||
|
||||
return md;
|
||||
|
||||
/* asprintf( &stored_path, "%s (%s); %s", path, typespec, argument_description ); */
|
||||
|
||||
/* _path_names.push_back( stored_path ); */
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::del_method ( const char *path, const char *typespec )
|
||||
{
|
||||
DMESSAGE( "Deleted OSC method %s (%s)", path, typespec );
|
||||
// DMESSAGE( "Deleted OSC method %s (%s)", path, typespec );
|
||||
|
||||
lo_server_del_method( _server, path, typespec );
|
||||
|
||||
for ( std::list<char *>::iterator i = _path_names.begin(); i != _path_names.end(); ++i )
|
||||
for ( std::list<Method_Data *>::iterator i = _methods.begin(); i != _methods.end(); ++i )
|
||||
{
|
||||
if ( ! *i )
|
||||
if ( ! (*i)->path )
|
||||
continue;
|
||||
|
||||
if ( ! strncmp( path, *i, index( *i, ' ' ) - *i ) )
|
||||
if ( ! strcmp( path, (*i)->path ) &&
|
||||
! strcmp( typespec, (*i)->typespec ) )
|
||||
{
|
||||
free( *i );
|
||||
i = _path_names.erase( i );
|
||||
delete *i;
|
||||
i = _methods.erase( i );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* void * */
|
||||
/* Endpoint::osc_thread ( void * arg ) */
|
||||
/* { */
|
||||
/* ((Endpoint*)arg)->osc_thread(); */
|
||||
void
|
||||
Endpoint::del_method ( const method_handle mh )
|
||||
{
|
||||
// DMESSAGE( "Deleted OSC method %s (%s)", path, typespec );
|
||||
|
||||
/* return NULL; */
|
||||
/* } */
|
||||
Method_Data *meth = const_cast<Method_Data*>( (const Method_Data*)mh );
|
||||
|
||||
/* void */
|
||||
/* Endpoint::osc_thread ( void ) */
|
||||
/* { */
|
||||
/* _thread.name( "OSC" ); */
|
||||
lo_server_del_method( _server, meth->path, meth->typespec );
|
||||
|
||||
/* DMESSAGE( "OSC Thread running" ); */
|
||||
delete meth;
|
||||
|
||||
/* for ( ;; ) */
|
||||
/* { */
|
||||
/* lo_server_recv( _sever ); */
|
||||
/* } */
|
||||
/* } */
|
||||
_methods.remove( meth );
|
||||
|
||||
|
||||
/* for ( std::list<Method_Data *>::iterator i = _methods.begin(); i != _methods.end(); ++i ) */
|
||||
/* { */
|
||||
/* if ( ! (*i)->path ) */
|
||||
/* continue; */
|
||||
|
||||
/* if ( ! strcmp( path, (*i)->path ) && */
|
||||
/* ! strcmp( typespec, (*i)->typespec ) ) */
|
||||
/* { */
|
||||
/* delete *i; */
|
||||
/* i = _methods.erase( i ); */
|
||||
|
||||
/* break; */
|
||||
/* } */
|
||||
/* } */
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *
|
||||
Endpoint::osc_thread ( void * arg )
|
||||
{
|
||||
((Endpoint*)arg)->osc_thread();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::osc_thread ( void )
|
||||
{
|
||||
_thread.name( "OSC" );
|
||||
|
||||
DMESSAGE( "OSC Thread running" );
|
||||
|
||||
run();
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::start ( void )
|
||||
{
|
||||
|
||||
/* if ( !_thread.clone( &Endpoint::osc_thread, this ) ) */
|
||||
/* FATAL( "Could not create OSC thread" ); */
|
||||
if ( !_thread.clone( &Endpoint::osc_thread, this ) )
|
||||
FATAL( "Could not create OSC thread" );
|
||||
|
||||
/* lo_server_thread_start( _st ); */
|
||||
|
||||
|
@ -173,6 +316,7 @@ namespace OSC
|
|||
void
|
||||
Endpoint::stop ( void )
|
||||
{
|
||||
_thread.join();
|
||||
// lo_server_thread_stop( _st );
|
||||
}
|
||||
|
||||
|
@ -227,9 +371,11 @@ namespace OSC
|
|||
switch ( ov->type() )
|
||||
{
|
||||
case 'f':
|
||||
DMESSAGE( "Adding float %f", ((OSC_Float*)ov)->value() );
|
||||
lo_message_add_float( m, ((OSC_Float*)ov)->value() );
|
||||
break;
|
||||
case 'i':
|
||||
DMESSAGE( "Adding int %i", ((OSC_Int*)ov)->value() );
|
||||
lo_message_add_int32( m, ((OSC_Int*)ov)->value() );
|
||||
break;
|
||||
case 's':
|
||||
|
@ -287,10 +433,83 @@ namespace OSC
|
|||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "s", v );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char * v1, float v2 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sf", v1, v2 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char * v1, const char *v2 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "ss", v1, v2 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char * v1, const char *v2, const char *v3 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sss", v1, v2, v3 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, int v2, int v3, int v4 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "siii", v1, v2, v3, v4 );
|
||||
}
|
||||
|
||||
int
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, int v4, int v5, int v6 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sssiii", v1, v2, v3, v4, v5, v6 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, int v2 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "si", v1, v2 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, int v1, const char *v2 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "is", v1, v2 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, int v2, const char *v3 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sis", v1, v2, v3 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, int v1, const char *v2, const char *v3, const char *v4 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "isss", v1, v2, v3, v4 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, int v2, const char *v3, const char *v4, const char *v5 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sisss", v1, v2, v3, v4, v5 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, const char *v4, const char *v5 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "sssss", v1, v2, v3, v4, v5 );
|
||||
}
|
||||
|
||||
int
|
||||
Endpoint::send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, const char *v4 )
|
||||
{
|
||||
return lo_send_from( to, _server, LO_TT_IMMEDIATE, path, "ssss", v1, v2, v3, v4 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <lo/lo.h>
|
||||
//#include "util/Thread.H"
|
||||
#include "Thread.H"
|
||||
#include <list>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace OSC
|
||||
{
|
||||
typedef void * method_handle;
|
||||
|
||||
class OSC_Value
|
||||
{
|
||||
|
@ -111,27 +113,77 @@ namespace OSC
|
|||
|
||||
|
||||
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 );
|
||||
|
||||
// Thread _thread;
|
||||
Thread _thread;
|
||||
|
||||
// lo_server_thread _st;
|
||||
lo_server _server;
|
||||
|
||||
std::list<char*> _path_names;
|
||||
|
||||
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;
|
||||
|
||||
static void *osc_thread ( void *arg );
|
||||
void osc_thread ( void );
|
||||
|
||||
public:
|
||||
|
||||
Endpoint ( const char *port = 0 );
|
||||
int init ( const char *port = 0 );
|
||||
Endpoint ( );
|
||||
|
||||
~Endpoint ( );
|
||||
|
||||
char *get_paths ( const char *prefix );
|
||||
void add_method ( const char *path, const char *typespec, lo_method_handler handler, void *user_data, const char *argument_description );
|
||||
method_handle 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 method_handle );
|
||||
void start ( void );
|
||||
void stop ( void );
|
||||
int port ( 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 check ( void ) const;
|
||||
void wait ( int timeout ) const;
|
||||
|
@ -145,20 +197,36 @@ namespace OSC
|
|||
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, long v );
|
||||
int send ( lo_address to, const char *path, const char * v1, float v2 );
|
||||
int send ( lo_address to, const char *path, const char *v );
|
||||
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, int v2, int v3, int v4 );
|
||||
int send ( lo_address to, const char *path, const char *v1, const char *v2, int v3, int v4, int v5 );
|
||||
|
||||
int send ( lo_address to, const char *path, const char *v1, int v2 );
|
||||
int send ( lo_address to, const char *path, int v1, const char *v2 );
|
||||
|
||||
int send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, int v4, int v5, int v6 );
|
||||
int send ( lo_address to, const char *path, const char *v1, int v2, const char *v3 );
|
||||
int send ( lo_address to, const char *path, int v1, const char *v2, const char *v3, const char *v4 );
|
||||
int send ( lo_address to, const char *path, const char *v1, int v2, const char *v3, const char *v4, const char *v5 );
|
||||
int send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, const char *v4, const char *v5 );
|
||||
int send ( lo_address to, const char *path, const char *v1, const char *v2, const char *v3, const char *v4 );
|
||||
|
||||
// can be used to point back to owning object.
|
||||
void *owner;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* helper macros for defining OSC handlers */
|
||||
#define OSC_NAME( name ) osc_ ## name
|
||||
#define OSC_DMSG() DMESSAGE( "Got OSC message: %s", path );
|
||||
#define OSC_HANDLER( name ) static int OSC_NAME( name ) ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
|
||||
|
||||
#define OSC_REPLY_OK() ((Mixer*)user_data)->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, "ok" )
|
||||
#define OSC_REPLY( msg_str ) ((Mixer*)user_data)->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, msg_str )
|
||||
#define OSC_REPLY_ERR() ((Mixer*)user_data)->osc_endpoint->send( lo_message_get_source( msg ), "/reply", path, "err" )
|
||||
#define OSC_REPLY_OK() ((OSC::Endpoint*)user_data)->send( lo_message_get_source( msg ), path, "ok" )
|
||||
#define OSC_REPLY( value ) ((OSC::Endpoint*)user_data)->send( lo_message_get_source( msg ), path, value )
|
||||
#define OSC_REPLY_ERR() ((OSC::Endpoint*)user_data)->send( lo_message_get_source( msg ), path, "err" )
|
||||
#define OSC_ENDPOINT() ((OSC::Endpoint*)user_data)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- mode: makefile; -*-
|
||||
|
||||
nonlib_SRCS := $(wildcard nonlib/*.C nonlib/JACK/*.C nonlib/LASH/*.C nonlib/OSC/*.C)
|
||||
nonlib_SRCS := $(wildcard nonlib/*.C nonlib/JACK/*.C nonlib/LASH/*.C nonlib/OSC/*.C nonlib/NSM/*.C)
|
||||
|
||||
nonlib_SRCS:=$(sort $(nonlib_SRCS))
|
||||
nonlib_OBJS:=$(nonlib_SRCS:.C=.o)
|
||||
|
|
|
@ -282,18 +282,18 @@ require_package ()
|
|||
|
||||
_test_version ()
|
||||
{
|
||||
if [ $# == 6 ]
|
||||
if [ $# = 6 ]
|
||||
then
|
||||
[ $1 -gt $4 ] && return 0
|
||||
[ $1 -eq $4 ] && [ $2 -gt $5 ] && return 0
|
||||
[ $1 -eq $4 ] && [ $2 -eq $5 ] && [ $3 -gt $6 ] && return 0
|
||||
[ $1 -eq $4 ] && [ $2 -eq $5 ] && [ $3 -eq $6 ] && return 0
|
||||
return 1
|
||||
elif [ $# == 4 ]
|
||||
elif [ $# = 4 ]
|
||||
then
|
||||
[ $1 -gt $3 ] && return 0
|
||||
[ $1 -eq $3 ] && [ $2 -eq $4 ] && return 0
|
||||
return 1;
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../FL
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
###############################################################################
|
||||
# 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. #
|
||||
###############################################################################
|
||||
|
||||
## Makefile for the Non-DAW.
|
||||
|
||||
##
|
||||
## Do not edit this file; run `make config` instead.
|
||||
##
|
||||
|
||||
VERSION := 1.0.0
|
||||
PACKAGE := SESSION
|
||||
|
||||
all: .config
|
||||
|
||||
.config: configure
|
||||
@ echo '<<< Configuring '$(PACKAGE)
|
||||
@ ./configure
|
||||
|
||||
config:
|
||||
@ echo '<<< Configuring '$(PACKAGE)
|
||||
@ ./configure
|
||||
|
||||
-include .config
|
||||
|
||||
export SYSTEM_PATH:=$(prefix)/share/
|
||||
export DOCUMENT_PATH:=$(prefix)/share/doc/
|
||||
export PIXMAP_PATH:=$(prefix)/share/pixmaps/
|
||||
|
||||
# a bit of a hack to make sure this runs before any rules
|
||||
ifneq ($(CALCULATING),yes)
|
||||
TOTAL := $(shell $(MAKE) CALCULATING=yes -n 2>/dev/null | sed -n 's/^.*Compiling: \([^"]\+\)"/\1/p' > .files )
|
||||
endif
|
||||
|
||||
ifeq ($(USE_DEBUG),yes)
|
||||
CFLAGS := -pipe -ggdb -fno-inline -Wall -Wextra -O0
|
||||
CXXFLAGS := -Wnon-virtual-dtor -Wno-missing-field-initializers -fno-rtti -fno-exceptions
|
||||
else
|
||||
CFLAGS := -pipe -O2 -DNDEBUG
|
||||
CXXFLAGS := -fno-rtti -fno-exceptions
|
||||
endif
|
||||
|
||||
|
||||
CFLAGS+=-DVERSION=\"$(VERSION)\" \
|
||||
-DINSTALL_PREFIX=\"$(prefix)\" \
|
||||
-DSYSTEM_PATH=\"$(SYSTEM_PATH)\" \
|
||||
-DDOCUMENT_PATH=\"$(DOCUMENT_PATH)\" \
|
||||
-DPIXMAP_PATH=\"$(PIXMAP_PATH)\"
|
||||
|
||||
CXXFLAGS += $(SNDFILE_CFLAGS) $(FLTK_CFLAGS) $(JACK_CFLAGS)
|
||||
CXXFLAGS := $(CFLAGS) $(CXXFLAGS)
|
||||
|
||||
INCLUDES := -I. -Iutil -IFL -Inonlib
|
||||
|
||||
include scripts/colors
|
||||
|
||||
ifneq ($(CALCULATING),yes)
|
||||
COMPILING="$(BOLD)$(BLACK)${PACKAGE} [$(SGR0)$(CYAN)`scripts/percent-complete .files "$<"`$(SGR0)$(BOLD)$(BLACK)]$(SGR0) $(BOLD)$(YELLOW)$<$(SGR0)"
|
||||
else
|
||||
COMPILING="Compiling: $<"
|
||||
endif
|
||||
|
||||
.C.o:
|
||||
@ echo $(COMPILING)
|
||||
@ $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
%.C : %.fl
|
||||
@ cd `dirname $<` && fluid -c ../$<
|
||||
|
||||
DONE := $(BOLD)$(GREEN)done$(SGR0)
|
||||
|
||||
include FL/makefile.inc
|
||||
#include nonlib/makefile.inc
|
||||
include makefile.inc
|
||||
|
||||
SRCS:=$(Session_SRCS)
|
||||
OBJS:=$(Session_OBJS)
|
||||
|
||||
# FIXME: isn't there a better way?
|
||||
$(OBJS): .config Makefile
|
||||
|
||||
TAGS: $(SRCS)
|
||||
etags $(SRCS)
|
||||
|
||||
.deps: .config $(SRCS)
|
||||
ifneq ($(CALCULATING),yes)
|
||||
@ echo -n Calculating dependencies...
|
||||
@ makedepend -f- -- $(CXXFLAGS) $(INCLUDES) -- $(SRCS) 2>/dev/null > .deps && echo $(DONE)
|
||||
@ # gcc -M $(CXXFLAGS) $(INCLUDES) $(SRCS) > .deps && echo $(DONE)
|
||||
endif
|
||||
|
||||
|
||||
install: all
|
||||
@ echo -n "Installing..."
|
||||
@ install src/nsmd $(prefix)/bin/nsmd
|
||||
@ install src/session-manager $(prefix)/bin/non-session-manager
|
||||
@ # mkdir -p $(SYSTEM_PATH)/non-mixer
|
||||
@ # mkdir -p $(PIXMAP_PATH)/non-mixer
|
||||
@ # cp pixmaps/*.png $(PIXMAP_PATH)/non-mixer
|
||||
@ # $(MAKE) -s -C doc install
|
||||
@ echo "$(DONE)"
|
||||
ifneq ($(USE_DEBUG),yes)
|
||||
@ echo -n "Stripping..."
|
||||
@ strip $(prefix)/bin/nsmd
|
||||
@ strip $(prefix)/bin/non-session-manager
|
||||
@ echo "$(DONE)"
|
||||
endif
|
||||
|
||||
clean_deps:
|
||||
@ rm -f .deps
|
||||
|
||||
.PHONEY: clean config depend clean_deps
|
||||
|
||||
clean: FL_clean Session_clean
|
||||
|
||||
dist:
|
||||
git archive --prefix=non-session-$(VERSION)/ v$(VERSION) | bzip2 > non-session-$(VERSION).tar.bz2
|
||||
|
||||
scan-gpl:
|
||||
@ scripts/scan-gpl $(SRCS) || echo $(BOLD)$(RED)Some source files do not contain proper license information!
|
||||
|
||||
-include .deps
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2008 Jonathan Moore Liles
|
||||
# This file is licensed under version 2 of the GPL.
|
||||
|
||||
. scripts/config-funcs
|
||||
|
||||
begin
|
||||
|
||||
begin_options
|
||||
|
||||
ask "Installation prefix" prefix /usr/local
|
||||
ask "Build for debugging" USE_DEBUG no
|
||||
|
||||
begin_tests
|
||||
|
||||
require_FLTK 1.1.7 images
|
||||
require_command FLUID fluid
|
||||
require_command ar ar
|
||||
require_command makedepend makedepend
|
||||
# require_package JACK 0.103.0 jack
|
||||
require_package lrdf 0.4.0 lrdf
|
||||
require_package liblo 0.23 liblo
|
||||
|
||||
test_version `version_of liblo` 0.26 || warn "Version $(version_of liblo) of liblo is slow to create servers. Consider upgrading to 0.26 or later"
|
||||
|
||||
end
|
|
@ -0,0 +1,492 @@
|
|||
|
||||
! title Non Session Management API
|
||||
! author Jonathan Moore Liles #(email,male@tuxfamily.org)
|
||||
! date August 1, 2010
|
||||
|
||||
-- Table Of Contents
|
||||
|
||||
: Non Session Management API version 0.7
|
||||
|
||||
The Non Session Management API is an API for session management used
|
||||
by the various parts of the Non music production suite. It comprises
|
||||
a simple OSC based protocol which can easily be implemented by other
|
||||
applications. NSM provides robust session management, including
|
||||
interactive features.
|
||||
|
||||
The Non project contains an implementation of the NSM server API
|
||||
called `nsmd` which can be controlled by the `non-session-manager`
|
||||
GUI, but the same server API can easily be implemented by other
|
||||
session managers (such as LADISH).
|
||||
|
||||
The only dependency for clients `liblo` (the OSC library), which
|
||||
several Linux audio applications already link to or plan to link to
|
||||
in the future.
|
||||
|
||||
The aim of this project is to thoroughly define the behavior
|
||||
required of clients. This is an area where other attempts at session
|
||||
management (LASH and JACK-Session) have failed. Often the difficulty
|
||||
with these previous system been, not in implementing support for
|
||||
them, but in attempting to interpret the confusing and ambiguous API
|
||||
documentation. For this reason, all LASH support has been removed
|
||||
from Non.
|
||||
|
||||
You *WILL* see a lot of unambiguous language in this document. These
|
||||
rules are meant to be followed and are non-negotiable. If an
|
||||
application does not conform to this specification it should be
|
||||
considered broken. Consistency across applications under session
|
||||
management is very important for a good user experience.
|
||||
|
||||
:: Client Behavior Under Session Management
|
||||
|
||||
Most graphical applications make available to the user a common set
|
||||
of file operations, typically presented under a File or Project
|
||||
menu.
|
||||
|
||||
These are: New, Open, Save, Save As, Close and Quit or Exit.
|
||||
|
||||
The following sub-sections describe how these options should behave when
|
||||
the application is part of an NSM session. These rules only apply
|
||||
when session management is active (that is, after the `announce`
|
||||
handshake described in the #(ref,NSM OSC Protocol) section).
|
||||
|
||||
In order to provide a consistent and predictable user experience, it
|
||||
is important for applications to adhere to these guidelines.
|
||||
|
||||
::: New
|
||||
|
||||
This option may empty\/reset the current file or project (possibly
|
||||
after user confirmation). *UNDER NO CIRCUMSTANCES* should it allow
|
||||
the user to create a new project\/file in another location.
|
||||
|
||||
::: Open
|
||||
|
||||
This option should be disabled.
|
||||
|
||||
The application may, however, elect to implement an option called
|
||||
'Import into Session', creates a copy of a file\/project which is
|
||||
then saved in the session path provided by NSM.
|
||||
|
||||
::: Save
|
||||
|
||||
This option should behave as normal, saving the current
|
||||
file\/project as established by the NSM `open` message.
|
||||
|
||||
*UNDER NO CIRCUMSTANCES* should this option present the user with a
|
||||
choice of where to save the file!
|
||||
|
||||
::: Save As
|
||||
|
||||
This option should be disabled.
|
||||
|
||||
The application may, however, elect to implement an option called
|
||||
'Export from Session', which creates a copy of the current
|
||||
file\/project which is then saved in a user-specified location
|
||||
outside of the session path provided by NSM.
|
||||
|
||||
::: Close (as distinguished from Quit or Exit)
|
||||
|
||||
This option should be disabled, unless its meaning is to disconnect
|
||||
the application from session management.
|
||||
|
||||
::: Quit or Exit
|
||||
|
||||
This option may behave as normal (even possibly asking the user to
|
||||
confirm exiting).
|
||||
|
||||
:: NSM OSC Protocol
|
||||
|
||||
All message parameters are *REQUIRED*. All messages *MUST* be sent
|
||||
from the same socket as the `announce` message, using the
|
||||
`lo\_send\_from` method of liblo (the server uses the return
|
||||
addresses to distinguish between clients).
|
||||
|
||||
::: Establishing a Connection
|
||||
|
||||
:::: Announce
|
||||
|
||||
When started clients *MUST* check the environment for the value of
|
||||
`NSM\_URL`. If present, the client *MUST* send the following message
|
||||
to the provided address as soon as it is ready to respond to the
|
||||
`\/nsm\/client\/open` event:
|
||||
|
||||
> /nsm/server/announce s:application_name s:capabilities i:api_version_major i:api_version_minor i:pid
|
||||
|
||||
If `NSM\_URL` is undefined, invalid, or unreachable, then the client
|
||||
should proceed assuming that session management is unavailable.
|
||||
|
||||
`api\_version\_major` and `api\_version\_minor` must be the two parts of
|
||||
the version number of the NSM API as defined by this document.
|
||||
|
||||
Note that if the application intends to register JACK clients,
|
||||
`application\_name` *MUST* be the same as the name that would
|
||||
normally by passed to `jack\_client\_open`. For example, Non-Mixer
|
||||
sends "Non-Mixer" as its `application\_name`. Applications *MUST
|
||||
NOT* register their JACK clients until receiving an `open` message;
|
||||
the `open` message will provide a unique client name prefix suitable
|
||||
for passing to JACK. This is probably the most complex requirement
|
||||
of the NSM API, but it isn't difficult to implement.
|
||||
|
||||
`capabilities` *MUST* be a string containing a colon separated list
|
||||
of the special capabilities the client
|
||||
possesses. e.g. ":dirty:switch:progress:"
|
||||
|
||||
// Available Client Capabilities
|
||||
[[ Name, Description
|
||||
[[ switch, client is capable of responding to multiple `open` messages without restarting
|
||||
[[ dirty, client knows when it has unsaved changes
|
||||
[[ progress, client can send progress updates during time-consuming operations
|
||||
[[ status, client can send textual status updates
|
||||
|
||||
:::: Response
|
||||
|
||||
The server will respond to the client's `announce` with the following message:
|
||||
|
||||
> /reply "/nsm/server/announce" s:message s:name_of_session_manager s:capabilities
|
||||
|
||||
`message` is a welcome message.
|
||||
|
||||
The value of `name\_of\_session\_manager` will depend on the
|
||||
implementation of the NSM server. It might say "Non Session Manager",
|
||||
or it might say "LADISH".
|
||||
|
||||
`capabilities` will be a string containing a colon separated list of
|
||||
special server capabilities.
|
||||
|
||||
Presently, the server `capabilities` are:
|
||||
|
||||
// Available Server Capabilities
|
||||
[[ Name, Description
|
||||
[[ server_control, client-to-server control
|
||||
|
||||
A client should not consider itself to be under session management
|
||||
until it receives this response (the Non programs activate their
|
||||
"SM" blinkers at this time.)
|
||||
|
||||
If there is an error, a reply of the following form will be sent to
|
||||
the client:
|
||||
|
||||
> /error "/nsm/server/announce" i:error_code s:error_message
|
||||
|
||||
The following table defines possible values of `error\_code`:
|
||||
|
||||
// Response codes
|
||||
[[ Code, Meaning
|
||||
[[ ERR_GENERAL, General Error
|
||||
[[ ERR_INCOMPATIBLE_API, Incompatible API version
|
||||
[[ ERR_BLACKLISTED, Client has been blacklisted.
|
||||
|
||||
::: Server to Client Control Messages
|
||||
|
||||
Compliant clients *MUST* accept the client control messages
|
||||
described in this section. All client control messages *REQUIRE* a
|
||||
response. Responses *MUST* be delivered back to the sender (NSM)
|
||||
from the same socket used by the client in its `announce` message
|
||||
(by using `lo\_send\_from`) *AFTER* the action has been completed or
|
||||
if an error is encountered. The required response is described in
|
||||
the subsection for each message.
|
||||
|
||||
If there is an error and the action cannot be completed, then
|
||||
`error\_code` *MUST* be set to a valid error code (see #(fig,Error Code Definitions))
|
||||
and `message` to a string describing the problem (suitable
|
||||
for display to the user).
|
||||
|
||||
The reply can take one of the following two forms, where `path` *MUST* be
|
||||
the path of the message being replied to (e.g. "/nsm\/client\/save"):
|
||||
|
||||
> /reply s:path s:message
|
||||
|
||||
> /error s:path i:error_code s:message
|
||||
|
||||
:::: Quit
|
||||
|
||||
There is no message for this. Clients will receive the Unix SIGTERM
|
||||
signal and *MUST* close cleanly *IMMEDIATELY*, without displaying
|
||||
any kind of dialog to the user and regardless of whether or not
|
||||
unsaved changes would be lost (when a session is closed the
|
||||
application will receive this signal soon after having responded to
|
||||
a `save` message).
|
||||
|
||||
:::: Open
|
||||
|
||||
> /nsm/client/open s:path_to_instance_specific_project s:client_id
|
||||
|
||||
The client *MUST* open an existing project, or create new one if one
|
||||
doesn't already exist, at `path\_to\_instance_specific\_project`
|
||||
|
||||
If the path provided doesn't exist, then the client *MUST*
|
||||
immediately create and open a new file\/project at the specified
|
||||
path (whether that means creating a single file or a project
|
||||
directory).
|
||||
|
||||
No file or directory will be created at the specified path by the
|
||||
server. It is up to the client to create what it needs.
|
||||
|
||||
The client may append to the path, creating a subdirectory,
|
||||
e.g. '/song.foo' or simply append the client's native file extension
|
||||
(e.g. '.non' or '.XML'). The same transformation *MUST* be applied
|
||||
to the name when opening an existing project, as NSM will only
|
||||
provide the instance specific part of the path.
|
||||
|
||||
For clients which *HAVE NOT* specified the 'switch' capability, the
|
||||
`open` message will only be delivered once, immediately after the
|
||||
'announce' response.
|
||||
|
||||
For client which *HAVE* specified the `:switch:` capability, the
|
||||
client *MUST* immediately switch to the specified project or create
|
||||
a new one if it doesn't exist.
|
||||
|
||||
Clients which are incapable of switching projects or are prone to
|
||||
crashing upon switching *MUST NOT* include `:switch:` in their
|
||||
capability string.
|
||||
|
||||
If the user the is allowed to run two or more instances of the
|
||||
application simultaneously (that is to say, there is no technical
|
||||
limitation preventing them from doing so, even if it doesn't make
|
||||
sense to the author), then such an application *MUST* prepend the
|
||||
provided `client\_id` string to any names it registers with common
|
||||
subsystems (e.g. JACK client names). This ensures that the multiple
|
||||
instances of the same application can be restored in any order
|
||||
without scrambling the JACK connections or causing other
|
||||
conflicts. The provided `client\_id` will be a concatenation of the
|
||||
value of `application\_name` sent by the client in its `announce`
|
||||
message and a unique identifier. Therefore, applications which
|
||||
create single JACK clients can use the value of `client\_id` directly
|
||||
as their JACK client name. Applications which register multiple JACK
|
||||
clients (e.g. Non-Mixer) *MUST* prepend `client_id` value to the
|
||||
client names they register with JACK and the application determined
|
||||
part *MUST* be unique for that (JACK) client.
|
||||
|
||||
For example, a suitable JACK client name would be:
|
||||
|
||||
> $CLIENT_ID/track-1
|
||||
|
||||
A response is *REQUIRED* *AFTER* the load\/new operation has been
|
||||
completed. Ongoing progress may be indicated by sending messages to
|
||||
`\/nsm\/client\/progress`.
|
||||
|
||||
::::: Response
|
||||
|
||||
The client *MUST* respond to the 'open' message with:
|
||||
|
||||
> /reply "/nsm/client/open" s:message
|
||||
|
||||
Or
|
||||
|
||||
> /error "/nsm/client/open" i:error_code s:message
|
||||
|
||||
// Response Codes
|
||||
[[ Code, Meaning
|
||||
[[ ERR, General Error
|
||||
[[ ERR_BAD_PROJECT, An existing project file was found to be corrupt
|
||||
[[ ERR_CREATE_FAILED, A new project could not be created
|
||||
[[ ERR_UNSAVED_CHANGES, Unsaved changes would be lost
|
||||
[[ ERR_NOT_NOW, Operation cannot be completed at this time
|
||||
|
||||
:::: Save
|
||||
|
||||
> /nsm/client/save
|
||||
|
||||
The client *MUST* immediately save the current application specific
|
||||
project data to the project path previously established in the
|
||||
'open' message. *UNDER NO CIRCUMSTANCES* should a dialog be
|
||||
displayed to the user (giving a choice of where to save, etc.)
|
||||
|
||||
::::: Response
|
||||
|
||||
The client *MUST* respond to the 'save' message with:
|
||||
|
||||
> /reply "/nsm/client/save" s:message
|
||||
|
||||
Or
|
||||
|
||||
> /error "/nsm/client/save" i:error_code s:message
|
||||
|
||||
// Response Codes
|
||||
[[ Code, Meaning
|
||||
[[ ERR, General Error
|
||||
[[ ERR_SAVE_FAILED, Project could not be saved
|
||||
[[ ERR_NOT_NOW, Operation cannot be completed at this time
|
||||
|
||||
::: Server to Client Informational Messages
|
||||
|
||||
:::: Session is Loaded
|
||||
|
||||
Accepting this message is optional. The intent is to signal to
|
||||
clients which may have some interdependency (say, peer to peer OSC
|
||||
connections) that the session is fully loaded and all their peers
|
||||
are available.
|
||||
|
||||
> /nsm/client/session_is_loaded
|
||||
|
||||
This message does not require a response.
|
||||
|
||||
::: Client to Server Informational Messages
|
||||
|
||||
These are optional messages which a client can send to the NSM
|
||||
server to inform it about the client's status. The client should not
|
||||
expect any reply to these messages. If a client intends to send a
|
||||
message described in this section, then it *MUST* add the
|
||||
appropriate value to its `capabilities` string when composing the
|
||||
`announce` message.
|
||||
|
||||
:::: Progress
|
||||
|
||||
> /nsm/client/progress f:progress
|
||||
|
||||
For potentially time-consuming operations, such as `save` and
|
||||
`open`, progress updates may be indicated throughout the duration by
|
||||
sending a floating point value between 0.0 and 1.0, 1.0 indicating
|
||||
completion, to the NSM server.
|
||||
|
||||
The server will not send a response to these messages, but will
|
||||
relay the information to the user.
|
||||
|
||||
Note that, even when using the `progress` feature, the final
|
||||
response to the `save` or `open` message is still *REQUIRED*.
|
||||
|
||||
Clients which intend to send `progress` messages should include
|
||||
`:progress:` in their `announce` capability string.
|
||||
|
||||
:::: Dirtiness
|
||||
|
||||
> /nsm/client/is_dirty
|
||||
|
||||
> /nsm/client/is_clean
|
||||
|
||||
Some clients may be able to inform the server when they have unsaved
|
||||
changes pending. Such clients may optionally send `is\_dirty` and `is\_clean`
|
||||
messages.
|
||||
|
||||
Clients which have this capability should include `:dirty:` in their
|
||||
`announce` capability string.
|
||||
|
||||
:::: Status Messages
|
||||
|
||||
> /nsm/client/message i:priority s:message
|
||||
|
||||
Clients may send miscellaneous status updates to the server for
|
||||
possible display to the user. This may simply be chatter that is normally
|
||||
written to the console. `priority` should be a number from 0 to 3, 3
|
||||
being the most important.
|
||||
|
||||
Clients which have this capability should include `:message:` in their
|
||||
`announce` capability string.
|
||||
|
||||
::: Error Code Definitions
|
||||
|
||||
// Error Code Definitions
|
||||
[[ Symbolic Name, Integer Value
|
||||
[[ ERR_GENERAL, -1
|
||||
[[ ERR_INCOMPATIBLE_API, -2
|
||||
[[ ERR_BLACKLISTED, -3
|
||||
[[ ERR_LAUNCH_FAILED, -4
|
||||
[[ ERR_NO_SUCH_FILE, -5
|
||||
[[ ERR_NO_SESSION_OPEN, -6
|
||||
[[ ERR_UNSAVED_CHANGES, -7
|
||||
[[ ERR_NOT_NOW, -8
|
||||
[[ ERR_BAD_PROJECT, -9
|
||||
[[ ERR_CREATE_FAILED, -10
|
||||
|
||||
::: Client to Server Control
|
||||
|
||||
If the server publishes the `server\_control` capability, then
|
||||
clients can also initiate action by the server. For example, a
|
||||
client might implement a 'Save All' option which sends a
|
||||
`\/nsm\/server\/save` message to the server, rather than requiring
|
||||
the user to switch to the session management interface to effect the
|
||||
save.
|
||||
|
||||
::: Server Control API
|
||||
|
||||
The session manager not only manages clients via OSC, but it is itself
|
||||
controlled via OSC messages. The server responds to the following
|
||||
messages.
|
||||
|
||||
All of the following messages will be responded to back to the sender's address
|
||||
with one of the two following messages:
|
||||
|
||||
> /reply s:path s:message
|
||||
|
||||
> /error s:path i:error_code s:message
|
||||
|
||||
The first parameter of the reply is the path to the message being
|
||||
replied to. The `\/error` reply includes an integer error code
|
||||
(non-zero indicates error). `message` will be a description of the
|
||||
error.
|
||||
|
||||
The possible errors are:
|
||||
|
||||
// Responses
|
||||
[[ Code, Meaning
|
||||
[[ ERR_GENERAL, General Error
|
||||
[[ ERR_LAUNCH_FAILED, Launch failed
|
||||
[[ ERR_NO_SUCH_FILE, No such file
|
||||
[[ ERR_NO_SESSION, No session is open
|
||||
[[ ERR_UNSAVED_CHANGES, Unsaved changes would be lost
|
||||
|
||||
= /nsm/server/add s:path_to_executable
|
||||
Adds a client to the current session.
|
||||
|
||||
= /nsm/server/save
|
||||
Saves the current session.
|
||||
|
||||
= /nsm/server/load s:project_name
|
||||
Saves the current session and loads a new session.
|
||||
|
||||
= /nsm/server/new s:project_name
|
||||
Saves the current session and creates a new session.
|
||||
|
||||
= /nsm/server/close
|
||||
Saves and closes the current session.
|
||||
|
||||
= /nsm/server/abort
|
||||
Closes the current session *WITHOUT SAVING*
|
||||
|
||||
= /nsm/server/quit
|
||||
Saves and closes the current session and terminates the server.
|
||||
|
||||
= /nsm/server/duplicate s:new_project
|
||||
Saves and closes the current session, creates a complete copy of
|
||||
it as `new_project` and opens it. The existing project should ideally be
|
||||
a lightweight template, as copying any audio data could be very time
|
||||
consuming.
|
||||
|
||||
= /nsm/server/list
|
||||
Lists available projects. One `\/reply` message will be sent for each existing project.
|
||||
|
||||
|
||||
|
||||
# = /nsm/server/client/list
|
||||
# Lists clients in the current session, their client IDs and statuses
|
||||
# = /nsm/server/ve
|
||||
|
||||
:::: Client to Client Communication
|
||||
|
||||
If the server includes `:broadcast:` in its capability string, then
|
||||
clients may send broadcast messages to each other through the NSM
|
||||
server.
|
||||
|
||||
Clients may send messages to the server at the path
|
||||
`\/broadcast`.
|
||||
|
||||
The format of this message is as follows:
|
||||
|
||||
> /nsm/server/broadcast s:path [other parameters...]
|
||||
|
||||
The message will then be relayed to all clients in the session at
|
||||
the path given in the `path` parameter and with the other parameters
|
||||
shifted forward by one.
|
||||
|
||||
For example the message:
|
||||
|
||||
> /nsm/server/broadcast /tempomap/update "0,120,4/4:12351234,240,4/4"
|
||||
|
||||
Would broadcast the following message to all clients in the session
|
||||
(except for the sender), some of which might respond to the message
|
||||
by updating their own tempo maps.
|
||||
|
||||
> /tempomap/update "0,120,4/4:12351234,240,4/4"
|
||||
|
||||
Clients may use this feature to establish peer to peer OSC
|
||||
communication with symbolic names without having to remember the OSC
|
||||
URLs of peers.
|
|
@ -0,0 +1,449 @@
|
|||
|
||||
/* Example CSS Style for MUP */
|
||||
|
||||
a:link {
|
||||
color: yellow;
|
||||
}
|
||||
a:visited {
|
||||
color: olive;
|
||||
}
|
||||
a:active {
|
||||
color: white;
|
||||
}
|
||||
a:link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* #(url) */
|
||||
a.ext:link {
|
||||
color: red;
|
||||
text-decoration: none;
|
||||
border-bottom: dashed silver 1;
|
||||
}
|
||||
a.ext:visited {
|
||||
color: darkred;
|
||||
border-bottom: dashed silver 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* #(ref) */
|
||||
a.int:link {
|
||||
border-bottom: dashed silver 0.15em;
|
||||
}
|
||||
a.int:link:hover {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/*
|
||||
a[href^="#"]:link {
|
||||
border-bottom: dashed silver 0.15em;
|
||||
}
|
||||
a[href^="#"]:link:hover {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
*/
|
||||
|
||||
p:contains("Warning:") {
|
||||
background: #d00;
|
||||
color: white;
|
||||
border: dotted gray 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* First letter of first paragraph of every chapter */
|
||||
/*
|
||||
h1 + p:first-letter {
|
||||
text-transform: uppercase;
|
||||
float: left;
|
||||
line-height: 0.8em;
|
||||
font-size: 350%;
|
||||
font-family: Serif;
|
||||
letter-spacing: 0;
|
||||
margin-right: 0.1em;
|
||||
margin-top: 0.1em;
|
||||
border: solid gray 1px;
|
||||
padding: 1px;
|
||||
color: #d00;
|
||||
text-shadow: #666 3px 3px 3px;
|
||||
}
|
||||
*/
|
||||
/* First paragraph of every chapter */
|
||||
/*
|
||||
h1 + p {
|
||||
text-indent: 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/* cover */
|
||||
#cover * {
|
||||
background: transparent;
|
||||
}
|
||||
#cover {
|
||||
position: relative;
|
||||
background: #da0;
|
||||
color: black;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
#cover h1, #cover h3 {
|
||||
text-shadow: #444 0.2em 0.2em 0.2em;
|
||||
color: white;
|
||||
border: none;
|
||||
letter-spacing: 0.2em;
|
||||
line-height: 0.8em;
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
#cover h1:before, #cover h1:after {
|
||||
content: "::";
|
||||
font-size: 300%;
|
||||
color: black;
|
||||
}
|
||||
#cover h1:before {
|
||||
position: absolute;
|
||||
top: 0.2em;
|
||||
left: 0.1em;
|
||||
}
|
||||
#cover h1:after {
|
||||
position: absolute;
|
||||
top: 0.2em;
|
||||
right: 0.1em;
|
||||
}
|
||||
#cover hr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
hr:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 0.2em;
|
||||
background: #555;
|
||||
color: #555;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
#cover a:visited {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* endnote */
|
||||
#endnote {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* TOC */
|
||||
#toc {
|
||||
position: relative;
|
||||
}
|
||||
#toc hr {
|
||||
}
|
||||
#toc h1 {
|
||||
}
|
||||
#toc ul {
|
||||
font-size: 125%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
#toc ul ul {
|
||||
font-size: 90%;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#toc li {
|
||||
list-style: none;
|
||||
|
||||
}
|
||||
#toc a:link {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #222;
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
/* */
|
||||
#body {
|
||||
position: relative;
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
/* ;, : */
|
||||
h1 {
|
||||
color: #ff0;
|
||||
border-bottom: solid #444 0.1em;
|
||||
}
|
||||
/* ::, :::, ::::, :::::, :::::: */
|
||||
h2, h3, h4, h5, h6 {
|
||||
color: #dd0;
|
||||
}
|
||||
|
||||
/* tables, figures */
|
||||
.fig caption {
|
||||
color: gray;
|
||||
text-align: center;
|
||||
/* Required for Mozilla */
|
||||
margin: auto;
|
||||
}
|
||||
.fig table {
|
||||
border: none;
|
||||
margin: auto;
|
||||
/* border-collapse: collapse; */
|
||||
}
|
||||
/* / */
|
||||
.fig.table th {
|
||||
border: none;
|
||||
background: gray;
|
||||
color: black;
|
||||
}
|
||||
/* [ */
|
||||
.fig.table td {
|
||||
border: none;
|
||||
background: silver;
|
||||
color: black;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
/* < */
|
||||
|
||||
.fig.image table {
|
||||
border: dashed silver 0.2em;
|
||||
background: transparent;
|
||||
/* Every browser should support border radii */
|
||||
-moz-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
|
||||
}
|
||||
.fig.image tr, .fig.image td {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* */
|
||||
p {
|
||||
margin-right: 2%;
|
||||
text-align: justify;
|
||||
text-indent: 1em;
|
||||
}
|
||||
/* > */
|
||||
.example *
|
||||
{
|
||||
background: transparent;
|
||||
}
|
||||
.example table
|
||||
{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
caption-side: top;
|
||||
overflow: auto;
|
||||
}
|
||||
.example caption
|
||||
{
|
||||
caption-side: top;
|
||||
|
||||
}
|
||||
.example {
|
||||
}
|
||||
.example p {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.example pre {
|
||||
margin-top: 0;
|
||||
font-family: Monospace;
|
||||
padding: 1em;
|
||||
border: dashed 0.3em gray;
|
||||
background: #111;
|
||||
color: white;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
/* Every browser should support border radii */
|
||||
-moz-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
/* " */
|
||||
/*
|
||||
.quote:before {
|
||||
float: left;
|
||||
font-size: 500%;
|
||||
content: "\201C";
|
||||
}
|
||||
*/
|
||||
/*
|
||||
blockquote:after {
|
||||
content: "\201D";
|
||||
}*/
|
||||
.quote blockquote {
|
||||
padding: 0.5em;
|
||||
margin-left: 0.5em;
|
||||
font-family: Serif;
|
||||
border-left: solid 0.4em gray;
|
||||
/* background: #333; */
|
||||
color: white;
|
||||
}
|
||||
/* ^ */
|
||||
small {
|
||||
/*
|
||||
color: silver;
|
||||
font-size: 50%;
|
||||
*/
|
||||
}
|
||||
.footnote p {
|
||||
color: silver;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Popup footnotes */
|
||||
.footnote p {
|
||||
display: none;
|
||||
}
|
||||
.footnote p:target {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
left: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
max-width: 50%;
|
||||
border: solid 0.3em white;
|
||||
-moz-border-radius: 0.5em;
|
||||
background: black;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
/* { */
|
||||
.admonition * {
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
.admonition dl
|
||||
{
|
||||
display: table;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #333;
|
||||
border: dotted black 0.3em;
|
||||
width: 90%
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.admonition dt
|
||||
{
|
||||
display: table-cell;
|
||||
vertical-align: center;
|
||||
border-right: solid silver 0.4em;
|
||||
font-weight: bold;
|
||||
font-size: 115%;
|
||||
font-family: Serif;
|
||||
background: gray;
|
||||
width: 0;
|
||||
text-shadow: black 0.15em 0.15em 0.15em;
|
||||
}
|
||||
.admonition dd
|
||||
{
|
||||
padding-left: 0.4em;
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.admonition table
|
||||
{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #333;
|
||||
border: dotted black 0.3em;
|
||||
width: 90%
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.admonition td {
|
||||
width: 100%;
|
||||
text-align: justify;
|
||||
}
|
||||
.admonition td:first-child:contains("Warning:") {
|
||||
background: #900;
|
||||
}
|
||||
.admonition td:first-child:contains("Caution:") {
|
||||
background: #960;
|
||||
}
|
||||
.admonition td:first-child:contains("Note:") {
|
||||
background: #690;
|
||||
}
|
||||
.admonition td:first-child {
|
||||
border-right: solid silver 0.4em;
|
||||
font-weight: bold;
|
||||
font-size: 115%;
|
||||
font-family: Serif;
|
||||
background: gray;
|
||||
width: 0;
|
||||
text-shadow: black 0.15em 0.15em 0.15em;
|
||||
}
|
||||
|
||||
|
||||
/* #(b) */
|
||||
/* b { color: olive; } */
|
||||
/* #(c) */
|
||||
tt {
|
||||
color: #7f0;
|
||||
}
|
||||
/* ! keywords ... */
|
||||
p em {
|
||||
color: gray;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* *, + */
|
||||
/* Bullet, numbe */
|
||||
li {
|
||||
color: #f0f;
|
||||
}
|
||||
/* Text */
|
||||
li span, li p {
|
||||
color: white;
|
||||
}
|
||||
li p {
|
||||
color: red;
|
||||
display: block;
|
||||
}
|
||||
ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
dl {
|
||||
margin-left: 2%;
|
||||
margin-top: 1em;
|
||||
}
|
||||
/* = */
|
||||
dt {
|
||||
background: #181818;
|
||||
padding: 0.2em;
|
||||
/* font-variant: small-caps; */
|
||||
font-weight: bold;
|
||||
color: #f0f;
|
||||
}
|
||||
dd {
|
||||
color: white;
|
||||
text-align: justify;
|
||||
margin-right: 5%;
|
||||
}
|
||||
dt a:link, dt a:visited {
|
||||
color: #f0f;
|
||||
}
|
||||
dt a:link:hover {
|
||||
color: silver;
|
||||
text-decoration: underline;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
# -*- mode: makefile; -*-
|
||||
|
||||
all: Session
|
||||
|
||||
Session_SRCS := $(wildcard src/*.C src/*.fl)
|
||||
# Session_SRCS += util/debug.C util/Thread.C util/file.C
|
||||
|
||||
Session_SRCS:=$(Session_SRCS:.fl=.C)
|
||||
Session_SRCS:=$(sort $(Session_SRCS))
|
||||
Session_OBJS:=$(Session_SRCS:.C=.o)
|
||||
|
||||
Session_LIBS := $(LIBLO_LIBS)
|
||||
|
||||
src/nsmd: src/nsmd.o nonlib/libnonlib.a
|
||||
@ echo -n Linking session handler.
|
||||
@ $(CXX) $(CXXFLAGS) $(Session_LIBS) src/nsmd.o -o $@ -Lnonlib -lnonlib -ldl && echo $(DONE)
|
||||
|
||||
src/session-manager: src/session-manager.o nonlib/libnonlib.a
|
||||
@ echo -n Linking session handler.
|
||||
@ $(CXX) $(CXXFLAGS) $(FLTK_LIBS) $(Session_LIBS) src/session-manager.o -o $@ -Lnonlib -lnonlib -ldl && echo $(DONE)
|
||||
|
||||
src/send_osc: src/send_osc.o nonlib/libnonlib.a
|
||||
@ $(CXX) $(CXXFLAGS) $(Session_LIBS) src/send_osc.o -o $@ -Lnonlib -lnonlib -ddl && echo $(DONE)
|
||||
|
||||
Session: src/send_osc src/nsmd src/session-manager
|
||||
|
||||
Session_clean:
|
||||
rm -f $(Session_OBJS) src/nsmd
|
|
@ -0,0 +1 @@
|
|||
../nonlib/
|
|
@ -0,0 +1 @@
|
|||
../scripts
|
|
@ -0,0 +1 @@
|
|||
../../FL
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,112 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2010 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 <lo/lo.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <OSC/Endpoint.H>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* helper macros for defining OSC handlers */
|
||||
#define OSC_NAME( name ) osc_ ## name
|
||||
// #define OSCDMSG() DMESSAGE( "Got OSC message: %s", path );
|
||||
#define OSC_HANDLER( name ) static int OSC_NAME( name ) ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
|
||||
|
||||
static bool got_response = false;
|
||||
|
||||
/************************/
|
||||
/* OSC Message Handlers */
|
||||
/************************/
|
||||
|
||||
OSC_HANDLER( reply )
|
||||
{
|
||||
// OSCDMSG();
|
||||
|
||||
printf( "Reply: " );
|
||||
|
||||
for ( int i = 0; i < argc; ++i )
|
||||
{
|
||||
switch ( types[i] )
|
||||
{
|
||||
case 's':
|
||||
printf( "\"%s\" ", &argv[i]->s );
|
||||
break;
|
||||
case 'f':
|
||||
printf( "%f ", argv[i]->f );
|
||||
break;
|
||||
case 'i':
|
||||
printf( "%i ", argv[i]->i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf( "\n" );
|
||||
|
||||
got_response = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
OSC::Endpoint s;
|
||||
|
||||
s.init( NULL );
|
||||
|
||||
s.add_method( NULL, NULL, OSC_NAME( reply ), 0, "");
|
||||
|
||||
int r;
|
||||
|
||||
std::list<OSC::OSC_Value> args;
|
||||
|
||||
for ( int i = 3; i < argc; ++i )
|
||||
{
|
||||
const char *s = argv[i];
|
||||
|
||||
if ( strspn( s, "+-0123456789" ) == strlen( s ) )
|
||||
{
|
||||
args.push_back( OSC::OSC_Int( atol( s ) ) );
|
||||
}
|
||||
else if ( strspn( s, ".+-0123456789" ) == strlen( s ) )
|
||||
args.push_back( OSC::OSC_Float( atof( s ) ) );
|
||||
else
|
||||
{
|
||||
args.push_back( OSC::OSC_String( s ) );
|
||||
}
|
||||
}
|
||||
|
||||
lo_address t = lo_address_new_from_url( argv[1] );
|
||||
|
||||
fprintf( stderr, "Sending to %s\n", argv[1] );
|
||||
|
||||
s.send( t, argv[2], args );
|
||||
|
||||
printf( "Waiting for reply...\n" );
|
||||
|
||||
while ( ! got_response )
|
||||
s.wait( 1000 * 30 );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,763 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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 "OSC/Endpoint.H"
|
||||
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Double_Window.H>
|
||||
#include <FL/Fl_Widget.H>
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
#include <FL/Fl_Pack.H>
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include <FL/Fl_Progress.H>
|
||||
#include "debug.h"
|
||||
#include <FL/Fl_Browser.H>
|
||||
#include <FL/Fl_Select_Browser.H>
|
||||
#include <FL/Fl_Tile.H>
|
||||
|
||||
#include "FL/Fl_Packscroller.H"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#define APP_NAME "Non Session Manager"
|
||||
|
||||
static lo_address nsm_addr = NULL;
|
||||
static time_t last_ping_response;
|
||||
|
||||
static OSC::Endpoint *osc_endpoint;
|
||||
|
||||
class NSM_Client : public Fl_Group
|
||||
{
|
||||
char *_client_id;
|
||||
|
||||
// Fl_Box *client_name;
|
||||
Fl_Progress *_progress;
|
||||
Fl_Light_Button *_dirty;
|
||||
Fl_Button *_remove_button;
|
||||
Fl_Button *_restart_button;
|
||||
|
||||
public:
|
||||
|
||||
void
|
||||
name ( const char *v )
|
||||
{
|
||||
label( strdup( v ) );
|
||||
}
|
||||
|
||||
void
|
||||
client_id ( const char *v )
|
||||
{
|
||||
if ( _client_id )
|
||||
free( _client_id );
|
||||
|
||||
_client_id = strdup( v );
|
||||
}
|
||||
|
||||
void
|
||||
progress ( float f )
|
||||
{
|
||||
_progress->value( f );
|
||||
_progress->redraw();
|
||||
}
|
||||
|
||||
void
|
||||
dirty ( bool b )
|
||||
{
|
||||
_dirty->value( b );
|
||||
_dirty->redraw();
|
||||
}
|
||||
|
||||
void
|
||||
stopped ( bool b )
|
||||
{
|
||||
if ( b )
|
||||
{
|
||||
_remove_button->show();
|
||||
_restart_button->show();
|
||||
color( FL_RED );
|
||||
redraw();
|
||||
}
|
||||
else
|
||||
{
|
||||
_restart_button->hide();
|
||||
_remove_button->hide();
|
||||
}
|
||||
|
||||
/* _restart_button->redraw(); */
|
||||
/* _remove_button->redraw(); */
|
||||
}
|
||||
|
||||
void
|
||||
pending_command ( const char *command )
|
||||
{
|
||||
char *cmd = strdup( command );
|
||||
|
||||
free( (void*)_progress->label() );
|
||||
|
||||
_progress->label( cmd );
|
||||
|
||||
stopped( 0 );
|
||||
|
||||
if ( ! strcmp( command, "ready" ) )
|
||||
{
|
||||
color( FL_GREEN );
|
||||
// _progress->value( 0.0f );
|
||||
}
|
||||
else if ( ! strcmp( command, "quit" ) ||
|
||||
! strcmp( command, "kill" ) ||
|
||||
! strcmp( command, "error" ) )
|
||||
{
|
||||
color( FL_RED );
|
||||
}
|
||||
else if ( ! strcmp( command, "stopped" ) )
|
||||
{
|
||||
stopped( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
color( FL_YELLOW );
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cb_button ( Fl_Widget *o, void * v )
|
||||
{
|
||||
((NSM_Client*)v)->cb_button( o );
|
||||
}
|
||||
|
||||
void
|
||||
cb_button ( Fl_Widget *o )
|
||||
{
|
||||
if ( o == _dirty )
|
||||
{
|
||||
MESSAGE( "Sending save.");
|
||||
osc_endpoint->send( nsm_addr, "/nsm/gui/client/save", _client_id );
|
||||
}
|
||||
if ( o == _remove_button )
|
||||
{
|
||||
MESSAGE( "Sending remove.");
|
||||
osc_endpoint->send( nsm_addr, "/nsm/gui/client/remove", _client_id );
|
||||
}
|
||||
else if ( o == _restart_button )
|
||||
{
|
||||
MESSAGE( "Sending resume" );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/gui/client/resume", _client_id );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
client_id ( void )
|
||||
{ return _client_id; }
|
||||
|
||||
NSM_Client ( int X, int Y, int W, int H, const char *L ) :
|
||||
Fl_Group( X, Y, W, H, L )
|
||||
{
|
||||
|
||||
_client_id = NULL;
|
||||
|
||||
align( FL_ALIGN_LEFT | FL_ALIGN_INSIDE );
|
||||
color( FL_RED );
|
||||
box( FL_UP_BOX );
|
||||
|
||||
{ Fl_Progress *o = _progress = new Fl_Progress( ( X + W ) - ( W / 4) - 20, Y + 5, ( W / 4 ), H - 10, NULL );
|
||||
o->label( strdup( "launch" ) );
|
||||
o->minimum( 0.0f );
|
||||
o->maximum( 1.0f );
|
||||
}
|
||||
{ Fl_Light_Button *o = _dirty = new Fl_Light_Button( _progress->x() - 30, Y + 7, 25, 25 );
|
||||
o->box( FL_UP_BOX );
|
||||
o->type(0);
|
||||
o->color();
|
||||
o->selection_color( FL_YELLOW );
|
||||
o->value( 0 );
|
||||
o->callback( cb_button, this );
|
||||
}
|
||||
{ Fl_Button *o = _remove_button = new Fl_Button( _progress->x() - 60, Y + 7, 25, 25 );
|
||||
o->box( FL_UP_BOX );
|
||||
o->type(0);
|
||||
o->color( FL_RED );
|
||||
o->value( 0 );
|
||||
o->label( "X" );
|
||||
o->tooltip( "Remove" );
|
||||
o->hide();
|
||||
o->callback( cb_button, this );
|
||||
}
|
||||
{ Fl_Button *o = _restart_button = new Fl_Button( _progress->x() - 90, Y + 7, 25, 25 );
|
||||
o->box( FL_UP_BOX );
|
||||
o->type(0);
|
||||
o->color( FL_GREEN );
|
||||
o->value( 0 );
|
||||
o->label( "@>" );
|
||||
o->tooltip( "Resume" );
|
||||
o->hide();
|
||||
o->callback( cb_button, this );
|
||||
}
|
||||
|
||||
end();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class NSM_Controller : public Fl_Group
|
||||
{
|
||||
public:
|
||||
|
||||
Fl_Pack *clients_pack;
|
||||
Fl_Pack *buttons_pack;
|
||||
Fl_Button *close_button;
|
||||
Fl_Button *abort_button;
|
||||
Fl_Button *save_button;
|
||||
Fl_Button *open_button;
|
||||
Fl_Button *new_button;
|
||||
Fl_Button *add_button;
|
||||
Fl_Button *duplicate_button;
|
||||
|
||||
Fl_Select_Browser *session_browser;
|
||||
|
||||
static void cb_handle ( Fl_Widget *w, void *v )
|
||||
{
|
||||
((NSM_Controller*)v)->cb_handle( w );
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
cb_handle ( Fl_Widget *w )
|
||||
{
|
||||
if ( w == abort_button )
|
||||
{
|
||||
if ( 0 == fl_choice( "Are you sure you want to abort this session? Unsaved changes will be lost.", "Abort", "Cancel", NULL ) )
|
||||
{
|
||||
MESSAGE( "Sending abort." );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/abort" );
|
||||
}
|
||||
}
|
||||
if ( w == close_button )
|
||||
{
|
||||
MESSAGE( "Sending close." );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/close" );
|
||||
}
|
||||
else if ( w == save_button )
|
||||
{
|
||||
MESSAGE( "Sending save." );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/save" );
|
||||
}
|
||||
else if ( w == open_button )
|
||||
{
|
||||
const char *name = fl_input( "Open Session", NULL );
|
||||
|
||||
if ( ! name )
|
||||
return;
|
||||
|
||||
MESSAGE( "Sending open for: %s", name );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/open", name );
|
||||
}
|
||||
else if ( w == duplicate_button )
|
||||
{
|
||||
const char *name = fl_input( "New Session", NULL );
|
||||
|
||||
if ( ! name )
|
||||
return;
|
||||
|
||||
MESSAGE( "Sending duplicate for: %s", name );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/duplicate", name );
|
||||
}
|
||||
else if ( w == session_browser )
|
||||
{
|
||||
const char *name = session_browser->text( session_browser->value());
|
||||
|
||||
/* strip out formatting codes */
|
||||
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/open", index( name, ' ' ) + 1 );
|
||||
}
|
||||
else if ( w == new_button )
|
||||
{
|
||||
const char *name = fl_input( "New Session", NULL );
|
||||
|
||||
if ( !name )
|
||||
return;
|
||||
|
||||
MESSAGE( "Sending new for: %s", name );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/new", name );
|
||||
}
|
||||
else if ( w == add_button )
|
||||
{
|
||||
const char *name = fl_input( "Add Client" );
|
||||
|
||||
if ( !name )
|
||||
return;
|
||||
|
||||
MESSAGE( "Sending add for: %s", name );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/add", name );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ForwardSort( Fl_Browser *b ) {
|
||||
for ( int t=1; t<=b->size(); t++ ) {
|
||||
for ( int r=t+1; r<=b->size(); r++ ) {
|
||||
if ( strcmp(b->text(t), b->text(r)) > 0 ) {
|
||||
b->swap(t,r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sort_sessions ( void )
|
||||
{
|
||||
ForwardSort( session_browser );
|
||||
}
|
||||
|
||||
NSM_Client *
|
||||
client_by_id ( const char *id )
|
||||
{
|
||||
for ( int i = clients_pack->children(); i--; )
|
||||
{
|
||||
NSM_Client *c = (NSM_Client*)clients_pack->child( i );
|
||||
|
||||
if ( ! strcmp( c->client_id(), id ) )
|
||||
{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
session_name ( const char *name )
|
||||
{
|
||||
if ( clients_pack->label() )
|
||||
free( (char*)clients_pack->label() );
|
||||
|
||||
clients_pack->parent()->label( strdup( name ) );
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void
|
||||
client_stopped ( const char *client_id )
|
||||
{
|
||||
NSM_Client *c = client_by_id( client_id );
|
||||
|
||||
if ( c )
|
||||
{
|
||||
c->stopped( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
client_quit ( const char *client_id )
|
||||
{
|
||||
NSM_Client *c = client_by_id( client_id );
|
||||
|
||||
if ( c )
|
||||
{
|
||||
clients_pack->remove( c );
|
||||
delete c;
|
||||
}
|
||||
|
||||
if ( clients_pack->children() == 0 )
|
||||
{
|
||||
((Fl_Packscroller*)clients_pack->parent())->yposition( 0 );
|
||||
}
|
||||
|
||||
parent()->redraw();
|
||||
}
|
||||
|
||||
void
|
||||
client_new ( const char *client_id, const char *client_name )
|
||||
{
|
||||
|
||||
NSM_Client *c;
|
||||
|
||||
c = client_by_id( client_id );
|
||||
|
||||
if ( c )
|
||||
{
|
||||
c->name( client_name );
|
||||
return;
|
||||
}
|
||||
|
||||
c = new NSM_Client( 0, 0, w(), 40, NULL );
|
||||
|
||||
c->name( client_name );
|
||||
c->client_id( client_id );
|
||||
c->stopped( 0 );
|
||||
|
||||
clients_pack->add( c );
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void client_pending_command ( NSM_Client *c, const char *command )
|
||||
{
|
||||
if ( c )
|
||||
{
|
||||
if ( ! strcmp( command, "removed" ) )
|
||||
{
|
||||
clients_pack->remove( c );
|
||||
delete c;
|
||||
|
||||
parent()->redraw();
|
||||
}
|
||||
else
|
||||
c->pending_command( command );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void add_session_to_list ( const char *name )
|
||||
{
|
||||
char *s;
|
||||
asprintf( &s, "@S18@C3 %s", name );
|
||||
session_browser->add( s );
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
NSM_Controller ( int X, int Y, int W, int H, const char *L ) :
|
||||
Fl_Group( X, Y, W, H, L )
|
||||
{
|
||||
align( FL_ALIGN_RIGHT | FL_ALIGN_CENTER | FL_ALIGN_INSIDE );
|
||||
|
||||
{ Fl_Pack *o = buttons_pack = new Fl_Pack( X, Y, W, 30 );
|
||||
o->type( Fl_Pack::HORIZONTAL );
|
||||
o->box( FL_NO_BOX );
|
||||
{ Fl_Button *o = open_button = new Fl_Button( 0, 0, 80, 50, "Open" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
{ Fl_Button *o = close_button = new Fl_Button( 0, 0, 80, 50, "Close" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
{ Fl_Button *o = abort_button = new Fl_Button( 0, 0, 80, 50, "Abort" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->color( FL_RED );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
{ Fl_Button *o = save_button = new Fl_Button( 0, 0, 80, 50, "Save" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
{ Fl_Button *o = new_button = new Fl_Button( 0, 0, 80, 50, "New" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
{ Fl_Button *o = duplicate_button = new Fl_Button( 0, 0, 100, 50, "Duplicate" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
{ Fl_Button *o = add_button = new Fl_Button( 0, 0, 100, 100, "Add Client" );
|
||||
o->box( FL_UP_BOX );
|
||||
o->callback( cb_handle, (void*)this );
|
||||
}
|
||||
|
||||
o->end();
|
||||
add(o);
|
||||
}
|
||||
{ Fl_Tile *o = new Fl_Tile( X, Y + 50, W, H - 50 );
|
||||
{
|
||||
Fl_Select_Browser *o = session_browser = new Fl_Select_Browser( X, Y + 50, W / 3, H - 50 );
|
||||
o->callback( cb_handle, (void *)this );
|
||||
o->color( fl_darker( FL_GRAY ) );
|
||||
o->box( FL_ROUNDED_BOX );
|
||||
o->label( "Sessions" );
|
||||
}
|
||||
{
|
||||
Fl_Packscroller *o = new Fl_Packscroller( X + ( W / 3 ), Y + 50, ( W / 3 ) * 2, H - 50 );
|
||||
o->align( FL_ALIGN_TOP );
|
||||
o->labeltype( FL_SHADOW_LABEL );
|
||||
{
|
||||
Fl_Pack *o = clients_pack = new Fl_Pack( X + ( W / 3 ), Y + 50, ( W / 3 ) * 2, H - 50 );
|
||||
o->align( FL_ALIGN_TOP );
|
||||
o->type( Fl_Pack::VERTICAL );
|
||||
o->end();
|
||||
Fl_Group::current()->resizable( o );
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
|
||||
Fl_Group::current()->resizable( this );
|
||||
|
||||
end();
|
||||
|
||||
deactivate();
|
||||
}
|
||||
|
||||
int min_h ( void )
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
void
|
||||
ping ( void )
|
||||
{
|
||||
if ( nsm_addr )
|
||||
{
|
||||
osc_endpoint->send( nsm_addr, "/osc/ping" );
|
||||
}
|
||||
|
||||
if ( last_ping_response )
|
||||
{
|
||||
if ( time(NULL) - last_ping_response > 10 )
|
||||
{
|
||||
if ( active() )
|
||||
{
|
||||
deactivate();
|
||||
fl_alert( "Server is not responding..." );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int init_osc ( void )
|
||||
{
|
||||
osc_endpoint = new OSC::Endpoint();
|
||||
|
||||
if ( int r = osc_endpoint->init() )
|
||||
return r;
|
||||
|
||||
osc_endpoint->owner = this;
|
||||
|
||||
osc_endpoint->url();
|
||||
|
||||
osc_endpoint->add_method( "/error", "sis", osc_handler, osc_endpoint, "msg" );
|
||||
osc_endpoint->add_method( "/reply", "ss", osc_handler, osc_endpoint, "msg" );
|
||||
osc_endpoint->add_method( "/reply", "s", osc_handler, osc_endpoint, "" );
|
||||
|
||||
osc_endpoint->add_method( "/nsm/gui/announce", "s", osc_handler, osc_endpoint, "msg" );
|
||||
osc_endpoint->add_method( "/nsm/gui/session/session", "s", osc_handler, osc_endpoint, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/gui/session/name", "s", osc_handler, osc_endpoint, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/gui/client/new", "ss", osc_handler, osc_endpoint, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/gui/client/status", "ss", osc_handler, osc_endpoint, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/gui/client/switch", "ss", osc_handler, osc_endpoint, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/gui/client/progress", "sf", osc_handler, osc_endpoint, "path,display_name" );
|
||||
osc_endpoint->add_method( "/nsm/gui/client/dirty", "si", osc_handler, osc_endpoint, "path,display_name" );
|
||||
|
||||
osc_endpoint->start();
|
||||
}
|
||||
|
||||
|
||||
void announce ( const char *nsm_url )
|
||||
{
|
||||
nsm_addr = lo_address_new_from_url( nsm_url );
|
||||
|
||||
osc_endpoint->send( nsm_addr, "/nsm/gui/announce" );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static int osc_handler ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
OSC_DMSG();
|
||||
|
||||
NSM_Controller *controller = (NSM_Controller*)((OSC::Endpoint*)user_data)->owner;
|
||||
|
||||
Fl::lock();
|
||||
|
||||
if ( !strcmp( path, "/nsm/gui/session/session" ) )
|
||||
{
|
||||
controller->add_session_to_list( &argv[0]->s );
|
||||
controller->sort_sessions();
|
||||
}
|
||||
else if ( !strcmp( path, "/nsm/gui/announce" ) )
|
||||
{
|
||||
controller->activate();
|
||||
|
||||
if ( ! nsm_addr )
|
||||
nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ) );
|
||||
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/list" );
|
||||
}
|
||||
else if ( !strcmp( path, "/nsm/gui/session/name" ))
|
||||
{
|
||||
controller->session_name( &argv[0]->s );
|
||||
}
|
||||
else if (!strcmp( path, "/error" ))
|
||||
{
|
||||
int err = argv[1]->i;
|
||||
|
||||
if ( err != 0 )
|
||||
fl_alert( "ERROR: %s failed with: %s", &argv[0]->s, &argv[2]->s );
|
||||
}
|
||||
else if (!strcmp( path, "/reply" ))
|
||||
{
|
||||
if ( !strcmp( &argv[0]->s, "/nsm/server/list" ) )
|
||||
{
|
||||
controller->add_session_to_list( &argv[1]->s );
|
||||
controller->sort_sessions();
|
||||
}
|
||||
else if ( !strcmp( &argv[0]->s, "/osc/ping" ) )
|
||||
{
|
||||
last_ping_response = time( NULL );
|
||||
}
|
||||
else
|
||||
MESSAGE( "%s says %s", &argv[0]->s, &argv[1]->s);
|
||||
}
|
||||
|
||||
if ( !strncmp( path, "/nsm/gui/client/", strlen( "/nsm/gui/client/" ) ) )
|
||||
{
|
||||
if ( !strcmp( path, "/nsm/gui/client/new" ))
|
||||
{
|
||||
controller->client_new( &argv[0]->s, &argv[1]->s );
|
||||
}
|
||||
else
|
||||
{
|
||||
NSM_Client *c = controller->client_by_id( &argv[0]->s );
|
||||
|
||||
if ( c )
|
||||
{
|
||||
if ( !strcmp( path, "/nsm/gui/client/status" ))
|
||||
{
|
||||
controller->client_pending_command( c, &argv[1]->s );
|
||||
}
|
||||
else if ( !strcmp( path, "/nsm/gui/client/progress" ))
|
||||
{
|
||||
c->progress( argv[1]->f );
|
||||
}
|
||||
else if ( !strcmp( path, "/nsm/gui/client/dirty" ))
|
||||
{
|
||||
c->dirty( argv[1]->i );
|
||||
}
|
||||
else if ( !strcmp( path, "/nsm/gui/client/switch" ) )
|
||||
{
|
||||
c->client_id( &argv[1]->s );
|
||||
}
|
||||
}
|
||||
else
|
||||
MESSAGE( "Got message %s from unknown client", path );
|
||||
}
|
||||
}
|
||||
|
||||
Fl::unlock();
|
||||
Fl::awake();
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static NSM_Controller *controller;
|
||||
|
||||
void
|
||||
ping ( void *v )
|
||||
{
|
||||
controller->ping();
|
||||
Fl::repeat_timeout( 1.0, ping, NULL );
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
|
||||
Fl::get_system_colors();
|
||||
Fl::scheme( "plastic" );
|
||||
Fl::lock();
|
||||
|
||||
Fl_Double_Window *main_window;
|
||||
|
||||
{
|
||||
Fl_Double_Window *o = main_window = new Fl_Double_Window( 600, 800, APP_NAME );
|
||||
{
|
||||
main_window->xclass( APP_NAME );
|
||||
|
||||
Fl_Widget *o = controller = new NSM_Controller( 0, 0, main_window->w(), main_window->h(), NULL );
|
||||
Fl_Group::current()->resizable(o);
|
||||
}
|
||||
o->end();
|
||||
|
||||
o->size_range( main_window->w(), controller->min_h(), 0, 0 );
|
||||
|
||||
// o->callback( (Fl_Callback*)cb_main, main_window );
|
||||
o->show( argc, argv );
|
||||
// o->show();
|
||||
}
|
||||
|
||||
const char *nsm_url = getenv( "NSM_URL" );
|
||||
|
||||
if ( nsm_url )
|
||||
{
|
||||
MESSAGE( "Found NSM URL of \"%s\" in environment, attempting to connect.", nsm_url );
|
||||
|
||||
if ( ! controller->init_osc() )
|
||||
{
|
||||
controller->announce( nsm_url );
|
||||
}
|
||||
else
|
||||
FATAL( "Could not create OSC server" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( controller->init_osc() )
|
||||
FATAL( "Could not create OSC server" );
|
||||
|
||||
/* start a new daemon... */
|
||||
MESSAGE( "Starting daemon..." );
|
||||
|
||||
char *url = osc_endpoint->url();
|
||||
|
||||
if ( ! fork() )
|
||||
{
|
||||
char *args[] = { "nsmd", "--gui-url", url, NULL };
|
||||
|
||||
if ( -1 == execvp( "nsmd", args ) )
|
||||
{
|
||||
FATAL( "Error starting process: %s", strerror( errno ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fl::add_timeout( 1.0, ping, NULL );
|
||||
Fl::run();
|
||||
|
||||
if ( ! nsm_url )
|
||||
{
|
||||
MESSAGE( "Telling server to quit" );
|
||||
osc_endpoint->send( nsm_addr, "/nsm/server/quit" );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -27,5 +27,8 @@ require_command ar ar
|
|||
require_command makedepend makedepend
|
||||
require_package JACK 0.103.0 jack
|
||||
require_package sndfile 1.0.17 sndfile
|
||||
require_package liblo 0.23 liblo
|
||||
|
||||
test_version `version_of liblo` 0.26 || warn "Version $(version_of liblo) of liblo is slow to create servers. Consider upgrading to 0.26 or later"
|
||||
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ Timeline_SRCS:=$(Timeline_SRCS:.fl=.C)
|
|||
Timeline_SRCS:=$(sort $(Timeline_SRCS))
|
||||
Timeline_OBJS:=$(Timeline_SRCS:.C=.o)
|
||||
|
||||
Timeline_LIBS := $(FLTK_LIBS) $(JACK_LIBS) $(SNDFILE_LIBS)
|
||||
Timeline_LIBS := $(FLTK_LIBS) $(JACK_LIBS) $(SNDFILE_LIBS) $(LIBLO_LIBS)
|
||||
|
||||
src/timeline: $(Timeline_OBJS) FL/libfl_widgets.a nonlib/libnonlib.a
|
||||
@ echo -n Linking timeline...
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
Engine::Engine ( ) : _thread( "RT" )
|
||||
{
|
||||
_buffers_dropped = 0;
|
||||
|
||||
DMESSAGE( "Creating audio I/O engine" );
|
||||
}
|
||||
|
||||
Engine::~Engine ( )
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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 "const.h"
|
||||
#include "debug.h"
|
||||
#include "Timeline.H"
|
||||
#include "TLE.H"
|
||||
#include "NSM.H"
|
||||
#include "Project.H"
|
||||
|
||||
#define OSC_INTERVAL 0.2f
|
||||
|
||||
extern char *instance_name;
|
||||
extern Timeline *timeline;
|
||||
|
||||
extern NSM_Client *nsm;
|
||||
|
||||
NSM_Client::NSM_Client ( )
|
||||
{
|
||||
}
|
||||
|
||||
int command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg );
|
||||
int command_save ( char **out_msg );
|
||||
|
||||
int
|
||||
NSM_Client::command_save ( char **out_msg )
|
||||
{
|
||||
if ( timeline->command_save() )
|
||||
return ERR_OK;
|
||||
else
|
||||
{
|
||||
*out_msg = strdup( "Failed to save for unknown reason");
|
||||
return ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
NSM_Client::command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg )
|
||||
{
|
||||
if ( instance_name )
|
||||
free( instance_name );
|
||||
|
||||
instance_name = strdup( client_id );
|
||||
|
||||
if ( Project::validate( name ) )
|
||||
{
|
||||
if ( timeline->command_load( name, display_name ) )
|
||||
return ERR_OK;
|
||||
else
|
||||
{
|
||||
*out_msg = strdup( "Failed to load for unknown reason" );
|
||||
return ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( timeline->command_new( name, display_name ) )
|
||||
return ERR_OK;
|
||||
else
|
||||
{
|
||||
*out_msg = strdup( "Failed to load for unknown reason" );
|
||||
return ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2012 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
|
||||
|
||||
#include "NSM/Client.H"
|
||||
|
||||
class NSM_Client : public NSM::Client
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
NSM_Client ( );
|
||||
~NSM_Client ( ) { };
|
||||
|
||||
protected:
|
||||
|
||||
int command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg );
|
||||
int command_save ( char **out_msg );
|
||||
};
|
|
@ -44,10 +44,15 @@
|
|||
#include "file.h"
|
||||
#include "Block_Timer.H"
|
||||
|
||||
#include "Transport.H"
|
||||
|
||||
extern Transport *transport;
|
||||
extern TLE *tle;
|
||||
|
||||
const int PROJECT_VERSION = 1;
|
||||
|
||||
extern char *instance_name;
|
||||
|
||||
|
||||
|
||||
const char *Project::_errstr[] =
|
||||
|
@ -200,6 +205,9 @@ Project::close ( void )
|
|||
|
||||
release_lock( &_lockfd, ".lock" );
|
||||
|
||||
delete engine;
|
||||
engine = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -233,6 +241,25 @@ Project::validate ( const char *name )
|
|||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
Project::make_engine ( void )
|
||||
{
|
||||
if ( engine )
|
||||
FATAL( "Engine should be null!" );
|
||||
|
||||
engine = new Engine;
|
||||
|
||||
if ( ! engine->init( instance_name, JACK::Client::SLOW_SYNC | JACK::Client::TIMEBASE_MASTER ))
|
||||
FATAL( "Could not connect to JACK!" );
|
||||
|
||||
timeline->sample_rate( engine->sample_rate() );
|
||||
|
||||
/* always start stopped (please imagine for me a realistic
|
||||
* scenario requiring otherwise */
|
||||
transport->stop();
|
||||
}
|
||||
|
||||
|
||||
/** Try to open project /name/. Returns 0 if sucsessful, an error code
|
||||
* otherwise */
|
||||
int
|
||||
|
@ -262,13 +289,19 @@ Project::open ( const char *name )
|
|||
if ( version != PROJECT_VERSION )
|
||||
return E_VERSION;
|
||||
|
||||
/* normally, engine will be NULL after a close or on an initial open,
|
||||
but 'new' will have already created it to get the sample rate. */
|
||||
if ( ! engine )
|
||||
make_engine();
|
||||
|
||||
{
|
||||
Block_Timer timer( "Replayed journal" );
|
||||
if ( ! Loggable::open( "history" ) )
|
||||
return E_INVALID;
|
||||
}
|
||||
|
||||
timeline->sample_rate( rate );
|
||||
/* /\* really a good idea? *\/ */
|
||||
/* timeline->sample_rate( rate ); */
|
||||
|
||||
if ( creation_date )
|
||||
{
|
||||
|
@ -319,6 +352,9 @@ Project::create ( const char *name, const char *template_name )
|
|||
mkdir( "sources", 0777 );
|
||||
creat( "history", 0666 );
|
||||
|
||||
if ( ! engine )
|
||||
make_engine();
|
||||
|
||||
/* TODO: copy template */
|
||||
|
||||
write_info();
|
||||
|
|
|
@ -33,9 +33,9 @@ class Project
|
|||
|
||||
static bool write_info ( void );
|
||||
static bool read_info ( int *version, nframes_t *sample_rate, char **creation_date, char **created_by );
|
||||
static void set_name ( const char *name );
|
||||
static const char *_errstr[];
|
||||
|
||||
static void make_engine ( void );
|
||||
public:
|
||||
|
||||
enum
|
||||
|
@ -47,6 +47,8 @@ public:
|
|||
E_VERSION = -5
|
||||
};
|
||||
|
||||
static void set_name ( const char *name );
|
||||
|
||||
static const char *errstr ( int n ) { return _errstr[ ( 0 - n ) - 1 ]; }
|
||||
|
||||
static const char *name ( void ) { return Project::_name; }
|
||||
|
|
|
@ -67,43 +67,44 @@ decl {\#include "FL/About_Dialog.H"} {}
|
|||
decl {extern char project_display_name[256];} {global
|
||||
}
|
||||
|
||||
decl {\#include "NSM.H"} {}
|
||||
|
||||
decl {extern NSM_Client *nsm;} {global
|
||||
}
|
||||
|
||||
decl {extern char *user_config_dir;} {global
|
||||
}
|
||||
|
||||
class TLE {open
|
||||
} {
|
||||
decl {Fl_Color system_colors[3];} {}
|
||||
Function {save_options()} {open
|
||||
} {
|
||||
code {
|
||||
|
||||
const char options_filename[] = "options";
|
||||
// const char state_filename[] = "state";
|
||||
|
||||
// save options
|
||||
|
||||
char *path;
|
||||
asprintf( &path, "%s/%s", user_config_dir, options_filename );
|
||||
((Fl_Menu_Settings*)menubar)->dump( menubar->find_item( "&Options" ), path );
|
||||
free( path );
|
||||
} {}
|
||||
}
|
||||
Function {save()} {open
|
||||
} {
|
||||
code {const char options_filename[] = "options";
|
||||
// const char state_filename[] = "state";
|
||||
|
||||
// save options
|
||||
|
||||
char *path;
|
||||
asprintf( &path, "%s/%s", user_config_dir, options_filename );
|
||||
((Fl_Menu_Settings*)menubar)->dump( menubar->find_item( "&Options" ), path );
|
||||
free( path );} {}
|
||||
code {
|
||||
timeline->command_save();} {}
|
||||
}
|
||||
Function {quit()} {} {
|
||||
code {Project::close();
|
||||
|
||||
save();
|
||||
|
||||
while ( Fl::first_window() ) Fl::first_window()->hide();} {}
|
||||
code {
|
||||
timeline->command_quit();} {}
|
||||
}
|
||||
Function {open( const char *name )} {} {
|
||||
code {if ( ! name )
|
||||
return;
|
||||
|
||||
int r = Project::open( name );
|
||||
|
||||
if ( r < 0 )
|
||||
{
|
||||
const char *s = Project::errstr( r );
|
||||
|
||||
fl_alert( "Could not open project \\"%s\\":\\n\\n\\t%s", name, s );
|
||||
}} {}
|
||||
code {
|
||||
timeline->command_load( name, NULL );} {}
|
||||
}
|
||||
Function {save_timeline_settings()} {open
|
||||
} {
|
||||
|
@ -219,7 +220,7 @@ Loggable::progress_callback( &TLE::progress_cb, this );} {}
|
|||
Fl_Window main_window {
|
||||
label {Non DAW : Timeline}
|
||||
callback {if ( Fl::event_key() != FL_Escape )
|
||||
o->hide();} open
|
||||
timeline->command_quit();} open
|
||||
private xywh {705 125 1025 770} type Double resizable xclass Non_DAW visible
|
||||
} {
|
||||
Fl_Menu_Bar menubar {open
|
||||
|
@ -808,7 +809,7 @@ p->label( s );} {}
|
|||
|
||||
update_progress( capture_buffer_progress, cbp, timeline->total_input_buffer_percent() );
|
||||
update_progress( playback_buffer_progress, pbp, timeline->total_output_buffer_percent() );
|
||||
update_progress( cpu_load_progress, clp, engine->cpu_load() );
|
||||
update_progress( cpu_load_progress, clp, engine ? engine->cpu_load() : 0 );
|
||||
|
||||
update_progress( disk_usage_progress, dup, percent_used( "." ) );
|
||||
|
||||
|
@ -820,15 +821,22 @@ if ( timeline->total_playback_xruns() )
|
|||
|
||||
static char stats[100];
|
||||
|
||||
if ( engine && ! engine->zombified() )
|
||||
{
|
||||
snprintf( stats, sizeof( stats ), "latency: %.1fms, xruns: %d",
|
||||
engine->frames_to_milliseconds( timeline->total_output_latency() ),
|
||||
engine->xruns() );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( stats, sizeof( stats ), "%s", "DISCONNECTED" );
|
||||
}
|
||||
|
||||
stats_box->label( stats );
|
||||
|
||||
static bool zombie = false;
|
||||
|
||||
if ( engine->zombified() && ! zombie )
|
||||
if ( engine && engine->zombified() && ! zombie )
|
||||
{
|
||||
zombie = true;
|
||||
fl_alert( "Disconnected from JACK!" );
|
||||
|
@ -840,6 +848,13 @@ sm_blinker->value( timeline->session_manager_name() != NULL );
|
|||
sm_blinker->tooltip( timeline->session_manager_name() );
|
||||
selected_blinker->value( timeline->nselected() );
|
||||
seek_blinker->value( timeline->seek_pending() );
|
||||
|
||||
if ( timeline->session_manager_name() != NULL )
|
||||
{
|
||||
find_item( menubar, "&Project/&New" )->deactivate();
|
||||
find_item( menubar, "&Project/&Open" )->deactivate();
|
||||
}
|
||||
|
||||
project_name->redraw();} {}
|
||||
}
|
||||
Function {update_cb( void *v )} {open private return_type {static void}
|
||||
|
@ -877,6 +892,7 @@ else if ( 0 == p )
|
|||
|
||||
static char pat[10];
|
||||
|
||||
nsm->progress( p / 100.0f );
|
||||
update_progress( progress, pat, p );
|
||||
|
||||
progress->redraw();
|
||||
|
|
|
@ -46,6 +46,14 @@
|
|||
#include "const.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* these headers are just for the NSM support */
|
||||
#include "Project.H"
|
||||
#include "TLE.H"
|
||||
/* */
|
||||
|
||||
#include "NSM.H"
|
||||
extern NSM_Client *nsm;
|
||||
|
||||
#ifdef USE_WIDGET_FOR_TIMELINE
|
||||
#define BASE Fl_Group
|
||||
#define redraw_overlay()
|
||||
|
@ -74,6 +82,9 @@ bool Timeline::center_playhead = true;
|
|||
|
||||
const float UPDATE_FREQ = 0.02f;
|
||||
|
||||
extern const char *instance_name;
|
||||
extern TLE *tle;
|
||||
|
||||
|
||||
|
||||
/** return the combined height of all visible children of (veritcal)
|
||||
|
@ -1472,3 +1483,66 @@ Timeline::remove_track ( Track *track )
|
|||
/* FIXME: why is this necessary? doesn't the above add do DAMAGE_CHILD? */
|
||||
redraw();
|
||||
}
|
||||
|
||||
/************/
|
||||
/* Commands */
|
||||
/************/
|
||||
|
||||
void
|
||||
Timeline::command_quit ( )
|
||||
{
|
||||
Project::close();
|
||||
|
||||
command_save();
|
||||
|
||||
while ( Fl::first_window() ) Fl::first_window()->hide();
|
||||
}
|
||||
|
||||
bool
|
||||
Timeline::command_load ( const char *name, const char *display_name )
|
||||
{
|
||||
if ( ! name )
|
||||
return false;
|
||||
|
||||
int r = Project::open( name );
|
||||
|
||||
if ( r < 0 )
|
||||
{
|
||||
const char *s = Project::errstr( r );
|
||||
|
||||
fl_alert( "Could not open project \"%s\":\n\n\t%s", name, s );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Project::set_name ( display_name ? display_name : name );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Timeline::command_save ( )
|
||||
{
|
||||
tle->save_options();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Timeline::command_new ( const char *name, const char *display_name )
|
||||
{
|
||||
return Project::create( name, NULL );
|
||||
|
||||
Project::set_name ( display_name );
|
||||
|
||||
/* FIXME: there's other stuff that needs to be done here! */
|
||||
/* tle->update_menu(); */
|
||||
|
||||
/* tle->main_window->redraw(); */
|
||||
}
|
||||
|
||||
const char *
|
||||
Timeline::session_manager_name ( void )
|
||||
{
|
||||
return nsm->session_manager_name();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <assert.h>
|
||||
#include <list>
|
||||
|
||||
#include "OSC/Endpoint.H"
|
||||
|
||||
class Fl_Scroll;
|
||||
class Fl_Pack;
|
||||
class Fl_Scrollbar;
|
||||
|
@ -149,7 +151,7 @@ public:
|
|||
|
||||
void update_tempomap ( void );
|
||||
|
||||
const char *session_manager_name ( void ) { return NULL; }
|
||||
const char *session_manager_name ( void );
|
||||
|
||||
nframes_t fpp ( void ) const { return 1 << _fpp; }
|
||||
void range ( nframes_t start, nframes_t length );
|
||||
|
@ -221,6 +223,11 @@ public:
|
|||
void wait_for_buffers ( void );
|
||||
bool seek_pending ( void );
|
||||
|
||||
bool command_load ( const char *name, const char *display_name );
|
||||
bool command_new ( const char *name, const char *display_name );
|
||||
bool command_save ( void );
|
||||
void command_quit ( void );
|
||||
|
||||
private:
|
||||
|
||||
static void snapshot ( void *v ) { ((Timeline*)v)->snapshot(); }
|
||||
|
|
|
@ -23,10 +23,6 @@
|
|||
|
||||
#include "Engine/Engine.H"
|
||||
|
||||
// Transport transport;
|
||||
|
||||
#define client engine->jack_client()
|
||||
|
||||
|
||||
|
||||
Transport::Transport ( int X, int Y, int W, int H, const char *L )
|
||||
|
@ -36,6 +32,18 @@ Transport::Transport ( int X, int Y, int W, int H, const char *L )
|
|||
rolling = false;
|
||||
_stop_disables_record = true;
|
||||
|
||||
bar = 0;
|
||||
beat = 0;
|
||||
tick = 0;
|
||||
beats_per_minute = 120;
|
||||
ticks_per_beat = 1920;
|
||||
beat_type = 4;
|
||||
beats_per_bar = 4;
|
||||
next_time = 0;
|
||||
frame_time =0;
|
||||
frame_rate = 48000;
|
||||
frame = 0;
|
||||
|
||||
const int bw = W / 3;
|
||||
|
||||
type( HORIZONTAL );
|
||||
|
@ -156,9 +164,10 @@ Transport::handle ( int m )
|
|||
void
|
||||
Transport::poll ( void )
|
||||
{
|
||||
|
||||
jack_transport_state_t ts;
|
||||
|
||||
ts = jack_transport_query( client, this );
|
||||
ts = engine->transport_query( this );
|
||||
|
||||
rolling = ts == JackTransportRolling;
|
||||
}
|
||||
|
@ -166,9 +175,12 @@ Transport::poll ( void )
|
|||
void
|
||||
Transport::locate ( nframes_t frame )
|
||||
{
|
||||
if ( ! engine )
|
||||
return;
|
||||
|
||||
if ( ! recording )
|
||||
// don't allow seeking while record is in progress
|
||||
jack_transport_locate( client, frame );
|
||||
engine->transport_locate( frame );
|
||||
}
|
||||
|
||||
|
||||
|
@ -182,7 +194,8 @@ Transport::start ( void )
|
|||
update_record_state();
|
||||
}
|
||||
|
||||
jack_transport_start( client );
|
||||
if ( engine )
|
||||
engine->transport_start();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -197,7 +210,8 @@ Transport::stop ( void )
|
|||
update_record_state();
|
||||
}
|
||||
|
||||
jack_transport_stop( client );
|
||||
if ( engine )
|
||||
engine->transport_stop();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include "Track.H"
|
||||
|
||||
#include "TLE.H"
|
||||
|
||||
#include "Timeline.H"
|
||||
#include "../FL/Boxtypes.H"
|
||||
|
||||
#include "Project.H"
|
||||
|
@ -48,10 +48,15 @@
|
|||
|
||||
#include "Thread.H"
|
||||
|
||||
#include "NSM.H"
|
||||
|
||||
Engine *engine;
|
||||
Timeline *timeline;
|
||||
Transport *transport;
|
||||
TLE *tle;
|
||||
NSM_Client *nsm;
|
||||
|
||||
char *instance_name = NULL;
|
||||
|
||||
/* TODO: put these in a header */
|
||||
#define USER_CONFIG_DIR ".non-daw/"
|
||||
|
@ -60,6 +65,8 @@ const char APP_NAME[] = "Non-DAW";
|
|||
const char APP_TITLE[] = "The Non-DAW";
|
||||
const char COPYRIGHT[] = "Copyright (C) 2008-2010 Jonathan Moore Liles";
|
||||
|
||||
#define OSC_INTERVAL 0.2f
|
||||
|
||||
#define PACKAGE "non"
|
||||
|
||||
|
||||
|
@ -96,6 +103,34 @@ shift ( char **argv, int *argc, int n )
|
|||
argc -= n;
|
||||
}
|
||||
|
||||
extern Timeline *timeline;
|
||||
|
||||
void
|
||||
check_osc ( void * v )
|
||||
{
|
||||
nsm->check();
|
||||
Fl::repeat_timeout( OSC_INTERVAL, check_osc, v );
|
||||
}
|
||||
|
||||
static int got_sigterm = 0;
|
||||
|
||||
void
|
||||
sigterm_handler ( int )
|
||||
{
|
||||
got_sigterm = 1;
|
||||
Fl::awake();
|
||||
}
|
||||
|
||||
void
|
||||
check_sigterm ( void * )
|
||||
{
|
||||
if ( got_sigterm )
|
||||
{
|
||||
MESSAGE( "Got SIGTERM, quitting..." );
|
||||
timeline->command_quit();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main ( int argc, char **argv )
|
||||
{
|
||||
|
@ -104,6 +139,8 @@ main ( int argc, char **argv )
|
|||
Thread thread( "UI" );
|
||||
thread.set();
|
||||
|
||||
signal( SIGTERM, sigterm_handler );
|
||||
|
||||
fl_register_images();
|
||||
|
||||
/* welcome to C++ */
|
||||
|
@ -129,32 +166,85 @@ main ( int argc, char **argv )
|
|||
|
||||
tle = new TLE;
|
||||
|
||||
MESSAGE( "Initializing JACK" );
|
||||
instance_name = strdup( APP_NAME );
|
||||
|
||||
/* we don't really need a pointer for this */
|
||||
engine = new Engine;
|
||||
// will be created on project new/open
|
||||
engine = NULL;
|
||||
|
||||
const char *jack_name;
|
||||
nsm = new NSM_Client;
|
||||
|
||||
if ( ! ( jack_name = engine->init( APP_NAME, JACK::Client::SLOW_SYNC | JACK::Client::TIMEBASE_MASTER ) ) )
|
||||
FATAL( "Could not connect to JACK!" );
|
||||
const char *osc_port = NULL;
|
||||
|
||||
timeline->sample_rate( engine->sample_rate() );
|
||||
{
|
||||
int r = argc - 1;
|
||||
int i = 1;
|
||||
for ( ; i < argc; ++i, --r )
|
||||
|
||||
if ( !strcmp( argv[i], "--osc-port" ) )
|
||||
{
|
||||
if ( r > 1 )
|
||||
{
|
||||
MESSAGE( "Using OSC port \"%s\"", argv[i+1] );
|
||||
osc_port = argv[i+1];
|
||||
--r;
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL( "Missing OSC port" );
|
||||
}
|
||||
}
|
||||
|
||||
/* always start stopped (please imagine for me a realistic
|
||||
* scenario requiring otherwise */
|
||||
transport->stop();
|
||||
|
||||
MESSAGE( "Starting GUI" );
|
||||
|
||||
tle->run();
|
||||
|
||||
if ( argc > 1 )
|
||||
tle->open( argv[ 1 ] );
|
||||
char *nsm_url = getenv( "NSM_URL" );
|
||||
|
||||
if ( nsm_url )
|
||||
{
|
||||
if ( ! nsm->init() );
|
||||
{
|
||||
nsm->announce( nsm_url, APP_NAME, ":progress:switch:", argv[0] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( r >= 1 )
|
||||
{
|
||||
MESSAGE( "Loading \"%s\"", argv[i] );
|
||||
|
||||
tle->open( argv[i] );
|
||||
|
||||
/* ) */
|
||||
/* { */
|
||||
/* fl_alert( "Error opening project specified on commandline" ); */
|
||||
/* } */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* poll so we can keep OSC handlers running in the GUI thread and avoid extra sync */
|
||||
Fl::add_timeout( OSC_INTERVAL, check_osc, NULL );
|
||||
|
||||
Fl::add_check( check_sigterm );
|
||||
|
||||
Fl::run();
|
||||
|
||||
if ( engine )
|
||||
{
|
||||
delete engine;
|
||||
engine = NULL;
|
||||
}
|
||||
|
||||
delete tle;
|
||||
tle = NULL;
|
||||
|
||||
delete nsm;
|
||||
nsm = NULL;
|
||||
|
||||
MESSAGE( "Your fun is over" );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue