Replace crappy LASH support with fancy new NSM support.
This commit is contained in:
parent
f1dc015b08
commit
3ac28ce0de
6
Makefile
6
Makefile
|
@ -57,9 +57,9 @@ CFLAGS+=-DINSTALL_PREFIX=\"$(prefix)\" \
|
|||
-DDOCUMENT_PATH=\"$(DOCUMENT_PATH)\" \
|
||||
-DPIXMAP_PATH=\"$(PIXMAP_PATH)\"
|
||||
|
||||
CXXFLAGS:=$(CFLAGS) $(CXXFLAGS) $(FLTK_CFLAGS) $(SIGCPP_CFLAGS) $(LASH_CFLAGS) $(XPM_CFLAGS)
|
||||
CXXFLAGS:=$(CFLAGS) $(CXXFLAGS) $(FLTK_CFLAGS) $(SIGCPP_CFLAGS) $(LIBLO_CFLAGS) $(XPM_CFLAGS)
|
||||
|
||||
LIBS:=$(FLTK_LIBS) $(JACK_LIBS) $(LASH_LIBS) $(SIGCPP_LIBS) $(XPM_LIBS)
|
||||
LIBS:=$(FLTK_LIBS) $(JACK_LIBS) $(SIGCPP_LIBS) $(LIBLO_LIBS) $(XPM_LIBS)
|
||||
|
||||
ifeq ($(JACK_MIDI_PROTO_API),yes)
|
||||
CXXFLAGS+=-DJACK_MIDI_PROTO_API
|
||||
|
@ -68,7 +68,7 @@ endif
|
|||
# uncomment this line to print each playback event to the console (not RT safe)
|
||||
# CXXFLAGS+= -DDEBUG_EVENTS
|
||||
|
||||
SRCS:=$(wildcard src/*.C src/gui/*.fl src/gui/*.C)
|
||||
SRCS:=$(wildcard src/*.C src/gui/*.fl src/gui/*.C src/NSM/*.C)
|
||||
|
||||
SRCS:=$(SRCS:.fl=.C)
|
||||
SRCS:=$(sort $(SRCS))
|
||||
|
|
|
@ -10,7 +10,6 @@ begin
|
|||
begin_options
|
||||
|
||||
ask "Installation prefix" prefix /usr/local
|
||||
ask "Use the LASH Audio Session Handler" USE_LASH yes
|
||||
ask "Build for debugging" USE_DEBUG no
|
||||
|
||||
begin_tests
|
||||
|
@ -20,9 +19,10 @@ require_command FLUID fluid
|
|||
require_package JACK 0.103.0 jack
|
||||
suggest_package XPM 2.0.0 xpm
|
||||
test_version `version_of jack` 0.105.0 || append "JACK_MIDI_PROTO_API=yes"
|
||||
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"
|
||||
|
||||
require_package sigcpp 2.0.0 sigc++-2.0
|
||||
|
||||
using LASH && require_package LASH 0.5.4 lash-1.0
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* 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 "NSM.H"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "non.H"
|
||||
#include "jack.H"
|
||||
#include "transport.H"
|
||||
|
||||
#include "gui/ui.H"
|
||||
|
||||
#define OSC_INTERVAL 0.2f
|
||||
|
||||
extern Transport transport;
|
||||
extern char *instance_name;
|
||||
|
||||
extern NSM_Client *nsm;
|
||||
|
||||
extern UI *ui;
|
||||
|
||||
NSM_Client::NSM_Client ( )
|
||||
{
|
||||
project_filename = 0;
|
||||
}
|
||||
|
||||
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 ( transport.rolling )
|
||||
{
|
||||
*out_msg = strdup( "Cannot save while transport is running." );
|
||||
return ERR_NOT_NOW;
|
||||
}
|
||||
else
|
||||
{
|
||||
save_song( nsm->project_filename );
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
NSM_Client::command_open ( const char *name, const char *display_name, const char *client_id, char **out_msg )
|
||||
{
|
||||
if ( transport.rolling )
|
||||
{
|
||||
*out_msg = strdup( "Cannot open while transport is running." );
|
||||
|
||||
return ERR_NOT_NOW;
|
||||
}
|
||||
|
||||
if ( song.dirty() )
|
||||
{
|
||||
*out_msg = strdup( "Song has unsaved changes!" );
|
||||
|
||||
return ERR_UNSAVED_CHANGES;
|
||||
}
|
||||
|
||||
if ( instance_name )
|
||||
free( instance_name );
|
||||
|
||||
instance_name = strdup( client_id );
|
||||
|
||||
if ( ! midi_is_active() )
|
||||
{
|
||||
setup_jack();
|
||||
}
|
||||
else
|
||||
{
|
||||
midi_all_sound_off();
|
||||
midi_shutdown();
|
||||
setup_jack();
|
||||
}
|
||||
|
||||
char *new_filename;
|
||||
|
||||
asprintf( &new_filename, "%s.non", name );
|
||||
|
||||
struct stat st;
|
||||
|
||||
if ( 0 == stat( new_filename, &st ) )
|
||||
{
|
||||
if ( ! load_song( new_filename ) )
|
||||
{
|
||||
*out_msg = strdup( "Could not open file" );
|
||||
return ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
save_song( new_filename );
|
||||
}
|
||||
|
||||
if ( nsm->project_filename )
|
||||
free( nsm->project_filename );
|
||||
|
||||
nsm->project_filename = new_filename;
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void
|
||||
NSM_Client::command_active ( bool b )
|
||||
{
|
||||
if ( b )
|
||||
{
|
||||
ui->sm_indicator->activate();
|
||||
ui->sm_indicator->tooltip( session_manager_name() );
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->sm_indicator->tooltip( NULL );
|
||||
ui->sm_indicator->deactivate();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2008 Jonathan Moore Liles */
|
||||
/* 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 */
|
||||
|
@ -19,22 +19,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "NSM/Client.H"
|
||||
|
||||
#ifdef HAVE_LASH
|
||||
#include <lash/lash.h>
|
||||
#endif
|
||||
|
||||
class Lash
|
||||
class NSM_Client : public NSM::Client
|
||||
{
|
||||
|
||||
#ifdef HAVE_LASH
|
||||
lash_client_t *_client;
|
||||
#endif
|
||||
char *project_filename;
|
||||
|
||||
public:
|
||||
Lash ( );
|
||||
bool init ( int *argc, char ***argv, const char *jack_name );
|
||||
void process ( void );
|
||||
|
||||
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 );
|
||||
int command_quit ( char **out_msg );
|
||||
|
||||
void command_active ( bool );
|
||||
};
|
|
@ -0,0 +1,298 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* 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>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
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 *application_name, const char *capabilities, const char *process_name )
|
||||
{
|
||||
MESSAGE( "Announcing to NSM" );
|
||||
|
||||
lo_address to = lo_address_new_from_url( nsm_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::broadcast ( lo_message msg )
|
||||
{
|
||||
if ( nsm_is_active )
|
||||
{
|
||||
lo_send_message_from( nsm_addr, _server, "/nsm/server/broadcast", msg );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::check ( int timeout )
|
||||
{
|
||||
if ( lo_server_wait( _server, timeout ) )
|
||||
while ( 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 ( const char *nsm_url )
|
||||
{
|
||||
this->nsm_url = nsm_url;
|
||||
|
||||
lo_address addr = lo_address_new_from_url( nsm_url );
|
||||
int proto = lo_address_get_protocol( addr );
|
||||
lo_address_free( addr );
|
||||
|
||||
_server = lo_server_new_with_proto( NULL, proto, 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 );
|
||||
lo_server_add_method( _server, "/nsm/client/session_is_loaded", "", &Client::osc_session_is_loaded, this );
|
||||
lo_server_add_method( _server, NULL, NULL, &Client::osc_broadcast, this );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Client::init_thread ( const char *nsm_url )
|
||||
{
|
||||
this->nsm_url = nsm_url;
|
||||
|
||||
lo_address addr = lo_address_new_from_url( nsm_url );
|
||||
int proto = lo_address_get_protocol( addr );
|
||||
lo_address_free( addr );
|
||||
|
||||
_st = lo_server_thread_new_with_proto( NULL, proto, 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 );
|
||||
lo_server_thread_add_method( _st, "/nsm/client/session_is_loaded", "", &Client::osc_session_is_loaded, this );
|
||||
lo_server_thread_add_method( _st, NULL, NULL, &Client::osc_broadcast, this );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************/
|
||||
/* OSC Message Handlers */
|
||||
/************************/
|
||||
|
||||
int
|
||||
Client::osc_broadcast ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
|
||||
{
|
||||
return ((NSM::Client*)user_data)->command_broadcast( path, msg );
|
||||
}
|
||||
|
||||
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,109 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* 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:
|
||||
|
||||
const char *nsm_url;
|
||||
|
||||
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 *appliction_name, const char *capabilities, const char *process_name );
|
||||
|
||||
void broadcast ( lo_message msg );
|
||||
|
||||
/* init without threading */
|
||||
int init ( const char *nsm_url );
|
||||
/* init with threading */
|
||||
int init_thread ( const char *nsm_url );
|
||||
|
||||
/* call this periodically to check for new messages */
|
||||
void check ( int timeout = 0 );
|
||||
|
||||
/* 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 ) { }
|
||||
|
||||
virtual void command_session_is_loaded ( void ) { }
|
||||
|
||||
/* invoked when an unrecognized message is received. Should return 0 if you handled it, -1 otherwise. */
|
||||
virtual int command_broadcast ( const char *, lo_message ) { return -1; }
|
||||
|
||||
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 );
|
||||
static int osc_broadcast ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data );
|
||||
|
||||
};
|
||||
};
|
|
@ -40,9 +40,12 @@ decl {\#include "event_edit.H"} {private local
|
|||
decl {\#include "../jack.H"} {private local
|
||||
}
|
||||
|
||||
decl {\#include "../lash.H"} {private local
|
||||
decl {\#include "../NSM.H"} {private local
|
||||
}
|
||||
|
||||
decl {extern NSM_Client *nsm;} {private local
|
||||
}
|
||||
|
||||
decl {extern const char *BUILD_ID;} {private local
|
||||
}
|
||||
|
||||
|
@ -73,8 +76,6 @@ Function {update_transport( void * )} {open return_type void
|
|||
|
||||
handle_midi_input();
|
||||
|
||||
lash.process();
|
||||
|
||||
ui->progress_group->do_callback();
|
||||
|
||||
ui->vmetro_widget->update();
|
||||
|
@ -103,6 +104,16 @@ if ( transport.rolling != oldstate )
|
|||
}
|
||||
}
|
||||
|
||||
if ( nsm && nsm->is_active() )
|
||||
{
|
||||
if ( ui->menu_new->active() )
|
||||
{
|
||||
ui->menu_new->deactivate();
|
||||
ui->menu_open->deactivate();
|
||||
ui->menu_save_as->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
// JUST A TEST
|
||||
if ( transport.rolling )
|
||||
{
|
||||
|
@ -137,6 +148,8 @@ Fl::scheme( "plastic" );
|
|||
|
||||
canvas_background_color = FL_GREEN;
|
||||
|
||||
playback_mode_menu = NULL;
|
||||
|
||||
main_window = make_main_window();
|
||||
seq_window = make_seq_window();
|
||||
|
||||
|
@ -235,7 +248,7 @@ if ( name )
|
|||
code0 {song.signal_dirty.connect( sigc::mem_fun( o, &Fl_Menu_Item::activate ) );}
|
||||
code1 {song.signal_clean.connect( sigc::mem_fun( o, &Fl_Menu_Item::deactivate ) );}
|
||||
}
|
||||
MenuItem {} {
|
||||
MenuItem menu_save_as {
|
||||
label {Save &As}
|
||||
callback {save_dialog( NULL );}
|
||||
xywh {0 0 40 25}
|
||||
|
@ -1032,6 +1045,10 @@ else
|
|||
xywh {0 0 40 24}
|
||||
}
|
||||
}
|
||||
Fl_Box sm_indicator {
|
||||
label SM selected
|
||||
xywh {805 6 50 17} box ROUNDED_BOX color 50 labelcolor 3 deactivate
|
||||
}
|
||||
}
|
||||
Fl_Group {} {open
|
||||
xywh {-1 772 869 33}
|
||||
|
|
37
src/jack.C
37
src/jack.C
|
@ -88,10 +88,19 @@ static port_t input[2]; /* contro
|
|||
|
||||
jack_nframes_t nframes; /* for compatibility with older jack */
|
||||
|
||||
bool
|
||||
midi_is_active ( void )
|
||||
{
|
||||
return client != NULL;
|
||||
}
|
||||
|
||||
/** get next recorded event, if any--runs in UI thread */
|
||||
bool
|
||||
midi_input_event ( int port, midievent *me )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
return NULL;
|
||||
|
||||
if ( jack_ringbuffer_read_space( input[ port ].ring_buf ) >= sizeof( midievent ) )
|
||||
{
|
||||
if ( jack_ringbuffer_read( input[ port ].ring_buf, (char *)me, sizeof( midievent ) ) )
|
||||
|
@ -100,11 +109,15 @@ midi_input_event ( int port, midievent *me )
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queue an event for output. /tick/ is relative to the current cycle! */
|
||||
void
|
||||
midi_output_event ( int port, const midievent *e )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
return;
|
||||
|
||||
event *fe = freelist.first();
|
||||
|
||||
if ( ! fe )
|
||||
|
@ -150,6 +163,9 @@ midi_output_event ( int port, const midievent *e )
|
|||
void
|
||||
midi_output_event ( int port, const midievent *e, tick_t duration )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
return;
|
||||
|
||||
if ( duration )
|
||||
{
|
||||
note_duration[ port ][ e->channel() ][ e->note() ] = (duration + e->timestamp()) * subticks_per_tick;
|
||||
|
@ -194,6 +210,9 @@ midi_write_event ( int port, const midievent *e )
|
|||
void
|
||||
midi_output_immediate_event ( int port, const midievent *e )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
return;
|
||||
|
||||
if ( jack_ringbuffer_write( output[ port ].ring_buf, (const char *)e, sizeof( midievent ) ) != sizeof( midievent ) )
|
||||
WARNING( "output ringbuffer overrun" );
|
||||
else
|
||||
|
@ -208,6 +227,9 @@ midi_output_immediate_event ( int port, const midievent *e )
|
|||
void
|
||||
midi_all_sound_off ( void )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
return;
|
||||
|
||||
MESSAGE( "stopping all sound" );
|
||||
|
||||
midievent e;
|
||||
|
@ -525,14 +547,11 @@ schedule:
|
|||
}
|
||||
|
||||
const char *
|
||||
midi_init ( void )
|
||||
midi_init ( const char *name )
|
||||
{
|
||||
MESSAGE( "Initializing Jack MIDI" );
|
||||
|
||||
/* if (( client = jack_client_new ( APP_NAME )) == 0 ) */
|
||||
/* return 0; */
|
||||
|
||||
if (( client = jack_client_open ( APP_NAME, (jack_options_t)0, NULL )) == 0 )
|
||||
if (( client = jack_client_open ( name, (jack_options_t)0, NULL )) == 0 )
|
||||
return NULL;
|
||||
|
||||
/* create output ports */
|
||||
|
@ -605,6 +624,10 @@ void
|
|||
midi_shutdown ( void )
|
||||
{
|
||||
// TODO: wait for all queued events to play.
|
||||
|
||||
jack_deactivate( client );
|
||||
if ( client )
|
||||
{
|
||||
jack_deactivate( client );
|
||||
jack_client_close( client );
|
||||
client = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,11 @@ enum { CONTROL, PERFORMANCE };
|
|||
class midievent;
|
||||
|
||||
bool midi_input_event ( int port, midievent *e );
|
||||
bool midi_is_active ( void );
|
||||
midievent * midi_input_event ( int port );
|
||||
void midi_output_event ( int port, const midievent *e );
|
||||
void midi_output_event ( int port, const midievent *e, tick_t duration );
|
||||
void midi_all_sound_off ( void );
|
||||
const char * midi_init ( void );
|
||||
const char * midi_init ( const char *name );
|
||||
void midi_shutdown ( void );
|
||||
void midi_output_immediate_event ( int port, const midievent *e );
|
||||
|
|
109
src/lash.C
109
src/lash.C
|
@ -1,109 +0,0 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2008 Jonathan Moore Liles */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify it */
|
||||
/* under the terms of the GNU General Public License as published by the */
|
||||
/* Free Software Foundation; either version 2 of the License, or (at your */
|
||||
/* option) any later version. */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, but WITHOUT */
|
||||
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
|
||||
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
|
||||
/* more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License along */
|
||||
/* with This program; see the file COPYING. If not,write to the Free Software */
|
||||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
/*******************************************************************************/
|
||||
|
||||
#include "lash.H"
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "non.H"
|
||||
|
||||
// TODO: produce Save_Project events...
|
||||
|
||||
#ifndef HAVE_LASH
|
||||
|
||||
Lash::Lash ( ) {}
|
||||
bool Lash::init ( int *argc, char ***argv, const char *jack_name ) { return true; }
|
||||
void Lash::process ( void ) {}
|
||||
|
||||
#else
|
||||
|
||||
Lash::Lash ( )
|
||||
{
|
||||
_client = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
Lash::init ( int *argc, char ***argv, const char *jack_name )
|
||||
{
|
||||
MESSAGE( "Initializing LASH" );
|
||||
|
||||
if ( ! ( _client = lash_init( lash_extract_args( argc, argv ), APP_NAME,
|
||||
LASH_Config_File, LASH_PROTOCOL( 2, 0 ) ) ) )
|
||||
return false;
|
||||
|
||||
/* register name */
|
||||
lash_jack_client_name( _client, jack_name );
|
||||
|
||||
lash_event_t *e = lash_event_new_with_type( LASH_Client_Name );
|
||||
lash_event_set_string( e, APP_TITLE );
|
||||
lash_send_event( _client, e );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** process any queued events */
|
||||
void
|
||||
Lash::process ( void )
|
||||
{
|
||||
lash_event_t *e;
|
||||
|
||||
char *name;
|
||||
|
||||
while ( ( e = lash_get_event( _client ) ) )
|
||||
{
|
||||
asprintf( &name, "%s/%s", lash_event_get_string( e ), "song.non" );
|
||||
|
||||
const int t = lash_event_get_type ( e );
|
||||
|
||||
switch ( t )
|
||||
{
|
||||
case LASH_Save_File:
|
||||
{
|
||||
MESSAGE( "LASH wants us to save \"%s\"", name );
|
||||
|
||||
save_song( name );
|
||||
|
||||
lash_send_event( _client, lash_event_new_with_type( LASH_Save_File ) );
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
case LASH_Restore_File:
|
||||
{
|
||||
MESSAGE( "LASH wants us to load \"%s\"", name );
|
||||
|
||||
if ( ! load_song( name ) )
|
||||
/* FIXME: should we tell lash that we couldn't load the song? */;
|
||||
|
||||
lash_send_event( _client, lash_event_new_with_type( LASH_Restore_File ) );
|
||||
|
||||
break;
|
||||
}
|
||||
case LASH_Quit:
|
||||
MESSAGE( "LASH wants us to quit" );
|
||||
quit();
|
||||
break;
|
||||
default:
|
||||
WARNING( "unhandled LASH event (%d)", t );
|
||||
}
|
||||
|
||||
lash_event_destroy( e );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
129
src/main.C
129
src/main.C
|
@ -25,7 +25,7 @@
|
|||
// #include "gui/input.H"
|
||||
#include "gui/ui.H"
|
||||
#include "jack.H"
|
||||
#include "lash.H"
|
||||
#include "NSM.H"
|
||||
|
||||
#include "pattern.H"
|
||||
#include "phrase.H"
|
||||
|
@ -41,6 +41,8 @@
|
|||
extern const char *BUILD_ID;
|
||||
extern const char *VERSION;
|
||||
|
||||
const double NSM_CHECK_INTERVAL = 0.25f;
|
||||
|
||||
Canvas *pattern_c, *phrase_c, *trigger_c;
|
||||
|
||||
sequence *playlist;
|
||||
|
@ -48,7 +50,9 @@ sequence *playlist;
|
|||
global_settings config;
|
||||
song_settings song;
|
||||
|
||||
Lash lash;
|
||||
NSM_Client *nsm;
|
||||
|
||||
char *instance_name;
|
||||
|
||||
/* default to pattern mode */
|
||||
|
||||
|
@ -78,9 +82,9 @@ quit ( void )
|
|||
}
|
||||
|
||||
void
|
||||
init_song ( void )
|
||||
clear_song ( void )
|
||||
{
|
||||
song.filename = NULL;
|
||||
// song.filename = NULL;
|
||||
|
||||
pattern_c->grid( NULL );
|
||||
phrase_c->grid( NULL );
|
||||
|
@ -93,6 +97,21 @@ init_song ( void )
|
|||
song.dirty( false );
|
||||
}
|
||||
|
||||
void
|
||||
init_song ( void )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
setup_jack();
|
||||
|
||||
if ( !( nsm && nsm->is_active() ) )
|
||||
song.filename = NULL;
|
||||
|
||||
clear_song();
|
||||
|
||||
if ( nsm && nsm->is_active() )
|
||||
save_song( song.filename );
|
||||
}
|
||||
|
||||
void
|
||||
handle_midi_input ( void )
|
||||
{
|
||||
|
@ -106,6 +125,9 @@ handle_midi_input ( void )
|
|||
bool
|
||||
load_song ( const char *name )
|
||||
{
|
||||
if ( ! midi_is_active() )
|
||||
setup_jack();
|
||||
|
||||
MESSAGE( "loading song \"%s\"", name );
|
||||
|
||||
Grid *pattern_grid = pattern_c->grid();
|
||||
|
@ -148,6 +170,52 @@ save_song ( const char *name )
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
setup_jack ( )
|
||||
{
|
||||
const char *jack_name;
|
||||
|
||||
jack_name = midi_init( instance_name );
|
||||
if ( ! jack_name )
|
||||
ASSERTION( "Could not initialize MIDI system! (is Jack running and with MIDI ports enabled?)" );
|
||||
|
||||
if ( ! transport.valid )
|
||||
{
|
||||
if ( transport.master )
|
||||
ASSERTION( "The version of JACK you are using does not appear to be capable of passing BBT positional information." );
|
||||
else
|
||||
ASSERTION( "Either the version of JACK you are using does pass BBT information, or the current timebase master does not provide it." );
|
||||
}
|
||||
}
|
||||
|
||||
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..." );
|
||||
quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
check_nsm ( void * v )
|
||||
{
|
||||
nsm->check();
|
||||
Fl::repeat_timeout( NSM_CHECK_INTERVAL, check_nsm, v );
|
||||
}
|
||||
|
||||
int
|
||||
main ( int argc, char **argv )
|
||||
{
|
||||
|
@ -177,26 +245,16 @@ main ( int argc, char **argv )
|
|||
phrase_c = new Canvas;
|
||||
trigger_c = new Canvas;
|
||||
|
||||
init_song();
|
||||
nsm = new NSM_Client;
|
||||
|
||||
song.filename = NULL;
|
||||
clear_song();
|
||||
|
||||
pattern::signal_create_destroy.connect( mem_fun( phrase_c, &Canvas::v_zoom_fit ) );
|
||||
pattern::signal_create_destroy.connect( mem_fun( song, &song_settings::set_dirty ) );
|
||||
phrase::signal_create_destroy.connect( mem_fun( song, &song_settings::set_dirty ) );
|
||||
|
||||
const char *jack_name;
|
||||
|
||||
jack_name = midi_init();
|
||||
if ( ! jack_name )
|
||||
ASSERTION( "Could not initialize MIDI system! (is Jack running and with MIDI ports enabled?)" );
|
||||
|
||||
if ( ! transport.valid )
|
||||
{
|
||||
if ( transport.master )
|
||||
ASSERTION( "The version of JACK you are using does not appear to be capable of passing BBT positional information." );
|
||||
else
|
||||
ASSERTION( "Either the version of JACK you are using does pass BBT information, or the current timebase master does not provide it." );
|
||||
}
|
||||
|
||||
//
|
||||
song.dirty( false );
|
||||
|
||||
init_colors();
|
||||
|
@ -210,18 +268,39 @@ main ( int argc, char **argv )
|
|||
#endif
|
||||
ui->main_window->show( argc, argv );
|
||||
|
||||
if ( ! lash.init( &argc, &argv, jack_name ) )
|
||||
WARNING( "error initializing LASH" );
|
||||
instance_name = strdup( APP_NAME );
|
||||
|
||||
if ( argc > 1 )
|
||||
const char *nsm_url = getenv( "NSM_URL" );
|
||||
|
||||
if ( nsm_url )
|
||||
{
|
||||
/* maybe a filename on the commandline */
|
||||
if ( ! load_song( argv[ 1 ] ) )
|
||||
ASSERTION( "Could not load song \"%s\" specified on command line", argv[ 1 ] );
|
||||
if ( ! nsm->init( nsm_url ) )
|
||||
{
|
||||
nsm->announce( APP_NAME, ":switch:dirty:", argv[0] );
|
||||
|
||||
song.signal_dirty.connect( sigc::mem_fun( nsm, &NSM_Client::is_dirty ) );
|
||||
song.signal_clean.connect( sigc::mem_fun( nsm, &NSM_Client::is_clean ) );
|
||||
|
||||
// poll so we can keep OSC handlers running in the GUI thread and avoid extra sync
|
||||
Fl::add_timeout( NSM_CHECK_INTERVAL, check_nsm, NULL );
|
||||
}
|
||||
else
|
||||
WARNING( "Error initializing NSM" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( argc > 1 )
|
||||
{
|
||||
/* maybe a filename on the commandline */
|
||||
if ( ! load_song( argv[ 1 ] ) )
|
||||
ASSERTION( "Could not load song \"%s\" specified on command line", argv[ 1 ] );
|
||||
}
|
||||
}
|
||||
|
||||
MESSAGE( "Initializing GUI" );
|
||||
|
||||
Fl::add_check( check_sigterm );
|
||||
|
||||
ui->run();
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue